Documentation Unclear
CMake's documentation is unclear here. The Makefiles generators of CMake do create the source file make rules in sub Makefiles which are not visible in the main Makefile. In the main Makefile you will find only the PHONY rules for your CMake targets. The only exception I know of is the Ninja
Makefiles generator which puts all build rules into single file.
Translating Post-Processing Steps into CMake
From my experience - if post_process
is a script - you should probably think about rewriting your post-processing steps with/inside the CMake scripts, because CMake should know about all the file dependencies and variables used for post-processing (it then will e.g. handle all the necessary re-build or clean-up steps for you).
Here is a simplified/modified version of what I do:
function(my_add_elf _target)
set(_source_list ${ARGN})
add_executable(${_target}_in ${_source_list})
set_target_properties(
${_target}_in
PROPERTIES
POSITION_INDEPENDENT_CODE 0
SUFFIX .elf
)
add_custom_command(
OUTPUT ${_target}_step1.elf
COMMAND some_conversion_cmd $<TARGET_FILE:${_target}_in> > ${_target}_step1.elf
DEPENDS ${_target}_in
)
add_custom_target(
${_target}_step1
DEPENDS
${_target}_step1.elf
)
add_custom_command(
OUTPUT ${_target}_out.elf
COMMAND final_post_process_cmd ${_target}_step1.elf > ${_target}_out.elf
DEPENDS ${_target}_step1
)
add_custom_target(
${_target}_out
DEPENDS
${_target}_out.elf
)
# alias / PHONY target
add_custom_target(${_target} DEPENDS ${_target}_out)
endfunction(my_add_elf)
and then call
my_add_elf(foo foo.c)
It's only an example, but I hope it gives the idea: you could call make foo
for the final ELF output, make foo_in
or make foo_step1
for one of the other steps. And I think all steps are transparent for the user and CMake.
Can't give your Target the same name as one of the Outputs
When you're trying to give a custom target the same name as one of its outputs e.g. like this:
add_executable(foo_in foo.c)
add_custom_command(
OUTPUT foo_out
COMMAND post_process foo_in > foo_out
DEPENDS foo_in
)
add_custom_target(foo_out DEPENDS foo_out)
You end-up with invalid make files. I've raised an issue about this in the hope that there could be a possible solution by extending CMake itself and got the following reply:
CMake is not intended to produce specific content in a Makefile.
Top-level target names created by add_custom_target are always logical
(i.e. phony) names. It is simply not allowed to have a file of the
same name.
Posible Workarounds
So there are some workarounds, but they all have one or the other disadvantage.
1. Shortest Version:
macro(my_add_elf_we _target)
add_executable(${_target}_in ${ARGN})
add_custom_target(
${_target}_out
COMMAND post_process $<TARGET_FILE:${_target}_in> > ${_target}_out
DEPENDS ${_target}_in
)
set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${_target}_out)
endmacro(my_add_elf_we)
You can't declare OUTPUT
s in the add_custom_target()
itself, but in this case you don't want to (because you don't want to have any naming confusions). But if you don't declare any outputs:
- The target will always considered out-of-date
- You need to add the "invisible" outputs the
clean
build rule
2. Force Output Name Version
Here is a version of the above macro that forces target and output names to given values:
macro(my_add_elf_in_out _target_in _target_out)
add_executable(${_target_in} ${ARGN})
set_target_properties(
${_target_in}
PROPERTIES
SUFFIX ""
OUTPUT_NAME "${_target_in}"
)
add_custom_target(
${_target_out}
COMMAND post_process ${_target_in} > ${_target_out}
DEPENDS ${_target_in}
)
set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${_target_out})
endmacro(my_add_elf_in_out)
You call it with:
my_add_elf_in_out(foo_in.elf foo_out.elf foo.c)
3. Object Libary Version
The following version uses object libraries, but the system will not reuse the foo_in
target linkage:
macro(my_add_elf_obj_in_out _target_in _target_out)
add_library(${_target_in}_obj OBJECT ${ARGN})
add_executable(${_target_in} $<TARGET_OBJECTS:${_target_in}_obj>)
set_target_properties(
${_target_in}
PROPERTIES
SUFFIX ""
OUTPUT_NAME "${_target_in}"
)
add_executable(${_target_out} $<TARGET_OBJECTS:${_target_in}_obj>)
set_target_properties(
${_target_out}
PROPERTIES
SUFFIX ""
OUTPUT_NAME "${_target_out}"
EXCLUDE_FROM_ALL 1
)
add_custom_command(
TARGET ${_target_out}
POST_BUILD
COMMAND post_process ${_target_in} > ${_target_out}
)
endmacro(my_add_elf_obj_in_out)
4. Last and Final Version
And one final version that definitely works only for with Makefile generators and that got me posting the issue at CMake's bug tracker:
macro(my_add_elf_ext_in_out _target_in _target_out)
add_executable(${_target_in} ${ARGN})
set_target_properties(
${_target_in}
PROPERTIES
SUFFIX ""
OUTPUT_NAME "${_target_in}"
)
add_executable(${_target_out} NotExisting.c)
set_source_files_properties(
NotExisting.c
PROPERTIES
GENERATED 1
HEADER_FILE_ONLY 1
)
set_target_properties(
${_target_out}
PROPERTIES
SUFFIX ""
OUTPUT_NAME "${_target_out}"
RULE_LAUNCH_LINK "# "
)
add_custom_command(
TARGET ${_target_out}
POST_BUILD
COMMAND post_process ${_target_in} > ${_target_out}
)
add_dependencies(${_target_out} ${_target_in})
endmacro(my_add_elf_ext_in_out)
Some references
foo
as an input to any other target (not necessarily a custom target). Theadd_custom_command()
documentation says "This defines a command to generate specified OUTPUT file(s). A target created in the same directory (CMakeLists.txt file) that specifies any output of the custom command as a source file is given a rule to generate the file using the command at build time." – Brenanbar
target, I still can't buildfoo
directly (if I domake foo
nothing happens) even thoughbar
depends onfoo
. – Volcanicadd_custom_target(foo_out DEPENDS foo_out)
in CMake because it tries to match the dependencies to target names. And after doing some testing with a GNU Makefile generator I see that the actual build rule forfoo_out
as in your example is generated only into ´CMakeFiles\bar.dir\build.make´ and is not made visible in the main Makefile. So I tired:add_custom_target(foo_out COMMAND post_process foo_in > foo_out)
withset_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES foo_out)
. Not nice, but the names match ... – Brenan