How to substitute the extension for a cmake list of filenames
Asked Answered
M

3

7

I am trying to build a c++ library from some protobuf definitions using cmake. I added a custom command to compile the proto to c++, but I have some issues with the output part. I need to specify which are the expected output files after protoc does its job. For this I would like to replace in my PROTO_SOURCEfile glob, the proto extension with .pb.cc and .pb.h

I basically need something like this, but for cmake.

I am building this command manually because I don't have protobuf cmake support available.

project(messages)

set(PROTO_PATH "${CMAKE_CURRENT_SOURCE_DIR}/proto_definitions")
file(GLOB PROTO_FILES "${PROTO_PATH}/*.proto")
#set(PROTO_SOURCES ???) # This needs to contain '*.pb.cc' and '*.pb.h'

add_custom_command(COMMAND protoc --cpp_out=${CMAKE_CURRENT_SOURCE_DIR}/compiled_proto ${PROTO_FILES}
               WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
               DEPENDS ${PROTO_FILES}
               OUTPUT ${PROTO_SOURCES})

add_library(${PROJECT_NAME} STATIC ${PROTO_SOURCES})
Medellin answered 14/6, 2019 at 19:53 Comment(0)
M
6

Use string(REGEX REPLACE) function:

# Replace .proto -> .pb.cc
string(REGEX REPLACE "[.]proto$" ".pb.cc" OUTPUT_SOURCES ${PROTO_FILES})
# Replace .proto -> .pb.h
string(REGEX REPLACE "[.]proto$" ".pb.h" OUTPUT_HEADERS ${PROTO_FILES})

add_custom_command(COMMAND protoc <...>
   OUTPUT ${OUTPUT_SOURCES} ${OUTPUT_HEADERS})
Maegan answered 14/6, 2019 at 20:10 Comment(4)
yeah, this was the backup plan, I was thinking that there must be a shorter syntax similar to make for thisMedellin
PROTO_FILES is a list and when I print it with MESSAGE, I get a continuous string without any separation between the items in the list, so the REPLACE just replaces the last item extension. I ended up using a FOREACH with REPLACE for each item and appending to a new list the results. Now I have different issue but I will ask a separate question for it.Medellin
"PROTO_FILES is a list and when I print it with MESSAGE, I get a continuous string without any separation between the items in the list" - This is correct behavior. For view the separator in message() call, wrap the list into double quotes. But do not do that in other cases: double quotes makes the list to be a single value. The function string(REGEX REPLACE) correctly works with lists, when they are passed correctly (without double quotes).Maegan
I get the exact same behavior as @Medellin & @tjysdsg. This solution results in all the values in ${PROTO_FILES} being concatenated together before substitution is performed (and no quotes are used!). As such, the result is a single concatenated string with only the last file getting replaced. The documentation supports this as it says "All <input> arguments are concatenated before matching."Spratt
S
4

The accepted answer didn't work for me, as the first few elements in the list weren't properly substituted.

So I loop over the list, replace them one by one, and append each one to a list:

foreach (CXX_SRC ${LIST_OF_CXX})
    string(REGEX REPLACE "[.]cpp$" ".o" OBJ ${CXX_SRC})
    set(OBJ_FILES ${OBJ_FILES} ${OBJ})
endforeach ()
message(STATUS "Object files compiled from *.cpp: ${OBJ_FILES}")
Spontaneous answered 29/7, 2021 at 18:43 Comment(0)
K
0

The accepted answer concatenated all the list entries first, and just replaced the extension on the last one (as others noted). Using the TRANSFORM list operation worked to transform the list in-place and replace the file extensions on all entries

file(GLOB MY_LIST *.foo)

# transforms MY_LIST=["a.foo", "b.foo"] to MY_LIST=["a.bar", "b.bar"]
list(TRANSFORM MY_LIST REPLACE "[.]foo$" ".bar")
Kurtzman answered 16/5, 2023 at 14:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.