2012-07-11 85 views
38

我有以下佈局: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

回答

27

好吧,我想通了:這比應該更痛苦。直到最近,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

1

做的另一種方法是提供源文件和頭文件的路徑所有項目,並將它們一起構建以生成.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

4

我的解決辦法是簡單地添加/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