CMake的add_custom_command命令没有执行

77

我正在尝试使用add_custom_command在编译期间生成一个文件。该命令似乎从未被执行,因此我创建了这个测试文件。

cmake_minimum_required( VERSION 2.6 )

add_custom_command(
  OUTPUT hello.txt
  COMMAND touch hello.txt
  DEPENDS hello.txt
)

我试着运行:
cmake .  
make

而且hello.txt没有生成。我做错了什么?


add_custom_target可以作为add_custom_command的替代方案。 - bartolo-otrit
请注意,您的示例代码肯定是有问题的。您不能同时输出 hello.txt 并依赖于 hello.txt - Alexis Wilke
4个回答

78

add_custom_target(run ALL ...这个解决方案可以在构建单一目标时正常工作,但是当你有多个顶层目标(例如app和tests)时,它会失效。

我遇到了同样的问题,当我试图将一些测试数据文件打包成对象文件,以便我的单元测试不依赖于任何外部文件时。我使用add_custom_command和一些额外的依赖关系魔法以及set_property解决了这个问题。

add_custom_command(
  OUTPUT testData.cpp
  COMMAND reswrap 
  ARGS    testData.src > testData.cpp
  DEPENDS testData.src 
)
set_property(SOURCE unit-tests.cpp APPEND PROPERTY OBJECT_DEPENDS testData.cpp)

add_executable(app main.cpp)
add_executable(tests unit-tests.cpp)

现在,testData.cpp将在编译unit-tests.cpp之前生成,并且每当testData.src更改时都会重新生成。如果你调用的命令非常慢,那么当你只构建app目标时,你不必等待该命令完成(因为只有测试可执行文件需要它),这是额外的好处。

虽然上面没有显示,但是小心使用${PROJECT_BINARY_DIR}、${PROJECT_SOURCE_DIR}和include_directories()会使您的源代码树不再包含生成的文件。


8
有时候最佳答案并不是那个有绿色勾选图标的答案,这种情况让人感到尴尬 :) 谢谢 Rian! - user405725
2
“add_dependencies” 不应该能够完成 “set_property(…” 行的工作吗? - dom0
CMake有很多其他好处。我真正喜欢的主要是生成器(Makefiles、ninja文件、Visual Studio、Eclipse等)。CMake语言不像任何常规高级编程语言那样发展,但一旦你掌握了它,就非常容易,只需要几个小时。Autotools曾经很受欢迎。想想这个:为什么有这么多人使用CMake而不是Autotools?它更容易学习,并提供了更多的好处。 - leodotcloud
2
@dom0,你可以使用add_dependencies来实现,但是这有点棘手。你不能直接在add_custom_command和其他东西之间添加依赖关系,你首先必须创建一个add_custom_target(它是空的,只是提供一个你可以稍后命名的目标)。原因是add_dependencies只能将目标作为参数,而不能将文件作为参数。请参阅此博客以了解更多信息:https://samthursfield.wordpress.com/2015/11/21/cmake-dependencies-between-targets-and-files-and-custom-commands/ - Elliott Slaughter
更尴尬的是,最佳答案下面两个答案中的另一个才是最好的答案,而且应该作为答案的第一条评论听起来很专业,而不是讽刺。 - activedecay

48

现有两个答案的问题在于它们要么将依赖关系全局化(add_custom_target(name ALL ...)),要么将其分配给特定的单个文件(set_property(...)),如果您有许多需要该依赖项的文件,则会变得麻烦。相反,我们希望有一个目标,可以使它成为另一个目标的依赖项。

实现这一点的方法是使用add_custom_command定义规则,然后使用add_custom_target基于该规则定义新目标。然后,您可以通过add_dependencies将该目标添加为另一个目标的依赖项。

# this defines the build rule for some_file
add_custom_command(
  OUTPUT some_file
  COMMAND ...
)
# create a target that includes some_file, this gives us a name that we can use later
add_custom_target(
  some_target
  DEPENDS some_file
)
# then let's suppose we're creating a library
add_library(some_library some_other_file.c)
# we can add the target as a dependency, and it will affect only this library
add_dependencies(some_library some_target)

这种方法的优点:

  • some_target 不是 ALL 的依赖项,这意味着仅在特定目标需要时才构建它。(而 add_custom_target(name ALL ...) 会无条件地为所有目标构建它。)
  • 因为 some_target 是整个库的依赖项,所以将在该库中的所有文件之前构建它。这意味着如果库中有很多文件,我们不必在每个文件上都执行 set_property
  • 如果我们在 add_custom_command 中添加 DEPENDS,则只有在其输入更改时才会重新构建它。(与使用 add_custom_target(name ALL ...) 的方法相比,该命令会在每次构建时运行,而不管它是否需要。)

有关为什么会这样工作的更多信息,请参阅此博客文章:https://samthursfield.wordpress.com/2015/11/21/cmake-dependencies-between-targets-and-files-and-custom-commands/


46

添加以下内容:

add_custom_target(run ALL
    DEPENDS hello.txt)

如果您熟悉makefile,那么这意味着:

all: run
run: hello.txt

1
这个不行,对我来说,在CMake 3.6.1和OSX上。我已经在我的CMakeLists.txt中添加了以下内容:add_custom_command( OUTPUT hello.txt COMMAND touch ARGS hello.txt DEPENDS hello.txt ) 并添加了add_custom_target(run ALL DEPENDS hello.txt ) - linello
2
每次都运行add_custom_target,建议改用Rian提倡的add_custom_command - Anne van Rossum
1
@linello 请从你的 add_custom_command() 调用中删除 DEPENDS,它会创建循环依赖。只有 add_custom_target() 应该在这里使用 DEPENDS 参数。当你修复这个问题时,它在我的 OS X 上可以工作(使用 CMake 3.8.0 进行测试)。 - Craig Scott

0

这个问题很老了,但即使我按照建议的推荐去做,也不总是有效。

我正在使用Android Studio,并且需要调用cMake来构建C++库。它可以正常工作,直到我添加运行自定义脚本的代码(实际上,我现在尝试运行'touch',就像上面的例子一样)。

首先,

add_custom_command

完全不起作用。
我已经尝试过了

execute_process (
        COMMAND touch hello.txt
)

它有时候工作,但不总是如此!
我尝试了清理项目,手动删除已创建的文件等方法,但效果一样。
尝试了cMake版本:
3.10.2
3.18.1
3.22.1
当它们工作时,它们会根据cMake版本产生不同的结果,可能是一个文件或几个文件。这并不重要,只要它们能够工作就好,但问题就在这里。
有人可以为这个谜团提供一些线索吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接