2012-07-11 2 views
38

Ho il seguente schema:CMake: come creare una singola libreria condivisa da tutte le librerie statiche di sottoprogetti?

top_project 
    + subproject1 
    + subproject2 

Ognuno di subproject1 e subproject2 crea una libreria statica. Vorrei collegare queste librerie statiche in una singola libreria condivisa al livello top_project.

Le informazioni che ho raccolto finora è:

  • O compilazione usando -fPic (necessario su tutto, ma Windows) al fine di creare codice indipendente dalla posizione che permetterà che collega le librerie statiche in una singola libreria condivisa o decomprimere tutte le librerie statiche (ad esempio utilizzando ar) e ri-collegare in una libreria condivisa (che credo sia un & soluzione non portatile poco elegante)
  • tutti i file di origine devono essere fornite in modo esplicito al comando add_library: per qualche ragione che Non riesco a capire, semplicemente scrivendo add_library(${PROJECT_NAME} SHARED subproject1 subproject2) non funziona come previsto (in sostanza crea una libreria vuota & non registra correttamente le dipendenze)
  • C'è una funzione di libreria OBJECT in CMake ma non penso che lo scopo sia davvero quello di fare ciò che voglio.

Qualche idea?

+0

Sto usando cmake 3.4. + E aggiungo semplicemente librerie statiche alla libreria condivisa e sono compilate in un unico file :) L'ho provato su Android :) –

+0

qualcuno avrebbe un suggerimento su come farlo in MSVC? Sto usando qmake e non cmake ma posso gestire i passaggi da solo, se riesco a capirli ... –

risposta

27

OK, l'ho capito: questo è molto più doloroso di quanto dovrebbe essere. Fino a poco tempo fa, le persone di Kitware non capivano perché qualcuno avrebbe mai voluto creare una DLL dalle librerie statiche. La loro argomentazione è che dovrebbero sempre esserci file sorgente nella directory principale (ad esempio top_project nel mio caso) perché è effettivamente un progetto a sé stante. Io vedo le cose in modo diverso & Ho bisogno di rompere top_project in sottoprogetti più piccoli che non dovrebbero esistere indipendentemente (cioè non è necessario creare un progetto completo per loro & aggiungerli usando ExternalProject_Add). Inoltre, quando spedisco la mia libreria condivisa (per esempio, ad esempio con una Java Native Interface), non voglio spedire dozzine di librerie condivise perché ciò equivarrebbe a esporre il layout interno del mio progetto. Ad ogni modo, avendo - penso - fatto un caso per creare una libreria condivisa da librerie statiche, procederò con i dettagli tecnici.

Nel CMakeLists.txt di subproject1 e subproject2, è necessario creare il tuo target utilizzando la funzione di libreria di oggetti (introdotto nel CMake 2.8.8):

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

dove SRC indica l'elenco dei file di origine (nota che questi dovrebbero essere impostati esplicitamente nel file CMakeLists.txt poiché consente a make di rilanciare CMake quando viene rilevata una modifica di CMakeLists.txt, ad es.durante l'aggiunta o la rimozione di un file)

Nel top_project, aggiungere i sottoprogetti utilizzando:

add_subdirectory(subproject1) 
add_subdirectory(subproject2) 

Per vedere i simboli dalla libreria statica, uso:

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

È quindi possibile creare la libreria condivisa utilizzando:

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

Ho trovato che qualsiasi "normale" libr ary (cioè non oggetto) deve essere aggiunto in un comando separato add_library, altrimenti viene semplicemente ignorato.

per gli eseguibili, è possibile utilizzare:

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} 

ripeto che questo funziona solo a partire dalla versione 2.8.8 di CMake. Inoltre, CMake gestisce le dipendenze estremamente bene. & è multipiattaforma perché non è molto meno doloroso dei semplici vecchi Makefile & sicuramente meno flessibili.

+0

Bah, fastidiosamente Ubuntu 12.04 è bloccato su CMake 2.8.7, c'è un'alternativa per le versioni precedenti? Dobbiamo solo fare riferimento a tutti i file sorgente quando definiamo la libreria? – Ibrahim

+2

Ho risolto i miei problemi compilando le mie librerie statiche con -fPIC, la mia libreria condivisa si collegava correttamente, ma non so se effettivamente funziona anche se non ho ancora provato a usarlo. – Ibrahim

1

Un altro modo di farlo è quello di fornire il percorso dei file di origine ei file di intestazione di tutti i tuoi progetti e costruirli insieme per produrre il .so. Di solito è il metodo consigliato, invece di creare le librerie statiche e quindi una libreria condivisa.

Fondamentalmente si dovrebbe procedere come segue:

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

Questo è molto improbabile che sia utile. Il problema è che spesso hai un mix di vecchi sistemi di compilazione che producono file .o con una magia interna che NON vuoi modificare. Raramente sarai solo in grado di aggiungere un elenco di fonti in qualsiasi sistema di produzione. –

4

La mia soluzione è semplicemente quello di aggiungere /WHOLEARCHIVE, -all_load o --whole-archive alle bandiere linker, in modo che quando la libreria principale è legato, tutte le librerie sub sono . incluso, compresi tutti i loro simboli (il comportamento predefinito è quello di includere solo i simboli delle librerie secondarie che vengono utilizzati dalla biblioteca principale ad esempio:

File di origine

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

Naive 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) 

L'esecuzione (su 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 
$ 

corretta CMakeLists.txt

Append questo:

# 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() 

L'esecuzione (si noti l'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 

Nota che ho solo provato in realtà -all_load finora, e /WHOLEARCHIVE è un'opzione MSVC 2015.

+0

Sono su Linux, aggiungendo semplicemente '-Wl, - whole-archive' ha provocato tonnellate di errori" multiple definition "relativi a' libgcc.a' – nodakai

+0

Sì, l'opzione '/ WHOLEARCHIVE' non sembra funzionare bene o così sono andato con l'approccio alla libreria degli oggetti. – Timmmm