Header generated with configure_file not being generated due to dependency

I am currently using cmake version 3.23.1. I am using the Eclipse environment with esp-idf plugin for ESP32 development.

I am using configure_file to generate a simple header file containing the current project name and version for my project. This file is included in two of my source files.

If I start with a new build without an existing build directory, this works fine. configure_file generates the include file before the build process tries to compile the source files and the build is successful.

If I then just do a “build clean” in Eclipse, the build directory isn’t completely erased but some select files are removed. In particular, the generated header file is removed. If I attempt to do a build after the “build clean” it results in an error trying to compile the source since it now cannot find the header file since it hasn’t been generated prior to the compilation step.

Any ideas on why this is occurring? I’ve read several descriptions online and a detailed section in a cmake book on how configure_file works, and looked at countless examples. They’re all basically the same and mine follows that pattern. So I’m not sure what I’m doing wrong.

cmake_minimum_required(VERSION 3.16.0)
cmake_policy(SET CMP0076 NEW)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}) 

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

project(BD-OTB)
set(PROJECT_VERSION "1.0.1")

set(PROJECT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(GENERATED_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR})

configure_file(
	${PROJECT_INCLUDE_DIR}/Application.hpp.in
	${GENERATED_INCLUDE_DIR}/Application.hpp)

add_custom_target(gen_h DEPENDS
	${GENERATED_INCLUDE_DIR}/Application.hpp
	${PROJECT_INCLUDE_DIR}/Application.hpp.in)

set_property(TARGET ${PROJECT_NAME}.elf APPEND
    PROPERTY ADDITIONAL_CLEAN_FILES ${GENERATED_INCLUDE_DIR}/Application.hpp)

add_dependencies(${PROJECT_NAME}.elf gen_h)

Hi.

What is the purpose of the gen_h target? It doesn’t seem to do anything.

You’re adding the generated header into ADDITIONAL_CLEAN_FILES of a target, so it’s natural that it’s removed by a make clean equivalent. Since it’s not a build artifact (it’s created at CMake configure time, not at build time), why are you adding it there?

This was described in several places I’ve read in order to establish the dependency for the build target. gen_h target is subsequently set here as the dependency:

add_dependencies(${PROJECT_NAME}.elf gen_h)

Again, this was recommended in several places I’ve read. Since the header file is created by the build process (yes, configuration not literal build, but nonetheless not a “source” file) then it was recommended that it can be removed on a clean. If I do a “full clean” and remove the build directory, of course the file is removed in that case, too, but the build then works.

Is there no way in cmake to define a generated file as a dependency for the compilation of a source file? This would be fairly simply in a grungy old makefile.

I believe you may be mixing up two scenarios. One is a file generated by a build step (that is, by add_custom_command(OUTPUT)). Such files should indeed be cleaned up by make clean (but CMake will arrange that for you automatically), and they can often benefit from a custom target that serves as their driver.

However, your file is generated by configure_file() during CMake configure time, so the custom target does absolutely nothing. I would not expect such files to be cleaned up by make clean, since CMake configuration is not a build.

Based on how you’ve described your setup, I would simply remove the gen_h target and the ADDITIONAL_CLEAN_FILES setup.

Nevertheless, if you do want to keep the file cleaned up by make clean, you will indeed have to trigger CMake re-configuration during your build to regenerate the file. This can be done by adding the output file to the directory property CMAKE_CONFIGURE_DEPENDS.

Thanks for the additional reply.

So, I get that if I use configure_file then I shouldn’t remove its output using a clean. Given that I could have written a script instead (but less convenient) and used add_custom_command instead, makes this seem a little arbitrary to me, but so be it.

I can certainly remove it. This additional target was recommended by numerous sites to handle this scenario.

I suppose whether I clean the file up (remove it) with make clean isn’t the biggest concern for me. Rather, make clean is just one way the file might turn up missing. What happens if I remove the file by some other means? Generally, I think the build system should be smart enough to rebuild it if it is a generated file and it turns up missing when it’s needed. I will explore the CMAKE_CONFIGURE_DEPENDS method and see how that goes.

Thanks very much, again, for responding and for the tips.

Hi Petr

I tried this approach. I removed my gen_h target, as suggested and kept the set_property which allows the build clean to remove the generated file. I also added the following line (I’m not sure if this is correct):

list(APPEND CMAKE_CONFIGURE_DEPENDS ${GENERATED_INCLUDE_DIR}/Application.hpp)

I did note (using a message) that the CMAKE_CONFIGURE_DEPENDS only has my file path in it, so it was empty before I appended to it.

As before, if I do a build from scratch, it’s all good. However, if I do a build clean afterwards (which removes the generated file) then do another build, it fails because it cannot find the generated header file needed to compile a couple of the source files.

I don’t feel like what I’m trying to achieve is anything unusual. All I want for the build system to do is recognize that I have source files that depend upon a generated file, and if that generated file needs to be updated or created, then update or create it before attempting to build the source files. The reason why the generated file may be missing is unimportant to me (whether it’s from a build clean or otherwise). It sounds simple enough, but I haven’t achieved this goal.

CMAKE_CONFIGURE_DEPENDS is not a variable; as I wrote, it’s a directory property. So the correct way to update it is this:

set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${GENERATED_INCLUDE_DIR}/Application.hpp)

(After the DIRECTORY keyword, before the APPEND, you can also specify to whcih directory you want to make the change; it defaults to the directory currently being processed, i.e. the current CMakeList).

It’s certainly up to you to include such dependency if you want to, to cover for the situation that the file is “removed by some other means.” I don’t think most projects do that for products of the configuration step (as opposed to products of a build step), but it’s a matter of preference.

For me, a file produced by configure_file is philosophically similar to other files produced by CMake such as the makefiles/project files. I wouldn’t expect CMake to auto-trigger if one of these goes missing, either.

Can you link to one which does this for an output of configure_file() or similar CMake-time step? I am curious about their rationale.