Ist es möglich, CMake dazu zu bringen, sowohl eine statische als auch eine gemeinsam genutzte Version derselben Bibliothek zu erstellen?

141

Dieselbe Quelle möchte nur eine statische und eine gemeinsam genutzte Version. Leicht zu schaffen?

gct
quelle

Antworten:

123

Ja, es ist mäßig einfach. Verwenden Sie einfach zwei "add_library" -Befehle:

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

Selbst wenn Sie viele Quelldateien haben, würden Sie die Liste der Quellen in eine cmake-Variable einfügen, sodass dies immer noch einfach ist.

Unter Windows sollten Sie wahrscheinlich jeder Bibliothek einen anderen Namen geben, da es eine ".lib" -Datei für gemeinsam genutzte und statische Dateien gibt. Unter Linux und Mac können Sie beiden Bibliotheken sogar den gleichen Namen geben (z. B. libMyLib.aund libMyLib.so):

set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)

Ich empfehle jedoch nicht, sowohl der statischen als auch der dynamischen Version der Bibliothek den gleichen Namen zu geben. Ich bevorzuge die Verwendung unterschiedlicher Namen, da dies die Auswahl einer statischen oder dynamischen Verknüpfung in der Kompilierungszeile für Tools erleichtert, die mit der Bibliothek verknüpft sind. Normalerweise wähle ich Namen wie libMyLib.so(geteilt) und libMyLib_static.a(statisch). (Das wären die Namen unter Linux.)

Christopher Bruns
quelle
Ich hatte gehofft, dass sie den gleichen Namen haben, aber na ja. Eine andere Frage: Können Sie CMake anweisen, statische Bibliotheken nach Möglichkeit mit der gemeinsam genutzten Bibliothek zu verknüpfen?
gct
Weitere Informationen zu "Gleicher Name": Wenn Sie unter Windows denselben Namen für beide Bibliotheken möchten und die freigegebene LIB-Datei nicht benötigen, können Sie eine statische LIB und eine freigegebene DLL erstellen. Sie benötigen diese freigegebene LIB-Datei jedoch, wenn Sie Ihre Bibliothek für die normale Verknüpfung zur Kompilierungszeit verwenden.
Christopher Bruns
1
Ich bin nicht sicher, ob ich Ihre Frage zum Verknüpfen statischer Bibliotheken mit der gemeinsam genutzten Bibliothek verstehe.
Christopher Bruns
5
Beachten Sie, dass dies nicht mehr der empfohlene Weg ist. Bei Projekten mit nicht trivialer Größe (solche, deren Kompilierung Minuten und nicht Sekunden dauert) ist es wunderbar, eine Verdoppelung der Kompilierungszeit zu vermeiden. Siehe Benutzer465139 Antwort unten für die Verwendung der Objektbibliothek
KymikoLoco
3
@KymikoLoco: Der Objektbibliotheksansatz reduziert zwar die Kompilierungszeit um die Hälfte, erfordert jedoch, dass statische Bibliotheken als positionsunabhängiger Code (dh mit -fPIC) erstellt werden, wodurch bei Verwendung dieser statischen Bibliotheken ein geringer Laufzeitaufwand entsteht. Für maximale Leistung ist diese Antwort immer noch die beste.
John Zwinck
95

Seit CMake Version 2.8.8 können Sie "Objektbibliotheken" verwenden , um die doppelte Kompilierung der Objektdateien zu vermeiden . Am Beispiel von Christopher Bruns für eine Bibliothek mit zwei Quelldateien:

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

Aus den CMake-Dokumenten :

Eine Objektbibliothek kompiliert Quelldateien, archiviert oder verknüpft ihre Objektdateien jedoch nicht in einer Bibliothek. Stattdessen können andere Ziele, die von den Objekten erstellt wurden add_library()oder auf diese add_executable()verweisen, einen Ausdruck des Formulars $<TARGET_OBJECTS:objlib>als Quelle verwenden, wobei objlib der Name der Objektbibliothek ist.

Einfach ausgedrückt, weist der add_library(objlib OBJECT ${libsrc})Befehl CMake an, die Quelldateien zu *.oObjektdateien zu kompilieren . Diese Sammlung von *.oDateien wird dann als $<TARGET_OBJECT:objlib>in den beiden add_library(...)Befehlen bezeichnet, die die entsprechenden Befehle zum Erstellen von Bibliotheken aufrufen, mit denen die gemeinsam genutzten und statischen Bibliotheken aus demselben Satz von Objektdateien erstellt werden. Wenn Sie viele Quelldateien haben, *.okann das Kompilieren der Dateien ziemlich lange dauern. Mit Objektbibliotheken kompilieren Sie diese nur einmal.

Der Preis, den Sie zahlen, ist, dass die Objektdateien als positionsunabhängiger Code erstellt werden müssen, da gemeinsam genutzte Bibliotheken dies benötigen (statische Bibliotheken kümmern sich nicht darum). Beachten Sie, dass positionsunabhängiger Code möglicherweise weniger effizient ist. Wenn Sie also maximale Leistung anstreben, sollten Sie sich für statische Bibliotheken entscheiden. Darüber hinaus ist es einfacher, statisch verknüpfte ausführbare Dateien zu verteilen.

Kehlkopf Decidua
quelle
3
Dies funktionierte für mich wie ein Zauber - die einzige Einschränkung waren nachfolgende target_link_libraries()Aufrufe, die von Ihrer Bibliothek abhängen. Sie können die "Objektbibliothek" nicht zum Verknüpfen verwenden. Diese müssen auf die neuen gemeinsam genutzten oder statischen Bibliotheken abzielen (und können dupliziert werden). Aber im Gegensatz zu den Erfahrungen der ersten Kommentatoren war dies sehr nützlich und erlaubte mir, alle doppelten Ziele zu entfernen und alle meine CMakeLists.txtDateien um fast die Hälfte zu schneiden .
fish2000
1
Müssen Sie der Obblib "entkommen", wenn Sie die Zieleigenschaften festlegen? dh set_property (TARGET $ {objlib} PROPERTY ...) vs set_property (TARGET objlib PROPERTY ...)
gnac
1
Wer auch immer dies abgelehnt hat ... könnte diese Person eine Erklärung liefern, was sie für falsch hält? Umso mehr, als dies die empfohlene Methode ist, um das zu tun, was das OP will, siehe die CMake-Dokumente.
Laryx Decidua
1
@ user465139 Vielleicht sollten Sie erklären, warum es funktionieren sollte, Objektdateien sowohl für statische als auch für gemeinsam genutzte Ziele wiederzuverwenden. Insbesondere das Allgemeinwissen in SO ist immer noch sehr verwirrend, alte / Archive helfen auch nicht, es zu klären, z. cmake.org/pipermail/cmake/2008-March/020315.html Eine solide Erklärung des Status quo ist erforderlich. ps Ich war es nicht, der herabgestimmt hat
Mloskot
2
@gnac Ich kann das nicht bestätigen. In meinem Fall set_propertyfunktionierte das nur, wenn ich es benutzte objlibund nicht, wenn ich es benutzte ${objlib}. Vielleicht könnte diese Antwort korrigiert werden?
Josch
22

Es ist im Allgemeinen nicht erforderlich, ADD_LIBRARYAnrufe für Ihren Zweck zu duplizieren . Nutzen Sie einfach

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

beim Erstellen zuerst (in einem Out-of-Source-Verzeichnis) mit -DBUILD_SHARED_LIBS:BOOL=ONund mit OFFim anderen.

Jaroslaw Halchenko
quelle
43
Dies scheint nicht sowohl statische als auch gemeinsam genutzte Versionen zu erstellen, worum es bei dieser Frage meiner Meinung nach geht.
Nick Desaulniers
0

Es ist möglich, alles in den gleichen Zusammenstellungsatem zu packen, wie in den vorherigen Antworten vorgeschlagen, aber ich würde davon abraten, weil es am Ende ein Hack ist, der nur für einfache Projekte funktioniert. Beispielsweise benötigen Sie möglicherweise irgendwann unterschiedliche Flags für verschiedene Versionen der Bibliothek (insbesondere unter Windows, wo Flags normalerweise zum Umschalten zwischen dem Exportieren von Symbolen verwendet werden oder nicht). Wie oben erwähnt, möchten Sie möglicherweise .libDateien in verschiedenen Verzeichnissen ablegen, je nachdem, ob sie statischen oder gemeinsam genutzten Bibliotheken entsprechen. Jede dieser Hürden erfordert einen neuen Hack.

Es mag offensichtlich sein, aber eine Alternative, die zuvor nicht erwähnt wurde, besteht darin, den Typ der Bibliothek zu einem Parameter zu machen:

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

Gemeinsame und statische Versionen der Bibliothek in zwei verschiedenen Binärbäumen erleichtern die Handhabung verschiedener Kompilierungsoptionen. Ich sehe keinen ernsthaften Nachteil darin, Kompilierungsbäume voneinander zu unterscheiden, insbesondere wenn Ihre Kompilierungen automatisiert sind.

Beachten Sie, dass selbst wenn Sie beabsichtigen, Kompilierungen mithilfe einer Zwischenbibliothek zu verinnerlichen OBJECT(mit den oben genannten Einschränkungen, sodass Sie einen zwingenden Grund dafür benötigen), Endbibliotheken in zwei verschiedene Projekte eingefügt werden können.

P-Gn
quelle
-2

Es ist in der Tat möglich. Wie @Christopher Bruns in seiner Antwort sagte, müssen Sie zwei Versionen der Bibliothek hinzufügen:

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

Dann müssen Sie, wie hier beschrieben , angeben, dass beide Ziele denselben Ausgabenamen verwenden und die Dateien des anderen nicht überschreiben sollen:

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)

Auf diese Weise erhalten Sie sowohl libmylib.a als auch libmylib.so (unter Linux) oder mylib.lib und mylib.dll (unter Windows).

Alexander Amelkin
quelle
10
Dies ist bei Verwendung von CMake-Versionen über 2.8 nicht erforderlich. [0?], Da die Eigenschaft 2009 entfernt wurde und das bereitgestellte Verhalten jetzt die Standardeinstellung ist. Dies kann für Personen unter 2.8 nützlich sein, aber wenn Sie immer noch CMake <2.7 verwenden, bitte ich Sie, ein Upgrade durchzuführen. github.com/Kitware/CMake/commit/…
KymikoLoco