cmake的add_custom_command如何处理多输出+多依赖

intro

在一个复杂的项目中,免不了需要动态生成文件,此时可能就需要用到cmake的add_custom_command命令,这个命令可以生成cmake识别的输出文件,并作为构建过程中其它命令的依赖和输出。

add_custom_command(OUTPUT output1 [output2 ...]
COMMAND command1 [ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[MAIN_DEPENDENCY depend]
[DEPENDS [depends...]]
[IMPLICIT_DEPENDS depend1
[ depend2] ...]
[WORKING_DIRECTORY dir]
[COMMENT comment] [VERBATIM] [APPEND])

这个命令里有有一个很好用的功能:可以同时有多个个输(OUTPUT)出和多个依赖(DEPENDS),并且公用的是同一个命令。

这个功能翻译成Makefile看起来应该也很直观:

output1 output2: depend1 depend1
	command1
	command2

但是这里有一个隐藏的问题:make是支持多任务并发构建的

 -j [jobs], --jobs[=jobs]                                                                                                                                                                                                      
      Specifies the number of jobs (commands) to run simultaneously.  If there is more than one -j option, the last one is effective.  If the -j option is given without an argument, make will not limit the number of jobs t 
      can run simultaneously.                                                                                                                                                                                                  

在多任务构建时,假设output1和output2都需要重新生成,那么相同的命令会被多次执行。多次执行的冗余可能还是次要的,关键是这些命令可能都会修改相同的文件,很容易因为并发写而导致输出文件错乱。

cmake的处理

下面是一个简单的测试脚本:

cmake_minimum_required (VERSION 3.10)
project (tsecer)
add_executable(main main.cpp sub.cpp)

add_custom_command(OUTPUT main.cpp sub.cpp   
                    COMMAND touch main.cpp touch sub.cpp
                    DEPENDS main.h sub.h)

依赖关系在Makefile中的表示为

main.cpp: main.hmain.cpp: sub.h
    @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --blue --bold --progress-dir=/home/tsecer/cmake/add_custom_command_multiple_output/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Generating main.cpp, sub.cpp"    touch main.cpp touch sub.cpp

sub.cpp: main.cpp
    @$(CMAKE_COMMAND) -E touch_nocreate sub.cpp

其中比较巧妙的地方在于sub.cpp并不是和main.cpp有相同的依赖,而是sub.cpp会依赖于main.cpp,而main.cpp依赖cmake脚本描述的所有依赖。并且sub.cpp的脚本命令不会真正执行构建动作。而是假设main.cpp和sub.cpp有相同的时间戳,让main.cpp来替换sub.cpp的依赖。

由于make在多任务调度时会考虑这种同一个目标的依赖关系,所以避免了多任务执行时的并发写问题。

而其中的一个关键是cmake添加的,看似平平无奇的touch_nocreate命令

  touch_nocreate <file>...                                                                                                                   
         Touch a file if it exists but do not create it.  If a file does not exist it will be silently ignored.                              

outro

个人认为cmake使用简单的方法,优雅的解决了构建并发问题,是一个巧妙的思路。

posted on 2024-06-05 22:26  tsecer  阅读(8)  评论(0编辑  收藏  举报

导航