CMake编译生成的文件

24

我有一个在CMake构建过程中生成的文件列表。我想使用 "add_library" 编译这些文件,但在它们生成之前我不知道哪些文件会被生成。有没有办法将此集成到CMake脚本中?

4个回答

13

我认为这是可能的,所以我会分享一下我所做的。我的问题是,我必须编译几个CORBA idl文件以用作项目源代码的一部分,而我不想手动列出每个文件。我觉得找到这些文件会更好。因此,我是这样做的:

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}
)

在我的idl编译输出(*C.cpp,*C.h,*S.cpp和*S.h)中,如果某个文件未被创建,那么生成的属性很有用,这样构建命令就不会抱怨文件不存在。


12

使用CMake的 CMAKE_CONFIGURE_DEPENDS 目录属性可以实现这一点。如果任何指定的文件发生更改,这将强制CMake重新配置。

简单解决方案

以下代码展示了用于单个模型文件的方法,该文件用作代码生成的输入:

set(MODEL_FILE your_model_file)
set_directory_properties(PROPERTIES CMAKE_CONFIGURE_DEPENDS ${MODEL_FILE})
set(GENERATED_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE})

file(REMOVE_RECURSE ${GENERATED_SOURCE_DIR})
file(MAKE_DIRECTORY ${GENERATED_SOURCE_DIR})
execute_process(COMMAND your_code_generation_tool -o ${GENERATED_SOURCE_DIR} ${MODEL_FILE})

file(GLOB LIBGENERATED_FILES ${GENERATED_SOURCE_DIR}/*)
add_library(libgenerated ${LIBGENERATED_FILES})
target_include_directories(libgenerated ${GENERATED_SOURCE_DIR})
使用以上方法,每当模型文件发生变化时,CMake将重新配置,导致模型重新生成。
高级解决方案
简单解决方案的问题在于,即使对模型进行最小可能的更改,也必须重建生成文件的所有依赖项。
高级方法使用CMake的“copy_if_different”功能,仅让受模型更改影响的生成文件出现修改,从而提高构建时间。为了实现这一点,我们使用一个临时目录作为生成器的目标,并随后同步内容与先前编译运行的生成器输出。
set(MODEL_FILE your_model_file)
set(GENERATOR_STAGING_DIR ${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE}.staging)
set(GENERATOR_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE})

set_directory_properties(PROPERTIES CMAKE_CONFIGURE_DEPENDS ${MODEL_FILE})

# Create fresh staging/final output directory
file(REMOVE_RECURSE ${GENERATOR_STAGING_DIR})
file(MAKE_DIRECTORY ${GENERATOR_STAGING_DIR})
file(MAKE_DIRECTORY ${GENERATOR_OUTPUT_DIR})

# Run code generation
execute_process(COMMAND your_code_generation_tool -o ${GENERATOR_STAGING_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_FILE}")

# Remove stale files from final generator output directory
file(GLOB GENERATED_FILES RELATIVE "${GENERATOR_OUTPUT_DIR}/" "${GENERATOR_OUTPUT_DIR}/*")
foreach(FILE ${GENERATED_FILES})
    if(NOT EXISTS "${GENERATOR_STAGING_DIR}/${FILE}")
        file(REMOVE "${GENERATOR_OUTPUT_DIR}/${FILE}")
    endif()    
endforeach()

# Copy modified files from staging to final generator output directory
file(GLOB GENERATED_FILES RELATIVE "${GENERATOR_STAGING_DIR}/" "${GENERATOR_STAGING_DIR}/*")
foreach(FILE ${GENERATED_FILES})
    execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${GENERATOR_STAGING_DIR}/${FILE}" "${GENERATOR_OUTPUT_DIR}")
endforeach()

file(GLOB LIBGENERATED_FILES "${GENERATOR_OUTPUT_DIR}/*")
add_library(libgenerated ${LIBGENERATED_FILES})
target_include_directories(libgenerated PUBLIC ${GENERATOR_OUTPUT_DIR})

仅在文件更改时进行编译是一个不错的功能。最近,我一直在考虑通过此功能来优化我的代码以加快编译时间。如果可能的话,也许我会尝试这种方法或将其适应到我的代码中。 - Salsa

2
如果您不知道将要生成的文件名,可以对它们所在的文件夹进行“glob”操作。 file(GLOB_RECURSE MY_SRC dest_folder/*.cpp) add_library(libname SHARED ${MY_SRC}) 现在我不确定什么会触发这些文件的生成。当您手动运行 CMake 时,“globbing” 将会发生:它无法自动检测到新文件的存在。

3
这些文件是在“make”时生成的。因此,在cmake中使用GLOB_RECURSE或任何通配符都不起作用。 - Jonathan Sternberg
也许您需要将构建过程分为两个部分。第一个cmake项目将以生成这些文件结束。第二个项目将使用新文件的文件夹进行全局匹配并进行构建。您可以使用Hudson(https://hudson.dev.java.net/)来实现这一点。您可以在两个项目之间创建依赖关系,并在一个项目构建完成后触发另一个项目的构建。 - Mathieu JVL

1

把这个当作一个非答案的信息:

最近我需要处理一个问题,其中我有一个自动生成的 .cpp 文件,但我无法弄清楚如何让 CMake 构建 Visual Studio 项目文件,然后编译它。我不得不使用一些相当糟糕的方法:我必须从另一个位于 ${CMAKE_CURRENT_SOURCE} 目录下的文件中 #include <the_generated.cpp> 文件。在你的情况下,这并不能帮助你太多,因为我怀疑你有几个 .cpp 文件,所以这种方法并不可扩展。

此外,我发现添加到文件中的 GENERATED 源文件属性根本没有起到任何帮助。

我认为这种情况可能是 Visual Studio(在我的情况下是 VS2008 SP1)的一个 bug,或者是 CMake 生成 .vcproj 文件的方式的问题,或者两者都有。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接