Подтвердить что ты не робот

CMake: как создать единую общую библиотеку из всех статических библиотек подпроектов?

У меня есть следующий макет:

top_project
    + subproject1
    + subproject2

Каждый из subproject1 и subproject2 создает статическую библиотеку. Я хотел бы связать эти статические библиотеки в одной общей библиотеке на уровне top_project.

Информация, которую я собрал до сих пор:

  • Скомпилируйте с помощью -fPic (необходимо для всего, кроме Windows), чтобы создать независимый от позиции код, который позволит связать статические библиотеки с одной общей библиотекой или распаковать все статические библиотеки (например, с помощью ar) и повторно -свяжите их в общую библиотеку (которая, я думаю, является неэлегантным и не переносным решением).
  • Все исходные файлы должны быть явно указаны в команде add_library: по какой-то причине, которую я не могу понять, просто запись add_library(${PROJECT_NAME} SHARED subproject1 subproject2) не работает должным образом (она по существу создает пустую библиотеку и неправильно регистрирует зависимости)
  • В CMake есть функция библиотеки OBJECT, но я не думаю, что она предназначена для того, чтобы делать то, что я хочу.

Любые мысли?

4b9b3361

Ответ 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})