Réponses:
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.a
et 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.)
-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.
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()
ouadd_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 *.o
objet. Cette collection de *.o
fichiers 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 *.o
fichiers 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.
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.txt
fichiers de près de la moitié.
set_property
seul fonctionnait lorsque je l'utilisais objlib
et non lors de l'utilisation ${objlib}
. Alors peut-être que cette réponse pourrait être corrigée?
Il n'est généralement pas nécessaire de dupliquer les ADD_LIBRARY
appels 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=ON
et avec OFF
dans l'autre.
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 OBJECT
bibliothè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.
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).