CMake: как создать единую общую библиотеку из всех статических библиотек подпроектов?
У меня есть следующий макет:
top_project
+ subproject1
+ subproject2
Каждый из subproject1
и subproject2
создает статическую библиотеку. Я хотел бы связать эти статические библиотеки в одной общей библиотеке на уровне top_project
.
Информация, которую я собрал до сих пор:
- Скомпилируйте с помощью
-fPic
(необходимо для всего, кроме Windows), чтобы создать независимый от позиции код, который позволит связать статические библиотеки с одной общей библиотекой или распаковать все статические библиотеки (например, с помощью ar
) и повторно -свяжите их в общую библиотеку (которая, я думаю, является неэлегантным и не переносным решением).
- Все исходные файлы должны быть явно указаны в команде
add_library
: по какой-то причине, которую я не могу понять, просто запись add_library(${PROJECT_NAME} SHARED subproject1 subproject2)
не работает должным образом (она по существу создает пустую библиотеку и неправильно регистрирует зависимости)
- В CMake есть функция библиотеки OBJECT, но я не думаю, что она предназначена для того, чтобы делать то, что я хочу.
Любые мысли?
Ответы
Ответ 1
Хорошо, я понял: это гораздо больнее, чем должно быть. До недавнего времени люди в Kitware не понимали, почему кто-нибудь захочет создать DLL из статических библиотек. Их аргумент заключается в том, что всегда должны быть исходные файлы в основном каталоге (например, top_project
в моем случае), поскольку он фактически является собственным проектом. Я вижу вещи по-другому, и мне нужно разбить top_project
на более мелкие подпроекты, которые не должны существовать независимо (т.е. Нет смысла создавать полноценный проект для них и добавлять их с помощью ExternalProject_Add
). Кроме того, когда я отправляю свою общую библиотеку (для использования, например, с помощью Java Native Interface), я не хочу отправлять десятки разделяемых библиотек, потому что это будет подвергать внутреннему оформлению моего проекта. В любом случае, имея, как мне кажется, случай создания общей библиотеки из статических библиотек, я перейду к техническим деталям.
В CMakeLists.txt из subproject1
и subproject2
вы должны создать свою цель, используя библиотечную функцию OBJECT (представленную в CMake 2.8.8):
add_library(${PROJECT_NAME} OBJECT ${SRC})
где SRC
обозначает список исходных файлов (обратите внимание, что они должны быть явно указаны в файле CMakeLists.txt, так как он позволяет make повторно запускать CMake при обнаружении изменения CMakeLists.txt, например, при добавлении или удаление файла)
В top_project
добавьте подпроекты, используя:
add_subdirectory(subproject1)
add_subdirectory(subproject2)
Чтобы увидеть символы из статической библиотеки, используйте:
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--export-all-symbols")
Затем вы можете создать общую библиотеку, используя:
add_library(${PROJECT_NAME} SHARED $<TARGET_OBJECTS:subproject1>
$<TARGET_OBJECTS:subproject2>)
Я обнаружил, что любая "нормальная" библиотека (т.е. не объект) должна быть добавлена в отдельную команду add_library
, иначе она просто игнорируется.
Для исполняемых файлов вы можете использовать:
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}
Я повторяю, что это работает только с версии 2.8.8 CMake. Точно так же CMake очень хорошо управляет зависимостями и кросс-платформенным, потому что он не намного менее болезнен, чем простой старый Makefiles и, конечно, менее гибкий.
Ответ 2
Мое решение состоит в том, чтобы просто добавить /WHOLEARCHIVE
, -all_load
или --whole-archive
к флагам компоновщика, чтобы при подключении вашей основной библиотеки включались все вложенные библиотеки, включая все их символы (поведение по умолчанию чтобы включить только символы из библиотек, которые используются основной библиотекой. Например:
Исходные файлы
$ echo "void Func1() { }" > source1.cpp
$ echo "void Func2() { }" > source2.cpp
$ echo "void Func3() { }" > source3.cpp
$ echo "void Func4() { }" > source4.cpp
Наивный CMakeLists.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)
Запуск его (на 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
$
Правильный CMakeLists.txt
Добавить это:
# 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
Другой способ сделать это.
Этот способ кажется более простым, но я не уверен, насколько он совершенен:
fooobar.com/questions/139554/...
Ответ 4
Другой способ сделать это - предоставить путь к исходным файлам и файлам заголовков всех ваших проектов и собрать их вместе для создания .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})