CMake中的add_custom_command命令用于将自定义构建规则添加到生成的构建系统(Add a custom build rule to the generated build system),其格式如下:
add_custom_command(OUTPUT output1 [output2 ...]
COMMAND command1 [ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[MAIN_DEPENDENCY depend]
[DEPENDS [depends...]]
[BYPRODUCTS [files...]]
[IMPLICIT_DEPENDS <lang1> depend1
[<lang2> depend2] ...]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[DEPFILE depfile]
[JOB_POOL job_pool]
[VERBATIM] [APPEND] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS]) # Generating Files
add_custom_command(TARGET <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]) # Build Events
1.Generating Files:添加自定义命令以生成输出。这定义了生成指定输出文件的命令。
选项包括:
(1).APPEND:将COMMAND和DEPENDS选项值附加到指定的第一个输出的自定义命令。之前必须已经调用过具有相同输出的此命令。
(2).BYPRODUCTS:指定命令预期生成的文件。如果副产品(byproduct)名是相对路径,它将相对于当前源目录相对应的构建树目录进行解释。每个副产品文件都将自动标记为GENERATED源文件属性。
(3).COMMAND:指定要在构建时执行的命令行。如果指定了多个COMMAND,它们将按顺序执行,但不一定组成有状态的shell或batch脚本。可选的ARGS参数是为了向后兼容。
(4).COMMENT:在构建时执行命令之前显示给定的消息。
(5).DEPENDS:指定命令所依赖的文件。每个参数都转换为依赖项。如果未指定DEPENDS,则只要缺少OUTPUT,该命令就会运行;如果该命令实际上并未创建OUTPUT,则该规则将始终运行。
(6).COMMAND_EXPAND_LISTS:COMMAND参数中的列表(list)将被扩展。
(7).IMPLICIT_DEPENDS:请求扫描输入文件的隐式依赖项。给定的语言指定应使用其相应依赖扫描器的编程语言(programming language).目前仅支持C和CXX语言扫描器。必须为IMPLICIT_DEPENDS列表中的每个文件指定语言。IMPLICIT_DEPENDS选项目前仅支持Makefile生成器,其它生成器将忽略该选项。
此选项不能与DEPFILE选项同时指定。
(8).JOB_POOL:为Ninja生成器指定一个池(pool).
(9).MAIN_DEPENDENCY:指定命令的主要输入源文件。每个源文件最多可以有一个命令将其指定为其主要依赖项。编译命令(即用于库或可执行文件)算作隐式主要依赖项,它会被自定义命令规范悄悄覆盖。
(10).OUTPUT:指定命令预期生成的输出文件。如果输出名是相对路径,它将相对于当前源目录相对应的构建树目录进行解释。每个输出文件都将自动标记为GENERATED源文件属性。如果自定义命令的输出实际上并未创建为磁盘上的文件,则应使用SYMBOLIC源文件属性对其进行标记。
(11).USES_TERMINAL:如果可能,该命令将被授予直接访问终端(terminal)的权限。
(12).VERBATIM:命令的所有参数都将为构建工具正确转义(escaped),以便调用的命令接收每个参数不变。建议使用VERBATIM,因为它可以实现正确的行为。当没有给出VERBATIM时,行为是特定于平台的。
(13).WORKING_DIRECTORY:使用给定的当前工作目录执行命令。如果是相对路径,会被解释为相对于当前源目录对应的构建树目录。
(14).DEPFILE:指定一个包含自定义命令依赖项的depfile。它通常由自定义命令本身发出。这个关键字只能在生成器支持的情况下使用。
DEPFILE不能与Makefile Generators的IMPLICIT_DEPENDS选项同时指定。
message("##CMAKE_COMMAND: ${CMAKE_COMMAND}") # ## CMAKE_COMMAND: /usr/bin/cmake
# 只有在构建时add_custom_command才会真正生效,解析cmake阶段只会判断有无语法错误,在调用add_library命令时才会有xxx.cpp, main.cpp文件生成
# 在build目录下会生成xxx.cpp, main.cpp文件
# add.cpp的md5和COMMENT信息会直接在终端输出
add_custom_command(
OUTPUT xxx.cpp main.cpp
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/source/add.cpp xxx.cpp
COMMAND ${CMAKE_COMMAND} -E md5sum ${CMAKE_CURRENT_SOURCE_DIR}/source/add.cpp
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/samples/sample_add.cpp main.cpp
COMMENT "**** test cmake command: add_custom_command"
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/add.cpp
VERBATIM
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(add SHARED xxx.cpp)
add_executable(main main.cpp)
target_link_libraries(main add)
2.Build Events:将自定义命令添加到target,如库或可执行文件.这对于在构建target之前或之后执行操作非常有用。该命令将成为target的一部分,并且仅在构建target本身时执行。如果target已构建,则不会执行该命令。
这将定义一个新命令,该命令将与构建指定的<target>关联。<target>必须在当前目录中定义;不能指定在其它目录中定义的target。
命令何时发生取决于指定以下哪项:
(1).PRE_BUILD:在Visual Studio Generators上,在target中执行任何其它规则之前运行。在其它生成器(generator)上,在PRE_LINK命令之前运行。
(2).PRE_LINK:在编译源代码之后但在链接二进制文件(binary)或运行静态库的库管理员(librarian)或存档器工具(archiver tool)之前运行。这不是为add_custom_target命令创建的target定义的。
(3).POST_BUILD:在执行target中的所有其它规则后运行。
使用TARGET格式时,project应始终指定上述三个关键字之一。出于向后兼容性(backward compatibility)的原因,如果没有给出这样的关键字,则假定为POST_BUILD,但project应明确提供其中一个关键字以明确他们期望的行为。
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(add SHARED ${CMAKE_CURRENT_SOURCE_DIR}/source/add.cpp)
# 在执行add_custom_command前target add已存在,否则会报error: No TARGET 'add' has been created in this directory
# add.cpp的md5和COMMENT信息会直接在终端输出
add_custom_command(TARGET add POST_BUILD
COMMAND ${CMAKE_COMMAND} -E md5sum ${CMAKE_CURRENT_SOURCE_DIR}/source/add.cpp
COMMENT "**** test cmake command: add_custom_command"
VERBATIM
)
执行测试代码需要多个文件:
build.sh内容如下:
#! /bin/bash
# supported input parameters(cmake commands)
params=(function macro cmake_parse_arguments \
find_library find_path find_file find_program find_package \
cmake_policy cmake_minimum_required project include \
string list set foreach message option if while return \
math file configure_file \
include_directories add_executable add_library target_link_libraries install \
target_sources add_custom_command add_custom_target)
usage()
{
echo "Error: $0 needs to have an input parameter"
echo "supported input parameters:"
for param in ${params[@]}; do
echo " $0 ${param}"
done
exit -1
}
if [ $# != 1 ]; then
usage
fi
flag=0
for param in ${params[@]}; do
if [ $1 == ${param} ]; then
flag=1
break
fi
done
if [ ${flag} == 0 ]; then
echo "Error: parameter \"$1\" is not supported"
usage
exit -1
fi
if [[ ! -d "build" ]]; then
mkdir build
cd build
else
cd build
fi
echo "==== test $1 ===="
# test_set.cmake: cmake -DTEST_CMAKE_FEATURE=$1 --log-level=verbose ..
# test_option.cmake: cmake -DTEST_CMAKE_FEATURE=$1 -DBUILD_PYTORCH=ON ..
cmake -DTEST_CMAKE_FEATURE=$1 ..
# It can be executed directly on the terminal, no need to execute build.sh, for example: cmake -P test_set.cmake
make
# make install # only used in cmake files with install command
CMakeLists.txt内容如下:
cmake_minimum_required(VERSION 3.22)
project(cmake_feature_usage)
message("#### current cmake version: ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}")
include(test_${TEST_CMAKE_FEATURE}.cmake)
message("==== test finish ====")
test_add_custom_command.cmake内容为上面的所有测试代码段。
另外还包括三个目录:include,source,samples,它们都是非常简单的实现,仅用于测试,如下:
可能的执行结果如下图所示:
GitHub: https://github.com/fengbingchun/Linux_Code_Test