I have a project when all source files are preprocessed with custom template engine. I would want all template files to be generated first, then compiled, as separate stages, so that I do not get missing #include
s later. In my mind that's simple - I create a target that depends on all generated files and add_dependency between the library and generated target.
Take the following structure:
CMakeLists
src/CMakelists.txt
src/a.c
And we have:
mkdir -p src
touch src/a.c
cat >CMakeLists.txt <<'EOF'
cmake_minimum_required(VERSION 3.11)
project(test)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generatedtimestamp
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/generatedtimestamp
)
add_custom_target(generatedtarget DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generatedtimestamp)
set(GENERATED "" CACHE INTERNAL "") # bin to collect all generated files
function(template_generate A_SOURCE A_OUTPUT)
add_custom_command(
OUTPUT ${A_OUTPUT}
DEPENDS ${A_SOURCE}
COMMAND ${CMAKE_COMMAND} -E copy ${A_SOURCE} ${A_OUTPUT}
)
get_filename_component(A_OUTPUT ${A_OUTPUT} ABSOLUTE)
list(APPEND GENERATED "${A_OUTPUT}")
set(GENERATED "${GENERATED}" CACHE INTERNAL "")
endfunction()
add_subdirectory(src)
message(STATUS ${GENERATED})
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generatedtimestamp
DEPENDS ${GENERATED}
APPEND
)
EOF
cat >src/CMakeLists.txt <<'EOF'
template_generate(
${CMAKE_CURRENT_SOURCE_DIR}/a.c
${CMAKE_CURRENT_BINARY_DIR}/gen/a.c
)
add_executable(testtarget
${CMAKE_CURRENT_BINARY_DIR}/gen/a.c
)
add_dependencies(testtarget generatedtarget)
EOF
However this results in:
+ cmake -H. -B_build
-- Configuring done
-- Generating done
-- Build files have been written to: /dev/shm/.1000.home.tmp.dir/_build
+ cmake --build ./_build --parallel --verbose
ninja: error: dependency cycle: src/gen/a.c -> generatedtarget -> CMakeFiles/generatedtarget -> generatedtimestamp -> src/gen/a.c
I do not see that dependency cycle. In my mind, the dependency is linear (left <- right
represents that "right depends on left"):
testtarget -> generatedtarget -> generatedtimestamp -> gen/a.c -> src/a.c
\-------------------------->------------/
Why is there a dependency cycle?
... generatedtimestamp -> src/gen/a.c
- why does gen/a.c
depend on generatedtimestamp
?
Why is there a dependency cycle?
I'm going to name the participants in the cycle like so:
(A)
file: src/a.c
(B)
file: <bin>/src/gen/a.c
(C)
file: <bin>/generatedtimestamp
(D)
target: testtarget
(E)
target: generatedtarget
The error message in your question corresponds to the cycle (note that CMakeFiles/generatedtarget
is just an implementation detail):
(B) -> (E) -> (C)
\--<---<--*
Now here's what happens:
add_custom_command
and add_custom_target
, you establish (E) -> (C)
.src/CMakeLists.txt
, the first lines of template_generate
establish (B) -> (A)
.add_executable
establishes (D) -> (B)
.add_dependencies
function then explicitly establishes (D) -> (E)
.
(B)
-> (E)
. This is the problem. add_dependencies
forces the dependent to be completely built before the given target.add_custom_command(APPEND)
call then establishes (C)
-> (B)
So the whole graph is:
/--<---<--*
(D) -> (E) -> (C) -> (B) -> (A)
*-->--->--->-->--/
and you can see the original cycle in this log.