CMake: comment créer une seule bibliothèque partagée de toutes les bibliothèques statiques de sous-projets?
j'ai le schéma suivant:
top_project
+ subproject1
+ subproject2
chacun de subproject1
et subproject2
crée une bibliothèque statique. Je voudrais relier ces bibliothèques statiques dans une seule bibliothèque partagée à la top_project
niveau.
Les informations que j'ai recueillies jusqu'à présent est:
- soit compiler en utilisant
-fPic
(nécessaire sur tout sauf Windows) afin de créer un code indépendant de la position qui permettra de relier les bibliothèques statiques en une seule bibliothèque partagée ou décompresser toutes les bibliothèques statiques (par exemple en utilisantar
) et les relier de nouveau dans une bibliothèque partagée (qui je pense est une solution inélégante et non portable) - tous les fichiers source doivent être donnés explicitement à
add_library
commande: pour une raison que je ne peux comprendre, il suffit de la rédactionadd_library(${PROJECT_NAME} SHARED subproject1 subproject2)
ne fonctionne pas comme prévu (il essentiel de créer une bibliothèque vide et n'enregistre pas les dépendances correctement) - il y a une fonctionnalité de bibliothèque D'objets dans CMake mais je ne pense pas le but est vraiment de faire ce que je veux.
des idées?
4 réponses
OK, j'ai compris: c'est beaucoup plus douloureux que ce qu'elle devrait être. Jusqu'à très récemment, les gens de Kitware ne comprenaient pas pourquoi quelqu'un voudrait créer une DLL à partir de libs statiques. Leur argument est qu'il devrait toujours y avoir des fichiers source dans le main (par exemple top_project
dans mon cas) parce qu'il s'agit effectivement d'un projet à part entière. Je vois les choses différemment et j'ai besoin de briser top_project
dans des sous-projets plus petits qui ne devraient pas exister indépendamment (i.e. la création d'un projet à part entière pour eux et les ajouter à l'aide de ExternalProject_Add
). En outre, lorsque j'expédie Ma bibliothèque partagée (pour utilisation, par exemple avec une Interface Java Native), Je ne veux pas expédier des douzaines de bibliothèques partagées parce que cela reviendrait à exposer la disposition interne de mon projet. Quoi qu'il en soit, ayant - je pense - fait un cas pour la création d'une bibliothèque partagée à partir de bibliothèques statiques, je vais passer aux détails techniques.
dans les listes Cmakel.txt subproject1
et subproject2
, vous devez créer votre cible utilisant la fonctionnalité de bibliothèque D'objets (introduite dans CMake 2.8.8):
add_library(${PROJECT_NAME} OBJECT ${SRC})
où SRC
désigne la liste des fichiers source (notez que ceux-ci doivent être définis explicitement dans les listes Cmakel).fichier txt car elle permet de faire re-lancer CMake lorsqu'une modification de CMakeLists.txt est détecté, par exemple lors de l'ajout ou de la suppression d'un fichier)
Dans le top_project
, ajouter les sous-projets à l'aide de:
add_subdirectory(subproject1)
add_subdirectory(subproject2)
afin de voir les symboles de la bibliothèque statique, utilisation:
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--export-all-symbols")
vous pouvez alors créer la bibliothèque partagée en utilisant:
add_library(${PROJECT_NAME} SHARED $<TARGET_OBJECTS:subproject1>
$<TARGET_OBJECTS:subproject2>)
j'ai trouvé que toute "normale" de la bibliothèque (pas d'objet) doit être ajouté dans un autre add_library
commande, sinon elle est simplement ignorée.
pour les exécutables, vous pouvez utiliser:
add_executable(name_of_executable $<TARGET_OBJECTS:subproject1>
$<TARGET_OBJECTS:subproject2>)
set(LINK_FLAGS ${LINK_FLAGS} "-Wl,-whole-archive")
target_link_libraries(name_of_executable ${PROJECT_NAME}
je répète que cela ne fonctionne qu'à partir de la version 2.8.8 de CMake. Tout aussi bien CMake gère les dépendances extrêmement bien et est multiplateformes parce que ce n'est pas beaucoup moins douloureux que les vieux Makefiles simples et certainement moins flexible.
ma solution est simplement d'ajouter /WHOLEARCHIVE
,-all_load
, ou --whole-archive
aux options du linker, de sorte que lorsque votre bibliothèque principale est liée, toutes les bibliothèques secondaires sont incluses, y compris tous leurs Symboles (le comportement par défaut est de n'inclure que les symboles des bibliothèques secondaires qui sont utilisées par la bibliothèque principale). Par exemple:
Fichiers Source
$ echo "void Func1() { }" > source1.cpp
$ echo "void Func2() { }" > source2.cpp
$ echo "void Func3() { }" > source3.cpp
$ echo "void Func4() { }" > source4.cpp
Des Cmakélistes Naïfs.txt
cmake_minimum_required(VERSION 3.7)
# The 'sub' libraries, e.g. from an `add_subdirectory()` call.
add_library(sublib_a STATIC source1.cpp source2.cpp)
add_library(sublib_b STATIC source3.cpp source4.cpp)
# The main library that contains all of the sub libraries.
add_library(mainlib SHARED)
target_link_libraries(mainlib sublib_a sublib_b)
en cours d'Exécution (sur OSX):
$ make VERBOSE=1
...
[100%] Linking CXX shared library libmainlib.dylib
/usr/local/Cellar/cmake/3.7.1/bin/cmake -E cmake_link_script CMakeFiles/mainlib.dir/link.txt --verbose=1
/Library/Developer/CommandLineTools/usr/bin/c++ -dynamiclib -Wl,-headerpad_max_install_names -o libmainlib.dylib -install_name @rpath/libmainlib.dylib libsublib_a.a libsublib_b.a
[100%] Built target mainlib
$ nm libmainlib.dylib | grep Func
$
Correct CMakeLists.txt
ajouter ce qui suit:
# By default, symbols provided by the sublibs that are not used by mainlib (which is all of them in this case)
# are not used. This changes that.
if (WIN32)
set_target_properties(mainlib PROPERTIES
LINK_FLAGS "/WHOLEARCHIVE"
)
elseif (APPLE)
set_target_properties(mainlib PROPERTIES
LINK_FLAGS "-Wl,-all_load"
)
else ()
set_target_properties(mainlib PROPERTIES
LINK_FLAGS "-Wl,--whole-archive"
)
endif ()
L'exécuter (notez l'extra -all_load
):
$ make VERBOSE=1
[100%] Linking CXX shared library libmainlib.dylib
/usr/local/Cellar/cmake/3.7.1/bin/cmake -E cmake_link_script CMakeFiles/mainlib.dir/link.txt --verbose=1
/Library/Developer/CommandLineTools/usr/bin/c++ -dynamiclib -Wl,-headerpad_max_install_names -Wl,-all_load -o libmainlib.dylib -install_name @rpath/libmainlib.dylib libsublib_a.a libsublib_b.a
[100%] Built target mainlib
$ nm libmainlib.dylib | grep Func
0000000000001da0 T __Z5Func1v
0000000000001db0 T __Z5Func2v
0000000000001dc0 T __Z5Func3v
0000000000001dd0 T __Z5Func4v
Notez que je n'ai réellement testé -all_load
jusqu'à présent, et /WHOLEARCHIVE
est une option MSVC 2015.
une Autre façon de faire.
de Cette façon semble plus simple, mais je ne suis pas sûr de savoir comment parfait c'est:
une Autre façon de le faire est de fournir le chemin d'accès des fichiers source et les fichiers d'en-tête de tous vos projets, et à construire ensemble pour produire .. C'est généralement la méthode recommandée, au lieu de créer les bibliothèques statiques et puis une bibliothèque partagée de ceux-ci.
fondamentalement, vous devez faire ce qui suit:
FILE(GLOB subproject1_sources
<sub_project1_lib_sources_dir>/file1.c
<sub_project1_lib_sources_dir>/file2.c //... etc
)
FILE(GLOB subproject2_sources
<sub_project2_lib_sources_dir>/file1.c
<sub_project2_lib_sources_dir>/file2.c //... etc
)
FILE(GLOB topProject_sources
<top_project_lib_sources_dir>/file1.c
<top_project_lib_sources_dir>/file2.c //... etc
)
include_directories("<sub_project1_lib_sources_dir>")
include_directories("<sub_project2_lib_sources_dir>")
include_directories("<top_project_lib_sources_dir>") //should be "." if you're building from here
add_library(topProject SHARED ${topProject_sources} ${subproject1_sources} ${subproject2_sources})