Hum, it's a little difficult to explain, especially in the title...
Let's say I have the following files: main.cpp
, domain.cpp
, domain.hpp
, generated.hpp
and generator.cpp
. domain.cpp
includes domain.hpp
, which in turn includes generated.hpp
. I want to build two executables:
generator
, consisting of generator.cpp
and domain.cpp
main
, consisting of main.cpp
and domain.cpp
Here's the twist. Initially, generated.hpp
is empty. Before building main
, I want to build generator
(if needed), and always run it before building main
. generator
calculates stuff related to domains.cpp
, and rewrites generated.hpp
- but only if its content would be different. Thus, running generator
for the first time will rewrite the empty generated.hpp
; running it again immediately afterwards will not touch generated.hpp
; and running generator
after making changes to the domain.?pp
files may or may not rewrite generated.hpp
.
Thus, there is a cycle because generator
depends on its output, but it will be traversed only once.
Moreover, generator
has to run before ninja/make/whatever starts examining the dependencies of main
.
Can it be achieved in plain cmake
, without playing tricks with scripts?
It would be nice if the generated generated.hpp
goes in the build area.
The files are in C++17, thus solutions that involve __has_include
are acceptable. But I doubt it can help.
PS In case you wonder about the context, it's for my YOMM2 open multi-methods library. generator
calculates offsets in v-tables, and writes them as constexpr
offsets. If present, they are used to speed up method dispatch.
CMakeLists.txt
initially/generated.hpp # "Initially, generated.hpp is empty."
src/ main.cpp domain.cpp domain.hpp generator.cpp
Then:
# running generator for the first time will rewrite the empty generated.hpp
add_executable(generator_firsttime src/generator.cpp)
target_include_directories(generator_firsttime PRIVATE initially)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/firsttime/generated.hpp
COMMAND generator_firsttime
-o ${CMAKE_BINARY_DIR}/firsttime/generated.hpp
DEPENDS generator_firsttime src/domains.cpp
)
add_custom_target(generated_firsttime_generated_hpp DEPENDS ${CMAKE_BINARY_DIR}/firsttime/generated.hpp)
add_executable(generator_secondtime src/generator.cpp)
target_include_directories(generator_secondtime PRIVATE ${CMAKE_BINARY_DIR}/firsttime)
add_dependencies(generator_secondtime generated_firsttime_generated_hpp)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/secondtime/generated.hpp
COMMAND generator_secondtime
-o ${CMAKE_BINARY_DIR}/secondtime/generated.hpp
DEPENDS generator_secondtime src/domains.cpp
)
add_custom_target(generated_firsttime_generated_hpp DEPENDS ${CMAKE_BINARY_DIR}/secondtime/generated.hpp)
# for ease of use
add_library(generated_hpp INTERFACE)
add_dependency(firsttime_generated_hpp generated_firsttime_generated_hpp)
target_include_directories(generated_hpp INTERFACE {CMAKE_BINARY_DIR}/secondtime)
# not compile your program
add_executable(main src/main.cpp)
target_link_libraries(main PRIVATE generated_hpp)