简体   繁体   English

add_custom_command未生成目标

[英]add_custom_command is not generating a target

Perhaps this is impossible and I'm misreading the cmake 3.2 documentation , but I though creating a custom command would create a custom "target" in the Makefile so that I could build the target by invoking the name of the output file. 也许这是不可能的,并且我误读了cmake 3.2文档 ,但我虽然创建了一个自定义命令,但会在Makefile中创建一个自定义的“目标”,以便我可以通过调用输出文件的名称来构建目标。 The CMake docs says: CMake文档说:

In makefile terms this creates a new target in the following form: 用makefile术语,它以以下形式创建一个新目标:

  OUTPUT: MAIN_DEPENDENCY DEPENDS COMMAND 

so I thought I could then run make OUTPUT . 所以我想我可以再运行make OUTPUT Perhaps the documentation is confusing CMake targets with Makefile targets? 也许文档使CMake目标与Makefile目标混淆了?

For example, 例如,

 add_custom_command(OUTPUT foo_out
    COMMAND post_process foo_in > foo_out
    DEPENDS foo_in
 )

I would like to do 我想要做

 make foo_out

and it will make foo_out . 它将成为foo_out However, if I do this, I get 但是,如果我这样做,我得到

make: **** No rule to make target `foo_out`. Stop.

and sure enough, the word "foo_out" doesn't exist anywhere in any file in the cmake binary output directory. 并且可以肯定的是,在cmake二进制输出目录中的任何文件中的任何地方都不存在单词“ foo_out”。 If I change it to this 如果我改成这个

add_custom_target(bar DEPENDS foo_out)
add_custom_command(OUTPUT foo_out COMMAND post_process foo_in > foo_out)

Then I can do 那我就可以

make bar

and I can do 我可以做

make foo_in

but I still can't do 但我还是做不到

make foo_out

The problem with make bar is that it is unintuitive, as the actual file output is foo_out not bar . make bar的问题在于它不直观,因为实际文件输出是foo_out而不是bar

How do I do this? 我该怎么做呢?

In my case, I need to run a special processing step to the standard executable target which inserts optional resources into the ELF file. 就我而言,我需要对标准可执行目标执行一个特殊的处理步骤,该步骤将可选资源插入到ELF文件中。 I would like the ability to have both executables as Makefile targets, so I can build the naked ELF executable as well as the resource-injected ELF executable. 我希望能够将两个可执行文件都作为Makefile目标,因此我可以构建裸露的ELF可执行文件以及资源注入的ELF可执行文件。

If I was writing a custom Makefile, this is trivial to do! 如果我正在编写自定义的Makefile,这很简单!

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

foo_out: foo_in
    post_process $< > $@   

And I can do make foo_in and make foo_out . 我可以做make foo_inmake foo_out

add_custom_command does not create a new target. add_custom_command 不会创建一个新的目标。 You have to define targets explicitly by add_executable , add_library or add_custom_target in order to make them visible to make. 您必须通过add_executableadd_libraryadd_custom_target显式定义目标,以使其可见。

If you have to fix things up for deployment, you could 如果您必须为部署进行修复,则可以

1. use the install command (somewhere in your CMakeLists.txt) like this: 1.使用install命令(在CMakeLists.txt中的某个位置),如下所示:

install(SCRIPT <dir>/post_install.cmake)

to store commands which are executed only when you run make install in a separate .cmake file. 将仅当您运行make install时执行的命令存储在单独的.cmake文件中。 Or if the install target is already reserved for other things or you have more complex stuff going on: 或者,如果安装目标已经预留给其他用途,或者您正在执行更复杂的操作:

2. manually define a deploy target. 2.手动定义部署目标。 Once you got that, you can create a custom post-build command which is only executed when you explicitly run make on your deploy target. 一旦了解了这一点,就可以创建一个自定义的生成后命令,该命令仅在您在部署目标上显式运行make时才执行。 This allows you to execute commands through a separate target. 这使您可以通过单独的目标执行命令。

In your CMakeLists.txt this could look like: 在您的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>)

Both approaches allow you to separate dev builds from expensive ready-to-deploy builds (if I understand correctly, that's the goal here). 两种方法都可以让您将开发版本与昂贵的现成部署版本分开(如果我理解正确,那就是这里的目标)。 I would recommend option 1 since it's just cleaner. 我建议选择1,因为它更干净。

Hope this helps! 希望这可以帮助!

Documentation Unclear 文档不清楚

CMake's documentation is unclear here. CMake的文档在这里不清楚。 The Makefiles generators of CMake do create the source file make rules in sub Makefiles which are not visible in the main Makefile. CMake的Makefiles生成器会在子Makefile中创建源文件make规则,这些规则在主Makefile中不可见。 In the main Makefile you will find only the PHONY rules for your CMake targets. 在主Makefile中,您只会找到CMake目标的PHONY规则。 The only exception I know of is the Ninja Makefiles generator which puts all build rules into single file. 我知道的唯一例外是Ninja Makefiles生成器,它将所有构建规则放入单个文件中。

Translating Post-Processing Steps into CMake 将后处理步骤转换为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 eg handle all the necessary re-build or clean-up steps for you). 根据我的经验-如果post_process是脚本-您可能应该考虑使用CMake脚本/在其中编写重写后处理步骤,因为CMake应该知道用于后处理的所有文件依赖项和变量(然后它将处理所有必要的重建或清理步骤)。

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. 这只是一个示例,但我希望它能给出一个主意:您可以为最终的ELF输出调用make foo为其他步骤之一调用make foo_inmake foo_step1 And I think all steps are transparent for the user and CMake. 而且我认为所有步骤对于用户和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 eg 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. 您最终将获得无效的make文件。 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本身来解决问题,并得到以下答复:

CMake is not intended to produce specific content in a Makefile. CMake不能在Makefile中产生特定的内容。 Top-level target names created by add_custom_target are always logical (ie phony) names. 由add_custom_target创建的顶级目标名称始终是逻辑(即伪)名称。 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: 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)

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). 您不能在add_custom_target()本身中声明OUTPUT ,但是在这种情况下,您不想这样做(因为您不希望有任何命名混乱)。 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 您需要添加“不可见”输出的clean构建规则

2. Force Output Name Version 2.强制输出名称版本

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 3.对象库版本

The following version uses object libraries, but the system will not reuse the foo_in target linkage: 以下版本使用对象库,但是系统不会重用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. Last and Final Version 4.最终版本

And one final version that definitely works only for with Makefile generators and that got me posting the issue at CMake's bug tracker: 最后一个绝对只适用于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)

Some references 一些参考

Turning around the dependencies, and using the second signature of add_custom_command , this should work: 转过来的依赖项,并使用add_custom_command第二个签名 ,这应该工作:

add_custom_target(foo_out DEPENDS foo_in)
add_custom_command(TARGET foo_out POST_BUILD COMMAND post_process foo_in > foo_out)

Note: Adding BYPRODUCTS foo_out will cause (for example) ninja to say 注意:添加BYPRODUCTS foo_out将导致(例如) 忍者

multiple rules generate foo_out. 多个规则生成foo_out。 builds involving this target will not be correct; 涉及此目标的构建将是不正确的; continuing anyway 反正继续

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM