CMake Compiling Generated Files

2019-03-09 20:10发布

问题:

I have a list of files that get generated during the CMake build process. I want to compile these files using "add_library" afterward, but I won't know which files get generated until after they get generated. Is there anyway to build this into a CMake script?

回答1:

Well, I think it is possible, so I'll share what I've done. My problem was that I had to compile several CORBA idls to use as part of a project's source and I didn't want to manually list every file. I thought it would be better to find the files. So I did it like this:

file(GLOB IDLS "idls/*.idl")
set(ACE_ROOT ${CMAKE_FIND_ROOT_PATH}/ace/ACE-${ACE_VERSION})
foreach(GENERATE_IDL ${IDLS})
   get_filename_component(IDLNAME ${GENERATE_IDL} NAME_WE)
   set(OUT_NAME ${CMAKE_CURRENT_SOURCE_DIR}/idls_out/${IDLNAME})

   list(APPEND IDL_COMPILED_FILES ${OUT_NAME}C.h ${OUT_NAME}C.cpp ${OUT_NAME}S.h ${OUT_NAME}S.cpp)

   add_custom_command(OUTPUT ${OUT_NAME}C.h ${OUT_NAME}C.cpp ${OUT_NAME}S.h ${OUT_NAME}S.cpp
                      COMMAND ${ACE_ROOT}/bin/tao_idl -g ${ACE_ROOT}/bin/ace_gperf -Sci -Ssi -Wb,export_macro=TAO_Export -Wb,export_include=${ACE_ROOT}/include/tao/TAO_Export.h -Wb,pre_include=${ACE_ROOT}/include/ace/pre.h -Wb,post_include=${ACE_ROOT}/include/ace/post.h -I${ACE_ROOT}/include/tao -I${CMAKE_CURRENT_SOURCE_DIR} ${GENERATE_IDL} -o ${CMAKE_CURRENT_SOURCE_DIR}/idls_out/
                      COMMENT "Compiling ${GENERATE_IDL}")
endforeach(GENERATE_IDL)

set_source_files_properties(${IDL_COMPILED_FILES}
                            PROPERTIES GENERATED TRUE)

set(TARGET_NAME ${PROJECT_NAME}${DEBUG_SUFFIX})

add_executable(
   ${TARGET_NAME}
   ${SOURCE} 
   ${IDL_COMPILED_FILES}
)

The GENERATED properties is useful in case one of my idl compilation outputs (*C.cpp, *C.h, *S.cpp and *S.h) is not created, so that the build command doesn't complain that the file doesn't exist.



回答2:

Well, it is possible to do so with two CMake scripts using CMake's ExternalProject feature.

The simple solution

In the main CMake script you need to add a custom target that generates the source files as follows and a reference to the 2nd (external) CMake project:

# Main CMakeLists.txt    
add_custom_target(
    code_generation
    COMMAND your_code_generation_tool -o ${CMAKE_CURRENT_BINARY_DIR}/libgenerated/
)

include(ExternalProject)
ExternalProject_Add(
    libgenerated
    DEPENDS code_generation
    SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/path/to/external/project/
    BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/libgenerated-build
    CMAKE_ARGS -DGENERATED_SOURCE_DIR=${CMAKE_CURRENT_BINARY_DIR}/libgenerated
    BUILD_ALWAYS 1
    INSTALL_CMD ""
)

add_executable(
    ${PROJECT_NAME}
    ...
)

target_link_libraries(
    ${PROJECT_NAME} 
    ${CMAKE_CURRENT_BINARY_DIR}/libgenerated-build/libgenerated.a
)

add_dependencies(${PROJECT_NAME} libgenerated)

Now you can do file globbing in the 2nd (external) CMake script and link all files found into a static library:

# External project CMakeLists.txt
project(libgenerated)
file(GLOB_RECURSE SOURCES ${GENERATED_SOURCE_DIR}/*)
add_library(${PROJECT_NAME} ${SOURCES})

In this simple solution your files are generated and built every time you run the build step even if nothing changed. If you want to avoid this you can add a stamp file to your custom target like in the following improved solution:

The stamp file solution

# Main CMakeLists.txt    
add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/libgenerated-stamp
    COMMAND your_code_generation_tool -o ${CMAKE_CURRENT_BINARY_DIR}/libgenerated/
    COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/libgenerated-stamp
    DEPENDS the_input_file(s)_you_generate_your_code_from
)

add_custom_target(
    code_generation
    DEPENDS ${CMAKE_CURRENT_BUILD_DIR}/libgenerated-stamp
)

...

If a change in the input files for your code generator does not necessarily result in a change of all generated files you can improve the solution further by using CMake's copy_if_different command in the external project like in the following advanced solution:

The advanced solution

# External project CMakeLists.txt
project(libgenerated)
file(GLOB_RECURSE SOURCES ${GENERATED_SOURCE_DIR}/*)

add_custom_target(
    make_directory
    COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/generated
)

foreach(FILE ${SOURCES})
    get_filename_component(BASENAME ${FILE} NAME)
    list(APPEND ACTUAL_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/generated/${BASENAME}")
    add_custom_target(
        copy_${BASENAME}_if_different
        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${FILE}     ${CMAKE_CURRENT_BINARY_DIR}/generated
    )
    add_dependencies(make_directory copy_${BASENAME}_if_different)
endforeach()

add_library(${PROJECT_NAME} ${ACTUAL_SOURCES})
add_dependencies(${PROJECT_NAME} make_directory)

In this solution all generated files are copied to another location (${CMAKE_CURRENT_BINARY_DIR}/generated) if they have changed or have been added and are built from there. This approach results in a build of changed files only (but requires a clean when files have been removed).



回答3:

If you don't know the name of the files that will be generated, you can "glob" the folders where they reside.

file( GLOB_RECURSE MY_SRC dest_folder/*.cpp )
add_library( libname SHARED ${MY_SRC} )

Now I'm not sure what triggers the generation of these files. The "globbing" will happen only when you manually run cmake: it will not be able to detect automatically that new files are present.



回答4:

Treat this as a non-answer, just more info:

I recently had to do something for one case where I had a .cpp file that was auto-generated, but I could not figure out how to get CMake to construct the Visual Studio project file that would then compile it. I had to resort to something quite stinky: I had to #include <the_generated.cpp> file from another file that resided under the ${CMAKE_CURRENT_SOURCE} directory. That won't help you much in your case because I suspect you have several .cpp files, so this approach is not scalable.

Also, I found that the GENERATED source file property, when added to the file, did not help at all.

I consider this condition either a bug in Visual Studio (in my case this was VS2008 SP1), or in how CMake generates the .vcproj files, or both.



标签: cmake