I want to generate files in the per-configuration build directory:
add_executable(test_generator_gen test_generator.cpp)
target_compile_definitions(test_generator_gen PRIVATE GENERATE)
target_link_libraries(test_generator_gen YOMM2::yomm2)
add_custom_command(
OUTPUT ${GENERATED_FILES}
DEPENDS test_generator_gen
COMMAND test_generator_gen
WORKING_DIRECTORY $<TARGET_FILE_DIR:test_generator_gen>
)
The files are generated where I want (e.g. ./builds/headers-only/tests/Debug/test_generator_slots.hpp
and another file). My problem is, what do I put in ${GENERATED_FILES}
? If I put "${CMAKE_CURRENT_BINARY_DIR}/test_generator_slots.hpp"
, I am lying to cmake
, and the files get generated again and again. I tried what seemed natural to me:
add_custom_command(
OUTPUT $<TARGET_FILE_DIR:test_generator_gen>/test_generator_slots.hpp
DEPENDS test_generator_gen
COMMAND test_generator_gen
WORKING_DIRECTORY $<TARGET_FILE_DIR:test_generator_gen>
)
# ...
...but I get an error:
CMake Error at tests/CMakeLists.txt:95 (add_custom_command):
[cmake] Error evaluating generator expression:
[cmake]
[cmake] $<TARGET_FILE_DIR:test_generator_gen>
[cmake]
[cmake] No target "test_generator_gen"
I am puzzled. Why is the same generator expression in the same command accepted for some arguments and not others? And how do I specify the OUTPUT correctly?
cmake 3.22.1
While the error message looks confusing, the documentation is clear:
Arguments to
OUTPUT
may use a restricted set of generator expressions. Target-dependent expressions are not permitted.
It is known that for multi-configuration generators the name of default output directory for executables and libraries is the same as configuration name. So you could use $<CONFIG>
expression for it:
OUTPUT ${CMAKE_BINARY_DIR}/$<CONFIG>/test_generator_slots.hpp
For make the project to work both with multi- and single-configuration generators you need to use if
:
if(CMAKE_CONFIGURATION_TYPES)
# Multi-configuration generator
set(output_dir ${CMAKE_BINARY_DIR}/$<CONFIG>)
else()
# Single-configuration generator
set(output_dir ${CMAKE_BINARY_DIR})
endif()
add_custom_command(
OUTPUT ${output_dir}/test_generator_slots.hpp
...
)
Alternatively, you could explicitly set CMAKE_RUNTIME_OUTPUT_DIRECTORY variable for control output directory of the executables. And use that variable when need to refer to that directory:
if(CMAKE_CONFIGURATION_TYPES)
# Multi-configuration generator
#
# Explicitly use generator expression,
# so CMake won't automatically append the subdirectory with a config name.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<CONFIG>/bin)
else()
# Single-configuration generator
#
# While it is allowable to create config-specific directory,
# we don't do that: every configuration already has its own build tree.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
endif()
# The executable will be created in the directory specified by
# CMAKE_RUNTIME_OUTPUT_DIRECTORY variable.
add_executable(test_generator_gen test_generator.cpp)
add_custom_command(
OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_generator_slots.hpp
...
)