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

问题描述:

我有以下布局:CMake的:如何从子项目的所有静态库创建一个单一的共享库?

top_project 
    + subproject1 
    + subproject2 

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

我迄今收集的信息是:

  • 要么编译,以创建与位置无关的代码,这将使静态库链接到一个单一的共享库或使用-fPic(必要的一切,但视窗)解压缩所有静态库(例如使用ar)并将它们重新链接到一个共享库(我认为这是一个不便携式解决方案)
  • 所有源文件必须明确给出add_library命令:出于某种原因我无法理解,只是写add_library(${PROJECT_NAME} SHARED subproject1 subproject2)不能正常工作(它本质上创建一个空库&不正确注册依赖关系)
  • CMake中有一个OBJECT库功能,但我不认为它的目的是真正做我想做的。

有什么想法?

+0

我正在使用cmake 3.4。+,我只是将静态库添加到共享库,并且它们被编译为单个文件:)我在android上测试了这个:) – 2016-11-16 10:54:29

+0

任何人都会有关于如何在MSVC下执行此操作的提示?我使用的是qmake而不是cmake,但是我可以自己处理这些步骤,如果我可以弄清楚它们...... – 2017-05-04 13:50:26

好吧,我想通了:这比应该更痛苦。直到最近,Kitware的人才不明白为什么有人会想要从静态库创建一个DLL。他们的论据是,应该有始终处于主(对我来说如top_project)目录中的源文件,因为它实际上是它自己的项目。我看到一些与众不同的事情&我需要打破top_project成不应该独立存在小的子项目(即有在创造一个完全成熟的项目为他们&使用ExternalProject_Add添加它们是没有意义的)。此外,当我船我的共享库(使用,例如,用Java本地接口),我不想出货几十共享库的,因为这等于暴露我的项目的内部布局。无论如何,我认为 - 从静态库创建共享库的情况下,我会继续讨论技术细节。

subproject1subproject2中的CMakeLists.txt,你应该使用对象库功能(在CMake的2.8.8中引入)创建目标:

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

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

top_project时,使用添加子项目:

add_subdirectory(subproject1) 
add_subdirectory(subproject2) 

为了看到从静态库,使用这些符号:

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

然后,您可以创建共享库使用:

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

我发现任何“正常”的libr ary(即不是对象)需要在单独的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也很好地管理依赖关系&是跨平台的,因为它不会比原来的Makefiles &更不灵活。

+0

Bah,烦人的是Ubuntu 12.04卡在CMake 2.8.7上,是否有旧版本的替代方案?定义库时,我们是否必须引用所有源文件? – Ibrahim 2012-11-21 05:52:38

+2

我用-fPIC编译我的静态库来解决我的问题,我的共享库确实链接正确,但我不知道它是否真正起作用,因为我还没有尝试过使用它。 – Ibrahim 2012-11-21 07:34:09

这样做的另一种方法。

这样看起来简单,但我不知道它是多么完美:

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

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

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

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}) 
+0

这不太可能有用。问题在于,你经常会混合使用一些内部魔法产生.o文件的旧版构建系统,而这些魔法是你不想改变的。你很少能够在任何生产系统中添加源代码列表。 – 2018-01-05 00:23:56

我的解决办法是简单地添加/WHOLEARCHIVE-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, e.g. 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() 

运行它(请注意额外的-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 

请注意,我只实际测试-all_load,到目前为止,/WHOLEARCHIVE是MSVC 2015年选项。

+0

我在Linux上,简单地添加'-Wl, - whole-archive'导致了大量与'libgcc.a'有关的'多重定义'错误 – nodakai 2017-03-19 23:02:37

+0

是的/ WHOLULTSIVE选项似乎不起作用好吧,或者我只是用对象库方法。 – Timmmm 2017-03-20 15:42:44