[CMake] Issues with add_custom_command and config_file at build time

Mark Stijnman mark.stijnman at gmail.com
Tue May 28 11:53:13 EDT 2013


Hi all,

I'm trying to do some build-time configuration, for things like getting
revision information into header files, packaging scripts, etc. So I'm
using add_custom_command to run cmake scripts that use configure_file to
create new files. However, I'm running into an issue that I'm not sure is a
bug, or whether I'm just understanding things wrong.

Here's what I'm trying to to do: at build time a script
"get_revision.cmake" computes the revision number and writes it to
"revision.cmake" using configure_file. I use a dummy output that never gets
created, so that it runs on every build. A second add_custom_command calls
configure_revision.cmake, which includes the revision.cmake and uses
configure_file to create revision.h. Because I use configure_file, which
uses CopyFileIfDifferent internally, it should also mean that the second
custom command shouldn't run if the revision number is constant (which it
always is in this example), and the main.cpp doesn't get recompiled if
revision.h doesn't change.

Here's a self-contained minimal example CMakeLists.txt:

--- CMakeLists.txt ---

cmake_minimum_required (VERSION 2.8)
project (version)

# define file names
set(GET_REVISION_CMAKE ${CMAKE_CURRENT_BINARY_DIR}/get_revision.cmake)
set(REVISION_CMAKE ${CMAKE_CURRENT_BINARY_DIR}/revision.cmake)
set(REVISION_CMAKE_IN ${REVISION_CMAKE}.in)
set(REVISION_H ${CMAKE_CURRENT_BINARY_DIR}/revision.h)
set(REVISION_H_IN ${REVISION_H}.in)
set(CONFIGURE_REVISION_H
${CMAKE_CURRENT_BINARY_DIR}/configure_revision.cmake)
set(SRC ${CMAKE_CURRENT_BINARY_DIR}/main.cpp)

# create source files
file(WRITE ${REVISION_CMAKE_IN}
  "set(REVISION @REVISION@)")
file(WRITE ${GET_REVISION_CMAKE}
  "set(REVISION 1234)\n"
  "configure_file(${REVISION_CMAKE_IN} ${REVISION_CMAKE} @ONLY)")
file(WRITE ${REVISION_H_IN}
  "#define REVISION @REVISION@")
file(WRITE ${CONFIGURE_REVISION_H}
  "include(\"${REVISION_CMAKE}\")\n"
  "configure_file(${REVISION_H_IN} ${REVISION_H} @ONLY)"
)
file(WRITE ${SRC}
  "#include <iostream>\n"
  "#include \"${REVISION_H}\"\n"
  "int main(int argc, char**argv) { \n"
  " std::cout << REVISION << std::endl;\n"
  "}")

# build time configure commands
add_custom_command(
  OUTPUT ${REVISION_CMAKE} ${REVISION_CMAKE}.alwaysbuild
  COMMAND ${CMAKE_COMMAND} -P ${GET_REVISION_CMAKE}
  DEPENDS ${GET_REVISION_CMAKE}
  COMMENT "Updating revision info"
)
add_custom_command(
  OUTPUT ${REVISION_H}
  COMMAND ${CMAKE_COMMAND} -P ${CONFIGURE_REVISION_H}
  DEPENDS ${REVISION_H_IN} ${REVISION_CMAKE}
  COMMENT "Generating revision header"
)
add_custom_target(version DEPENDS ${REVISION_H})
add_executable(version_test ${SRC})
add_dependencies(version_test version)

--- CMakeLists.txt end ---

However, when I try this, the "Generating revision header" command always
runs, both on linux (cmake 2.8.7 with make) and windows (cmake 2.8.10.1
with Visual Studio 2008). This is not what I want, but more importantly,
not what I would expect. Is this a bug or are my expectations wrong?

Troubleshooting I've tried already:

On linux, I see that the revision.cmake modification date is always
updated, and therefore the second command always runs. If I change the
first add_custom_command to use a fake dependency instead of a fake output,
like so:
--- snippet ---
add_custom_command(
  OUTPUT ${REVISION_CMAKE}.alwaysbuild
  COMMAND ${CMAKE_COMMAND} -E echo_append
  COMMENT "(Run always)"
)
add_custom_command(
  OUTPUT ${REVISION_CMAKE}
  COMMAND ${CMAKE_COMMAND} -P ${GET_REVISION_CMAKE}
  DEPENDS ${GET_REVISION_CMAKE} ${REVISION_CMAKE}.alwaysbuild
  COMMENT "Updating revision info"
)
--- snippet end ---
oddly enough, the revision.cmake file is no longer touched, as expected,
and the header rule is no longer invoked unnecessarily. So somehow, the
presence of a fake output file causes the real output file to be touched.
It seems to me, though, that it should have worked in the original too.

On the other hand, in Visual Studio, in both cases the modification date of
revision.cmake stays the same. However, in both cases, I still see
"Generating revision header" on each build, and I don't understand why - if
the dependencies of the second command don't change, why is it rerunning?

Note that in all cases (linux and windows) the main.cpp is not rebuilt
unnecessarily, so the combination of add_custom_command and configure_file
works as expected there.

Can anyone explain what is going on here?

best regards Mark
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.cmake.org/pipermail/cmake/attachments/20130528/edfe524d/attachment-0001.htm>


More information about the CMake mailing list