不幸的是,没有任何内置功能可以帮助您做到这一点。通过接口属性传播自定义命令不是 CMake 已经实现的(或计划,afaik)。
但是,这有点被诅咒,这是一种方法。
您创建一个函数来扫描目录中链接到您的特殊库的目标。对于这些目标中的每一个,它都会在二进制目录中附加一个特殊的源文件和一个用于生成该文件的命令。它使用自定义属性(此处MAGIC
为 )来确定是否实际生成源文件并将其包含在目标源中。
然后,用于cmake_language(DEFER CALL ...)
在当前目录的构建脚本末尾运行该函数。这部分确保函数不必手动调用,即使在find_package
场景中也是如此。
待办事项:
- 运行此代码两次可能会导致错误。但是,您可以通过标记目标是否已使用另一个定制属性进行处理来避免问题。
# ./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