以下情况可能需要在CMake中执行shell脚本:
有的需要shell脚本的返回值,而有的不需要,这个关系不大。本文主要关注的是在cmake中执行shell脚本的方法。
主要涉及三个命令:execute_process、add_custom_target和add_custom_command。
通过execute_process方法可以执行多个子进程。
原型如下:
execute_process(COMMAND []
[COMMAND []]...
[WORKING_DIRECTORY ]
[TIMEOUT ]
[RESULT_VARIABLE ]
[RESULTS_VARIABLE ]
[OUTPUT_VARIABLE ]
[ERROR_VARIABLE ]
[INPUT_FILE ]
[OUTPUT_FILE ]
[ERROR_FILE ]
[OUTPUT_QUIET]
[ERROR_QUIET]
[COMMAND_ECHO ]
[OUTPUT_STRIP_TRAILING_WHITESPACE]
[ERROR_STRIP_TRAILING_WHITESPACE]
[ENCODING ]
[ECHO_OUTPUT_VARIABLE]
[ECHO_ERROR_VARIABLE]
[COMMAND_ERROR_IS_FATAL ])
命令COMMAND会并行执行,每个子进程的标准输出映射到下一个进程的标准输入上,所有进程共用standard error管道。
各选项说明如下:
示例如下:
cmake_minimum_required(VERSION 3.2)
project(cmake_test)
execute_process(COMMAND echo "hello world"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
TIMEOUT 3
RESULT_VARIABLE result_var
OUTPUT_VARIABLE output_var
ERROR_VARIABLE error_var
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE)
message(STATUS "result: ${result_var}")
message(STATUS "output: ${output_var}")
message(STATUS "error: ${error_var}")
输出如下:
-- result: 0
-- output: hello world
-- error:
如果要执行一个shell脚本,只需要把echo命令替换为如:bash a.sh 即可。
添加自定义的没有输出的目标,它总会被构建。
原型如下:
add_custom_target(Name [ALL] [command1 [args1...]]
[COMMAND command2 [args2...] ...]
[DEPENDS depend depend depend ... ]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[JOB_POOL job_pool]
[VERBATIM] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS]
[SOURCES src1 [src2...]])
目标没有输出文件,总是被认为是过时的,可以使用 add_custom_command() 命令生成依赖的文件供 DEPENDS 参数使用。
常用参数说明如下:
从以上说明可以看出,可以直接使用add_custom_target执行shell命令,并且使用DEPENDS可以让各个目标之间产生关联。
通常和add_custom_command命令配合使用来产生DEPENDS。
比如我们在编译boost库时,需要执行shell命令,示例如下:
add_custom_target(build_boost_libs
COMMAND ./bootstrap.sh --prefix=/usr/local/boost
COMMAND ./b2 link=static runtime-link=static threading=multi --with-system --with-thread --with-filesystem
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/boost/"
COMMENT "begin build boost libs...")
这样,我们自定义了目标build_boost_libs,它是通过两行命令来构建完成的。
关于add_custom_command,下面介绍。
为构建系统添加自定义的构建规则。
它有两种形式的原型,下面分别介绍。
签名如下:
add_custom_command(OUTPUT output1 [output2 ...]
COMMAND command1 [ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[MAIN_DEPENDENCY depend]
[DEPENDS [depends...]]
[BYPRODUCTS [files...]]
[IMPLICIT_DEPENDS depend1
[ depend2] ...]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[DEPFILE depfile]
[JOB_POOL job_pool]
[VERBATIM] [APPEND] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS])
使用命令生成指定的输出文件。具体参数不再说明,详情可参考后文资料。
使用示例:
add_custom_command(
OUTPUT out.c
COMMAND someTool -i ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
-o out.c
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
VERBATIM)
add_library(myLib out.c)
这样,在生成myLib库时依赖out.c,而out.c由add_custom_command生成,每次in.txt的变动都会导致add_custom_command中命令的执行。
add_custom_command指定的DEPENDS可以是某个target(通过add_library/add_executable/add_custom_target创建),或者直接是某个文件。
如果add_custom_command命令不指定DEPENDS的话,那么只要没有这个OUTPUT的文件,都会生成自己并执行command。
为库、可执行文件等目标添加自定义命令,可以在构建目标前或者构建目标后执行一些命令。
要执行的命令会成为目标的一部分,并且只在目标构建时执行,如果目标已经构建完成,这些命令也不会执行。
原型:
add_custom_command(TARGET
PRE_BUILD | PRE_LINK | POST_BUILD
COMMAND command1 [ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[VERBATIM] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS])
这样就为指定的target关联了要执行的命令,target必须在当前目录里定义。
命令执行的时机:
其他参数不再说明。
示例:
# 在目标构建完成后执行一些操作
add_executable(myExe myExe.c)
add_custom_command(
TARGET myExe POST_BUILD
COMMAND someHasher -i "$"
-o "$.hash"
VERBATIM)
add_custom_target有依赖文件时,经常和add_custom_command的生成文件模式搭配使用。
它们之间的关系比较暧昧,这里说明一下。
当add_custom_target所要生成的target依赖add_custom_command所生成的文件时,这个文件就是一个纽带。
add_custom_command命令输出的OUTPUT文件和命令里的command之间的关系是:每当这个文件需要被重新生成时,都会执行这段command。
这个文件会不会被生成,取决于构建的target是否depends这个output文件。
这个文件会不会被重新生成,取决于这个output文件depends的东西变了没。
上面也有点绕,举例说明一下:
add_custom_command(OUTPUT config_bootstrap
COMMAND ./bootstrap.sh --prefix=/usr/local
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/boost/"
COMMENT "begin config_bootstrap")
add_custom_target(build_boost_libs
COMMAND ./b2 link=static runtime-link=static threading=multi --with-system --with-thread --with-filesystem
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/boost/"
DEPENDS config_bootstrap
COMMENT "begin build_boost_libs")
执行流程为:
这个流程与MakeFile决定是否重新编译目标是一个道理,它会自动识别模块间的依赖关系,并自己构建需要构建的模块。
写cmake的过程,也是告诉cmake模块间依赖关系的过程。
cmake提供了对执行自定义命令的支持,可以很方便地使用它们执行shell命令。
但它们使用上有一些区别,比如有的会无条件每次执行,有的则依赖于依赖文件是否被更新。
具体使用哪种模式,就看需求了。
execute_process
add_custom_target
add_custom_command