Search code examples
cmakeninjabuild-systemcmake-custom-command

add_custom_command: Remove redirected output on failure


Consider the following custom command (latest CMake + ninja):

add_custom_command(
        OUTPUT
            ${OUTPUT}
        COMMAND
            ${Python3_EXECUTABLE} script.py ${INPUT} > ${OUTPUT}
        DEPENDS
            ${INPUT}
        VERBATIM
        COMMAND_EXPAND_LISTS
    )

When script.py runs without errors, it works fine.

However, when script.py fails with an error, ${OUTPUT} is still created.
So current build fails as expected but next build sees ${OUTPUT} newer than ${INPUT} and does not try to run the custom command again as it should.

I would expect the build system to automatically delete ${OUTPUT} when the command fails, to prevent such case, but apparently this does not happen.

  • Is there a way to perform an action "on failure" of a custom command?
    If there was, I could delete ${OUTPUT} there.
  • Alternatively, what is the simplest way to prevent output creation unless command succeeds?

I've tried naively to do something like:

${Python3_EXECUTABLE} script.py ${INPUT} > ${OUTPUT} || rm -f ${OUTPUT}

But that doesn't work because the command result code is in fact the rm result code instead of Python's result code, therefore the custom command doesn't fail as it should on a subsequent build.


Solution

  • You almost there, just add after rm a command, which would return an error code. E.g. that way:

    ${Python3_EXECUTABLE} script.py ${INPUT} > ${OUTPUT} || (rm -f ${OUTPUT} && /bin/false)
    

    With such command VERBATIM option for add_custom_command should NOT be used: With that option CMake quotes the brackets (( and )), which prevents a shell to interpret them for grouping purposes.


    I would expect the build system to automatically delete ${OUTPUT} when the command fails

    Note, that CMake by itself is NOT a build system, it just generates a code for the build system.

    As example, when CMake generates a Makefile, it adds .DELETE_ON_ERROR target, so make utility actually deletes output files on failure.

    It seems, that Ninja doesn't have such functionality as Make does. Or CMake doesn't use this functionality when generate a code for Ninja.