add_custom_command未生成目标

Mar*_*ata 18 cmake add-custom-command

也许这是不可能的,我误读了cmake 3.2文档,但我创建一个自定义命令会在Makefile中创建一个自定义"目标",这样我就可以通过调用输出文件的名称来构建目标.CMake文档说:

在makefile术语中,这将以下列形式创建一个新目标:

 OUTPUT: MAIN_DEPENDENCY DEPENDS
    COMMAND
Run Code Online (Sandbox Code Playgroud)

所以我想我可以跑了make OUTPUT.也许文档将CMake目标与Makefile目标混淆了?

例如,

 add_custom_command(OUTPUT foo_out
    COMMAND post_process foo_in > foo_out
    DEPENDS foo_in
 )
Run Code Online (Sandbox Code Playgroud)

我想要做

 make foo_out
Run Code Online (Sandbox Code Playgroud)

它会使foo_out.但是,如果我这样做,我明白了

make: **** No rule to make target `foo_out`. Stop.
Run Code Online (Sandbox Code Playgroud)

果然,cmake二进制输出目录中的任何文件中都不存在"foo_out"这个词.如果我把它改成这个

add_custom_target(bar DEPENDS foo_out)
add_custom_command(OUTPUT foo_out COMMAND post_process foo_in > foo_out)
Run Code Online (Sandbox Code Playgroud)

然后我就能做到

make bar
Run Code Online (Sandbox Code Playgroud)

我能做到

make foo_in
Run Code Online (Sandbox Code Playgroud)

但我还是做不到

make foo_out
Run Code Online (Sandbox Code Playgroud)

问题make bar是它不直观,因为实际的文件输出foo_out不是bar.

我该怎么做呢?

在我的例子中,我需要对标准可执行目标运行一个特殊的处理步骤,该目标将可选资源插入到ELF文件中.我希望能够将两个可执行文件作为Makefile目标,因此我可以构建裸ELF可执行文件以及注入资源的ELF可执行文件.

如果我正在编写自定义Makefile,这很简单!

foo_in: foo.c
    $(CC) $< -o $@

foo_out: foo_in
    post_process $< > $@   
Run Code Online (Sandbox Code Playgroud)

而我能做的make foo_inmake foo_out.

qCr*_*ing 12

add_custom_command没有创建一个新的目标.您必须明确定义目标add_executable,add_library或者add_custom_target使它们可见.

如果你必须解决部署问题,你可以

1.使用install命令(在你的CMakeLists.txt中的某个地方),如下所示:

install(SCRIPT <dir>/post_install.cmake)
Run Code Online (Sandbox Code Playgroud)

存储仅make install在单独的.cmake文件中运行时执行的命令.或者,如果安装目标已经为其他事情保留,或者您正在进行更复杂的事情:

2.手动定义部署目标.完成后,您可以创建自定义的构建后命令,该命令仅在您在部署目标上显式运行make时执行.这允许您通过单独的目标执行命令.

在您的CMakeLists.txt中,这可能如下所示:

cmake_minimum_required(VERSION 3.0)

add_executable("App" <sources>)

# option 1: do deployment stuff only when installing
install(SCRIPT <dir>/post_install.cmake)

# option 2: define a deploy target and add post-build commands
add_custom_target("deploy")
add_custom_command(TARGET "deploy" POST_BUILD <some command>)
Run Code Online (Sandbox Code Playgroud)

这两种方法都允许您将开发构建与昂贵的可随时部署的构建分开(如果我理解正确,那么这就是目标).我会推荐选项1,因为它更干净.

希望这可以帮助!


Flo*_*ian 7

文件不清楚

CMake的文档在这里不清楚.CMake的Makefile生成器会在子Makefile中创建源文件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
    )

    # alias / PHONY target
    add_custom_target(${_target} DEPENDS ${_target}_out)

endfunction(my_add_elf)
Run Code Online (Sandbox Code Playgroud)

然后打电话

my_add_elf(foo foo.c)
Run Code Online (Sandbox Code Playgroud)

这只是一个例子,但我希望它能提出这样的想法:你可以调用make foo最终的ELF输出,make foo_in或者make foo_step1用于其他步骤之一.我认为所有步骤对用户和CMake都是透明的.

无法为您的Target提供与其中一个输出相同的名称

当您尝试为自定义目标提供与其输出之一相同的名称时,例如:

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)
Run Code Online (Sandbox Code Playgroud)

你最终得到了无效的make文件.我提出了一个问题,希望通过扩展CMake本身可能有一个可能的解决方案并得到以下答复:

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)
Run Code Online (Sandbox Code Playgroud)

你不能OUTPUTadd_custom_target()自己声明s ,但在这种情况下你不想(因为你不想有任何命名混淆).但是如果你没有声明任何输出:

  • 目标总是被认为是过时的
  • 您需要添加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)
Run Code Online (Sandbox Code Playgroud)

你打电话给:

my_add_elf_in_out(foo_in.elf foo_out.elf foo.c)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

4.最后和最终版本

最后一个版本肯定只适用于Makefile生成器,这让我在CMake的bug跟踪器上发布了这个问题:

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)
Run Code Online (Sandbox Code Playgroud)

一些参考