Est-il possible de demander à CMake de créer à la fois une version statique et partagée de la même bibliothèque?

141

Même source, tout ça, je veux juste une version statique et partagée à la fois. Facile à faire?

gct
la source

Réponses:

123

Oui, c'est modérément facile. Utilisez simplement deux commandes "add_library":

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

Même si vous avez de nombreux fichiers sources, vous placez la liste des sources dans une variable cmake, donc c'est toujours facile à faire.

Sous Windows, vous devriez probablement donner à chaque bibliothèque un nom différent, car il existe un fichier ".lib" pour les fichiers partagés et statiques. Mais sur Linux et Mac, vous pouvez même donner aux deux bibliothèques le même nom (par exemple libMyLib.aet libMyLib.so):

set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)

Mais je ne recommande pas de donner le même nom aux versions statiques et dynamiques de la bibliothèque. Je préfère utiliser des noms différents car cela facilite le choix de la liaison statique ou dynamique sur la ligne de compilation pour les outils liés à la bibliothèque. Habituellement, je choisis des noms comme libMyLib.so(partagé) et libMyLib_static.a(statique). (Ce seraient les noms sur Linux.)

Christopher Bruns
la source
J'espérais qu'ils porteraient le même nom, mais bon. Une autre question: pouvez-vous dire à CMake de lier les bibliothèques statiques à la bibliothèque partagée lorsque cela est possible?
gct
En savoir plus sur "même nom": Si vous êtes sous Windows et que vous voulez le même nom pour les deux bibliothèques et que vous n'avez pas besoin du fichier .lib partagé, il est possible de créer un .lib statique et un .dll partagé. Mais vous avez besoin de ce fichier .lib partagé si vous utilisez votre bibliothèque pour la liaison ordinaire lors de la compilation.
Christopher Bruns
1
Je ne suis pas sûr de comprendre votre question sur la liaison des bibliothèques statiques dans la bibliothèque partagée.
Christopher Bruns
5
Notez que ce n'est plus la manière suggérée de le faire. Pour les projets de taille non triviale (ceux qui prennent des minutes et non des secondes à compiler), éviter de doubler le temps de compilation est merveilleux. Voir la réponse user465139 ci-dessous pour l'utilisation de la bibliothèque d'objets ou la documentation: cmake.org/cmake/help/v3.8/command/…
KymikoLoco
3
@KymikoLoco: L'approche de la bibliothèque d'objets réduit en effet de moitié le temps de compilation, mais elle nécessite que les bibliothèques statiques soient construites en tant que code indépendant de la position (c'est-à-dire avec -fPIC), ce qui ajoute une petite quantité de temps d'exécution lorsque ces bibliothèques statiques sont utilisées. Donc, pour des performances maximales, cette réponse est toujours la meilleure.
John Zwinck
95

Depuis la version 2.8.8 de CMake, vous pouvez utiliser des "bibliothèques d'objets" pour éviter la compilation dupliquée des fichiers objets . En utilisant l'exemple de Christopher Bruns d'une bibliothèque avec deux fichiers source:

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

À partir de la documentation CMake :

Une bibliothèque d'objets compile les fichiers source mais n'archive ni ne lie leurs fichiers objets dans une bibliothèque. Au lieu de cela, d'autres cibles créées par add_library()ou add_executable()peuvent référencer les objets en utilisant une expression du formulaire $<TARGET_OBJECTS:objlib>comme source, où objlib est le nom de la bibliothèque d'objets.

En termes simples, la add_library(objlib OBJECT ${libsrc})commande demande à CMake de compiler les fichiers source en fichiers *.oobjet. Cette collection de *.ofichiers est alors appelée $<TARGET_OBJECT:objlib>dans les deux add_library(...)commandes qui invoquent les commandes de création de bibliothèque appropriées qui créent les bibliothèques partagées et statiques à partir du même ensemble de fichiers objets. Si vous avez beaucoup de fichiers source, la compilation des *.ofichiers peut prendre un certain temps; avec les bibliothèques d'objets, vous ne les compilez qu'une seule fois.

Le prix à payer est que les fichiers objets doivent être construits en tant que code indépendant de la position car les bibliothèques partagées en ont besoin (les bibliothèques statiques s'en moquent). Notez que le code indépendant de la position peut être moins efficace, donc si vous visez des performances maximales, vous opterez pour des bibliothèques statiques. De plus, il est plus facile de distribuer des exécutables liés statiquement.

Laryx Decidua
la source
3
Cela a fonctionné comme un charme pour moi - la seule mise en garde était les target_link_libraries()appels ultérieurs qui dépendent de votre bibliothèque ne peuvent pas utiliser la «bibliothèque d'objets» pour établir un lien; ceux-ci doivent cibler les nouvelles bibliothèques partagées ou statiques (et peuvent être dupliqués). Mais contrairement à l'expérience des premiers commentateurs, cela a été très utile et m'a permis de supprimer toutes les cibles dupliquées et de réduire tous mes CMakeLists.txtfichiers de près de la moitié.
fish2000
1
Avez-vous besoin «d'échapper» l'obblib lors de la définition des propriétés de la cible? ie set_property (TARGET $ {objlib} PROPERTY ...) vs set_property (TARGET objlib PROPERTY ...)
gnac
1
Quiconque a voté contre cela ... est-ce que cette personne pourrait expliquer ce qu'elle considérait comme incorrect? D'autant plus que c'est la manière recommandée de faire ce que veut l'OP, voir la documentation CMake.
Laryx Decidua
1
@ user465139 Vous devriez peut-être expliquer pourquoi il devrait fonctionner pour réutiliser des fichiers objets pour les cibles statiques et partagées. Surtout, les connaissances générales en SO sont encore très déroutantes à ce sujet, les anciens / archives n'aident pas non plus à le clarifier, par exemple. cmake.org/pipermail/cmake/2008-March/020315.html Une explication solide du statu quo est nécessaire. ps Ce n'est pas moi qui a
voté contre
2
@gnac Je ne peux pas le confirmer. Dans mon cas, le set_propertyseul fonctionnait lorsque je l'utilisais objlibet non lors de l'utilisation ${objlib}. Alors peut-être que cette réponse pourrait être corrigée?
josch
22

Il n'est généralement pas nécessaire de dupliquer les ADD_LIBRARYappels pour vos besoins. Utilisez simplement

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

lors de la construction, d'abord (dans un répertoire hors source) avec -DBUILD_SHARED_LIBS:BOOL=ONet avec OFFdans l'autre.

Yaroslav Halchenko
la source
43
Cela ne semble pas créer les DEUX versions statiques et partagées, ce qui, je pense, est l'objet de cette question.
Nick Desaulniers
0

Il est possible de tout emballer dans le même souffle de compilation, comme suggéré dans les réponses précédentes, mais je vous le déconseille, car au final c'est un hack qui ne fonctionne que pour des projets simples. Par exemple, vous pouvez avoir besoin à un moment donné de différents indicateurs pour différentes versions de la bibliothèque (en particulier sur Windows où les indicateurs sont généralement utilisés pour basculer entre les symboles d'exportation ou non). Ou comme mentionné ci-dessus, vous voudrez peut-être mettre.lib fichiers dans différents répertoires selon qu'ils correspondent à des bibliothèques statiques ou partagées. Chacun de ces obstacles nécessitera un nouveau hack.

Cela peut être évident, mais une alternative qui n'a pas été mentionnée précédemment est de faire du type de la bibliothèque un paramètre:

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

Le fait d'avoir des versions partagées et statiques de la bibliothèque dans deux arborescences binaires différentes facilite la gestion des différentes options de compilation. Je ne vois aucun inconvénient sérieux à garder les arbres de compilation distincts, surtout si vos compilations sont automatisées.

Notez que même si vous avez l'intention de mutualiser les compilations en utilisant une OBJECTbibliothèque intermédiaire (avec les mises en garde mentionnées ci-dessus, vous avez donc besoin d'une raison convaincante pour le faire), vous pourriez toujours avoir des bibliothèques de fin placées dans deux projets différents.

P-Gn
la source
-2

C'est en effet possible. Comme @Christopher Bruns l'a dit dans sa réponse, vous devez ajouter deux versions de la bibliothèque:

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

Ensuite, comme décrit ici , vous devez spécifier que les deux cibles doivent utiliser le même nom de sortie et ne pas écraser les fichiers de l'autre:

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)

De cette façon, vous obtiendrez à la fois libmylib.a et libmylib.so (sous Linux) ou mylib.lib et mylib.dll (sous Windows).

Alexandre Amelkin
la source
10
Cela n'est pas nécessaire lors de l'utilisation des versions de CMake supérieures à 2.8. [0?], Car la propriété a été supprimée en 2009 et le comportement qu'elle a fourni est désormais celui par défaut. Cela peut être utile pour les personnes en dessous de 2.8, mais si vous utilisez toujours CMake <2.7, je vous implore de mettre à jour. github.com/Kitware/CMake/commit/…
KymikoLoco