add_custom_command¶
向生成的构建系统添加自定义构建规则。
概要¶
Generating Files add_custom_command(OUTPUT <output1> [<output2> ...] COMMAND <command1> [ARGS] [<args1>...] [...]) Build Events add_custom_command(TARGET <target> PRE_BUILD | PRE_LINK | POST_BUILD [...])
生成文件¶
- add_custom_command(OUTPUT <output1> [<output2> ...] COMMAND <command1> [ARGS] [<args1>...] [...])¶
添加自定义命令以产生输出
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的参数可以使用受限的生成器表达式。不允许使用与目标相关的表达式。3.28 版本更改: 在使用 文件集 (File Sets) 的目标中,自定义命令的副产品现在被视为私有,除非它们在非私有文件集中列出。参阅策略
CMP0154。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的参数可以使用生成器表达式。使用TARGET_FILE生成器表达式可以在命令行稍后处引用目标位置(即作为命令参数,而非作为要执行的命令)。每当以下基于目标的生成器表达式被用作执行命令或在命令参数中提到时,将自动添加一个目标级依赖,以便所述目标将在任何使用此自定义命令的目标之前构建(参阅策略
CMP0112)。TARGET_FILETARGET_LINKER_FILETARGET_SONAME_FILETARGET_PDB_FILE
此目标级依赖不会添加文件级依赖,因此不会导致自定义命令在可执行文件被重新编译时重新运行。请使用
DEPENDS选项列出目标名称以添加此类文件级依赖。COMMENT在构建时执行命令之前显示给定的消息。如果给出了
APPEND,此消息将被忽略,但未来版本可能会使用它。3.26 版本新增:
COMMENT的参数可以使用生成器表达式。DEPENDS指定命令所依赖的文件。每个参数转换为依赖项的方式如下:
如果参数是目标名称(由
add_custom_target()、add_executable()或add_library()创建),则会创建一个目标级依赖,以确保该目标在任何使用此自定义命令的目标之前构建。此外,如果目标是可执行文件或库,则会创建一个文件级依赖,导致自定义命令在目标重新编译时重新运行。如果参数是绝对路径,则对该路径创建文件级依赖。
如果参数是已添加到目标或已设置源文件属性的源文件名称,则对该源文件创建文件级依赖。
如果参数是相对路径且存在于当前源目录中,则对当前源目录中的该文件创建文件级依赖。
否则,对相对于当前二进制目录的该路径创建文件级依赖。
如果任何依赖项是同一目录(
CMakeLists.txt文件)中另一个自定义命令的OUTPUT,CMake 会自动将另一个自定义命令引入到构建此命令的目标中。3.16 版本新增: 如果任何依赖项在同一目录中被列为目标或其构建事件的
BYPRODUCTS,则添加一个目标级依赖以确保副产品可用。如果未指定
DEPENDS,则命令将在OUTPUT缺失时运行;如果命令实际上没有创建OUTPUT,则该规则将始终运行。3.1 版本新增:
DEPENDS的参数可以使用生成器表达式。COMMAND_EXPAND_LISTS版本 3.8 新增。
COMMAND参数中的列表将被展开(包括通过生成器表达式创建的列表),允许正确展开类似${CC} "-I$<JOIN:$<TARGET_PROPERTY:foo,INCLUDE_DIRECTORIES>,;-I>" foo.cc的COMMAND参数。此关键字不能与
APPEND一起使用(参阅策略CMP0175)。如果附加命令需要设置此选项,则必须在第一次调用add_custom_command(OUTPUT...)时设置它。CODEGEN在版本 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版本 3.15 新增。
为
Ninja生成器指定一个池 (pool)。与隐含console池的USES_TERMINAL不兼容。使用未由JOB_POOLS定义的池会导致 ninja 在构建时报错。此关键字不能与
APPEND一起使用(参阅策略CMP0175)。任务池只能在第一次调用add_custom_command(OUTPUT...)时指定。JOB_SERVER_AWARE版本 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的参数可以使用受限的生成器表达式。不允许使用与目标相关的表达式。3.28 版本更改: 在使用 文件集 (File Sets) 的目标中,自定义命令的输出现在被视为私有,除非它们在非私有文件集中列出。参阅策略
CMP0154。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在给定的当前工作目录中执行命令。如果它是相对路径,则解释为相对于当前源目录对应的构建树目录。如果未指定,则设置为
CMAKE_CURRENT_BINARY_DIR。如果给出了
APPEND,此选项目前会被忽略,但未来版本可能会使用它。3.13 版本新增:
WORKING_DIRECTORY的参数可以使用生成器表达式。DEPFILE3.7 版本中新增。
指定一个包含自定义命令依赖项的依赖文件 (depfile)。它通常由自定义命令本身发出。此关键字仅可在生成器支持的情况下使用,如下详述。
预期的格式与
gcc使用-M选项生成的格式兼容,与生成器或平台无关。使用具有常规扩展的 BNF 符号指定的正式语法如下:
depfile ::=
rule* rule ::=targets(':' (separatordependencies?)?)?eoltargets ::=target(separatortarget)*separator* target ::=pathnamedependencies ::=dependency(separatordependency)*separator* dependency ::=pathnameseparator ::= (space|line_continue)+ line_continue ::= '\'eolspace ::= ' ' | '\t' pathname ::=character+ character ::=std_character|dollar|hash|whitespacestd_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 生成器 的支持。
注意
对于 Makefile 生成器,
DEPFILE不能与IMPLICIT_DEPENDS选项同时指定。3.21 版本新增: 添加了对 VS 2012 及以上版本的 Visual Studio 生成器 以及
Xcode生成器的支持。同时也添加了对生成器表达式的支持。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...)时设置。
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 中,来防止它们竞争生成该文件。生成 foo.cxx 和 bar.cxx 的自定义命令各自指定了对 generate_table_csv 的目标级依赖,因此使用它们的目标 foo 和 bar 在 generate_table_csv 目标构建完成之前不会构建。
构建事件¶
- add_custom_command(TARGET <target> PRE_BUILD | PRE_LINK | POST_BUILD [...])¶
向诸如库或可执行文件的目标添加构建事件。这对于在构建目标之前或之后执行操作非常有用。该命令成为目标的一部分,且仅在目标自身被构建时执行。如果目标已构建,则该命令不会执行。
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] [USES_TERMINAL])
此操作定义了一个将与构建指定
<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> 可以是 别名目标 (ALIAS 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,例如生成一个包含检查 myPlugin 哈希值代码的 .c 文件,以便 myExe 可执行文件在加载之前可以使用它进行验证。
Ninja 多配置¶
3.20 版本新增: add_custom_command 支持 Ninja Multi-Config 生成器的跨配置功能。有关更多信息,请参阅生成器文档。