Rebuild configuration file when there are no CMake changes - cmake

I have a project with a configuration file in it.
configure_file(version.h.in version/version.h)
The same code base is used for multiple build in a day, without cleaning between builds. The version.h file will be built the first time the build is run, but subsequent builds will not rebuild (and update the revision number) in the version.h.
Is there a way I can force CMake to always rebuild version.h regardless of changes, or better yet based on a change (or lack of change) in a subversion revision number?

There are a few issues you need to overcome to have this work.
When cmake is run any version information you extract from subversion is baked into the generated build files, and so becomes static.
Cmake is only rerun if it detects the generated build files have become out of date (eg: if a CMakeLists.txt file is updated)
You can create a custom_target which will be run every time you build (from the docs: "The target has no output file and is ALWAYS CONSIDERED OUT OF DATE") which generates the file, but that will force you to rebuild your generated version file every time.
Here is an approach which overcomes all of the above hurdles:
First, create a library which will contain the compiled version information.
add_library(version STATIC ${CMAKE_CURRENT_BINARY_DIR}/version_gen.cc)
Now, add an artificial dependency between the library and a custom_target called gen_version
add_dependencies(version gen_version)
This will force gen_version to be run before building the version library.
Now create a custom_target called gen_version which will generate the version information:
add_custom_target(
gen_version
ALL
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/gen_version.cmake)
This custom target is added to the ALL target, and will be run every time you build. It will execute another cmake script called gen_version.cmake
This is the first trick. We get cmake to execute a subprocess and run a new cmake script on each build, in which the required version information is calculated anew each build.
The next trick is to call configure_file with a temporary output file, and only update the real version file if necessary. This prevents the need to recompile when the version doesn't change.
In the below example I show obtaining the version information from git - you can swap this for your subversion method.
gen_version.cmake:
# obtain the git version
execute_process(
OUTPUT_VARIABLE ${VERSION}
COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
# configure the version file, but output to a temporary location
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/version_gen.in
${CMAKE_CURRENT_BINARY_DIR}/version_gen.cc.tmp
)
# compare with the real version file
execute_process(
COMMAND
${CMAKE_COMMAND} -E compare_files
${CMAKE_CURRENT_BINARY_DIR}/version_gen.cc.tmp
${CMAKE_CURRENT_BINARY_DIR}/version_gen.cc
RESULT_VARIABLE
VERSION_NEEDS_UPDATING
OUTPUT_QUIET
ERROR_QUIET
)
# update the real version file if necessary
if(VERSION_NEEDS_UPDATING)
execute_process(
COMMAND
${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_BINARY_DIR}/version_gen.cc.tmp
${CMAKE_CURRENT_BINARY_DIR}/version_gen.cc
)
endif()
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/version_gen.cc
PROPERTIES GENERATED TRUE)

For a pure CMake solution #stevelorimer's answer is great and the way to go. But, you can also look to your CI system (if you have one) and your RCS. You asked:
Is there a way I can force CMake to always rebuild version.h regardless of changes...?
The simplest way is to just touch your version.h.in as part of your build process. If you next call cmake, version.h will be rebuilt. If you call (let's say) make, it will re-run cmake and rebuild version.h.
...or better yet based on a change (or lack of change) in a subversion revision number?
If you have access to your svn repo you can add a pre-commit hook to your repo that modifies version.h.in (or even version.h) in situ, and adds it to the commit. While possibly more complicated than the CMake solution, it has the added benefit of not using SVN or CMake to determine the revision, possibly allowing other tooling to scrape the revision.

Related

Cause CMake to force recompile a file, but only if project is otherwise recompiled?

I have generally the same question as in Can CMake always force the compilation/build of a specific file?
I have a C++ file using __DATE__ to display the build date of my app. But if this file is not modified, it will not be rebuilt and the date will not be updated.
Can CMake always rebuild that specific file?
... except I want something slightly different:
In the CMake project I have (for C, transpiles to Makefile which I use), sometimes there are no actual changes to the code when I run make, which is detected nicely, in the sense that there is no recompilation (or relinking) of the program.
Obviously, in this case, I do not want to update the timestamp, and end up with a new executable, which is otherwise identical to the previous one - apart from the build date.
I have seen in the quoted post, that one simply has to ensure a changed timestamp on the file, to force a recompilation. So, assuming my __DATE__ usage is in use_date.c, what I'd want, is that the timestamp of use_date.c is updated (forcing recompilation), only if any other file in the project (say, main.c) has been changed, so it forces project recompilation and linking (obviously, this should also work if I just change use_date.c manually, and no other file).
So, assuming my project just generates an executable (no libraries):
add_executable(my_project use_date.c other_file.c main.c)
... is it possible to add a CMake step, that updates the timestamp of use_date.c (and thus causes its recompilation), only if otherwise the project is getting recompiled and relinked?
OK, found a way: it seems kind of a squeaky solution - hopefully someone more knowledgeable in CMake will post a proper solution eventually. But in the meantime:
I've found that add_custom_command with POST_BUILD runs the custom command only if a new binary (.elf for me) is generate, and otherwise does not run the custom command.
So, basically, we could have a small bash script in the custom command:
If this bash script is called, we can assume that POST_BUILD has called it, meaning that a new .elf (for whatever other reasons) has been built
So we can touch the use_date.c, and while in the bash script still, descend into the build directory, and explicitly run make again - which should recompile only use_date.c, and then link with the rest from the previous build.
This essentially works - except, it causes an infinite loop.
This can get solved with creating a temp file:
If this bash script is called, we can assume that POST_BUILD has called it, meaning that a new .elf (for whatever other reasons) has been built
check for a temp file:
if it does not exist, create it - and then we can touch the use_date.c, and while in the bash script still, descend into the build directory, and explicitly run make again - which should ...
if it does exist, then we've been called from the second .elf. build (for the touched use_date.c), so we want to remove the temp file, and just exit - stopping the infinite loop
This seems to work fine:
add_custom_command(TARGET ${PROJECT_NAME}
POST_BUILD
DEPENDS ALL
#COMMAND bash ARGS -c "touch ${CMAKE_CURRENT_SOURCE_DIR}/use_date.c && cd build && make" ## infinite loop!
COMMAND bash ARGS -c "if [ ! -f .refresh ]; then echo '.refresh' | tee .refresh && touch ${CMAKE_CURRENT_SOURCE_DIR}/use_date.c && cd build && make; else rm -v .refresh; fi" # OK!
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "remake with refreshed __DATE__ in use_date.c (${CMAKE_CURRENT_SOURCE_DIR})"
VERBATIM
)

CMake: How to set dependency from executable to custom command [duplicate]

I'm trying to use add_custom_command to generate a file during the build. The command never seemed to be run, so I made this test file.
cmake_minimum_required( VERSION 2.6 )
add_custom_command(
OUTPUT hello.txt
COMMAND touch hello.txt
DEPENDS hello.txt
)
I tried running:
cmake .
make
And hello.txt was not generated. What have I done wrong?
The add_custom_target(run ALL ... solution will work for simple cases when you only have one target you're building, but breaks down when you have multiple top level targets, e.g. app and tests.
I ran into this same problem when I was trying to package up some test data files into an object file so my unit tests wouldn't depend on anything external. I solved it using add_custom_command and some additional dependency magic with set_property.
add_custom_command(
OUTPUT testData.cpp
COMMAND reswrap
ARGS testData.src > testData.cpp
DEPENDS testData.src
)
set_property(SOURCE unit-tests.cpp APPEND PROPERTY OBJECT_DEPENDS testData.cpp)
add_executable(app main.cpp)
add_executable(tests unit-tests.cpp)
So now testData.cpp will generated before unit-tests.cpp is compiled, and any time testData.src changes. If the command you're calling is really slow you get the added bonus that when you build just the app target you won't have to wait around for that command (which only the tests executable needs) to finish.
It's not shown above, but careful application of ${PROJECT_BINARY_DIR}, ${PROJECT_SOURCE_DIR} and include_directories() will keep your source tree clean of generated files.
Add the following:
add_custom_target(run ALL
DEPENDS hello.txt)
If you're familiar with makefiles, this means:
all: run
run: hello.txt
The problem with two existing answers is that they either make the dependency global (add_custom_target(name ALL ...)), or they assign it to a specific, single file (set_property(...)) which gets obnoxious if you have many files that need it as a dependency. Instead what we want is a target that we can make a dependency of another target.
The way to do this is to use add_custom_command to define the rule, and then add_custom_target to define a new target based on that rule. Then you can add that target as a dependency of another target via add_dependencies.
# this defines the build rule for some_file
add_custom_command(
OUTPUT some_file
COMMAND ...
)
# create a target that includes some_file, this gives us a name that we can use later
add_custom_target(
some_target
DEPENDS some_file
)
# then let's suppose we're creating a library
add_library(some_library some_other_file.c)
# we can add the target as a dependency, and it will affect only this library
add_dependencies(some_library some_target)
The advantages of this approach:
some_target is not a dependency for ALL, which means you only build it when it's required by a specific target. (Whereas add_custom_target(name ALL ...) would build it unconditionally for all targets.)
Because some_target is a dependency for the library as a whole, it will get built before all of the files in that library. That means that if there are many files in the library, we don't have to do set_property on every single one of them.
If we add DEPENDS to add_custom_command then it will only get rebuilt when its inputs change. (Compare this to the approach that uses add_custom_target(name ALL ...) where the command gets run on every build regardless of whether it needs to or not.)
For more information on why things work this way, see this blog post: https://samthursfield.wordpress.com/2015/11/21/cmake-dependencies-between-targets-and-files-and-custom-commands/
This question is pretty old, but even if I follow the suggested recommendations, it does not work for me (at least not every time).
I am using Android Studio and I need to call cMake to build C++ library. It works fine until I add the code to run my custom script (in fact, at the moment I try to run 'touch', as in the example above).
First of,
add_custom_command
does not work at all.
I tried
execute_process (
COMMAND touch hello.txt
)
it works, but not every time!
I tried to clean the project, remove the created file(s) manually, same thing.
Tried cMake versions:
3.10.2
3.18.1
3.22.1
when they work, they produce different results, depending on cMake version, one file or several. This is not that important as long as they work, but that's the issue.
Can somebody shed light on this mystery?

How to change file timestamp from CMake?

I'm generating .pro files for QtCreator from a CMake script. A file in the project is to be generated later using a QMAKE_EXTRA_COMPILERS instruction (the file is re-generated from other ones using an external tool only if it is older than them).
To make sure the file to be generated is imported in QtCreator project without warning, I need to create it (else QtCreator reports warnings while parsing the .pro file).
To make sure the file gets generated upon first compilation, I need to change the timestamp to be very old (older than any input file used to generate it). This is needed for QMAKE_EXTRA_COMPILERS to consider the file as needing to be re-generated.
To do so, I do:
file(WRITE ${output_file_path} "To be generated..." )
execute_process( COMMAND touch.exe ${output_file_path} -t 0001010101 )
But, the execute_process( COMMAND touch.exe ${output_file_path} -t 0001010101 ) takes a while. I have hundreds of files like that in my project and commenting this execute_process line divides my CMake generation time by 2 (0m45s vs 1m30s). I bet the execution of an external process is slowing things down....
Is there any way to change a file timestamp using pure CMake commands?
cmake builtin commands are the preferred way. You can touch files by using:
execute_process(COMMAND ${CMAKE_COMMAND} -E touch ${output_file_path} -t 0001010101)
This also works in any platform, independent if linux or windows.
If you have 'hundreds of files' you can touch them in one statement by providing touch command with multiple arguments, thus saving time on running make/build system commands (less dependencies to resolve for cmake build system :) .

Custom command / target run only for default build not install

I want to build my documentation (doxygen) alongside my project with CMake by default. I let CMake generate Makefiles. I know of these approaches so far:
1)
add_custom_target(doc ALL
COMMAND ${DOXYGEN_COMMAND})
2)
add_custom_command(
TARGET my-executable
POST_BUILD
COMMAND ${DOXYGEN_COMMAND})
The issue I have with the first approach is that the target is build both during build and install (i.e. when running make and when running make install). This is a no-go.
The issue with the second approach is that it is not actually correct: the documentation doesn't have to be built after the executable (in fact, it can be done in parallel even). Moreover I cannot build only the executable now.
Is there a way to create this custom command / target such that it is run only during make (building) and not during make install (installing) but independently from other targets such as executables?
Basically I'd need a "default target". Also I really want to avoid having to execute CMake recursively or make directly.
Just create a file, which existence means that documentation file have been built:
set(documentation_file ${CMAKE__BINARY_DIR}/docs_ready)
# Documentation target
add_custom_target(docs ALL DEPENDS ${documentation_file})
# Documentation command
add_custom_command(OUTPUT ${documentation_file}
COMMAND ${DOXYGEN_COMMAND}
# And mark that documentation is created
COMMAND ${CMAKE_COMMAND} -E touch ${documentation_file}
)
The solution given by #Tsyvarev works perfectly except for one issue:
Running make docs manually doesn't cause a re-build of the documentation because the "output file" ${documentation_file} is always considered up to date (there are no dependencies specified that could be newer).
Three workarounds I found:
Run make -B docs. The -B flags causes GNU Make to consider all targets out of date, and thus causes the documentation to be build.
Add a "manual docs" target which depends on first a docs-clean target which removes ${documentation_file} and then on docs itself (to build the documentation and the file), and run make docs-manual:
add_custom_target(docs-clean
COMMAND ${CMAKE_COMMAND} -E remove ${documentation_file})
add_custom_target(docs-manual)
add_dependencies(docs-manual docs-clean docs)
Note that I fear that this is just a dirty hack: AFAIK the order in which the dependencies (docs-clean and docs) are build isn't specified, and as they don't have a dependency between them, they could even be built in parallel.
Using add_custom_command with a TARGET docs-manual and PRE_BUILD (which runs the command before building dependencies) instead of the docs-clean target would've been perfect, but this only works for Visual Studio 7 or later.
Add a custom target that depends on docs but removes the generated ${documentation_file}:
add_custom_target(docs-manual
COMMAND ${CMAKE_COMMAND} -E remove ${documentation_file})
add_dependencies(docs-manual docs)
This works except for when running it the first time after a make docs, because then the generated file will still be there when docs is built.
edit: Use add_dependencies instead of DEPENDS of add_custom_target because the later is only intended for files, not for dependencies on targets.

In CMake how do I create a file needed at configure time?

Edit: my question targets the early configure stage where CMake input files are still being parsed and thus have to be present before include() is being called. So the answer found here: Force CMake to generate configure_file target every build does not solve my problem since it generates files after include() statements have been interpreted.
I have a CMakeLists.txt that includes a file which is generated in the configure stage:
execute_process(COMMAND "my-generator -o generated.cmake")
include(generated.cmake)
Apart from the fact that this approach doesn't feel right (not to say elegant) I now need to re-generate that file before every build (my-generator produces output that incorporates the current time).
My assumption is that I can't use add_custom_command() or add_custom_target() because the file would be generated at compile time but needed in the configure-step.
This very old post suggests to touch the input file so I did this:
execute_process(
COMMAND "my-generator -o generated.cmake"
COMMAND cmake -E touch "${CMAKE_CURRENT_LIST_FILE}")
.. which does not produce errors but calling make multiple times won't run the configure step more than once.
What do I do wrong? Is there a better approach?