CMake: Regenerate source file if target gets rebuilt - cmake

I am trying to embed the build date into a source file, so that every time a specific target gets built, the embedded date is refreshed, without regenerating every time the overall project built.
I.e. I have a header file builddate.h that is generated by a command that has a set of #defines. This header file is then included from other source files.
My first attempt was this:
add_custom_target(builddate COMMAND <command that generates header file>)
add_library(mylibrary ...)
add_dependencies(mylibrary builddate)
This correctly generates the header file, however the header file is generated every time, regardless of whether the mylibrary target needs to be rebuilt.
Trying with a custom command instead, i.e.
add_custom_command(OUTPUT builddate.h COMMAND <command that generates header file>)
add_library(mylibrary ... builddate.h)
correctly generates the header once, but if the mylibrary target is rebuilt, the header is not regenerated as builddate.h is already up to date.
This feels like something that should be reasonably common, but I cannot figure out what incantation of custom commands and targets will give me the desired effect. What I want is to call the command every time the mylibrary target is built, without spurious rebuilds if nothing has changed or if unrelated targets (such as executables using mylibrary) are built.
Using a PRE_BUILD custom command would sound like a good idea, but the docs state that this gets invoked just prior to PRE_LINK commands for generators other than Visual Studio, i.e. after sources are compiled. This seems like it would make this unsuitable for this purpose, as the header is needed while compiling the sources.

Found an old thread at https://cmake.org/pipermail/cmake/2010-October/040247.html suggesting to call CMake's --build for a target as a PRE_LINK command:
# This is the library that I want to build
add_library(mylibrary ...)
# Set up a library that contains the code depending on the build date
# Use an OBJECT library because we don't need it to be a full static lib
# we just want to build some source that would "normally" have been part of mylibrary
add_library(builddate OBJECT EXCLUDE_FROM_ALL codethatusesbuilddate.cpp)
# Add a PRE_LINK command for mylibrary so that prior to linking we
# 1. Generate the builddate.h header
# 2. Call CMake to build the builddate library we just set up
add_custom_command(
TARGET mylibrary PRE_LINK
COMMAND <command that generates builddate.h>
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target builddate
)
# We also need to link with the library
# NOTE: uses the generator expression to link with the output files rather than the target
# to avoid CMake setting up a dependency from builddate to mylibrary which I think
# would cause builddate to be built prior to building mylibrary, but at that point we
# haven't generated the header yet. Which we could fix, but then we'd just build it twice
target_link_libraries(mylibrary PRIVATE $<TARGET_OBJECTS:builddate>)
This feels a little awkward, but it seems to work.
Footnote: Generating the header is easily done using CMake, i.e. first configure_file or similar to create a CMake script that does the generation, then invoke ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/generated_cmake_file.cmake as the command to generate the header.

Some time ago, I wrote a cmake makro. It adds custom command to generate version.cpp in current build directory by executing Cversion.cmake.
The generation of file is executed only when dependencies have changed.
With cmake-generator-expressions dependencies are set to dependencies of target minus its own (files).
It could be improved by adding libs dependencies to also generate anew version file.
macro(add_versioning T)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/version.cpp"
COMMAND ${CMAKE_COMMAND} "-DCMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}"
-P "${PROJECT_SOURCE_DIR}/Version/CVersion.cmake"
MAIN_DEPENDENCY "${PROJECT_SOURCE_DIR}/Version/version.cpp.in"
DEPENDS "$<FILTER:$<TARGET_OBJECTS:${T}>,EXCLUDE,version.cpp.+$>")
target_include_directories(${T} PUBLIC "${PROJECT_SOURCE_DIR}/Version")
target_sources(${T} PUBLIC "${PROJECT_SOURCE_DIR}/Version/version.h" PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/version.cpp")
endmacro()

Related

How can I have the dependencies on an external project target be automatically rebuilt when the external project changes?

I'm working on a cross-compilation project with a top-level CMakeLists.txt file generating Makefiles. Some of the cross-compiled source code is generated by a host-based code generation tool; since that tool needs a different toolchain, the top-level CMakeLists.txt uses ExternalProject_Add() to invoke the code generator subdirectory's CMakeLists.txt, with BUILD_ALWAYS set to TRUE. The code generation targets depend on the code generator target, so during the initial build the code generator is built, and then the code is generated, and then the whole shebang is compiled into the final result.
If I change the source of the code generator then the code generator executable will be properly rebuilt on the next make. Problem: the dependent code generation targets will NOT be rebuilt. There seems to be no way with an ExternalProject_Add() target to indicate when/if any dependencies of the target should be rebuilt.
For instance, if the code generator target were configured using Add_Custom_Command(), then I'd use the OUTPUT keyword to specify the resulting executable file, and any dependent targets could be configured to automatically rebuild when the code generator executable's timestamp was updated.
(Edit for clarity: after specifying an OUTPUT file for Add_Custom_Command(), I'd then create a convenience target using Add_Custom_Target() whose DEPENDS keyword referenced the Add_Custom_Command()'s OUTPUT file. I'd then have the code generation targets depend on this convenience target. But, AFAIK, that won't work for me.)
Is there a way to configure an ExternalProject_Add()-based target so that dependent targets will be automatically rebuilt when the main target's output changes?
I needed to IMPORT a target based on the code generator executable. In my top-level CMakeLists.txt:
# Run next-gen code generation
# Use ExternalProject_Add() because we are building this for the host
# We DO NOT want cross-compile toolchain applied here
set(CODEGEN_EXECUTABLE ${CMAKE_CURRENT_LIST_DIR}/codegen/_build/codegen)
ExternalProject_Add(codegenExecutable
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/codegen
# The two "--unset" options tell it to use the host defaults rather than the configured ones.
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env --unset=CC --unset=CXX ${CMAKE_COMMAND} -H. -B_build
INSTALL_COMMAND ""
BUILD_IN_SOURCE TRUE
EXCLUDE_FROM_ALL TRUE
BUILD_ALWAYS TRUE
)
# Imported target, so that any change in the code generator will result in rerunning the
# code generation.
add_executable(codegen IMPORTED)
set_property(
TARGET codegen
PROPERTY
IMPORTED_LOCATION "${CODEGEN_EXECUTABLE}"
)
add_dependencies(codegen codegenExecutable)
Any of the code generation targets are set to depend on codegen. The first time I build:
The codegenExecutable external project is built, creating the ${CODEGEN_EXECUTABLE} executable
The codegen target's timestamp is set by the executable's timestamp
All of the code generation tasks that depend on codegen are rebuilt
With no changes, subsequent builds do the following:
The codegenExecutable external project is built, but since none of its source has changed the executable isn't rebuilt and doesn't change its timestamp
The codegen target's timestamp doesn't change
The code generation tasks that depend on codegen aren't rebuilt
If I touch any codegen source and build, then:
The codegenExecutable external project is built, creating a new ${CODEGEN_EXECUTABLE} executable with an updated timestamp
The codegen target's timestamp is updated by the executable's timestamp
The code generation tasks that depend on codegen are rebuilt
I believe that if I set up an EXPORTed target from the codegen's CMakeLists.txt then I wouldn't have to hard-code the executable path. A task for the future...

CMake: how to make execute_process wait for subdirectory to finish?

Part of my source code is generated by a tool which is also built under our main project with a add_subdirectory. We execute this tool with a execute_process command. Clearly, if the tool is not built before we reach the execute_process statement it will fail.
I use a GLOB (file(GLOB...)) to find the source files generated. I do this because it is not possible to know beforehand how many files are generated, neither their names.
How do I force cmake to wait for the subproject to be compiled before the execute process? I would need something like a DEPENDS property for the execute_process but this option is not available.
# This subproject will source generator the tool
add_subdirectory(generator)
# I need something like: wait_for(generator)
execute_process(COMMAND generator ${CMAKE_SOURCE_DIR}/src)
file(GLOB GeneratedSources ${CMAKE_SOURCE_DIR}/src/*.cpp)
add_executable(mainprject.exe ${ProcessorSourceFiles}
Command execute_process executes its COMMAND immediately, at configuration stage. So it cannot be arranged after the executable is created with add_executable command: that executable will be built only at build stage.
You need to build subproject at configuration stage too. E.g. with
execute_process(COMMAND ${CMAKE_COMMAND}
-S ${CMAKE_SOURCE_DIR}/generator
-B ${CMAKE_BINARY_DIR}/generator
-G ${CMAKE_GENERATOR}
)
execute_process(COMMAND ${CMAKE_COMMAND}
--build ${CMAKE_BINARY_DIR}/generator
)
The first command invokes cmake for configure the 'generator' project, located under ${CMAKE_SOURCE_DIR}/generator directory. With -G option we use for subproject the same CMake generator, as one used for the main project.
The second command builds that project, so it produces generator executable.
After generator executable is created, you may use it for your project:
execute_process(COMMAND ${CMAKE_BINARY_DIR}/generator/<...>/generator ${CMAKE_SOURCE_DIR}/src)
Here you need to pass absolute path to the generator executable as the first parameter to COMMAND: CMake no longer have generator executable target, so it won't substitute its path automatically.
You will need to model this with target dependencies. The tool "generator" should be a cmake target. In that case use add_custom_target instead of execute_process somthing like this:
add_custom_target(generate_sources ALL COMMAND generator ${CMAKE_SOURCE_DIR}/src))
Then add a target dependency to "generator" using add_dependencies:
add_dependencies(generate_sources generator)
This will make sure your target "generate_sources", which runs the tool will only run during build after the target "generator" has been compiled.
The following is false, see the comments for more info:
Use add_dependencies to add a dependency from "mainproject.exe" to "generate_sources". Now this I have never tested, so take with a grain of salt: With CMake more recent than version 3.12, according to the entry on file, you should then be able to change your file command to:
file(GLOB GeneratedSources CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/src/*.cpp)
Which I interpret as this will re-glob the files during build if the directory changes.

How to include a cmake Makefile after a target has been executed?

Given the following minimal example.
cmake_minimum_required(VERSION 2.8)
project(include_test)
add_custom_command(OUTPUT OtherCMakeLists.txt
COMMAND "${CMAKE_CURRENT_BINARY_DIR}/create_other_cmakelists")
add_custom_target(do_something DEPENDS OtherCMakeLists.txt)
What do_something should do here is first create OtherCMakeLists.txt. Now, let's assume that do_something has to do something else afterwards, e.g. compiling some code. I'd like that when the targets from something else are executed, the CMakeLists.txt will behave as if OtherCMakeLists.txt was included with include.
Is this possible?
As an example why this could be useful: OtherCMakeLists.txt might add some compiler flags that have influence on further compiling.
To my knowledge, it is not possible to generate CMakeLists.txt file with a custom target/command and use include CMake command with generated CMakeLists.txt
The problem is that the include command is called at so-called "Configuration time" (when cmake executable tries to parse all CMakeLists.txt), but the generation of file (CMakeLists.txt) is performed at "Build time" (when make command is invoked on generated build system)
add_custom_command has 2 different signatures:
add_custom_command(OUTPUT ...) will be executed at build time, too late to apply rules from a generated CMakeLists.txt generated.
add_custom_command(TARGET ...) to attach a specific command to a target. This command can be run on PRE_BUILD, PRE_LINK or POST_BUILD. Probably not what you want to achieve...
If you are trying to add some dynamic to your compile process, adding custom commands or target may not be your best option.
You should try to read doc for some other CMake commands that can be helpful in your case:
configure_file() that can process a file (OtherCMakeLists.txt.in) into another file (OtherCMakeLists.txt) replacing variables by their values. This is achieved at configuration time
execute_process() to run a command a configuration time (thx to #ComicSansMS)
set_target_properties() to set some compiler or link flags to a specific target depending on some conditions
The list of properties you can set on targets

CMake with regarding generated files

Good day everyone.
I have the following situation: I have a CMake file, which is supposed to compile my application, which consists of:
one or more cpp files
some template files (ecpp), which on their turn are generated into cpp files, which are compiled into the application (they are listed below in the WEB_COMPONENTS so for each component there is the associated .ecpp file and the .cpp that will be generated from it).
And here is the CMakeLists.txt (simplified)
cmake_minimum_required (VERSION 2.6)
set (PROJECT sinfonifry)
set (ECPPC /usr/local/bin/ecppc)
set (WEB_COMPONENTS
images
menu
css
)
set(${PROJECT}_SOURCES
""
CACHE INTERNAL ${PROJECT}_SOURCES
)
foreach(comp ${WEB_COMPONENTS})
list(APPEND ${PROJECT}_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${comp}.cpp )
execute_process(COMMAND ${ECPPC} -o ${CMAKE_CURRENT_BINARY_DIR}/${comp}.cpp -v
${CMAKE_CURRENT_SOURCE_DIR}/${comp}.ecpp
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_QUIET
)
endforeach()
list(APPEND ${PROJECT}_SOURCES main.cpp )
add_executable(${PROJECT}_exe ${${PROJECT}_SOURCES})
target_link_libraries(${PROJECT}_exe cxxtools dl tntnet tntdb)
Now, what happens: for the very first time (ie: make the build directory, run cmake-gui, select web component, configure, generate, make) the CMake nicely executes the ${ECPPC} command, ie. it generates the required CPP files in the binary directory, and links them together.
After a while, obviously while I work, I modify one of the component files (such as images.ecpp) and run make again in the build directory. But now, CMake does not pick up the changes of the ecpp files. I have to go to cmake-gui, delete cache, restart everything from zero. This is very tiresome and slow.
So, two questions:
Cand I tell CMake to track the changes of the images.ecpp and call the ${ECPPC} compiler on it if it changed?
How can I make clean so that it also removes the generated cpp files.
Thank you for your time, f.
Instead of execute_process() you want to use add_custom_command(). See here: https://stackoverflow.com/a/2362222/4323
Basically you tell CMake the OUTPUT (the generated filename), COMMAND, and DEPENDS (the .ecpp filename). This makes it understand how to turn the source into the necessary C++ generated file. Then, add the generated file to some target, e.g. add_executable(), or to an add_custom_command() dependency (if it didn't need to be compiled you'd more likely need that).

Cmake: how to run bash command only when a file is updated?

I am trying to write a CMakeLists.txt to speed up compilation.
The executable depends on a script generated .cpp file: I use the cppcms web application library which has a templating system where .tmpl must be converted to .cpp files during the compilation like this:
cppcms_tmpl_cc page.tmpl -o page.cpp
There are related questions that cover the use of bash commands within cmake:
How to run a command at compile time within Makefile generated by CMake?
CMake : how to use bash command in CMakeLists.txt
These questions cover most of my needs.
What I want to know, now, is how to tell cmake to run the above command and re-generate page.cpp every time page.tmpl itself has changed, and only then?
The goal obviously is to improve the compile time and have an up to date binary with the latest template.
(can a moderator add the cppcms tag?)
[Edit: I am actually trying to convert the following Makefile to cmake:
LIBS=-lcppcms -lconfig++ -lboost_filesystem-mt
all: clean gitbrowser
gitbrowser: main.cpp view.cpp content.hpp gitbrowser.cpp
$(CXX) -Wall main.cpp gitbrowser.cpp view.cpp -o run ${LIBS}
view.cpp: page.tmpl content.hpp
cppcms_tmpl_cc page.tmpl -o view.cpp
[Edit2: I added a note about the solution in the official cppcms wiki:
http://art-blog.no-ip.info/wikipp/en/page/cppcms_1x_howto#How.to.compile.the.templates.with.cmake.
now = get_now_time()
time = get_last_upd_time()
if (now > time)
set (LAST_UPD_TIME time CACHE INTERNAL "Defines last update time of the file" FORCE)
# run bash command here
endif (now > time)
get_now_time and get_last_upd_time are fictional functions, returning timestamps (I guess you can use bash commands to get those timestamps). Then you can compare them and store last modification timestamp into cache.
However, this solution looks ugly for me, as I know if you properly define targets and dependencies between them CMake itself will take care of rebuilding only modified files, doesn't it? Could you show me target definitions?
edit
You can use following CMakeLists.txt (thougn I'm not sure, it's based on my project):
# add main target, the executable file
ADD_EXECUTABLE(gitbrowser main.cpp view.cpp content.hpp gitbrowser.cpp)
# linking it with necessary libraries
TARGET_LINK_LIBRARIES(gitbrowser "cppcms config++ boost_filesystem-mt")
# add page.cpp target
ADD_CUSTOM_COMMAND(
OUTPUT page.cpp
COMMAND "cppcms_tmpl_cc page.tmpl -o view.cpp"
DEPENDS page.tmpl content.hpp
)
# and finally add dependency of the main target
ADD_DEPENDENCIES(gitbrowser page.cpp)
Good luck
Take a look on this CMake file of Wikipp lines 66-72
You basically need something like this:
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/view.cpp
COMMAND cppcms_tmpl_cc
view.tmpl
-o ${CMAKE_CURRENT_BINARY_DIR}/view.cpp
DEPENDS view.tmpl)
Edit: Also if you want to improve compilation speed you may compile
the view into shared object and load it dynamically.
This would also allow you not to restart application if you only changed the view, the
shared object after recompilation would be automatically reloaded.
See: http://art-blog.no-ip.info/wikipp/en/page/cppcms_1x_config#views for more details.