如何通过CMake中的接口库传播文件级依赖项(自定义命令+自定义目标)?

时间:2018-05-15 21:55:41

标签: cmake

我有以下情况:

  1. 我工作的项目主要是静态库
  2. 因为这是一个嵌入式项目,我也有链接器脚本
  3. 每个链接描述文件在使用之前需要进行一些预处理
  4. 静态库,包含路径,编译器标志和使用预处理链接器脚本的链接器标志通过接口库传播给用户。
  5. 这通常可以正常工作,但是用户的应用程序和预处理的链接器脚本(通过接口库)之间只有目标级别的依赖关系。没有文件级依赖关系,因此当我修改链接描述文件的源时,会重新生成预处理的链接描述文件,但不会重新链接用户的应用程序。

    这是一个测试用例

    $ ls
    CMakeLists.txt  dummy.c  linker-script.ld-source  main.c
    
    $ cat CMakeLists.txt 
    cmake_minimum_required(VERSION 3.1)
    project(a_test)
    
    add_custom_command(OUTPUT linker-script.ld
            COMMAND cmake -E copy ${CMAKE_CURRENT_SOURCE_DIR}/linker-script.ld-source linker-script.ld
            DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/linker-script.ld-source)
    add_custom_target(linker-script DEPENDS linker-script.ld)
    
    add_library(static-library STATIC
            dummy.c)
    
    add_library(interface-library INTERFACE)
    target_link_libraries(interface-library INTERFACE
            # -Tlinker-script.ld
            static-library)
    add_dependencies(interface-library linker-script)
    
    add_executable(application
            main.c)
    target_link_libraries(application interface-library)
    
    $ cat main.c 
    int main(void)
    {
        return 0;
    }
    

    dummy.clinker-script.ld-source只是空的。实际上并未使用生成的链接描述文件,但推荐的片段显示了我打算如何使用它。让我们来运行它:

    $ mkdir output
    
    $ cd output
    
    $ cmake ..
    -- The C compiler identification is GNU 8.1.0
    -- The CXX compiler identification is GNU 8.1.0
    -- Check for working C compiler: /usr/bin/cc
    -- Check for working C compiler: /usr/bin/cc -- works
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Check for working CXX compiler: /usr/bin/c++
    -- Check for working CXX compiler: /usr/bin/c++ -- works
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /tmp/cmake/output
    
    $ make
    Scanning dependencies of target static-library
    [ 20%] Building C object CMakeFiles/static-library.dir/dummy.c.o
    [ 40%] Linking C static library libstatic-library.a
    [ 40%] Built target static-library
    Scanning dependencies of target linker-script
    [ 60%] Generating linker-script.ld
    [ 60%] Built target linker-script
    Scanning dependencies of target application
    [ 80%] Building C object CMakeFiles/application.dir/main.c.o
    [100%] Linking C executable application
    [100%] Built target application
    
    好的,一切似乎都很好。现在让我们说链接器脚本的源代码已更新:

    $ touch ../linker-script.ld-source
    
    $ make
    [ 40%] Built target static-library
    [ 60%] Generating linker-script.ld
    [ 60%] Built target linker-script
    [100%] Built target application
    

    如您所见,应用程序未重新链接,这是一个问题。有什么想法可以解决这种情况吗?

3 个答案:

答案 0 :(得分:0)

(至少现在)唯一正确的解决方案是使用LINK_DEPENDS目标属性。为此,我添加了自己的帮助函数,如下所示:

#
# Specifies linker script(s) used for linking `target`.
#
# `distortosTargetLinkerScripts(target [linker-script.ld ...])`
#
# Most common use will be with single linker script provided by distortos:
# `distortosTargetLinkerScripts(target $ENV{DISTORTOS_LINKER_SCRIPT})`
#
# Proper linker flag is added, as well as CMake dependency via LINK_DEPENDS target property.
#

function(distortosTargetLinkerScripts target)
    foreach(linkerScript IN LISTS ARGN)
        target_link_libraries(${target} PRIVATE
                -T"${linkerScript}")
        get_target_property(linkDepends ${target} LINK_DEPENDS)
        if(NOT linkDepends)
            unset(linkDepends)
        endif()
        list(APPEND linkDepends "${linkerScript}")
        set_target_properties(${target} PROPERTIES
                LINK_DEPENDS "${linkDepends}")
    endforeach()
endfunction()

答案 1 :(得分:0)

更新:通过为CMake 3.13(INTERFACE_LINK_DEPENDSLINK_DEPENDS中提到忍者的更新文档)中的接口目标添加新的目标属性,似乎似乎有了更多的支持。作为受支持的生成器(尽管不确定在什么版本的CMake中实际实现该功能)。


注意:这不是一个完整的答案,只是记录了我使用忍者作为生成器的情况

使用LINK_DEPENDS不适用于非Makefile生成器,例如Ninja

this邮件列表帖子中找到的方法中,我通常的方法是使用虚拟文件并使用

修改其时间戳。
add_custom_command(OUTPUT linker-script.ld
  [...]
  COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_SOURCE_DIR}/dummy.c
  [...])

不过,这仍然无法正常工作,因为在第一次调用时,它会检测到链接描述文件已过时,从而执行其复制并“接触”虚拟源文件。但是,需要第二次调用才能进行修改并重建所有相关目标。

这很不方便,但是我相信这是Ninja对依赖项进行分类(隐式,显式,仅顺序)引起的。使用add_dependencies命令只能创建仅订购的Ninja依赖项,因为链接程序脚本不会以CMake术语影响您要用来运行二进制目标的构建行。

对于interface库,我什至看不到它在这种情况下有什么帮助,因为它甚至没有创建构建输出。它主要像一个组织占位符。

答案 2 :(得分:0)

这是一篇可能有帮助的文章

https://samthursfield.wordpress.com/2015/11/21/cmake-dependencies-between-targets-and-files-and-custom-commands/

请参阅此部分:

  1. 自定义目标的文件级相关性不会传播

简而言之,您还需要将链接程序脚本依赖项添加到库中。是的,这很糟糕。

add_dependencies(interface-library linker-script linker-script.ld-source)