È possibile ottenere CMake per creare una versione statica e condivisa della stessa libreria?


141

Stessa fonte, tutto ciò, vogliono solo una versione statica e condivisa. Facile da fare?

Risposte:


123

Sì, è moderatamente facile. Basta usare due comandi "add_library":

add_library(MyLib SHARED source1.c source2.c)
add_library(MyLibStatic STATIC source1.c source2.c)

Anche se hai molti file sorgente, inseriresti l'elenco delle fonti in una variabile cmake, quindi è ancora facile da fare.

Su Windows probabilmente dovresti dare ad ogni libreria un nome diverso, dato che esiste un file ".lib" sia condiviso che statico. Ma su Linux e Mac puoi persino assegnare a entrambe le librerie lo stesso nome (es. libMyLib.aE libMyLib.so):

set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)

Ma non consiglio di dare lo stesso nome sia alla versione statica che a quella dinamica della libreria. Preferisco usare nomi diversi perché ciò semplifica la scelta di collegamenti statici e dinamici nella riga di compilazione per gli strumenti che si collegano alla libreria. Di solito scelgo nomi come libMyLib.so(condiviso) e libMyLib_static.a(statico). (Questi sarebbero i nomi su Linux.)


Speravo che avessero lo stesso nome, ma vabbè. Un'altra domanda: puoi dire a CMake di collegare le librerie statiche alla libreria condivisa quando possibile?
gct

Altre informazioni sullo "stesso nome": se si utilizza Windows e si desidera lo stesso nome per entrambe le librerie e non è necessario il file .lib condiviso, è possibile creare un .lib statico e un .dll condiviso. Ma è necessario quel file .lib condiviso se si utilizza la libreria per il normale collegamento in fase di compilazione.
Christopher Bruns,

1
Non sono sicuro di aver compreso la tua domanda sul collegamento di librerie statiche alla libreria condivisa.
Christopher Bruns,

5
Nota che questo non è più il modo suggerito per farlo. Per progetti di dimensioni non banali (che richiedono minuti, non secondi per la compilazione), evitare di raddoppiare il tempo di compilazione è meraviglioso. Vedi la risposta user465139 di seguito per l'utilizzo della Libreria oggetti o i documenti: cmake.org/cmake/help/v3.8/command/…
KymikoLoco,

3
@KymikoLoco: l'approccio Object Library in effetti riduce della metà il tempo di compilazione, ma richiede che le librerie statiche siano costruite come Position Independent Code (cioè con -fPIC), che aggiunge una piccola quantità di sovraccarico di runtime quando si usano quelle librerie statiche. Quindi, per ottenere le massime prestazioni, questa risposta è ancora la migliore.
John Zwinck,

95

Dalla versione 2.8.8 di CMake, è possibile utilizzare le "librerie di oggetti" per evitare la compilazione duplicata dei file oggetto . Utilizzando l'esempio di Christopher Bruns di una libreria con due file sorgente:

# list of source files
set(libsrc source1.c source2.c)

# this is the "object library" target: compiles the sources only once
add_library(objlib OBJECT ${libsrc})

# shared libraries need PIC
set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE 1)

# shared and static libraries built from the same object files
add_library(MyLib_shared SHARED $<TARGET_OBJECTS:objlib>)
add_library(MyLib_static STATIC $<TARGET_OBJECTS:objlib>)

Dai documenti di CMake :

Una libreria di oggetti compila i file di origine ma non archivia né collega i loro file di oggetti in una libreria. Invece, altri target creati da add_library()o add_executable()possono fare riferimento agli oggetti utilizzando un'espressione del modulo $<TARGET_OBJECTS:objlib>come sorgente, dove objlib è il nome della libreria di oggetti.

In poche parole, il add_library(objlib OBJECT ${libsrc})comando indica a CMake di compilare i file di origine in file *.ooggetto. Questa raccolta di *.ofile viene quindi indicata come $<TARGET_OBJECT:objlib>nei due add_library(...)comandi che invocano i comandi di creazione della libreria appropriati che creano le librerie condivise e statiche dallo stesso set di file oggetto. Se hai molti file sorgente, la compilazione dei *.ofile può richiedere parecchio tempo; con le librerie di oggetti le compili solo una volta.

Il prezzo da pagare è che i file oggetto devono essere creati come codice indipendente dalla posizione perché le librerie condivise ne hanno bisogno (le librerie statiche non se ne curano). Tieni presente che il codice indipendente dalla posizione potrebbe essere meno efficiente, quindi se miri a ottenere le massime prestazioni, sceglieresti librerie statiche. Inoltre, è più facile distribuire eseguibili collegati staticamente.


3
Questo ha funzionato come un incantesimo per me - l'unica avvertenza erano le target_link_libraries()chiamate successive che dipendono dalla tua libreria non possono usare la "libreria di oggetti" per collegarsi; quelli devono essere indirizzati alle nuove librerie condivise o statiche (e potrebbero essere duplicate). Ma contrariamente all'esperienza dei primi commentatori, questo è stato abbastanza utile e mi ha permesso di rimuovere tutti gli obiettivi duplicati e tagliare tutti i miei CMakeLists.txtfile di quasi la metà.
fish2000,

1
Devi "sfuggire" all'obblib quando imposti le proprietà del bersaglio? vale a dire set_property (TARGET $ {objlib} PROPERTY ...) vs set_property (TARGET objlib PROPERTY ...)
gnac,

1
Chiunque abbia votato a favore di questo ... quella persona potrebbe fornire una spiegazione di ciò che ha ritenuto errato? Tanto più che questo è il modo raccomandato di fare ciò che l'OP vuole, vedere i documenti di CMake.
Laryx Decidua,

1
@ user465139 Forse dovresti spiegare perché dovrebbe funzionare per riutilizzare i file oggetto sia per target statici che condivisi. Soprattutto, le conoscenze generali in SO sono ancora molto confuse al riguardo, vecchi / archivi non aiutano neanche a chiarirle, ad es. cmake.org/pipermail/cmake/2008-March/020315.html È necessaria una solida spiegazione dello status quo. ps Non sono stato io a effettuare il
downgrade

2
@gnac Non posso confermarlo. Nel mio caso, l' set_propertyunico ha funzionato quando l'ho usato objlibe non durante l'uso ${objlib}. Quindi forse questa risposta potrebbe essere corretta?
Jos

22

In genere non è necessario duplicare le ADD_LIBRARYchiamate per il proprio scopo. Basta usare

$> man cmake | grep -A6 '^ *BUILD_SHARED_LIBS$' 
   BUILD_SHARED_LIBS
          Global flag to cause add_library to create shared libraries if on.

          If present and true, this will cause all libraries to be built shared unless the library was
          explicitly added as a static library.  This variable is often added to projects as an OPTION
          so  that each user of a project can decide if they want to build the project using shared or
          static libraries.

durante la creazione, prima (in una directory out-of-source) con -DBUILD_SHARED_LIBS:BOOL=ONe con OFFnell'altra.


43
Questo non sembra creare ENTRAMBI le versioni statiche e condivise, che penso sia questa la domanda.
Nick Desaulniers,

0

È possibile racchiudere tutto nello stesso respiro di compilazione, come suggerito nelle risposte precedenti, ma sconsiglio di farlo, perché alla fine è un trucco che funziona solo per progetti semplici. Ad esempio, ad un certo punto potrebbero essere necessari flag diversi per le diverse versioni della libreria (specialmente su Windows in cui i flag vengono generalmente utilizzati per alternare tra i simboli di esportazione o meno). O come menzionato sopra, potresti voler inserire .libfile in directory diverse a seconda che corrispondano a librerie statiche o condivise. Ognuno di questi ostacoli richiederà un nuovo hack.

Può essere ovvio, ma un'alternativa che non è stata menzionata in precedenza è rendere il tipo di libreria un parametro:

set( ${PROJECT_NAME}_LIBTYPE CACHE STRING "library type" )
set_property( CACHE ${PROJECT_NAME}_LIBTYPE PROPERTY STRINGS "SHARED;STATIC" )
add_library( ${PROJECT_NAME} ${PROJECT_NAME}_LIBTYPE ${SOURCE_FILES} )

Avere versioni condivise e statiche della libreria in due diversi alberi binari semplifica la gestione di diverse opzioni di compilazione. Non vedo alcun serio svantaggio nel mantenere distinti gli alberi delle compilation, specialmente se le tue compilation sono automatizzate.

Nota che anche se hai intenzione di mutualizzare le compilazioni usando una OBJECTlibreria intermedia (con le avvertenze menzionate sopra, quindi hai bisogno di un motivo convincente per farlo), potresti avere ancora librerie finali inserite in due diversi progetti.


-2

È davvero possibile. Come ha detto @Christopher Bruns nella sua risposta, è necessario aggiungere due versioni della libreria:

set(libsrc source1.c source2.c source3.c)
add_library(mylib-static STATIC ${libsrc})
add_library(mylib-shared SHARED ${libsrc})

Quindi, come descritto qui , è necessario specificare che entrambe le destinazioni devono utilizzare lo stesso nome di output e non sovrascrivere i file degli altri:

SET_TARGET_PROPERTIES(mylib-static PROPERTIES OUTPUT_NAME mylib CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(mylib-shared PROPERTIES OUTPUT_NAME mylib CLEAN_DIRECT_OUTPUT 1)

In questo modo otterrai libmylib.a e libmylib.so (su Linux) o mylib.lib e mylib.dll (su Windows).


10
Ciò non è necessario quando si utilizzano le versioni CMake precedenti alla 2.8. [0?], Poiché la proprietà è stata rimossa nel 2009 e il comportamento fornito è ora predefinito. Questo potrebbe essere utile per le persone al di sotto di 2.8, ma se stai ancora usando CMake <2.7, ti imploro di aggiornare. github.com/Kitware/CMake/commit/…
KymikoLoco
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.