1

我有一种情况,我需要在 CMake 目标链接到时运行脚本,以便它可以在当前项目目录中自动生成用于与库交互的文件。

我知道当您链接到 CMake 目标时,它会自动拉入库的标头,以便它们对编译器可见,但我还需要它在链接对象的目录中生成一些文件,这些文件也将对编译器可见建造。

我如何告诉 CMake 我想运行一个脚本来生成每次my_cmake_target链接到的文件?

CMakeLists.txt 中的链接示例:

target_link_libraries(my_executable PRIVATE my_cmake_target)

我希望该命令在 CMake 根据传递给“target_link_libraries”的目标传递更新包含目录的同时运行。(在任何构建/链接实际发生之前)

有关其工作原理的更多信息,请参见此处:

https://schneide.blog/2016/04/08/modern-cmake-with-target_link_libraries/

使用 target_link_libraries 将 A 链接到内部目标 B 不仅会添加链接到 B 所需的链接器标志,还会添加定义、包含路径和其他设置——即使是传递的——如果它们是这样配置的。

4

1 回答 1

1

不幸的是,没有任何内置功能可以帮助您做到这一点。通过接口属性传播自定义命令不是 CMake 已经实现的(或计划,afaik)。

但是,这有点被诅咒,这是一种方法。

您创建一个函数来扫描目录中链接到您的特殊库的目标。对于这些目标中的每一个,它都会在二进制目录中附加一个特殊的源文件和一个用于生成该文件的命令。它使用自定义属性(此处MAGIC为 )来确定是否实际生成源文件并将其包含在目标源中。

然后,用于cmake_language(DEFER CALL ...)在当前目录的构建脚本末尾运行该函数。这部分确保函数不必手动调用,即使在find_package场景中也是如此。

待办事项:

  1. 运行此代码两次可能会导致错误。但是,您可以通过标记目标是否已使用另一个定制属性进行处理来避免问题。

# ./CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(example LANGUAGES CXX)

add_subdirectory(subdir)

add_executable(my_executable main.cpp)
target_link_libraries(my_executable PRIVATE my_cmake_target)

add_executable(excluded main.cpp default-name.cpp)
# ./subdir/CMakeLists.txt
function (MyProj_post_build)
  set(dirs ".")

  while (dirs)
    list(POP_FRONT dirs dir)

    get_property(subdirs DIRECTORY "${dir}" PROPERTY SUBDIRECTORIES)
    list(APPEND dirs ${subdirs})

    get_property(targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS)
    foreach (target IN LISTS targets)
      # Do whatever you want here, really. The key is checking
      # that $<BOOL:$<TARGET_PROPERTY:MAGIC>> is set on the 
      # target at generation time. I use a custom command here,
      # but you could use file(GENERATE).

      add_custom_command(
        OUTPUT "MyProj_${target}.cpp"
        COMMAND "${CMAKE_COMMAND}" -E echo "const char* Name = \"$<TARGET_PROPERTY:${target},NAME>\";" > "MyProj_${target}.cpp"
        VERBATIM
      )

      target_sources(
        "${target}"
        PRIVATE
        "$<$<BOOL:$<TARGET_PROPERTY:MAGIC>>:$<TARGET_OUT/MyProj_${target}.cpp>"
      )
    endforeach ()
  endwhile ()
endfunction ()

cmake_language(DEFER DIRECTORY "${CMAKE_SOURCE_DIR}" CALL MyProj_post_build)

add_library(my_cmake_target INTERFACE)
set_target_properties(my_cmake_target PROPERTIES INTERFACE_MAGIC ON)
set_property(TARGET my_cmake_target APPEND PROPERTY COMPATIBLE_INTERFACE_STRING MAGIC)
// main.cpp
#include <iostream>

extern const char* Name;

int main () { std::cout << Name << "\n"; }
// default-name.cpp
const char* Name = "default";

这是它有效的证据......

$ cmake -G Ninja -S . -B build
[1/7] cd /home/alex/test/build && /usr/bin/cmake -E echo "const char* Name = \"my_executable\";" > MyProj_my_executable.cpp
[2/7] /usr/bin/c++    -MD -MT CMakeFiles/excluded.dir/default-name.cpp.o -MF CMakeFiles/excluded.dir/default-name.cpp.o.d -o CMakeFiles/excluded.dir/default-name.cpp.o -c /home/alex/test/default-name.cpp
[3/7] /usr/bin/c++    -MD -MT CMakeFiles/my_executable.dir/MyProj_my_executable.cpp.o -MF CMakeFiles/my_executable.dir/MyProj_my_executable.cpp.o.d -o CMakeFiles/my_executable.dir/MyProj_my_executable.cpp.o -c /home/alex/test/build/MyProj_my_executable.cpp
[4/7] /usr/bin/c++    -MD -MT CMakeFiles/excluded.dir/main.cpp.o -MF CMakeFiles/excluded.dir/main.cpp.o.d -o CMakeFiles/excluded.dir/main.cpp.o -c /home/alex/test/main.cpp
[5/7] /usr/bin/c++    -MD -MT CMakeFiles/my_executable.dir/main.cpp.o -MF CMakeFiles/my_executable.dir/main.cpp.o.d -o CMakeFiles/my_executable.dir/main.cpp.o -c /home/alex/test/main.cpp
[6/7] : && /usr/bin/c++   CMakeFiles/my_executable.dir/main.cpp.o CMakeFiles/my_executable.dir/MyProj_my_executable.cpp.o -o my_executable   && :
[7/7] : && /usr/bin/c++   CMakeFiles/excluded.dir/main.cpp.o CMakeFiles/excluded.dir/default-name.cpp.o -o excluded   && :

$ ./build/my_executable
my_executable

$ ./build/excluded 
default
于 2022-01-24T22:42:38.380 回答