CMake: как создать единую разделяемую библиотеку из всех статических библиотек подпроектов?
Автор вопроса: DeimosОтветов: 5
У меня такой расклад:
top_project
+ subproject1
+ subproject2
Каждый из subproject1 и subproject2 создает статическую библиотеку. Я хотел бы связать эти статические библиотеки в единую разделяемую библиотеку на уровне top_project.
Информация, которую я собрал, такова:
Либо скомпилируйте с использованием -fPic (необходимо во всем, кроме Windows), чтобы создать независимый от позиции код, который позволит связать статические библиотеки в единую разделяемую библиотеку, либо распакуйте все статические библиотеки (например, используя ar) и повторно скомпонуйте их в общую библиотеку. (что, на мой взгляд, является неэлегантным и непереносимым решением)
Все исходные файлы должны быть явно переданы команде add_library: по какой-то причине, которую я не могу понять, простое написание add_library(${PROJECT_NAME} SHARED subproject1 subproject2) не работает должным образом (по сути, создается пустая библиотека и не регистрируются зависимости должным образом)
В CMake есть функция библиотеки OBJECT, но я не думаю, что ее цель - делать то, что я хочу.
Есть предположения?
Я использую cmake 3.4. +, И я просто добавляю статические библиотеки в общую библиотеку, и они компилируются в один файл :) Я тестировал это на android :) Dawid Drozd 16.11.2016
есть ли у кого-нибудь подсказка, как это сделать в MSVC? Я использую qmake, а не cmake, но я могу справиться с шагами сам, если смогу их понять ... S. Paris 04.05.2017
Ладно, разобрался: это намного больнее, чем должно быть. До недавнего времени люди в Kitware не понимали, зачем кому-то вообще захочется создавать DLL из статических библиотек. Их аргумент состоит в том, что всегда должны быть исходные файлы в основном (например, top_project в моем случае) каталоге, потому что это, по сути, собственный проект. Я вижу вещи по-другому, и мне нужно разбить top_project на более мелкие подпроекты, которые не должны существовать независимо (т.е. нет смысла создавать для них полноценный проект и добавлять их с помощью ExternalProject_Add). Кроме того, когда я отправляю свою общую библиотеку (для использования, например, с собственным интерфейсом Java), я не хочу отправлять десятки общих библиотек, потому что это будет означать раскрытие внутренней структуры моего проекта. В любом случае, как мне кажется, обосновав необходимость создания разделяемой библиотеки из статических библиотек, я перейду к техническим деталям.
В CMakeLists.txt для subproject1 и subproject2 вы должны создать свою цель с помощью функции библиотеки OBJECT (представленной в CMake 2.8.8):
add_library(${PROJECT_NAME} OBJECT ${SRC})
где SRC обозначает список исходных файлов (обратите внимание, что они должны быть явно установлены в файле CMakeLists.txt, поскольку он позволяет make повторно запустить CMake при обнаружении модификации CMakeLists.txt, например, при добавлении или удалении файла)
Я повторяю, что это работает только с версией CMake 2.8.8. Точно так же CMake очень хорошо управляет зависимостями и является кроссплатформенным, потому что это не намного менее болезненно, чем простые старые файлы Makefile, и, конечно, менее гибко.
Ответ 2 · рейтинг 10 · Timmmm
Мое решение - просто добавить /WHOLEARCHIVE, -all_load или --whole-archive к флагам компоновщика, чтобы, когда ваша основная библиотека связана, все вспомогательные библиотеки были включены, включая все их символы (поведение по умолчанию - включать только символы вспомогательной библиотеки). библиотеки, которые используются основной библиотекой. Например:
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)
# 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 ()
Запускаем (обратите внимание на лишний -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
Обратите внимание, что на самом деле я пока тестировал только -all_load, а /WHOLEARCHIVE - вариант MSVC 2015.
Ответ 3 · рейтинг 4 · matiu
Другой способ сделать это.
Этот способ кажется проще, но я не уверен, насколько он идеален:
Другой способ сделать это - указать путь к исходным файлам и файлам заголовков всех ваших проектов и собрать их вместе для создания .so. Обычно это рекомендуемый способ вместо создания статических библиотек, а затем совместной библиотеки из них.
В основном вы должны сделать следующее:
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})
Ответ 5 · рейтинг 1 · bartgol
Я не уверен, что это то, что вам нужно, но cmake также предлагает INTERFACE библиотеки, которые обслуживают (среди прочего) именно эту потребность.