【问题标题】:add_custom_command does not re-run on failureadd_custom_command 在失败时不会重新运行
【发布时间】:2017-01-14 03:32:47
【问题描述】:

在 CMake 中,我想将 add_custom_command(... POST_BUILD ...) 与可能会失败的 COMMAND 一起使用。

观察

  1. 第一次运行make会失败,因为add_custom_command( ... COMMAND exit 1)的退出码不是0。 --> 这是我所期望的。
  2. 第二次运行make会通过,因为指定的命令 在add_custom_command 中将不会再次运行。 --> 这不是我想要的。我希望 make 在自定义命令起作用之前失败。

最小失败示例

project(Foo)
cmake_minimum_required(VERSION 3.2)


# Create main.cc
##include <iostream>
#
#int main() {
#  std::cout << "Hello, World!" << std::endl;
#}
add_executable(main main.cc)

add_custom_command(TARGET main POST_BUILD
          COMMAND exit 1  # In the real example, I am changing capabilities of the resulting binary with /sbin/setcap, which might fail.
          COMMENT "Doing stuff."
          )

问题

  1. 如何解决此问题?
  2. 这是 CMake 的预期行为吗?

一个解决方案

我知道我可以创建一个不是POST_BUILD 的自定义命令,而是在成功时输出一个文件TARGET.passed。但是,我想避免这种情况。因为 POST_BUILD 似乎是这里最合适的用法。 (我正在更改生成文件的功能。)

【问题讨论】:

  • 嗯,in another question 观察到相反的行为:目标文件在命令失败时被删除。在任何情况下,您都可以在失败之前手动执行此操作:COMMAND rm $&lt;TARGET_FILE:main&gt; &amp;&amp; exit 1.
  • @Tsyvarev,谢谢你的链接。我试图调查并运行您链接到的问题中的代码。我的发现:我的代码和另一个代码都具有相同的行为。但是我在Linux(Ubuntu 14.04,cmake 3.2.2)上写了这个问题,而我只是在macOS Sierra(cmake 3.7.1)下检查了这些代码。
  • 我刚刚在 (docker) ubuntu:14.04 (cmake 2.8.12.2) 上测试:存在错误。 ubuntu:16.04 (cmake 3.5.1): 没有错误。 -- 我决定称它为错误,因为新版本具有可重现的行为,而旧版本不可重现。最后,我搜索了从 CMake 3.3 到 3.7 的发行说明,并没有发现提及此行为的变化。

标签: cmake cmake-custom-command


【解决方案1】:

这曾经是 CMake 中的一个错误。

它已在以下提交中修复:

commit 4adf1dad2a6e462364bae81030c928599d11c24f
Author: Brad King <brad.king@kitware.com>
Date:   Mon Mar 30 16:32:26 2015 -0400

    Makefile: Tell GNU make to delete rule outputs on error (#15474)

    Add .DELETE_ON_ERROR to the "build.make" files that contain the actual
    build rules that generate files.  This tells GNU make to delete the
    output of a rule if the recipe modifies the output but returns failure.
    This is particularly useful for custom commands that use shell
    redirection to produce a file.

    Do not add .DELETE_ON_ERROR for Borland or Watcom make tools because
    they may not tolerate it and would not honor it anyway.  Other make
    tools that do not understand .DELETE_ON_ERROR will not be hurt.

    Suggested-by: Andrey Vihrov <andrey.vihrov@gmail.com>

具有该修复的最早版本是 CMake 3.3.0

解决方法

  • 创建一个附加的输出目标,该目标取决于成功运行的自定义命令。 (下面这个叫main.done
  • 将自定义命令从POST_BUILD 上的TARGET 切换为独立命令。因此将其设为OUTPUT 一个文件(以下main.intermediate_step)。
  • 使用选项运行第二个COMMAND 以使用touch 创建所述文件。

代码:

project(Foo)
cmake_minimum_required(VERSION 3.2)


# Create main.cc
##include <iostream>
#
#int main() {
#  std::cout << "Hello, World!" << std::endl;
#}
add_executable(main main.cc)

add_custom_command(TARGET main POST_BUILD
          COMMAND exit 1  # In the real example, I am changing capabilities of the resulting binary with /sbin/setcap, which might fail.
          COMMENT "Doing stuff."
          )

add_custom_command(OUTPUT main.intermediate_step
                  COMMAND  exit 1  # In the real example, I am changing capabilities of the resulting binary with /sbin/setcap, which might fail.
                  COMMAND touch main_setcap.passed
                  DEPENDS main
                  COMMENT "Doing stuff."
)
add_custom_target(main.done ALL DEPENDS main.intermediate_step)

注意:make main 不会运行自定义命令。为此使用make main.donemakemake all 将按预期工作。

【讨论】:

  • 似乎修复了 make 的错误,但没有修复 ninja
  • @KayEss,对我来说似乎不是这样。在 Ninja 和 Make for CMake 3.11.1 中,问题中的问题已得到修复(即即使在第二次构建时构建也会失败)。你觉得在哪个版本下与 make/ninja 有区别?
  • 我还在 3.9.1,Android Studio 还在 3.6。如果它稍后修复,我不会尝试任何解决方法,看看我是否可以获得更高版本
  • 我尝试使用 3.9.1(在 macOS 上)并且无法在 ninja 下重现该问题。如果您观察到一个错误,它可能是另一个错误?对于问题中最小的失败示例,您是否看到与问题中相同的行为?如果这是一个不同的错误,您可能需要考虑提出一个单独的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-13
  • 2022-12-14
  • 1970-01-01
  • 2015-06-27
  • 2012-01-08
相关资源
最近更新 更多