Antworten:
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.a
und 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.)
-fPIC
) erstellt werden, wodurch bei Verwendung dieser statischen Bibliotheken ein geringer Laufzeitaufwand entsteht. Für maximale Leistung ist diese Antwort immer noch die beste.
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 dieseadd_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 *.o
Objektdateien zu kompilieren . Diese Sammlung von *.o
Dateien 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, *.o
kann 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.
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.txt
Dateien um fast die Hälfte zu schneiden .
set_property
funktionierte das nur, wenn ich es benutzte objlib
und nicht, wenn ich es benutzte ${objlib}
. Vielleicht könnte diese Antwort korrigiert werden?
Es ist im Allgemeinen nicht erforderlich, ADD_LIBRARY
Anrufe 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=ON
und mit OFF
im anderen.
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 .lib
Dateien 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.
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).