我是否需要做些什么来使CMake生成的构建系统并行编译彼此之间具有链接依赖关系的目标的源代码?

kmbjn2e3  于 2023-03-18  发布在  其他
关注(0)|答案(4)|浏览(131)

我正在做一个项目,它的依赖关系树如下:

main
|_ libB
   |_ libA

由于libB依赖于来自libA的头文件,因此我将执行以下操作:

target_link_libraries(libB PRIVATE libA)

但如果我理解正确的话,这意味着在这些库之间创建一个硬依赖关系?
因此在这一行之后,CMake将理解到需要在构建libB之前完全构建libA,从而在不需要的地方增加编译时间。
如何使libB依赖于libA的include目录,而不同时依赖于libA的完整编译?
PS:这个用例是我的实际问题的一个非常简化的版本,但它允许我简单地解释它。

rpppsulh

rpppsulh1#

我只想使用ninja来修复依赖项带来的编译顺序,但是对于没有为此进行优化的构建系统,也有一些变通方法。
解决方法是只添加另一个目标的include目录:

target_include_directories(libB PRIVATE $<TARGET_PROPERTY:libA,INCLUDE_DIRECTORIES>)

我仍然会添加一条评论,说这只是MSBuild缺乏的一个变通方法。

t2a7ltrp

t2a7ltrp2#

因此在这一行之后,CMake将理解需要在构建libB之前完全构建libA,从而在不需要的地方增加编译时间。
你从哪里得到这个想法的?构建系统如何处理这样的依赖关系取决于构建系统-而不是CMake。CMake只是生成构建系统。CMake可能需要做一些事情来帮助构建系统理解它是什么类型的依赖关系。但一般来说,我认为这是不必要的。只是尝试各种发电机。你我可能会观察到聪明的程序同时为这样的目标编译源代码,只有在链接步骤,两个相互链接的二进制程序才需要准备好链接。
但是对于有很多源代码的目标,您可能不会注意到,即使使用更智能的构建系统,也只是因为您的机器只有这么多内核(您只能获得这么多并行性),并且根据您的机器所具有的内核数量,在某个点上,并行构建多个目标的源代码不会提高速度(当您的目标有足够数量的源文件时)。
如何使libB依赖于libA的include目录,而不同时依赖于libA的完整编译?
如前所述,对于一个智能构建系统,你不需要做任何特殊的事情,让CMake做它的工作,让生成的构建系统做它的工作。
但是,如果您想支持某些内容,可以直接指定target include目录(使用target_include_directories())而不是让CMake使用target_link_libraries来完成它的工作。但是这样你就不得不使用更多的拐杖来处理链接目标之类的事情...除非没有要链接的东西-在这种情况下,它是一个只包含头文件的库,没有要构建的东西,你所担心的根本不是问题。

fdbelqdn

fdbelqdn3#

在Modern CMake(cmake〉3.0)中,你可以创建INTERFACE库,即“像”库一样工作但不是真实的库的抽象。
这使您可以做您所需要的事情-引用包含文件而不需要实际花费时间进行编译。

cmake_minimum_required( VERSION 3.0 )
project(main)

# In your library folder
add_library( test INTERFACE )
target_include_directories( test INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} )

# back in the root folder
add_executable( main main.cpp )
target_link_libraries( main test )

Godbolt链接:https://godbolt.org/z/7bea6178Y

bvk5enib

bvk5enib4#

我终于找到了一个不需要使用另一台发电机的解决方案。
我一直在使用MSBuild生成Visual Studio解决方案(我并不同意这一点)。
我找到的解决方案是我写的这个函数:

# Propagates include directories from target_src and its dependencies to target_dst
function(target_propagate_include_dirs target_dst target_root)
    if (${ARGC} LESS 3)
        set(target_src ${target_root})
    else()
        set(target_src ${ARGV2})
    endif()

    # Get dependencies of target
    get_target_property(DEPENDENCIES ${target_src} INTERFACE_LINK_LIBRARIES)

    foreach(MODULE IN LISTS DEPENDENCIES)
        if (TARGET "${MODULE}" AND NOT "${MODULE}" STREQUAL "${target_root}" AND NOT "${MODULE}" STREQUAL "${target_dst}")
            # Get include directories of current module
            get_target_property(INCLUDE_DIRS ${MODULE} INCLUDE_DIRECTORIES)

            # Propagate those directories into "module_include" target
            foreach(DIR IN LISTS INCLUDE_DIRS)
                target_include_directories(${target_dst} INTERFACE "${DIR}")
            endforeach()

            # Recurse over dependencies
            target_propagate_include_dirs(${target_dst} ${target_root} ${MODULE})
        endif()
    endforeach()
endfunction()

我的项目的更准确布局是:

|_module
  |_ moduleA
  |_ moduleB
  |_ ...
|_source
  |_libs
    |_ libA
    |_ libB
    |_ ...
  |_main

这里,我有两个INTERFACE库modulelibs,以及一个main可执行文件。
libs中的每个库都依赖于module,并且main依赖于libs
我找到的唯一解决方案是使用我的函数将include目录传播到另一个INTERFACE目标,例如module_include

add_library(module_include INTERFACE)

target_propagate_include_dirs(module_include module)

这样,我就可以依靠module_include自动获取include目录,而不依赖于每个模块的编译。

相关问题