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

COMMANDDEPENDS 选项的值附加到为第一个指定输出的自定义命令。必须已经有一次使用相同输出的此命令的先前调用。

如果先前的调用通过生成器表达式指定了输出,则当前调用指定的输出必须在评估生成器表达式后至少在一个配置中匹配。在这种情况下,附加的命令和依赖项适用于所有配置。

目前,当使用 APPEND 时,COMMENTMAIN_DEPENDENCYWORKING_DIRECTORY 选项将被忽略,但未来可能会使用它们。

BYPRODUCTS

版本 3.2 中新增。

指定命令预计会生成但其修改时间可能比依赖项更新或不更新的文件。如果副产品名称是相对路径,它将相对于当前源目录对应的构建树目录进行解释。每个副产品文件将自动标记 GENERATED 源文件属性。

有关此功能的动机,请参阅策略 CMP0058

对副产品的显式指定得到了 Ninja 生成器的支持,用于告知 ninja 构建工具在副产品丢失时如何重新生成它们。当其他构建规则(例如自定义命令)依赖于副产品时,它也很有用。Ninja 要求为任何生成的依赖于它的文件提供一个构建规则,即使有仅顺序依赖项,以确保副产品在其依赖项构建之前可用。

Makefile Generators 将在 make clean 时删除 BYPRODUCTS 和其他 GENERATED 文件。

此关键字不能与 APPEND 一起使用(请参阅策略 CMP0175)。所有副产品必须在第一次调用 add_custom_command(OUTPUT...) 来指定输出文件时设置。

版本 3.20 中添加: BYPRODUCTS 的参数可以使用受限的 生成器表达式。不允许使用 与目标相关的表达式

版本 3.28 中更改: 在使用 文件集 的目标中,自定义命令副产品现在被视为私有的,除非它们列在非私有文件集中。请参阅策略 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_FILE

  • TARGET_LINKER_FILE

  • TARGET_SONAME_FILE

  • TARGET_PDB_FILE

此目标级别的依赖关系**不会**添加一个文件级别的依赖关系,该依赖关系会导致自定义命令在可执行文件重新编译时重新运行。列出目标名称时使用 DEPENDS 选项来添加此类文件级别的依赖关系。

COMMENT

在构建时执行命令之前显示给定的消息。如果指定了 APPEND,则此选项将被忽略,尽管未来版本可能会使用它。

版本 3.26 中添加: COMMENT 的参数可以使用 生成器表达式

DEPENDS

指定命令依赖的文件。每个参数都将转换为一个依赖项,如下所示:

  1. 如果参数是目标名称(由 add_custom_target()add_executable()add_library() 命令创建),则会创建一个目标级别的依赖关系,以确保在任何使用此自定义命令的目标之前构建该目标。此外,如果目标是可执行文件或库,则会创建一个文件级别的依赖关系,以导致自定义命令在目标重新编译时重新运行。

  2. 如果参数是绝对路径,则会在该路径上创建文件级别依赖。

  3. 如果参数是已添加到目标或已设置了源文件属性的源文件名称,则会在该源文件上创建文件级别依赖。

  4. 如果参数是相对路径并且它存在于当前源目录中,则会在当前源目录中的该文件上创建文件级别依赖。

  5. 否则,会在相对于当前二进制目录的该路径上创建文件级别依赖。

如果任何依赖项是同一目录中另一个自定义命令的 OUTPUT,CMake 会自动将其他自定义命令添加到构建此命令的目标中。

版本 3.16 中添加: 如果在同一目录中,任何依赖项被列为目标或其任何构建事件的 BYPRODUCTS,则会添加一个目标级别的依赖关系,以确保副产品可用。

如果未指定 DEPENDS,则当 OUTPUT 缺失时,命令将运行;如果命令实际上没有创建 OUTPUT,则规则将始终运行。

版本 3.1 中添加: DEPENDS 的参数可以使用 生成器表达式

COMMAND_EXPAND_LISTS

版本 3.8 新增。

COMMAND 参数中的列表将被展开,包括使用 生成器表达式 创建的列表,允许 COMMAND 参数(如 ${CC} "-I$<JOIN:$<TARGET_PROPERTY:foo,INCLUDE_DIRECTORIES>,;-I>" foo.cc)被正确展开。

此关键字不能与 APPEND 一起使用(请参阅策略 CMP0175)。如果附加的命令需要设置此选项,则必须在第一次调用 add_custom_command(OUTPUT...) 来指定输出文件时设置。

CODEGEN

在版本 3.31 中添加。

将自定义命令添加到全局 codegen 目标,该目标可用于执行自定义命令,同时避免大部分构建图。

此选项仅受 Ninja GeneratorsMakefile Generators 支持,其他生成器将忽略它。此外,只有当策略 CMP0171 设置为 NEW 时,才允许此选项。

此关键字不能与 APPEND 一起使用(请参阅策略 CMP0175)。它只能在第一次调用 add_custom_command(OUTPUT...) 来指定输出文件时设置。

IMPLICIT_DEPENDS

请求扫描输入文件的隐式依赖项。指定的语言指定了应使用其对应的依赖项扫描仪的编程语言。目前仅支持 CCXX 语言扫描仪。语言必须在 IMPLICIT_DEPENDS 列表中为每个文件指定。从扫描中发现的依赖项将在构建时添加到自定义命令的依赖项中。请注意,IMPLICIT_DEPENDS 选项目前仅支持 Makefile 生成器,其他生成器将忽略它。

注意

此选项不能与 DEPFILE 选项同时指定。

JOB_POOL

版本 3.15 新增。

Ninja 生成器指定一个 。与 USES_TERMINAL 不兼容,后者隐含 console 池。使用未由 JOB_POOLS 定义的池会在构建时导致 ninja 出错。

此关键字不能与 APPEND 一起使用(请参阅策略 CMP0175)。作业池只能在第一次调用 add_custom_command(OUTPUT...) 来指定输出文件时设置。

JOB_SERVER_AWARE

版本 3.28 新增。

指定该命令是 GNU Make 作业服务器感知的。

对于 Unix MakefilesMSYS MakefilesMinGW Makefiles 生成器,这将在配方行中添加 + 前缀。有关更多信息,请参阅 GNU Make 文档

其他生成器将静默忽略此选项。

此关键字不能与 APPEND 一起使用(请参阅策略 CMP0175)。作业服务器感知只能在第一次调用 add_custom_command(OUTPUT...) 来指定输出文件时设置。

MAIN_DEPENDENCY

指定命令的主要输入源文件。这就像给 DEPENDS 选项提供的任何值一样,但还建议 Visual Studio Generators 在何处附加自定义命令。每个源文件最多可以有一个命令指定它为主依赖项。编译命令(即库或可执行文件)被视为隐式主依赖项,该依赖项会被自定义命令规范静默覆盖。

如果给出 APPEND,则目前会忽略此选项,但未来版本可能会使用它。

OUTPUT

指定命令预计会生成的目标文件。每个输出文件将自动标记 GENERATED 源文件属性。如果自定义命令的输出实际上并未作为磁盘文件创建,则应将其标记为 SYMBOLIC 源文件属性。

如果输出文件名是相对路径,则其绝对路径通过解释相对于

  1. 当前源目录对应的构建目录(CMAKE_CURRENT_BINARY_DIR),或

  2. 当前源目录(CMAKE_CURRENT_SOURCE_DIR)。

构建目录中的路径优先,除非源树中的路径在当前目录的其他地方被引用为绝对源文件路径。

输出文件路径不能包含 <> 字符。

版本 3.20 中添加: OUTPUT 的参数可以使用受限的 生成器表达式。不允许使用 与目标相关的表达式

版本 3.28 中更改: 在使用 文件集 的目标中,自定义命令输出现在被视为私有的,除非它们列在非私有文件集中。请参阅策略 CMP0154

版本 3.30 中更改: 输出文件路径现在可以使用 # 字符,除非使用 Borland Makefiles 生成器。

USES_TERMINAL

版本 3.2 中新增。

如果可能,该命令将被授予直接访问终端的权限。使用 Ninja 生成器时,它会将命令放入 console

此关键字不能与 APPEND 一起使用(请参阅策略 CMP0175)。如果附加的命令需要访问终端,则必须在第一次调用 add_custom_command(OUTPUT...) 来指定输出文件时设置。

VERBATIM

命令的所有参数都将被正确转义以供构建工具使用,以便调用的命令接收每个参数不变。请注意,CMake 语言处理器在 add_custom_command 看到参数之前仍然会进行一层转义。建议使用 VERBATIM,因为它能确保正确行为。当不给出 VERBATIM 时,行为是平台特定的,因为没有对工具特定的特殊字符进行保护。

此关键字不能与 APPEND 一起使用(请参阅策略 CMP0175)。如果附加的命令需要作为 VERBATIM 处理,则必须在第一次调用 add_custom_command(OUTPUT...) 来指定输出文件时设置。

WORKING_DIRECTORY

使用给定的当前工作目录执行命令。如果它是相对路径,则会相对于当前源目录对应的构建树目录进行解释。如果未指定,则设置为 CMAKE_CURRENT_BINARY_DIR

如果给出 APPEND,则目前会忽略此选项,但未来版本可能会使用它。

版本 3.13 中添加: WORKING_DIRECTORY 的参数可以使用 生成器表达式

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 Generators 的支持。

注意

DEPFILE 不能与 Makefile GeneratorsIMPLICIT_DEPENDS 选项同时指定。

版本 3.21 中添加: 添加了对 VS 2012 及更高版本的 Visual Studio GeneratorsXcode 生成器的支持。还添加了对 生成器表达式 的支持。

版本 3.29 中添加: 如果文件未列在 OUTPUTSBYPRODUCTS 中,Ninja Generators 将会把依赖项合并到其 "deps log" 数据库中。

DEPFILE 与上面未列出的生成器一起使用会报错。

如果 DEPFILE 参数是相对的,则它应该相对于 CMAKE_CURRENT_BINARY_DIR,并且 DEPFILE 中的任何相对路径也应相对于 CMAKE_CURRENT_BINARY_DIR。请参阅策略 CMP0116,对于 Makefile GeneratorsVisual Studio GeneratorsXcode 生成器,它始终为 NEW

此关键字不能与 APPEND 一起使用(请参阅策略 CMP0175)。Depfiles 只能在第一次调用 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 Generators 实际使用此信息来删除不必要的隐式依赖项。

另请参阅 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.csomeTool 作为依赖项被构建,但 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。由于 foobar 是独立的、可能并发构建的目标,我们通过将 table.csv 的自定义命令放在一个单独的目标 generate_table_csv 中来防止它们之间争用 table.csv 的生成。生成 foo.cxxbar.cxx 的自定义命令各自指定了对 generate_table_csv 的目标级别依赖,因此使用它们的 foobar 目标将在 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]
                   [USES_TERMINAL])

这定义了一个新命令,它将与构建指定 <target> 相关联。该 <target> 必须在当前目录中定义;不能指定在其他目录中定义的目标。

命令何时发生取决于指定了以下哪个项:

PRE_BUILD

此选项对于 Visual Studio Generators 具有独特的行为。在使用 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 Generators,这些命令或自定义命令将针对特定配置被省略,并且不会添加 "空字符串命令"。

这允许为每个配置添加单独的构建事件。

版本 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,例如生成一个包含用于检查 myPlugin 哈希值的代码的 .c 文件,该文件可供 myExe 可执行文件在加载前使用。

Ninja 多配置

版本 3.20 中添加: add_custom_command 支持 Ninja Multi-Config 生成器的跨配置功能。有关更多信息,请参阅生成器文档。

另请参阅