72

在 CMake 语义中,“目标”和“命令”之间存在某种区别,这让我感到困惑。在 Makefiles 中,没有这样的区别:

targetname:dependency
    command

即目标对应于同名的生成文件。

在 CMake 中,您有诸如“add_custom_command”和“add_custom_target”之类的命令,它们具有重叠的功能,甚至在官方文档中,语义也很混乱,即在“Mastering CMake,第 5 版”中,第 110 页的“添加自定义目标”下:

DEPENDS 参数设置自定义目标和自定义命令之间的依赖关系。

我的理解是目标(生成的文件)具有依赖关系(其他文件,生成或不生成),以及实际执行生成的命令。说目标取决于命令是荒谬的。更糟糕的是,“add_custom_command”有两种风格,它们要么将附加命令附加到现有目标,要么将命令吐出到以太中。

有人可以解释为什么这种区别甚至存在吗?

4

2 回答 2

56

目标

add_executable通常,目标包括通过调用or定义的可执行文件或库,add_library它们可以设置许多属性

它们可以相互依赖,这对于诸如此类的目标仅意味着依赖的目标将在它们的依赖之后构建。

但是,您也可以通过add_custom_target. 从文档:

添加具有给定名称的目标以执行给定命令。目标没有输出文件,并且即使命令尝试使用目标名称创建文件,也总是被认为已过期。使用 ADD_CUSTOM_COMMAND 生成具有依赖关系的文件。默认情况下,没有任何东西取决于自定义目标。使用 ADD_DEPENDENCIES 将依赖项添加到其他目标或从其他目标添加依赖项。

因此,这些与“普通”目标不同,因为它们不代表会产生 exe 或 lib 的东西,但它们仍然受益于目标可以拥有的所有属性,包括拥有或成为依赖项。它们显示为可以构建的目标(例如make MyCustomTargetmsbuild MyCustomTarget.vcxproj)。构建它们时,您只是调用为它们设置的命令。如果它们依赖于其他目标(普通或自定义),那么将首先构建这些目标。


自定义命令

via 定义的自定义命令add_custom_command完全不同,因为它不是“可构建”对象,并且不像目标那样具有可设置的属性 - 它不是命名对象,可以在添加后再次显式引用CMakeLists.txt。

它基本上是一个命令(或一组命令),在构建依赖目标之前将被调用。这就是“依赖”在这里的真正含义(至少我是这么看的)——它只是说如果 A 依赖于 B,那么 B 将在 A 构建之前构建/执行。

自定义命令的依赖项可以使用add_custom_command(TARGET target ...表单显式设置,也可以通过创建包含通过add_custom_command(OUTPUT output1 ...表单生成的文件的目标来隐式设置。

在第一种情况下,每次target构建时,都会先执行自定义命令。

在第二种情况下,它有点复杂。如果自定义命令具有依赖于其输出文件的目标(并且输出文件尚不存在),则在构建这些依赖对象之前调用它。当您这样做时,会隐式创建依赖项,add_library(MyLib output1.h ... )例如output1.h通过add_custom_command(OUTPUT output1.h ... ).

于 2012-08-15T23:12:37.470 回答
7

add_custom_command 添加一个可以定义输出的可调用函数(使用 OUTPUT 和 BYPRODUCTS 参数)。它还可以具有将在调用函数之前运行的依赖项。

请注意,由于奇怪的文档(makefile 示例非常具有误导性),它不会做您可能认为它会做的事情。特别是,它对执行的次数没有任何保证。例如,想象一下:

add_custom_command(OUTPUT /tmp/touched COMMAND echo touch COMMAND touch /tmp/touched)
add_custom_target(touched-one ALL DEPENDS /tmp/touched)
add_custom_target(touched-two ALL DEPENDS /tmp/touched)

“触摸”会打印多少次?你不知道,因为它没有在任何地方指定;make -j2 可能会打印两次,但它取决于时间:

Scanning dependencies of target touched-two
Scanning dependencies of target touched-one
[ 50%] Generating touched
touch
[100%] Generating touched
touch
[100%] Built target touched-two
[100%] Built target touched-one

但是 Ninja 只会打印一次,可能是:

# rm -rf * && cmake -GNinja ../c ; cmake --build . -- -j 5
[1/1] Generating touched
touch

通常,您将执行 add_custom_command 来完成一些工作并定义一个 OUTPUT,然后您将拥有一个依赖于自定义命令输出的 add_custom_target。任何想要输出的人都取决于目标,这确实给了你想要的保证。

警告:请参阅此错误,了解为什么构建跨平台元构建工具非常困难的一个很好的例子

于 2018-04-03T14:52:22.017 回答