Czy jest możliwe, aby CMake zbudował statyczną i udostępnioną wersję tej samej biblioteki?

141

Wszystko to z tego samego źródła, po prostu chcę mieć wersję statyczną i współdzieloną. Proste do zrobienia?

gct
źródło

Odpowiedzi:

123

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.aI 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.)

Christopher Bruns
źródło
Miałem nadzieję, że będą miały to samo imię, ale cóż. Kolejne pytanie: czy możesz powiedzieć CMake, aby łączył biblioteki statyczne z biblioteką współdzieloną, jeśli to możliwe?
gct
Więcej o „tej samej nazwie”: Jeśli używasz systemu Windows i chcesz mieć taką samą nazwę dla obu bibliotek i nie potrzebujesz udostępnionego pliku .lib, możesz utworzyć statyczny plik .lib i udostępniony plik .dll. Ale potrzebujesz tego udostępnionego pliku .lib, jeśli używasz swojej biblioteki do zwykłego łączenia w czasie kompilacji.
Christopher Bruns
1
Nie jestem pewien, czy rozumiem Twoje pytanie dotyczące łączenia bibliotek statycznych z biblioteką współdzieloną.
Christopher Bruns
5
Zauważ, że nie jest to już sugerowany sposób, aby to zrobić. W przypadku projektów o nietrywialnych rozmiarach (których kompilacja zajmuje minuty, a nie sekundy), unikanie podwajania czasu kompilacji jest cudowne. Zobacz odpowiedź użytkownika465139 poniżej na temat korzystania z biblioteki obiektów lub dokumentacji: cmake.org/cmake/help/v3.8/command/ ...
KymikoLoco,
3
@KymikoLoco: Podejście z biblioteką obiektów rzeczywiście skraca czas kompilacji o połowę, ale wymaga budowania bibliotek statycznych jako kodu niezależnego od pozycji (tj. Z -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.
John Zwinck,
95

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

Z dokumentacji CMake :

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()lub add_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 *.oplików obiektowych. Ta kolekcja *.oplikó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 *.oplikó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.

Laryx Decidua
źródło
3
To zadziałało dla mnie jak urok - jedynym zastrzeżeniem było to, że kolejne 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.txtpliki prawie o połowę.
fish2000
1
Czy musisz „uciec” przed obblib podczas ustawiania właściwości docelowych? ie set_property (TARGET $ {objlib} PROPERTY ...) vs set_property (TARGET objlib PROPERTY ...)
gnac
1
Ktokolwiek przegłosował to ... czy ta osoba może wyjaśnić, co uważa za niewłaściwe? Tym bardziej, że jest to zalecany sposób robienia tego, czego chce OP, zobacz dokumentację CMake.
Laryx Decidua,
1
@ user465139 Być może powinieneś wyjaśnić, dlaczego powinno działać ponowne użycie plików obiektowych zarówno dla celu statycznego, jak i współdzielonego. Zwłaszcza ogólna wiedza w SO jest nadal bardzo myląca, stare / archiwa też nie pomagają w wyjaśnieniu, np. cmake.org/pipermail/cmake/2008-March/020315.html Potrzebne jest solidne wyjaśnienie istniejącego stanu rzeczy. ps To nie ja przegłosowałem
mloskot
2
@gnac Nie mogę tego potwierdzić. W moim przypadku set_propertydziałał tylko wtedy, gdy korzystałem, objliba nie podczas używania ${objlib}. Może więc tę odpowiedź można poprawić?
josch
22

Generalnie nie ma potrzeby dublowania ADD_LIBRARYpołą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 OFFw drugiej.

Jarosław Halczenko
źródło
43
Wydaje się, że nie tworzy to ZARÓWNO statycznych, jak i współdzielonych wersji, i myślę, że do tego dochodzi to pytanie.
Nick Desaulniers
0

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ć .libpliki 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 OBJECTbiblioteki 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.

P-Gn
źródło
-2

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

Alexander Amelkin
źródło
10
Jest to niepotrzebne w przypadku używania wersji CMake powyżej 2.8. [0?], Ponieważ właściwość została usunięta w 2009 roku, a zachowanie, które zapewnia, jest teraz domyślne. Może to być przydatne dla osób poniżej 2.8, ale jeśli nadal używasz CMake <2.7, błagam Cię o aktualizację. github.com/Kitware/CMake/commit/…
KymikoLoco