Risposte:
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.a
E 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.)
-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.
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()
oadd_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 *.o
oggetto. Questa raccolta di *.o
file 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 *.o
file 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.
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.txt
file di quasi la metà.
set_property
unico ha funzionato quando l'ho usato objlib
e non durante l'uso ${objlib}
. Quindi forse questa risposta potrebbe essere corretta?
In genere non è necessario duplicare le ADD_LIBRARY
chiamate 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=ON
e con OFF
nell'altra.
È 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 .lib
file 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 OBJECT
libreria 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.
È 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).