0

I have a setup where I use a custom command to check the current hash of a git repository so that other commands can clone it if it has updated

add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
    COMMAND ${GIT_EXECUTABLE} ls-remote ${MODULE_URL} master > ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp
    COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
    COMMAND ${CMAKE_COMMAND} -E rm ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp
)

Of course this will only run once as CMake sees no reason to rerun it. I can force it to run by adding a second (dummy) output - CMake then recognises that this output doesn't exist and then reruns the rule. However the Makefile that this generates actually deletes module_VERSION.txt before running the command rendering the whole pursuit pointless (Ninja does not have this problem).

I am able to get this to work but in an extremely hacky way: creating another target that always runs and then generating a dependency on this.

# Use echo_append as a no-op
add_custom_command(
    OUTPUT module_FORCERUN
    COMMAND ${CMAKE_COMMAND} -E echo_append
)

add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
    COMMAND ${GIT_EXECUTABLE} ls-remote ${MODULE_URL} master > ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp
    COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
    COMMAND ${CMAKE_COMMAND} -E rm ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp
    DEPENDS module_FORCERUN
)

This seems just really hacky and like it could be relying on some corner cases in cmake which aren't guaranteed to be stable. Is there a better way to get this working?

I am using cmake 3.21.3

Jon Burr
  • 97
  • 7
  • That's a fair comment @Tsyvarev. I've updated the question (turns out this happens with the Makefile generator but not Ninja) – Jon Burr Aug 17 '22 at 10:56
  • I do not understand. `the whole pursuit pointless` Why? `deletes module_VERSION.txt` So? Your code does the same `rm ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt`. What is the point? Just don't create the output file. `-E echo_append` Just `-E true` – KamilCuk Aug 17 '22 at 10:58
  • Oops, that was a typo. It was meant to delete the tmp file – Jon Burr Aug 17 '22 at 12:01

1 Answers1

1

Use add_custom_target for implement "always run" functionality and via BYPRODUCTS keyword specify the file which it could produce/update:

add_custom_target(update_module_version
    BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
    COMMAND ${GIT_EXECUTABLE} ls-remote ${MODULE_URL} master > ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp
    COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
    COMMAND ${CMAKE_COMMAND} -E rm ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp
)

That way, if any other target will depend on update_module_version one, the module_VERSION.txt file will be created/updated before evaluation of the target.

Such target-level dependency will be created automatically by CMake, if given file will be listed as dependency for target/command in the same directory, where target update_module_version is created:

add_custom_command(OUTPUT <...>
  DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
  COMMAND <...>
)

From other directories the target-level dependency should be specified explicitly:

# If used in other directories
add_custom_command(OUTPUT <...>
  DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
          update_module_version
  COMMAND <...>
)
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153