立即构建工具,以便以后可以在同一个CMake运行中使用
我有一个有趣的鸡和蛋的问题和一个潜在的解决方案(见我贴出的答案),但这个解决方案以一种不同寻常的方式使用CMake。欢迎提出更好的备选方案或意见 问题: 问题的简单版本可以描述为具有以下特征的单个CMake项目:立即构建工具,以便以后可以在同一个CMake运行中使用,cmake,Cmake,我有一个有趣的鸡和蛋的问题和一个潜在的解决方案(见我贴出的答案),但这个解决方案以一种不同寻常的方式使用CMake。欢迎提出更好的备选方案或意见 问题: 问题的简单版本可以描述为具有以下特征的单个CMake项目: 构建目标之一是一个命令行可执行文件,我将其称为mycop,其源代码位于mycopdir中,不可能对该目录的内容进行任何修改 该项目包含文本文件(我将称之为代码> Fo..和代码> Bar。我的 >,它们需要MyCOMP在它们上运行,以产生一组C++源和头,以及一些 CMAKLISTH.
mycopdir
中,不可能对该目录的内容进行任何修改CMakeLists.txt
文件定义的库相链接。这些其他目标也有#包括
一些生成的头的源CMakeLists.txt
文件,但mycomp在构建时才可用,因此在第一次运行时无法尽早创建CMakeLists.txt
文件
无应答:
通常,基于外部项目的超级建筑安排将是解决这一问题的潜在方案,但以上是我正在处理的实际项目的一个相当大的简化,我没有将构建拆分为不同部分或执行其他大规模重组工作的自由。问题的关键是需要在运行CMake时提供mycomp,以便生成的
CMakeLists.txt
文件可以使用创建,然后使用添加子目录()
拉入。实现这一点的一种可能方法是使用execute\u process()
从主构建运行嵌套的cmake和构建。嵌套的cmake和构建将使用与顶级cmake运行完全相同的源目录和二进制目录(除非交叉编译)。主要顶级CMakeLists.txt
的一般结构如下:
# Usual CMakeLists.txt setup stuff goes here...
if(EARLY_BUILD)
# This is the nested build and we will only be asked to
# build the mycomp target (see (c) below)
add_subdirectory(mycompdir)
# End immediately, we don't want anything else in the nested build
return()
endif()
# This is the main build, setup and execute the nested build
# to ensure the mycomp executable exists before continuing
# (a) When cross compiling, we cannot re-use the same binary dir
# because the host and target are different architectures
if(CMAKE_CROSSCOMPILING)
set(workdir "${CMAKE_BINARY_DIR}/host")
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory "${workdir}")
else()
set(workdir "${CMAKE_BINARY_DIR}")
endif()
# (b) Nested CMake run. May need more -D... options than shown here.
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}"
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}
-DEARLY_BUILD=ON
${CMAKE_SOURCE_DIR}
WORKING_DIRECTORY "${workdir}")
# (c) Build just mycomp in the nested build. Don't specify a --config
# because we cannot know what config the developer will be using
# at this point. For non-multi-config generators, we've already
# specified CMAKE_BUILD_TYPE above in (b).
execute_process(COMMAND ${CMAKE_COMMAND} --build . --target mycomp
WORKING_DIRECTORY "${workdir}")
# (d) We want everything from mycompdir in our main build,
# not just the mycomp target
add_subdirectory(mycompdir)
# (e) Run mycomp on the sources to generate a CMakeLists.txt in the
# ${CMAKE_BINARY_DIR}/foobar directory. Note that because we want
# to support cross compiling, working out the location of the
# executable is a bit more tricky. We cannot know whether the user
# wants debug or release build types for multi-config generators
# so we have to choose one. We cannot query the target properties
# because they are only known at generate time, which is after here.
# Best we can do is hardcode some basic logic.
if(MSVC)
set(mycompsuffix "Debug/mycomp.exe")
elseif(CMAKE_GENERATOR STREQUAL "Xcode")
set(mycompsuffix "Debug/mycomp")
else()
set(mycompsuffix "mycomp")
endif()
set(mycomp_EXECUTABLE "${workdir}/mycompdir/${mycompsuffix}")
execute_process(COMMAND "${mycomp_EXECUTABLE}" -outdir foobar ${CMAKE_SOURCE_DIR}/foo.my ${CMAKE_SOURCE_DIR}/bar.my)
# (f) Now pull that generated CMakeLists.txt into the main build.
# It will create a CMake library target called foobar.
add_subdirectory(${CMAKE_BINARY_DIR}/foobar ${CMAKE_BINARY_DIR}/foobar-build)
# (g) Another target which links to the foobar library
# and includes headers from there
add_executable(gumby gumby.cpp)
target_link_libraries(gumby PUBLIC foobar)
target_include_directories(gumby PUBLIC foobar)
如果我们不在(b)和(c)处重复使用与主构建相同的二进制目录,我们最终将构建mycop
两次,这显然是我们想要避免的。对于交叉编译,我们无法避免这种情况,因此在这种情况下,我们在单独的二进制目录中构建mycomp
工具
我已经尝试过上述方法,而且它在现实世界项目中确实有效,至少对于Unix Makefiles、Ninja、Xcode(OSX和iOS)和VisualStudio生成器来说是如此。这种方法的一部分吸引力在于,它只需要在顶级CMakeLists.txt
文件中添加少量代码。尽管如此,仍应提出一些意见:
- 如果用于
的编译器或链接器命令及其源在嵌套构建和主构建之间存在任何不同,则mycomp
目标将在(d)处再次重建。如果没有差异,mycomp
在不交叉编译时只生成一次,这正是我们想要的mycop
- 我认为没有一种简单的方法可以将完全相同的参数传递给(b)处的CMake嵌套调用,就像传递给顶级CMake运行一样(基本上就是描述的问题)。读取
不是一个选项,因为它在第一次调用时不存在,并且不会从当前运行中提供任何新的或更改的参数。我能做的最好的事情就是设置那些我认为可能会被使用的CMake变量,这些变量可能会影响cmakcache.txt
的编译器和链接器命令。这可以通过在遇到我发现需要的变量时添加越来越多的变量来解决,但这并不理想mycomp
- 当重新使用相同的二进制目录时,我们依赖于CMake在生成阶段之前(至少在(c)处的构建完成之后)不会开始将其任何文件写入二进制目录。对于测试过的生成器,看起来我们还可以,但我不知道所有平台上的所有生成器是否也遵循这种行为(我无法测试每种组合来找出答案!)。这是我最关心的部分。如果有人能够通过推理和/或证据证实这对所有发电机和平台都是安全的,那么这将是非常有价值的(如果你想单独回答这个问题,那么值得投票)
更新:在对CMake熟悉程度不同的工作人员的许多实际项目中使用上述策略后,可以进行一些观察
- 让嵌套的生成重复使用与主生成相同的生成目录有时会导致问题。具体来说,如果用户在嵌套生成完成后但在主生成完成之前终止了CMake运行,则
文件将保留CMakeCache.txt
设置为EARLY\u build
。这使得所有后续的CMake运行都像一个嵌套的构建一样,因此在手动删除ON
文件之前,主构建基本上是丢失的。项目的CMakeCache.txt
文件中的某个错误也可能导致类似情况(未确认)。在它自己单独的构建目录中执行嵌套的构建,虽然没有这样的问题,但效果非常好CMakeLists.txt
- 嵌套的构建可能应该是发布的,而不是调试的。如果没有,请重新使用相同的生成目录