Including dynamically generated cmake files

I would like to have my CMakeLists.txt file include a file which is dynamically generated at build time. I can see that the include command can specify the OPTIONAL parameter so that it doesn’t fail on the initial configure when the file does not exist. But how do I force CMake to reparse the CMakeLists.txt files after I generate the include file ? As it stands it looks like I will have to run CMake twice to guarantee that it will pick up on the new contents of the include file.

Is this possible in CMake or does it not support this currently ?

It doesn’t really make sense for the configure step to depend on a build output. You can dynamically generate CMake code at configure time, but not at build time.

As part of my build I need to run InnoSetup to create some installers. The InnoSetup configuration file is expecting to include all files inside a staging folder into the installer. That staging folder is initially empty, and I need to populate it with the correct files. I have used add_custom_target() and add_custom_command() to create a set of targets (i.e. stage_feature1, stage_feature2 etc.) and rules describing the various files that might need to be copied into the staging directory . The final step is to include a staging.cmake file that adds some of the stage_featureX targets as dependencies of the InnoSetup target.

The problem is that it is not known at configure time which features are required to be staged. There is a BuildConfig.ini file that selects the various features. At build time I need to parse the BuildConfig.ini file and generate the appropriate staging.cmake file which then gets included.

You can list the generated CMake file in the CMAKE_CONFIGURE_DEPENDS property of the directory which wants to include it. That signifies that the buildsystem depends on that file, which in turn will cause any change in the file to trigger a CMake run at the start of the next build.

Something like this:

include(${CMAKE_CURRENT_BINARY_DIR}/path/to/generated_file.cmake OPTIONAL)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/path/to/generated_file.cmake)

Of course, you should make sure that your build only touches the file if it actually changes, otherwise you’ll get a reconfigure with every build. If the file generation process isn’t capable of this directly, you can use e…g cmake -E copy_if_different to achieve it.

Even if it looks like working some how, be aware, that it will be wrong.
As soon as the information changes in the first build steps, the setup will be created successfully, but with the outdated information from the previous run. Nothing is worse, than a successful build, with the wrong output.

You have to either get the information needed for your setup at configure time. Only the information like file names, not the files itself. It can be done using execute_process to create that CMake file before including it. But also be aware, that you should list every dependency for that information for re-configuration in CMAKE_CONFIGURE_DEPENDS.

The other way is, to create your installer also during the build phase in another CMake call. Then you can simply depend on that makefile and the other outputs. Ans just call the sub CMake/Build with your own add_custom_command or by using ExternalProject.

Using CMAKE_CONFIGURE_DEPENDS doesn’t sound like it will do what I want as only the second build after modifying the BuildConfig.ini file will be correct.

I am working on a different solution to my problem. I have split the staging logic into a separate folder per feature, each with a commands.cmake and a target.cmake file. The target.cmake file is processed at configure time and sets up the variables describing the files that need to be staged and creates a target that will run CMAKE_COMMAND -P on the commands.cmake file. After all the target.cmake files are processed I dump the existing CMake variables to a file called vars.cmake in the current project binary folder (using a call to get_cmake_property(variableNames VARIABLES)).

The commands.cmake files include the vars.cmake file which restores all the variables set up in the target.cmake file so that the commands have access to the necessary variables.

Splitting everything up like this allows me to run the python script at build time which parses the BuildConfig.ini file and generates a staging.cmake file in the project binary folder that runs CMAKE -P on the required commands.cmake files. This staging.cmake file is itself run via CMAKE -P immediately after it has been created by the python script.

I think this will allow me to manually depend on targets declared in the various target.cmake files if I need to, but the majority of situations will be handled automatically.