CMake Custom Command Depending On Variable - cmake

Part of my build process requires passing in a variable to a script to generate some source files. It looks something like this:
add_custom_command(OUTPUT
rebuild_files
COMMAND ${CMAKE_COMMAND} -E echo
COMMENT "Force rebuild of generated files"
)
add_custom_command(OUTPUT "File.c" "File.h"
COMMAND ruby generate_files.rb ${OPTIONS_LIST}
COMMENT "Generate files"
DEPENDS rebuild_files
)
Of course, this runs on every compilation, which isn't needed. The OPTIONS_LIST is set up at configuration time, so it could be cached.
Is there a mechanism to make a custom command dependent on a variable? The ultimate goal is to have this only compile if:
OPTIONS_LIST changes.
File.c or File.h do not exist.

Using configure_file one may convert "dependency from the variable" to "dependency from the file" which is naturally handled by custom target/command:
options_list_file.in:
${OPTIONS_LIST}
CMakeLists.txt:
# Variable which can be modified by a user
set(OPTIONS_LIST "a,b" CACHE STRING "<description>")
# Configure file which will be updated on changing the variable's content
configure_file("options_list_file.in" "options_list_file")
add_custom_command(OUTPUT "File.c" "File.h"
COMMAND ruby generate_files.rb ${OPTIONS_LIST}
COMMENT "Generate files"
DEPENDS "${CMAKE_CURRENT_BINART_DIR}/options_list_file" # dependency from the file
)
Changing the OPTIONS_LIST variable implies re-running CMake (configuration stage). Upon that re-run the configure_file will be called unconditionally.
But the file options_list_file will be updated only when its content would be changed. This is a main feature of configure_file.
That is, if the variable is set by a user to another value, then options_list_file will be updated, and this triggers re-running custom COMMAND on next build.
But if the variable's content is not changed, then configure_file wouldn't change the file options_list_file and its timestamp (this is important!). So, next build won't treat the file as updated and won't trigger the COMMAND re-run.

Related

How do I make CMake re-run a add_custom_command when a dependent file gets modified?

I want to have my custom commands rerun every time a file from a list I supply gets modified.
My example: my project has the following files:
main.cpp
CMakeLists.txt
dep1.txt
dep2.txt
cmake_minimum_required(VERSION 3.17)
project(dummy)
set(DummyFiles dep1.txt, dep2.txt)
add_executable(test_dummy main.cpp)
add_custom_command(TARGET test_dummy
COMMENT "ran custom command on file change"
DEPENDS ${DummyFiles}
)
My expectation is that after I have already configured that project, every time I modify dep1.txt or dep2.txt and reconfigure, CMake will print out the COMMENT section above. It however doesn't.
Any help would be appreciated.
There are two flows of command add_custom_command: "Generating Files" and "Build Events".
The option DEPENDS is available only for the first flow - "Generating Files", which requires OUTPUT as the first option.
You use TARGET as the first option to the command, which denotes "Build Events" command flow. This command flow doesn't support DEPENDS option (there is no such option in the synopsis for this command flow).
I want to have my custom commands rerun every time a file from a list I supply gets modified.
For that you need to use the first flow of the add_custom_command with OUTPUT option.
You may use dummy file as OUTPUT, so build system could be able to compare the timestamp of this file with the timestamps of the files from DEPENDS section. Whenever the timestamp of OUTPUT would be found older than the timestamp of one of DEPENDS, the command will be re-run.
set(DummyFiles dep1.txt dep2.txt)
add_custom_command(OUTPUT dummy.txt
COMMENT "ran custom command on file change"
DEPENDS ${DummyFiles}
)
# Need to create a custom target for custom command to work
add_custom_target(my_target ALL
# Use absolute path for the DEPENDS file.
# Relative paths are interpreted relative to the source directory.
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/dummy.txt
)

CMake: Regenerate source file if target gets rebuilt

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()

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

How do I add a dependency on a script to a target in CMake?

After my program is linked I need to perform some post-processing on it. I added a add_custom_command(TARGET ... and that worked fine. However, this extra custom command runs a script (that is not generated; it's checked into the codebase), and I want the target to be considered out of date if that script changes so it will be rebuilt properly.
The add_dependencies rule seems to only work between top-level elements, which this is not (it's just a script), and there's no DEPENDS element in this form of the add_custom_command that I can use.
How do I do this?
It's unfortunately a bit convoluted, but you can use add_custom_target to invoke CMake in script-processing mode via -P.
You need to use add_custom_target here since it will always execute, even if everything's up to date.
Having made this decision, we need to have the custom target execute commands which will check for a new version of your post-processing script file (let's call this "my_script" and assume it's in your root dir), and if it's changed cause your dependent target to go out of date.
This would comprise:
Compare a previous copy of "my_script" to the current "my_script" in the source tree. If the current "my_script" is different, or if the copy doesn't exist (i.e. this is the first run of CMake) then...
Copy "my_script" from the source tree to the build tree, and...
Touch a source file of the dependent target so that it goes out of date.
All of the commands required inside the CMake script can be achieved using execute_process to invoke cmake -E.
So the CMake script (called e.g. "copy_script.cmake") would be something like:
execute_process(COMMAND ${CMAKE_COMMAND} -E compare_files
${OriginalScript} ${CopiedScript} RESULT_VARIABLE Result)
if(Result)
execute_process(COMMAND ${CMAKE_COMMAND} -E copy
${OriginalScript} ${CopiedScript})
execute_process(COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${FileToTouch})
endif()
The CMake script needs to have required variables passed in via the -D args before calling -P, so the calling CMakeLists.txt would have something like:
set(FileToTouch ${CMAKE_SOURCE_DIR}/src/main.cpp)
add_custom_target(CopyScript ALL ${CMAKE_COMMAND}
-DOriginalScript=${CMAKE_SOURCE_DIR}/my_script
-DCopiedScript=${CMAKE_BINARY_DIR}/my_script
-DFileToTouch=${FileToTouch}
-P ${CMAKE_SOURCE_DIR}/copy_script.cmake)
add_executable(MyExe ${FileToTouch})
This will cause a full rebuild of the executable, since it thinks a source file has been modified. If you only require to force relinking there may be a better way to achieve this.

add_custom_command on xml files

our system uses some xml files to generate some code. So I've created a custom command to scan and parse these files, and generate stuff as accorded. This is what I did:
file(GLOB BEAN_XML_FILES "../../*.xml")
add_custom_command(TARGET communication PRE_BUILD
COMMAND python
ARGS bean_maker.py --input-directory ${SOURCE_DIR}/docs/beans --output-directory ${SOURCE_DIR}/beans
WORKING_DIRECTORY ${SOURCE_DIR}/tools/bean_maker
COMMENT "Running bean maker..."
DEPENDS ${BEAN_XML_FILES})
The problem is that add_custom_command only runs when I run cmake, even when I modified some xml inside the folder.
How could I do this to run when changes are made to the files?
Use the add_custom_command signature for adding a custom command to produce output files.
add_custom_command(OUTPUT ${GENERATED_SOURCE_FILES}
COMMAND command1 [ARGS] [args1...]
DEPENDS ${BEAN_XML_FILES})
At build time, the command will execute if the generated files are older than the files they depend on.
The issue is that your custom command only runs when the target needs to be compiled. You need to make CMake thing that the target needs to be recompiled each time you modify one of those xml files.
Here are two options:
Set a decadency that always is changing ( system time, incrementing variable )
Create a second custom command that writes out the latest modified time of all the xml files to a file in your build directory. Depend on that file and you should only see your target recompile after an xml file is changed.