Odpowiedzi:
Tak, jest to umiarkowanie łatwe. Wystarczy użyć dwóch poleceń „add_library”:
add_library(MyLib SHARED source1.c source2.c)
add_library(MyLibStatic STATIC source1.c source2.c)
Nawet jeśli masz wiele plików źródłowych, umieścisz listę źródeł w zmiennej cmake, więc nadal jest to łatwe.
W systemie Windows prawdopodobnie powinieneś nadać każdej bibliotece inną nazwę, ponieważ istnieje plik „.lib” zarówno dla współdzielonej, jak i statycznej. Ale w systemie Linux i Mac możesz nawet nadać obu bibliotekom tę samą nazwę (np. libMyLib.a
I libMyLib.so
):
set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)
Ale nie polecam nadawania tej samej nazwy statycznej i dynamicznej wersji biblioteki. Wolę używać różnych nazw, ponieważ ułatwia to wybór łączenia statycznego i dynamicznego w wierszu kompilacji dla narzędzi, które prowadzą do biblioteki. Zwykle wybieram nazwy takie jak libMyLib.so
(udostępnione) i libMyLib_static.a
(statyczne). (To byłyby nazwy w systemie Linux.)
-fPIC
), co dodaje niewielką ilość narzutu czasu wykonania, gdy te biblioteki statyczne są używane. Tak więc dla maksymalnej wydajności ta odpowiedź jest nadal najlepsza.
Od wersji 2.8.8 CMake można używać „bibliotek obiektów”, aby uniknąć powielania kompilacji plików obiektowych . Na przykładzie biblioteki Christophera Brunsa z dwoma plikami źródłowymi:
# 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>)
Biblioteka obiektów kompiluje pliki źródłowe, ale nie archiwizuje ani nie łączy swoich plików obiektowych z biblioteką. Zamiast tego inne cele utworzone przez
add_library()
lubadd_executable()
mogą odwoływać się do obiektów przy użyciu wyrażenia formularza$<TARGET_OBJECTS:objlib>
jako źródła, gdzie objlib jest nazwą biblioteki obiektów.
Mówiąc najprościej, add_library(objlib OBJECT ${libsrc})
polecenie instruuje CMake, aby skompilował pliki źródłowe do *.o
plików obiektowych. Ta kolekcja *.o
plików jest następnie określana tak, jak $<TARGET_OBJECT:objlib>
w dwóch add_library(...)
poleceniach, które wywołują odpowiednie polecenia tworzenia bibliotek, które budują biblioteki współużytkowane i biblioteki statyczne z tego samego zestawu plików obiektowych. Jeśli masz dużo plików źródłowych, kompilacja *.o
plików może zająć dość dużo czasu; w przypadku bibliotek obiektów kompilujesz je tylko raz.
Cena, jaką płacisz, jest taka, że pliki obiektowe muszą być zbudowane jako kod niezależny od pozycji, ponieważ biblioteki współdzielone tego potrzebują (biblioteki statyczne nie dbają o to). Zwróć uwagę, że kod niezależny od pozycji może być mniej wydajny, więc jeśli dążysz do maksymalnej wydajności, wybierz biblioteki statyczne. Ponadto łatwiej jest dystrybuować statycznie połączone pliki wykonywalne.
target_link_libraries()
wywołania zależne od Twojej biblioteki nie mogły używać „biblioteki obiektów” do łączenia się; muszą one być przeznaczone dla nowych bibliotek współdzielonych lub statycznych (i mogą zostać zduplikowane). Ale w przeciwieństwie do doświadczeń pierwszych komentatorów było to całkiem przydatne i pozwoliło mi usunąć wszystkie zduplikowane cele i wyciąć wszystkie moje CMakeLists.txt
pliki prawie o połowę.
set_property
działał tylko wtedy, gdy korzystałem, objlib
a nie podczas używania ${objlib}
. Może więc tę odpowiedź można poprawić?
Generalnie nie ma potrzeby dublowania ADD_LIBRARY
połączeń w swoim celu. Po prostu skorzystaj z
$> 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.
budując najpierw (w jednym out-of-źródła katalogu) z -DBUILD_SHARED_LIBS:BOOL=ON
, iz OFF
w drugiej.
Możliwe jest spakowanie wszystkiego w tym samym oddechu kompilacji, jak sugerowały poprzednie odpowiedzi, ale odradzałbym to, ponieważ w końcu to hack, który działa tylko w prostych projektach. Na przykład, w pewnym momencie możesz potrzebować różnych flag dla różnych wersji biblioteki (szczególnie w systemie Windows, gdzie flagi są zwykle używane do przełączania się między eksportowanymi symbolami lub nie). Lub, jak wspomniano powyżej, możesz chcieć umieścić .lib
pliki w różnych katalogach w zależności od tego, czy odpowiadają one bibliotekom statycznym, czy współdzielonym. Każda z tych przeszkód będzie wymagała nowego hakowania.
To może być oczywiste, ale jedną z alternatyw, o których wcześniej nie wspominano, jest uczynienie typu biblioteki parametrem:
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} )
Posiadanie udostępnionej i statycznej wersji biblioteki w dwóch różnych drzewach binarnych ułatwia obsługę różnych opcji kompilacji. Nie widzę żadnych poważnych wad w utrzymywaniu odrębności drzew kompilacji, zwłaszcza jeśli kompilacje są zautomatyzowane.
Zauważ, że nawet jeśli zamierzasz mutualizować kompilacje przy użyciu OBJECT
biblioteki pośredniej (z zastrzeżeniami wymienionymi powyżej, więc potrzebujesz do tego ważnego powodu), nadal możesz umieścić biblioteki końcowe w dwóch różnych projektach.
To rzeczywiście możliwe. Jak powiedział @Christopher Bruns w swojej odpowiedzi, musisz dodać dwie wersje biblioteki:
set(libsrc source1.c source2.c source3.c)
add_library(mylib-static STATIC ${libsrc})
add_library(mylib-shared SHARED ${libsrc})
Następnie, jak opisano tutaj , musisz określić, że oba cele powinny używać tej samej nazwy wyjściowej i nie nadpisywać swoich plików:
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)
W ten sposób otrzymasz libmylib.a i libmylib.so (w systemie Linux) lub mylib.lib i mylib.dll (w systemie Windows).