CMake 外部和内部静态库的循环依赖关系

CMake cyclic dependency of external and internal static library

本文关键字:循环 依赖 关系 静态 外部 内部 CMake      更新时间:2023-10-16

我有一个框架/代码生成器,带有一个静态库libA.a,其中包含由生成的代码(libB(定义的符号。所以从本质上讲,我认为依赖关系图是循环的,因为生成的代码使用 libA 中的 API:

libB -> libA -> libB

libB 我正在构建,然后用于创建可执行文件

set(LIBA libA.a)
add_library(libB "${libb_sources}")
target_link_libraries(libB libA) 
add_executable(execAB ${MAIN_SRC})
target_link_libraries(execAB libB)

我遇到的问题是,在链接过程中,libB 中定义的 libA 符号没有得到解决。相对正常的循环依赖,但包括构建期间未构建的外部库。

/usr/local/lib/libara_vsomeipbinding.a(vsomeip_proxy_factory.cc.o): In function `ara::com::internal::vsomeip::runtime::VSomeIPProxyFactoryImpl::AvailabilityHandler(unsigned short, unsigned short, bool)':
vsomeip_proxy_factory.cc:(.text+0x7ff): undefined reference to `ara::com::internal::vsomeip::runtime::VSomeIPServiceMapping::GetMappingForVSomeIPServiceId(unsigned short)'

到目前为止我尝试过:

  • 使libB成为OBJECT库可以解决这个问题,但是我需要将其设置为静态库,以与项目的其他部分保持一致,因此这不是解决方案

  • 定义一个中间静态库 libC,libB 是一个对象库 -> 没有帮助,因为情况基本上没有变化,我猜。

  • 具有中间库 libC,并多次定义依赖项以使它们在链接器命令行中多次出现。没有用,他们只出现了一次。
  • 定义libB和libC之间的循环依赖关系,希望依赖关系也加倍。不,只有这两个在命令行上出现两次。

其他问题:

  • --start-group/--end-group这样的仅限GCC的解决方案也不是一种选择,因为我们同时针对Visual Studio和gcc
  • 如果可能的话,我不想将 libA 添加为 execAB 的依赖项,依赖项应该封装在某些(如果可能的话是静态的(库目标中

如何让链接器将 libA 和 libB 链接为组/两次,以便正确解析所有符号?


更详细的信息:

我正在自适应AUTOSAR的ARA参考实现之上构建一个框架。在AUTOSAR中,服务/服务接口是在XML文件中定义的,ARA有一个代码生成器,可以读取这些代码并为这些服务/它们的通信生成代码,我的框架使用。在我的 cmake 构建过程中,ARAgen 生成器运行以生成代码。

当然,ARA API有一个静态部分,这就是libA。在代码的生成部分(即libB的一部分(中,例如,有ara::com::internal::vsomeip::runtime::VSomeIPServiceMapping::GetMappingForVSomeIPServiceId(unsigned short),根据名称,我会说将数字ID映射到特定的(生成的(服务。静态 ARA 部件使用它从服务 ID 映射到服务对象/类。

另一方面,静态ARA库当然包含生成的部分使用的代码(我猜是基类等(。

我也为此苦苦挣扎,但能够通过首先转到库"B"的 CMakeLists.txt 文件来解决循环依赖关系:

add_library( libraryB STATIC ${libraryB_sources} )
set_target_properties( libraryB PROPERTIES PREFIX lib )
target_link_libraries( libraryB libraryA )

然后,在 CMakeList 中.txt对于库 A 和将它们链接在一起的可执行文件:

add_library( libraryA STATIC ${libraryA_sources} )
target_link_libraries( libraryA libraryB )
add_executable( myexe ${myexe_sources} )
target_link_libraries( myexe libraryA )
# Don't know how far back the features used here go
# so just adjust the range of CMake versions you can use
cmake_minimum_required(VERSION 3.23...3.26) 
project(StackOverflowProject)
# Define libA as an imported library
add_library(A STATIC IMPORTED)
add_library(framework::A ALIAS A)
set_property(TARGET A PROPERTY
        # Set the location property of the imported library
        IMPORTED_LOCATION /path/to/my/framework/supplied/libA.a
)
# Link the framework library to your (soon to be)
# generated library libB.a
target_link_libraries(A INTERFACE generated::B)

# Define the generated code library
add_library(B STATIC)
add_library(generated::B ALIAS B)
# target_sources my be defined in one or more CMakeLists.txt files
target_sources(B PRIVATE b_generated_code.cpp)
# And link it to the supplied framework libA.a
target_link_libraries(B PUBLIC framework::A)
# In case there is a deeper cyclic dependency between the
# libraries you can increase the default value of 2 repetitions
# with e.g.
set_property(TARGET B PROPERTY LINK_INTERFACE_MULTIPLICITY 3)
# Your executable just links to the generated code and CMake will
# list "libB.a libA.a libB.a libA.a" when linking (or three times
# with LINK_INTERFACE_MULTIPLICITY set to 3)
add_executable(execAB main.cpp)
target_link_libraries(execAB PRIVATE generated::B)