1

In CMake, I would like to use add_custom_command(... POST_BUILD ...) with a COMMAND that might fail.

Observation

  1. Running make will fail the first time, because the exit code of add_custom_command( ... COMMAND exit 1) is not 0. --> This is what I would expect.
  2. Running make a second time will pass, because the command specified in the add_custom_command will not be run again. --> This is not what I want. I would want make to fail until the custom command works.

Minimium Failing Example

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."
          )

Question

  1. How can I resolve this issue?
  2. Is this intended behavior of CMake?

One solution

I am aware that I can create a custom command that is not a POST_BUILD but instead outputs a file TARGET.passed on success. However, I would like to avoid this. Because POST_BUILD seems the most appropriate usage here. (I am changing capabilities of the resulting file.)

Unapiedra
  • 15,037
  • 12
  • 64
  • 93
  • Hm, [in another question](http://stackoverflow.com/questions/39622247/cmake-add-custom-command-failure-target-gets-deleted) the **opposite behavior** has been observed: the target file is removed upon command fails. In any case, you may do that manually before failing: `COMMAND rm $ && exit 1`. – Tsyvarev Jan 11 '17 at 19:44
  • @Tsyvarev, thank you for that link. I tried to investigate and ran the code in the question you linked to. My finding: Both my code and the other have the same behavior. But I wrote the question on Linux (Ubuntu 14.04, cmake 3.2.2), while I just checked these codes under macOS Sierra (cmake 3.7.1). – Unapiedra Jan 11 '17 at 20:55
  • I just tested on (docker) ubuntu:14.04 (cmake 2.8.12.2): Bug exists. ubuntu:16.04 (cmake 3.5.1): No bug. -- I've decided to call it a bug since the newer version has reproducible behavior, and the old one is not reproducible. Finally, I've searched release notes from CMake 3.3 to 3.7 and did not find a mention of a change in this behavior. – Unapiedra Jan 11 '17 at 21:20

1 Answers1

2

This used to be a bug in CMake.

It was fixed in the following commit:

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>

The earliest release with that fix was CMake 3.3.0.

Workaround

  • Create an additional output target that depends on the custom command having successfully run. (Below this is called main.done)
  • Switch the custom command from being a POST_BUILD on a TARGET, to being a standalone command. Thus make it OUTPUT a file (main.intermediate_step below).
  • Use the option to run a second COMMAND to create said file using touch.

Code:

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)

Note: make main will not run the custom command. Use make main.done for that. make and make all will work as intended.

Unapiedra
  • 15,037
  • 12
  • 64
  • 93
  • Seems that the bug was fixed for `make` but not for `ninja`. – KayEss May 01 '18 at 05:16
  • @KayEss, it doesn't seem that way to me. The problem from the question is fixed (i.e. build fails even on second build), in both Ninja and Make for CMake 3.11.1. Under which version do you see a difference from make/ninja? – Unapiedra May 01 '18 at 05:32
  • I'm still on 3.9.1, and Android Studio is still on 3.6. If it's fixed later on I'll not try any workarounds and see if I can get later versions – KayEss May 01 '18 at 06:27
  • I tried with 3.9.1 (on macOS) and cannot reproduce the issue under ninja. If you observe a bug, it might be a different bug? For the minimal failing example in the question, do you see the same behavior as in the question? If this is a different bug, you may want to consider asking a separate question. – Unapiedra May 02 '18 at 04:19