Ruslan
Ruslan

Reputation: 19150

Why doesn't CMake detect dependency on my generated file?

I'm trying to generate a header with a custom command. The header should be updated on each rebuild, so that the source file which includes it would also be rebuilt. (Actual command is a script, but here is a simplified version.)

Here's my project:

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
set(test_SOURCES test.c)
include_directories("${CMAKE_BINARY_DIR}")

set(VERSION_H_PATH "${CMAKE_BINARY_DIR}/version.h")
message("VERSION_H_PATH: ${VERSION_H_PATH}")
add_custom_command(OUTPUT "${VERSION_H_PATH}" COMMAND "touch" "${VERSION_H_PATH}")
#add_custom_target(GENERATE COMMAND "touch" "${VERSION_H_PATH}")
add_executable(myprog ${test_SOURCES})
add_dependencies(myprog GENERATE)

test.c

#include <version.h>

int main()
{
    return 0;
}

Now the problem is that the CMakeList.txt, as presented above, doesn't result in version.h being created at all. Only after I switch from add_custom_target to add_custom_command does it do. But then, if I change the file somehow, next make doesn't rebuild the project.

Looks like CMake doesn't recognize that test.c depends on version.h, although it does explicitly #include it. What am I doing wrong here?

Upvotes: 4

Views: 9361

Answers (4)

Ruslan
Ruslan

Reputation: 19150

OK, actually the question was badly formulated from the beginning. I shouldn't have simplified the generating command as much as I did. But the problems I had seem to have been solved by removing content of build directory and re-running cmake. For some reason some changes to CMakeLists.txt are not easy for cmake to understand. So here's the CMakeLists.txt which does work, and with not too much simplified generator command:

cmake_minimum_required(VERSION 2.8)
set(test_SOURCES test.c)
include_directories("${CMAKE_BINARY_DIR}")

set(VERSION_H_PATH "${CMAKE_BINARY_DIR}/version.h")
message("VERSION_H_PATH: ${VERSION_H_PATH}")
add_custom_target(GENERATE COMMAND "bash" "-c" "[ -e ${VERSION_H_PATH} ] \\|\\| touch ${VERSION_H_PATH}")
add_executable(myprog ${test_SOURCES})
add_dependencies(myprog GENERATE)

This appears to work even when using the reserved test target instead of myprog.

Upvotes: 0

Antonio
Antonio

Reputation: 20336

Change:

add_custom_command(OUTPUT "${VERSION_H_PATH}" COMMAND "touch" "${VERSION_H_PATH}")
#add_custom_target(GENERATE COMMAND "touch" "${VERSION_H_PATH}")
add_executable(myprog ${test_SOURCES})
add_dependencies(myprog GENERATE)

Into:

add_custom_command(OUTPUT "${VERSION_H_PATH}" COMMAND ${CMAKE_COMMAND} -E touch "${VERSION_H_PATH}") #More reliable touch, use cmake itself to touch the file
add_custom_target(generate_version_h DEPENDS "${VERSION_H_PATH}")
add_executable(myprog ${test_SOURCES})
add_dependencies(myprog generate_version_h)

See:

  1. CMake command line tool mode.
  2. add_custom_target:

DEPENDS:

Reference files and outputs of custom commands created with add_custom_command() command calls in the same directory (CMakeLists.txt file). They will be brought up to date when the target is built.

  1. add_dependencies:

Make a top-level depend on other top-level targets to ensure that they build before does. A top-level target is one created by one of the add_executable(), add_library(), or add_custom_target() commands (but not targets generated by CMake like install).

By the way, I do not know your specific case, but you might consider using configure_file to generate your header.

Upvotes: 10

sakra
sakra

Reputation: 65991

The problem seems to be that the name test used for the executable target is a name reserved by CMake. See policy CMP0037. Using a different name for the executable seems to work as expected with the custom target:

add_custom_target(GENERATE COMMAND "touch" "${VERSION_H_PATH}")
add_executable(testexe ${test_SOURCES})
add_dependencies(testexe GENERATE)

Upvotes: 1

Every custom command needs a driver (something that will depend on its output). In your case, the best driver would be the test executable, which means you should add the generated file into your executable's sources:

add_executable(test ${test_SOURCES} ${VERSION_H_PATH})

Upvotes: 0

Related Questions