为什么CMake要区分“目标”和“命令”?

时间:2012-08-15 15:07:48

标签: cmake

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

targetname:dependency
    command

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

在CMake中,你有“add_custom_command”和“add_custom_target”等具有重叠功能的命令,甚至在官方文档中也会混淆语义,即在“掌握CMake,第5版”,第110页“添加自定义目标”下“:

  

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

我的理解是目标(生成的文件)具有依赖性(其他文件,生成或否),以及实际生成的命令。说目标取决于命令是没有意义的。更糟糕的是,有两种“add_custom_command”可以将附加命令附加到现有目标,或者将命令吐出到以太中。

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

2 个答案:

答案 0 :(得分:40)

定位

通常,目标包含可执行文件或库,这些文件是通过调用add_executableadd_library来定义的,并且可以设置很多properties

它们可以彼此依赖,对于这些目标而言,这意味着依赖的目标将在它们的依赖之后构建。

但是,您也可以通过add_custom_target定义“自定义目标”。来自文档:

  

添加具有执行给定命令的给定名称的目标。目标没有输出文件,即使命令尝试创建具有目标名称的文件,也始终在日期之后考虑。使用ADD_CUSTOM_COMMAND生成具有依赖项的文件。默认情况下,不依赖于自定义目标。使用ADD_DEPENDENCIES向其他目标添加依赖项。

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


自定义命令

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

它基本上是一个命令(或命令集),它将在构建依赖目标之前调用。这就是“依赖”在这里真正意味着什么(至少我是如何看待它) - 它只是说如果A依赖于B,那么B将在构建A之前构建/执行。

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

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

在第二种情况下,它有点复杂。如果自定义命令具有依赖于其输出文件的目标(并且输出文件尚不存在),则在构建这些依赖对象之前调用它。当您执行以下操作时,将隐式创建依赖关系。 add_library(MyLib output1.h ... )其中output1.h是通过add_custom_command(OUTPUT output1.h ... )生成的文件。

答案 1 :(得分:0)

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,它取决于自定义命令的输出。任何想要输出的人都取决于目标,这确实可以为你提供所需的保证。

警告:请参阅this bug,了解构建跨平台元构建工具真的很难的一个很好的例子。