CMake:如何从子项目的所有静态库创build单个共享库?

我有以下布局:

top_project + subproject1 + subproject2 

每个subproject1subproject2创build一个静态库。 我想将这些静态库链接到top_project级别的单个共享库中。

我目前收集到的信息是:

  • 要么编译使用-fPic (除了Windows之外的所有必要的),以创build位置无关的代码,这将允许将静态库链接到单个共享库或解压缩所有静态库(例如使用ar )并将它们重新链接到共享图书馆(我认为这是一个不雅的和非便携式的解决scheme)
  • 所有的源文件必须明确地给予add_library命令:出于某种我不理解的原因,简单地写add_library(${PROJECT_NAME} SHARED subproject1 subproject2)不能按预期工作(它本质上创build一个空的库&不注册依赖关系正确)
  • CMake中有一个OBJECT库特性,但是我不认为它的目的是真正做我想做的事情。

有什么想法吗?

好吧,我明白了:这比应该更痛苦。 直到最近,Kitware的人才不明白为什么有人会想从静态库创build一个DLL。 他们的论点是,在主目录(例如top_project中总是应该有源文件,因为它实际上是它自己的项目。 我看到了不同的东西,我需要把top_project分解成更小的子项目,这些子项目不应该独立存在(也就是说没有必要为它们创build一个完整的项目并使用ExternalProject_Add添加它们)。 此外,当我运送我的共享库(例如使用Java本地接口)时,我不想运送数十个共享库,因为这将暴露我的项目的内部布局。 无论如何,我想 – 从静态库创build一个共享库的情况下,我会进入技术细节。

subproject1subproject2的CMakeLists.txt中,应该使用OBJECT库特性(在CMake 2.8.8中引入)创build目标:

 add_library(${PROJECT_NAME} OBJECT ${SRC}) 

SRC指定源文件列表(注意,这些文件应该在CMakeLists.txt文件中明确设置,因为它允许在检测到CMakeLists.txt的修改时重新启动CMake,例如添加或删除文件时)

top_project ,添加子项目使用:

 add_subdirectory(subproject1) add_subdirectory(subproject2) 

为了看到静态库中的符号,使用:

 set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--export-all-symbols") 

然后可以使用以下命令创build共享库:

 add_library(${PROJECT_NAME} SHARED $<TARGET_OBJECTS:subproject1> $<TARGET_OBJECTS:subproject2>) 

我发现任何“正常”库(即不是对象)需要添加在一个单独的add_library命令,否则它被简单地忽略。

对于可执行文件,您可以使用:

 add_executable(name_of_executable $<TARGET_OBJECTS:subproject1> $<TARGET_OBJECTS:subproject2>) set(LINK_FLAGS ${LINK_FLAGS} "-Wl,-whole-archive") target_link_libraries(name_of_executable ${PROJECT_NAME} 

我再说一遍,这只适用于CMake的2.8.8版本。 同样,CMake也非常好地pipe理依赖关系,并且是跨平台的,因为它比普通的Makefiles不那么痛苦,当然也不太灵活。

另一种方法做到这一点。

这种方式似乎更简单,但我不知道它是多么完美:

https://stackoverflow.com/a/14347487/602340

我的解决scheme是简单地将/WHOLEARCHIVE-all_load-all_load --whole-archive到链接器标志,以便当您的主库链接时,包括所有的子库,包括所有的符号(默认行为是只包含主库使用的子库的符号,例如:

源文件

 $ echo "void Func1() { }" > source1.cpp $ echo "void Func2() { }" > source2.cpp $ echo "void Func3() { }" > source3.cpp $ echo "void Func4() { }" > source4.cpp 

天真CMakeLists.txt

 cmake_minimum_required(VERSION 3.7) # The 'sub' libraries, eg from an `add_subdirectory()` call. add_library(sublib_a STATIC source1.cpp source2.cpp) add_library(sublib_b STATIC source3.cpp source4.cpp) # The main library that contains all of the sub libraries. add_library(mainlib SHARED) target_link_libraries(mainlib sublib_a sublib_b) 

运行它(在OSX上):

 $ make VERBOSE=1 ... [100%] Linking CXX shared library libmainlib.dylib /usr/local/Cellar/cmake/3.7.1/bin/cmake -E cmake_link_script CMakeFiles/mainlib.dir/link.txt --verbose=1 /Library/Developer/CommandLineTools/usr/bin/c++ -dynamiclib -Wl,-headerpad_max_install_names -o libmainlib.dylib -install_name @rpath/libmainlib.dylib libsublib_a.a libsublib_b.a [100%] Built target mainlib $ nm libmainlib.dylib | grep Func $ 

正确CMakeLists.txt

追加这个:

 # By default, symbols provided by the sublibs that are not used by mainlib (which is all of them in this case) # are not used. This changes that. if (WIN32) set_target_properties(mainlib PROPERTIES LINK_FLAGS "/WHOLEARCHIVE" ) elseif (APPLE) set_target_properties(mainlib PROPERTIES LINK_FLAGS "-Wl,-all_load" ) else () set_target_properties(mainlib PROPERTIES LINK_FLAGS "-Wl,--whole-archive" ) endif () 

运行它(注意extra -all_load ):

 $ make VERBOSE=1 [100%] Linking CXX shared library libmainlib.dylib /usr/local/Cellar/cmake/3.7.1/bin/cmake -E cmake_link_script CMakeFiles/mainlib.dir/link.txt --verbose=1 /Library/Developer/CommandLineTools/usr/bin/c++ -dynamiclib -Wl,-headerpad_max_install_names -Wl,-all_load -o libmainlib.dylib -install_name @rpath/libmainlib.dylib libsublib_a.a libsublib_b.a [100%] Built target mainlib $ nm libmainlib.dylib | grep Func 0000000000001da0 T __Z5Func1v 0000000000001db0 T __Z5Func2v 0000000000001dc0 T __Z5Func3v 0000000000001dd0 T __Z5Func4v 

请注意,我目前实际上只testing了-all_load ,而/WHOLEARCHIVE是一个MSVC 2015选项。

另一种方法是提供所有项目的源文件和头文件的path,并将它们一起构build以产生.so。 这通常是推荐的方法,而不是创build静态库,然后创build一个共享库。

基本上你应该做以下几点:

 FILE(GLOB subproject1_sources <sub_project1_lib_sources_dir>/file1.c <sub_project1_lib_sources_dir>/file2.c //... etc ) FILE(GLOB subproject2_sources <sub_project2_lib_sources_dir>/file1.c <sub_project2_lib_sources_dir>/file2.c //... etc ) FILE(GLOB topProject_sources <top_project_lib_sources_dir>/file1.c <top_project_lib_sources_dir>/file2.c //... etc ) include_directories("<sub_project1_lib_sources_dir>") include_directories("<sub_project2_lib_sources_dir>") include_directories("<top_project_lib_sources_dir>") //should be "." if you're building from here add_library(topProject SHARED ${topProject_sources} ${subproject1_sources} ${subproject2_sources})