Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

cmakeshared-libraries

Ответы

Ответ 1 · рейтинг 34 · Deimos

Ладно, разобрался: это намного больнее, чем должно быть. До недавнего времени люди в 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, например, при добавлении или удалении файла)

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

Я повторяю, что это работает только с версией CMake 2.8.8. Точно так же CMake очень хорошо управляет зависимостями и является кроссплатформенным, потому что это не намного менее болезненно, чем простые старые файлы Makefile, и, конечно, менее гибко.

Ответ 2 · рейтинг 10 · Timmmm

Мое решение - просто добавить /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 · рейтинг 4 · matiu

Другой способ сделать это.

Этот способ кажется проще, но я не уверен, насколько он идеален:

https://stackoverflow.com/a/14347487/602340

Ответ 4 · рейтинг 2 · sasfour

Другой способ сделать это - указать путь к исходным файлам и файлам заголовков всех ваших проектов и собрать их вместе для создания .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 библиотеки, которые обслуживают (среди прочего) именно эту потребность.

add_library(bundle INTERFACE)
target_link_libraries(bundle lib1 lib2)

объединит lib1 и lib2 в одну библиотеку и унаследует разделы PUBLIC и INTERFACE lib1 и lib2.

Дополнительная информация здесь.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы