文档不清晰
CMake的文档在这里并不清晰。CMake的Makefiles生成器会在子Makefiles中创建源文件的make规则,但在主Makefile中是不可见的。在主Makefile中,您将仅找到用于CMake目标的PHONY规则。我知道的唯一例外是Ninja
Makefiles生成器,它将所有构建规则放入单个文件中。
将后处理步骤转换为CMake
根据我的经验——如果post_process
是一个脚本——您应该考虑使用CMake脚本重写/内嵌您的后处理步骤,因为CMake应该了解所有用于后处理的文件依赖项和变量(然后将处理所有必要的重新构建或清理步骤)。下面是我所做的简化/修改版本:
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
)
add_custom_target(${_target} DEPENDS ${_target}_out)
endfunction(my_add_elf)
然后调用
my_add_elf(foo foo.c)
这只是一个例子,但我希望它能够传达出想法:你可以调用make foo
来生成最终的ELF输出,调用make foo_in
或make foo_step1
来生成其中一个步骤的输出。我认为所有步骤对于用户和CMake都是透明的。
不能给目标与其输出相同的名称
当您试图将自定义目标命名为其输出之一的相同名称时,例如像这样:
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)
由于CMake本身不会为Makefile生成特定的内容,使用add_custom_target创建的顶级目标名称总是逻辑(即伪)名称。有关此问题的可能解决方案已在此处提出并得到回复。虽然存在一些解决方法,但它们都有一个或多个缺点。
1. 最短版本:
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)
add_custom_target()
中不能声明 OUTPUT
,但在这种情况下您不需要这样做(因为您不想有任何命名混淆)。但如果您没有声明任何输出:
- 目标将始终被视为过时
- 您需要在
clean
构建规则中添加“不可见”的输出
2. 强制输出名称版本
以下是上述宏的一个版本,它强制指定目标和输出名称的值:
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)
你需要使用以下方式进行调用:
my_add_elf_in_out(foo_in.elf foo_out.elf foo.c)
3. 对象库版本
以下版本使用对象库,但系统不会重用foo_in
目标链接:
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. 最终版本
最后一个版本仅适用于Makefile生成器,这是让我在CMake的错误跟踪器上发布问题的原因:
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)
一些参考资料
foo
指定为任何其他目标的输入(不一定是自定义目标)。add_custom_command()
文档中说:“这定义了一个命令以生成指定的OUTPUT文件。在同一目录(CMakeLists.txt文件)中创建的目标,只要将自定义命令的任何输出指定为源文件,就会得到一个规则来使用构建时的命令生成该文件。” - Florianadd_custom_target(foo_out DEPENDS foo_out)
这样做,因为它试图将依赖项与目标名称匹配。通过使用GNU Makefile生成器进行一些测试后,我发现您示例中的foo_out
的实际构建规则仅生成到´CMakeFiles\bar.dir\build.make´中,并未在主Makefile中显示。所以我尝试了:add_custom_target(foo_out COMMAND post_process foo_in > foo_out)
和set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES foo_out)
。虽然不太好看,但名称是匹配的... - Florian