add_custom_command¶
向生成的构建系统添加自定义构建规则。
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 <lang1> depend1
[<lang2> depend2] ...]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[DEPFILE depfile]
[JOB_POOL job_pool]
[JOB_SERVER_AWARE <bool>]
[VERBATIM] [APPEND] [USES_TERMINAL]
[CODEGEN]
[COMMAND_EXPAND_LISTS]
[DEPENDS_EXPLICIT_ONLY])
这定义了一个命令来生成指定的 OUTPUT
文件。在同一目录(CMakeLists.txt
文件)中创建的目标,如果指定自定义命令的任何输出作为源文件,则在构建时获得使用该命令来生成文件的规则。
不要在多个可能并行构建的独立目标中列出输出,否则规则的实例可能会冲突。相反,请使用 add_custom_target()
命令来驱动该命令,并使其他目标依赖于它。请参见下面的 示例:为多个目标生成文件。
选项包括
APPEND
将
COMMAND
和DEPENDS
选项值附加到第一个指定输出的自定义命令。之前必须已经对具有相同输出的命令调用过此命令。如果之前的调用通过生成器表达式指定了输出,则当前调用指定的输出必须在评估生成器表达式后至少在一个配置中匹配。在这种情况下,附加的命令和依赖项适用于所有配置。
当给出
APPEND
时,COMMENT
、MAIN_DEPENDENCY
和WORKING_DIRECTORY
选项当前会被忽略,但将来可能会使用。BYPRODUCTS
在 3.2 版本中添加。
指定命令预期要生成但其修改时间可能或可能不会比依赖项更新的文件。如果副产品名称是相对路径,则它将被解释为相对于当前源目录对应的构建树目录。每个副产品文件都将自动标记为
GENERATED
源文件属性。参见策略
CMP0058
以了解此功能背后的动机。副产品的显式规范由
Ninja
生成器支持,以告诉ninja
构建工具在丢失副产品时如何重新生成副产品。当其他构建规则(例如自定义命令)依赖于副产品时,它也很有用。Ninja 要求任何生成文件的构建规则(另一个规则依赖于它),即使有仅顺序依赖项,以确保副产品在它们的依赖项构建之前可用。Makefile 生成器 将在
make clean
期间删除BYPRODUCTS
和其他GENERATED
文件。此关键字不能与
APPEND
一起使用(参见策略CMP0175
)。所有副产品必须在对add_custom_command(OUTPUT...)
的第一次调用中为输出文件设置。在 3.20 版本中添加:
BYPRODUCTS
的参数可以使用一组受限的generator expressions
。不允许使用 目标依赖表达式。COMMAND
指定在构建时要执行的命令行。通常会至少给出一次
COMMAND
,但某些模式可能会省略它,例如使用 APPEND 在单独的调用中添加命令。如果指定了多个
COMMAND
,它们将按顺序执行,但不一定构成一个状态化的 shell 或批处理脚本。要运行完整的脚本,请使用configure_file()
命令或file(GENERATE)
命令来创建它,然后指定一个COMMAND
来启动它。可选的
ARGS
参数用于向后兼容,并将被忽略。如果
COMMAND
指定了一个可执行目标名称(由add_executable()
命令创建),如果以下任一条件成立,它将自动被构建时创建的可执行文件的位置替换目标没有进行交叉编译(即
CMAKE_CROSSCOMPILING
变量未设置为 true)。在 3.6 版本中添加: 目标正在进行交叉编译,并且提供了一个模拟器(即其
CROSSCOMPILING_EMULATOR
目标属性已设置)。在这种情况下,CROSSCOMPILING_EMULATOR
的内容将被附加到目标可执行文件的位置之前的命令。
如果上述两个条件都不满足,则假设命令名称是在构建时可以在
PATH
上找到的程序。COMMAND
的参数可以使用generator expressions
。使用TARGET_FILE
生成器表达式来引用命令行后面的目标位置(即作为命令参数,而不是作为要执行的命令)。只要以下基于目标的生成器表达式之一用作要执行的命令或在命令参数中被提及,目标级依赖项就会自动添加,以便在使用此自定义命令的任何目标之前构建所提及的目标(参见策略
CMP0112
)。TARGET_FILE
TARGET_LINKER_FILE
TARGET_SONAME_FILE
TARGET_PDB_FILE
此目标级依赖项不会添加文件级依赖项,该依赖项会导致每次重新编译可执行文件时重新运行自定义命令。使用
DEPENDS
选项列出目标名称以添加此类文件级依赖项。COMMENT
在构建时执行命令之前显示给定消息。如果给出
APPEND
,这将被忽略,尽管将来的版本可能会使用它。在 3.26 版本中添加:
COMMENT
的参数可以使用generator expressions
。DEPENDS
指定命令所依赖的文件。每个参数将转换为依赖项,如下所示
如果参数是目标的名称(由
add_custom_target()
、add_executable()
或add_library()
命令创建),则会创建一个目标级依赖项,以确保目标在使用此自定义命令的任何目标之前构建。此外,如果目标是可执行文件或库,则会创建一个文件级依赖项,以在每次重新编译目标时使自定义命令重新运行。如果参数是绝对路径,则会在该路径上创建一个文件级依赖项。
如果参数是已添加到目标或已对其设置源文件属性的源文件名称,则会在该源文件上创建一个文件级依赖项。
如果参数是相对路径,并且它存在于当前源目录中,则会在当前源目录中的该文件上创建一个文件级依赖项。
否则,将在相对于当前二进制目录的路径上创建一个文件级依赖项。
如果任何依赖项是同一目录(
CMakeLists.txt
文件)中另一个自定义命令的OUTPUT
,CMake 会自动将另一个自定义命令包含到构建此命令的目标中。Added in version 3.16: 如果任何依赖项被列为同一目录中目标或其任何构建事件的
BYPRODUCTS
,则会添加目标级依赖项,以确保副产品可用。如果未指定
DEPENDS
,则该命令将在每次缺少OUTPUT
时运行;如果该命令实际上未创建OUTPUT
,则该规则将始终运行。Added in version 3.1: 传递给
DEPENDS
的参数可以使用generator expressions
。COMMAND_EXPAND_LISTS
Added in version 3.8.
COMMAND
参数中的列表将被展开,包括使用generator expressions
创建的列表,从而允许COMMAND
参数(例如${CC} "-I$<JOIN:$<TARGET_PROPERTY:foo,INCLUDE_DIRECTORIES>,;-I>" foo.cc
)被正确展开。此关键字不能与
APPEND
一起使用(参见策略CMP0175
)。如果附加的命令需要设置此选项,则必须在对add_custom_command(OUTPUT...)
的第一次调用中为输出文件设置此选项。CODEGEN
Added in version 3.31.
将自定义命令添加到全局
codegen
目标,该目标可用于执行自定义命令,同时避免大多数构建图。此选项仅受 Ninja 生成器 和 Makefile 生成器 支持,其他生成器会忽略它。此外,仅当策略
CMP0171
设置为NEW
时才允许此选项。此关键字不能与
APPEND
一起使用(参见策略CMP0175
)。它只能在对add_custom_command(OUTPUT...)
的第一次调用中为输出文件设置。IMPLICIT_DEPENDS
请求扫描输入文件的隐式依赖项。给定的语言指定了应使用其相应依赖项扫描程序的编程语言。当前仅支持
C
和CXX
语言扫描程序。必须为IMPLICIT_DEPENDS
列表中的每个文件指定语言。从扫描中发现的依赖项将在构建时添加到自定义命令的依赖项中。请注意,IMPLICIT_DEPENDS
选项当前仅受 Makefile 生成器支持,其他生成器将忽略它。注意
此选项不能与
DEPFILE
选项同时指定。JOB_POOL
Added in version 3.15.
为
Ninja
生成器指定一个pool
。与USES_TERMINAL
不兼容,这意味着console
池。使用JOB_POOLS
未定义的池会导致 ninja 在构建时出错。此关键字不能与
APPEND
一起使用(参见策略CMP0175
)。作业池只能在对add_custom_command(OUTPUT...)
的第一次调用中为输出文件指定。JOB_SERVER_AWARE
Added in version 3.28.
指定该命令是 GNU Make 作业服务器感知的。
对于
Unix Makefiles
、MSYS Makefiles
和MinGW Makefiles
生成器,这将在配方行中添加+
前缀。有关更多信息,请参见 GNU Make 文档。其他生成器会静默忽略此选项。
此关键字不能与
APPEND
一起使用(参见策略CMP0175
)。作业服务器感知只能在对add_custom_command(OUTPUT...)
的第一次调用中为输出文件指定。
MAIN_DEPENDENCY
指定命令的主要输入源文件。这与传递给
DEPENDS
选项的任何值一样,但也会向 Visual Studio 生成器建议将自定义命令挂在哪里。每个源文件最多可能只有一个命令将其指定为其主要依赖项。编译命令(即用于库或可执行文件)被视为隐式主要依赖项,该依赖项会被自定义命令规范静默覆盖。如果给出
APPEND
,此选项当前会被忽略,但未来版本可能会使用它。OUTPUT
指定该命令预期生成的输出文件。每个输出文件将自动标记为
GENERATED
源文件属性。如果自定义命令的输出实际上未作为磁盘上的文件创建,则应将其标记为SYMBOLIC
源文件属性。如果输出文件名是相对路径,则其绝对路径将通过将其解释为相对于以下位置来确定
对应于当前源目录的构建目录(
CMAKE_CURRENT_BINARY_DIR
)或当前源目录(
CMAKE_CURRENT_SOURCE_DIR
)。
除非在当前目录中的其他位置将源代码树中的路径作为绝对源文件路径提及,否则构建目录中的路径优先。
输出文件路径不能包含
<
或>
字符。3.20 版本新增: 传递给
OUTPUT
的参数可以使用受限的generator expressions
。不允许使用 与目标相关的表达式。3.30 版本变更: 输出文件路径现在可以使用
#
字符,但使用Borland Makefiles
生成器时除外。USES_TERMINAL
在 3.2 版本中添加。
如果可能,该命令将被直接赋予终端访问权限。使用
Ninja
生成器,这将把命令放在console
pool
中。此关键字不能与
APPEND
一起使用(请参阅策略CMP0175
)。如果附加的命令需要访问终端,则必须在对add_custom_command(OUTPUT...)
的第一次调用中为输出文件设置它。VERBATIM
传递给命令的所有参数都将被正确转义,以便调用的命令接收到的每个参数保持不变。请注意,即使在
add_custom_command
看到参数之前,CMake 语言处理器仍会使用一层转义。建议使用VERBATIM
,因为它可以实现正确的行为。当未给出VERBATIM
时,行为是平台特定的,因为没有对特定于工具的特殊字符进行保护。此关键字不能与
APPEND
一起使用(请参阅策略CMP0175
)。如果附加的命令需要被视为VERBATIM
,则必须在对add_custom_command(OUTPUT...)
的第一次调用中为输出文件设置它。WORKING_DIRECTORY
使用给定的当前工作目录执行命令。如果它是一个相对路径,它将相对于对应于当前源目录的构建树目录进行解释。
如果给出
APPEND
,此选项当前会被忽略,但未来版本可能会使用它。3.13 版本新增: 传递给
WORKING_DIRECTORY
的参数可以使用generator expressions
。DEPFILE
3.7 版本新增。
指定一个 depfile,它保存自定义命令的依赖项。它通常由自定义命令本身发出。此关键字只能在生成器支持的情况下使用,如下所述。
预期的格式与
gcc
使用选项-M
生成的格式兼容,与生成器或平台无关。使用 BNF 符号和常规扩展,正式语法如下
depfile ::=
rule
* rule ::=targets
(':' (separator
dependencies
?)?)?eol
targets ::=target
(separator
target
)*separator
* target ::=pathname
dependencies ::=dependency
(separator
dependency
)*separator
* dependency ::=pathname
separator ::= (space
|line_continue
)+ line_continue ::= '\'eol
space ::= ' ' | '\t' pathname ::=character
+ character ::=std_character
|dollar
|hash
|whitespace
std_character ::= <any character except '$', '#' or ' '> dollar ::= '$$' hash ::= '\#' whitespace ::= '\ ' eol ::= '\r'? '\n'注意
作为
pathname
的一部分,任何斜杠和反斜杠都被解释为目录分隔符。3.7 版本新增: 自该关键字首次添加以来,
Ninja
生成器支持DEPFILE
。3.17 版本新增: 添加了
Ninja Multi-Config
生成器,其中包含对DEPFILE
关键字的支持。3.20 版本新增: 添加了对 Makefile 生成器 的支持。
注意
DEPFILE
不能与 Makefile 生成器 的IMPLICIT_DEPENDS
选项同时指定。3.21 版本新增: 添加了对使用 VS 2012 及更高版本的 Visual Studio 生成器 的支持,以及对
Xcode
生成器的支持。还添加了对generator expressions
的支持。3.29 版本新增: 如果文件未在
OUTPUTS
或BYPRODUCTS
中列出,则 Ninja 生成器 现在将把依赖项合并到其“deps log”数据库中。在上面列出的生成器以外的生成器中使用
DEPFILE
会导致错误。如果
DEPFILE
参数是相对路径,它应该相对于CMAKE_CURRENT_BINARY_DIR
,并且DEPFILE
中的任何相对路径也应该相对于CMAKE_CURRENT_BINARY_DIR
。请参阅策略CMP0116
,该策略对 Makefile 生成器、Visual Studio 生成器 和Xcode
生成器始终为NEW
。此关键字不能与
APPEND
一起使用(请参阅策略CMP0175
)。只能在对add_custom_command(OUTPUT...)
的第一次调用中为输出文件设置 depfiles。
DEPENDS_EXPLICIT_ONLY
3.27 版本新增。
指示命令的
DEPENDS
参数表示命令所需的所有文件,并且不需要隐式依赖项。如果没有此选项,如果任何目标使用自定义命令的输出,CMake 将在自定义命令需要这些目标隐式创建的文件的情况下,将该目标的依赖项视为自定义命令的隐式依赖项。
可以通过将
CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY
设置为ON
,在所有自定义命令上启用此选项。此关键字不能与
APPEND
一起使用(请参阅策略CMP0175
)。只能在对add_custom_command(OUTPUT...)
的第一次调用中为输出文件设置它。只有 Ninja 生成器 实际上使用此信息来删除不必要的隐式依赖项。
另请参阅
OPTIMIZE_DEPENDENCIES
目标属性,它可能在某些情况下提供另一种减少目标依赖影响的方法。
示例:生成文件¶
自定义命令可用于生成源文件。例如,代码
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)
向库中添加一个自定义命令,运行 someTool
来生成 out.c
,然后编译生成的源文件。只要 in.txt
发生更改,生成规则就会重新运行。
版本 3.20 中添加: 可以使用生成器表达式指定每个配置的输出。例如,代码
add_custom_command(
OUTPUT "out-$<CONFIG>.c"
COMMAND someTool -i ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
-o "out-$<CONFIG>.c"
-c "$<CONFIG>"
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
VERBATIM)
add_library(myLib "out-$<CONFIG>.c")
向库中添加一个自定义命令,运行 someTool
来生成 out-<config>.c
,其中 <config>
是构建配置,然后编译生成的源文件。
版本 3.31 中添加: 使用 CODEGEN
选项将自定义命令的输出添加到内置的 codegen
目标中。这对于使生成的代码可用于静态分析而无需构建整个项目很有用。例如
add_executable(someTool someTool.c)
add_custom_command(
OUTPUT out.c
COMMAND someTool -o out.c
CODEGEN)
add_library(myLib out.c)
用户可以构建 codegen
目标来生成 out.c
。someTool
被构建为依赖项,但 myLib
完全没有构建。
示例:为多个目标生成文件¶
如果多个独立目标需要相同的自定义命令输出,则必须将其附加到它们都依赖的单个自定义目标。考虑以下示例
add_custom_command(
OUTPUT table.csv
COMMAND makeTable -i ${CMAKE_CURRENT_SOURCE_DIR}/input.dat
-o table.csv
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/input.dat
VERBATIM)
add_custom_target(generate_table_csv DEPENDS table.csv)
add_custom_command(
OUTPUT foo.cxx
COMMAND genFromTable -i table.csv -case foo -o foo.cxx
DEPENDS table.csv # file-level dependency
generate_table_csv # target-level dependency
VERBATIM)
add_library(foo foo.cxx)
add_custom_command(
OUTPUT bar.cxx
COMMAND genFromTable -i table.csv -case bar -o bar.cxx
DEPENDS table.csv # file-level dependency
generate_table_csv # target-level dependency
VERBATIM)
add_library(bar bar.cxx)
输出 foo.cxx
仅由目标 foo
需要,输出 bar.cxx
仅由目标 bar
需要,但两个目标都需要 table.csv
(传递性)。由于 foo
和 bar
是可以并发构建的独立目标,我们通过将生成 table.csv
的自定义命令放在一个单独的目标(generate_table_csv
)中来防止它们争用生成 table.csv
。生成 foo.cxx
和 bar.cxx
的自定义命令分别指定了对 generate_table_csv
的目标级依赖项,因此使用它们的 target(foo
和 bar
)在目标 generate_table_csv
构建完成之前不会构建。
构建事件¶
第二个签名将自定义命令添加到目标,例如库或可执行文件。这对在构建目标之前或之后执行操作很有用。该命令成为目标的一部分,并且仅在目标本身构建时执行。如果目标已经构建,则命令将不会执行。
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]
[COMMAND_EXPAND_LISTS])
这定义了一个新命令,该命令将与构建指定的 <target>
相关联。 <target>
必须在当前目录中定义;其他目录中定义的目标不得指定。
命令何时发生取决于指定了以下哪一项
PRE_BUILD
此选项对于 Visual Studio 生成器 具有独特行为。使用 Visual Studio 生成器之一时,该命令将在目标内执行任何其他规则之前运行。对于所有其他生成器,此选项的行为与
PRE_LINK
相同。因此,建议不要使用PRE_BUILD
,除非已知正在使用 Visual Studio 生成器。PRE_LINK
在编译源代码之后但链接二进制文件或运行静态库的库工具或存档工具之前运行。这对于由
add_custom_target()
命令创建的目标未定义。POST_BUILD
在目标内所有其他规则执行完毕后运行。
使用 TARGET
形式时,项目应始终指定上述三个关键字之一。请参阅策略 CMP0175
。
上述签名中显示的所有其他关键字都与其在 add_custom_command(OUTPUT)
命令形式中的含义相同。必须至少给出一个 COMMAND
,请参阅策略 CMP0175
。
注意
由于生成器表达式可以在自定义命令中使用,因此可以定义 COMMAND
行或整个自定义命令,这些命令或自定义命令对于某些配置将评估为空字符串。对于 Visual Studio 生成器,这些命令行或自定义命令将被特定配置省略,并且不会添加任何“空字符串命令”。
这允许为每个配置添加单独的构建事件。
版本 3.21 中添加: 支持目标相关的生成器表达式。
版本 3.29 中添加: <target>
可以是 别名目标。
示例:构建事件¶
POST_BUILD
事件可用于在链接后对二进制文件进行后处理。例如,代码
add_executable(myExe myExe.c)
add_custom_command(
TARGET myExe POST_BUILD
COMMAND someHasher -i "$<TARGET_FILE:myExe>"
-o "$<TARGET_FILE:myExe>.hash"
VERBATIM)
将在链接后运行 someHasher
来生成可执行文件旁边的 .hash
文件。
版本 3.20 中添加: 可以使用生成器表达式指定每个配置的副产品。例如,代码
add_library(myPlugin MODULE myPlugin.c)
add_custom_command(
TARGET myPlugin POST_BUILD
COMMAND someHasher -i "$<TARGET_FILE:myPlugin>"
--as-code "myPlugin-hash-$<CONFIG>.c"
BYPRODUCTS "myPlugin-hash-$<CONFIG>.c"
VERBATIM)
add_executable(myExe myExe.c "myPlugin-hash-$<CONFIG>.c")
将在链接 myPlugin
后运行 someHasher
,例如,生成包含代码的 .c
文件,该代码用于检查 myPlugin
的哈希值, myExe
可执行文件可以在加载之前使用它来验证 myPlugin
。
Ninja 多配置¶
版本 3.20 中添加: add_custom_command
支持 Ninja Multi-Config
生成器的跨配置功能。有关更多信息,请参阅生成器文档。