首页 > 解决方案 > Allow mutual dependencies in Cmake

问题描述

I am trying to migrate a huge base of code from a custom in-house toolchain to Cmake. This code is comprised of lots of shared libraries and some executable.

Some of these libraries are interdependent. Let's take as examples Lib1 and Lib2, where Lib1 uses symbols from Lib2, and Lib2 uses symbols from Lib1.
In order to to create the Lib1 DLL, we need exported symbols from Lib2, which in turn need exported symbols from Lib1. Currently we resolved this by linking in two phases, as described here : Mutual Imports ). This gives us the following steps :

  1. Create lib1.lib and lib1.exp
    Create lib2.lib and lib2.exp
  2. Create lib1.DLL using lib1.obj and lib2.lib/lib2.exp
    Create lib2.DLL using lib2.obj and lib1.lib/lib1.exp

I know that this is a code smell, but the code base is way too huge to try and correct all of these cycles. So I would like to get a similar result using Cmake. I did not find any way of doing this from the documentation, except using only custom commands which would completely defeat the purpose of this tool.

The legacy toolchain does the following calls :

# compile sources
cl lib1\SRC\lib1help.cpp ... /Fo"build\cl\lib1.obj"
cl lib2\SRC\lib2help.cpp ... /Fo"build\cl\lib2.obj"

# create lib + exp symbols
lib /DEF build\cl\lib1.obj /OUT:build\link1\lib1.lib
lib /DEF build\cl\lib2.obj /OUT:build\link1\lib2.lib

# create mutual dependant dll using exp
link build\cl\lib1.obj build\link1\lib2.lib /DLL /OUT:build\link2\lib1.dll 
link build\cl\lib2.obj build\link1\lib1.lib /DLL /OUT:build\link2\lib2.dll

As of now I have the following CmakeLists.txt:

add_library(lib2 SHARED lib2/src/lib2help.cpp)
add_library(lib1 SHARED lib1/src/lib1help.cpp)

target_include_directories(lib1 PUBLIC lib1/include/)
target_compile_definitions(lib1 PRIVATE __LIB1)
target_link_libraries(lib1 PRIVATE lib2)

target_include_directories(lib2 PUBLIC lib2/include/)
target_compile_definitions(lib2 PRIVATE __LIB2)
target_link_libraries(lib2 PRIVATE lib1)

This gives me the following result :

[cmake] CMake Error: The inter-target dependency graph contains the following strongly connected component (cycle)  
[cmake]   "lib2" of type SHARED_LIBRARY  
[cmake]     depends on "lib1" (weak)  
[cmake]   "lib1" of type SHARED_LIBRARY  
[cmake]     depends on "lib2" (weak)  

Do you guys know a way I could implement this using Cmake ?
For now I plan on only targeting windows systems, but a OS agnostic would be awesome !

Thanks

标签: c++cmakelinker

解决方案


我非常专注于制作一个 SHARED 库,所以我没有探索 STATIC+SHARED 的方式。感谢@arrowd@tsyvaref的建议。

我使用以下 CmakeLists.txt 让它工作

add_library(lib1_obj OBJECT src/lib1help.cpp)
target_include_directories(lib1_obj PUBLIC include/)
target_include_directories(lib1_obj PRIVATE $<TARGET_PROPERTY:lib2_obj,INCLUDE_DIRECTORIES>)
target_compile_definitions(lib1_obj PRIVATE __LIB1)

add_library(lib1_static STATIC $<TARGET_OBJECTS:lib1_obj>)
target_link_libraries(lib1_static PUBLIC lib2_static)
set_target_properties(lib1_static PROPERTIES STATIC_LIBRARY_OPTIONS "/DEF") 
set_target_properties(lib1_static PROPERTIES OUTPUT_NAME lib1)

add_library(lib1_shared SHARED $<TARGET_OBJECTS:lib1_obj>)
target_link_libraries(lib1_shared PRIVATE lib1_static)
set_target_properties(lib1_shared PROPERTIES RUNTIME_OUTPUT_NAME lib1)

add_library(lib1 ALIAS lib1_shared) 

完全相同的文件用于 lib2,当然除了切换名称。我不确定这是不是最好的方法,但我现在很高兴。这是正在发生的事情:

  • 将源代码编译到 OBJECT 库中
  • 将相互依赖的库链接为静态,使用“/DEF”创建导出文件
  • 重命名静态库输出以方便它们用于链接器
  • 使用预先创建的 .obj、.lib 和 .exp 链接共享库
  • 重命名共享库 dll 以便更容易使用

在这里,我们可以使用Dependency Walker查看每个 DLL 的内容: 在此处输入图像描述


推荐阅读