Статические библиотеки связаны с другими статическими библиотеками с помощью CMake - одна работает, другая нет. Зачем? - PullRequest
0 голосов
/ 27 августа 2018

Фон У меня есть проект, который использует другие небольшие проекты. Эти проекты сами сделаны из других проектов. Многое из этого является наследием или имеет иные управленческие причины для того, чтобы все было организовано как есть, поэтому объединить все в один проект невозможно. Некоторые библиотеки предварительно скомпилированы на удаленных общих ресурсах.

У меня есть 2 основных подпроекта, которые вызывают у меня головную боль:

  • Project Foo - это исполняемый файл и библиотека, которая связывает несколько статических подпроектов (foo_subproject_1, foo_subproject_n). Эти подпроекты также связаны со статическими библиотеками в удаленных местах (some_lib, some_other_lib). Исполняемый файл Project Foo компилируется, связывается и работает правильно

  • Панель проекта - исполняемый файл, который связывает несколько других проектов, включая libFoo. Сбой связывания с «неопределенной ссылкой на» foo_subproject функции

Насколько я могу судить, оба проекта организованы аналогично инструкциям по связыванию. Глядя на SO, я обнаружил, что связывание статических библиотек со статическими библиотеками не должно работать , но потом я запутался в том, как Project Foo успешно компилируется.

gcc и g ++ 4.9.2 являются компиляторами (внешние проблемы "C" из-за наличия некоторых частей в C, а некоторых в C ++ уже проверены)


Вопрос

Я неправильно понял что-либо о том, как работает CMake add_subdirectory, или о том, как работает компоновщик . Может кто-нибудь объяснить, как Project Foo работает успешно, а Project Bar (не) работает как положено?

Обновление Я посмотрел ближе на foo_lib.a и foo_runtime.

Я должен был определить, что что-то не так, чтобы начать, потому что foo_runtime имеет размер почти 100 МБ, а foo_lib всего 10 КБ.

nm показывает, что foo_lib.a ссылается на несколько десятков символов, большинство из которых не определены. foo_runtime тем временем ссылается на все .

Не менее запутанным является то, что foo_subproject_1.a аналогично в основном не определено. Опять же, это то, что я ожидаю увидеть; но я не понимаю, как из этого можно построить foo_runtime?

Мне до сих пор неясно, почему some_library -> subproject -> foo_runtime успешен, а some_library -> subproject -> foo_lib -> bar нет. На этом этапе моих расследований я ожидаю сбоя обеих команд.


Project Foo организован (используя CMake) таким образом:

cmake_minimum_required(VERSION 2.6)
project(foo)

set(FOO_SRCS
    # source and headers for main foo project
)

# Project Foo libraries are subdirectories within this project
add_subdirectory(subs/foo_subproject_1)
add_subdirectory(subs/foo_subproject_2)

# Runtime executable
add_executable(foo_runtime main.c ${FOO_SRCS})
target_link_libraries(foo_runtime foo_subproject_1 foo_subproject_2)

# Library version (static library)
add_library(foo_lib STATIC ${FOO_SRCS})
target_link_libraries(foo_lib foo_subproject_1 foo_subproject_2)

Подкаталоги Project Foo имеют следующую архитектуру:

cmake_minimum_required(VERSION 2.6)
project(foo_subproject_<n>)

set(FOO_SUBPROJECT_<N>_SRCS
    # source and headers for subproject
)

# foo_subproject's remote libraries are all static
add_library(some_lib STATIC IMPORTED)
set_target_properties(some_lib PROPERTIES IMPORTED_LOCATION /path/to/libsome_lib.a)

add_library(some_other_lib STATIC IMPORTED)
set_target_properties(some_other_lib PROPERTIES IMPORTED_LOCATION /path/to/libsome_other_lib.a)

include_directories(/paths/to/libs/include/)

# Static library for foo_subproject_N, links against static libs above
add_library(foo_subproject_<N> STATIC ${FOO_SUBPROJECT_<N>_SRCS})
target_link_libraries(foo_subproject_<N> some_library some_other_library)

Панель проекта устроена так:

cmake_minimum_required(VERSION 2.6)
project(bar)

set(BAR_SRCS
    # source and headers for main bar project
)

# Project Bar libraries are remote from Bar's perspective
add_library(foo_lib STATIC IMPORTED)
set_target_properties(foo_lib PROPERTIES IMPORTED_LOCATION /path/to/foo/libfoo_lib.a)

include_directories(/path/to/foo/include/)

# Runtime executable
add_executable(bar main.c ${BAR_SRCS} foo_lib)

Project Bar не удается связать (компилируется нормально) с несколькими ошибками в форме:

bar_frobulator.cpp:123: undefined reference to 'foo_subproject_1_init_frobulation'

где foo_subproject_1_init_frobulation живет в foo_subproject_1

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

Ответ Цыварева достаточно хорошо описал то, что я на самом деле делал (а не то, что, как я думал, я делал), чтобы заставить меня искать нужные вещи, чтобы ответить на основной вопрос, который у меня был («Почему foo_runtime работает, а bar_runtime не ', когда обе ссылки на статические библиотеки связаны со статическими библиотеками? ")

С документация CMake для target_link_libraries :

Зависимости библиотеки по умолчанию транзитивны с этой подписью. Когда эта цель связана с другой целью, библиотеки, связанные с этой целью, появятся в строке ссылки и для другой цели.

target_link_libraries не вызывает связывание в статических библиотеках foo_subproject_1 и foo_subproject_2 ( статические библиотеки не вызывают компоновщик ). Что делает, это делает список необходимых библиотек доступным для всего, что пытается связать с foo_subproject_1 или foo_subproject_2

Таким образом, моя команда foo_runtime и foo_lib target_link_libraries, насколько это касается CMake:

target_link_libraries(foo_runtime foo_subproject_1 some_library some_other_library foo_subproject_2 some_library some_other_library)
target_link_libraries(foo_lib foo_subproject_1 some_library some_other_library foo_subproject_2 some_library some_other_library)

foo_lib, будучи статичным, не вызывает компоновщик . foo_runtime, будучи исполняемым файлом, делает.

bar - это совершенно другой проект, поэтому он не может использовать преимущества транзитивных зависимостей (и связывание не удается, потому что я пропускаю все символы из библиотек более низкого уровня).

Такое поведение target_link_libraries не ожидалось, так как такое поведение проекта в целом было несколько вводящим в заблуждение (поскольку выглядело, как будто я собирал несколько библиотек на уровне подпроекта, а затем связывал их на верхнем уровне На самом деле, я только что сказал верхнему уровню, какие библиотеки ему нужны).

К лету:

Q"Почему foo_runtime работает, а bar_runtime - нет, когда оба ссылаются на статические библиотеки, связанные со статическими библиотеками?"

A"foo_runtime не связывается со статическими библиотеками, связанными со статическими библиотеками. Foo_runtime связывается с большим количеством статических библиотек, чем вы изначально думали, что это связывание.

bar_runtime не работает, потому что ваш foo_lib в основном пустой "

0 голосов
/ 27 августа 2018

Может кто-нибудь объяснить, как Project Foo работает успешно, а Project Bar (не) работает как положено?

Вкратце: создание библиотеки STATIC не включает в себя этап связывания!

Детали

В проекте Foo у вас есть исполняемый файл foo_runtime, который «работает», потому что он связан с соответствующими библиотеками (например, с библиотекой foo_subproject_1, которая определяет символ foo_subproject_1_init_frobulation).

Исполняемый файл bar из проекта Bar не выполняет эту связь, поэтому он не работает. Линия

target_link_libraries(bar foo_lib)

связывается с foo_lib, но эта библиотека не определяет необходимый символ foo_subproject_1_init_frobulation.

Обратите внимание, что строка

target_link_libraries(foo_lib foo_subproject_1 foo_subproject_2)

в проекте Foo не выполняет фактическое связывание : в общем случае создание статической библиотеки не включает этап связывания.

Для данной строки просто распространяется включает каталоги (и другие compile-features ) из библиотек foo_subproject_* в foo_lib.

Как заставить это работать

Поскольку статическая библиотека foo_lib не отслеживает ее зависимость, вам нужно связать bar с библиотекой, которая знает это. Например, сделать общий доступ foo_lib или объединить библиотеки foo_subproject_* в архивную библиотеку, как предложено в указанном вопросе Как объединить несколько библиотек C / C ++ в одну? .

В качестве альтернативы вы можете построить подпроект Foo внутри Bar one и вместо создания цели IMPORTED foo_lib использовать "обычную" цель foo_lib, созданную в проекте Foo. В этом случае строка

target_link_libraries(bar foo_lib)

будет означать, что CMake (на самом деле) связывает bar с foo_subproject_* библиотеками, потому что эти библиотеки "связаны" (в смысле CMake) в foo_lib. Опять же, последнее «связывание» имеет значение только для CMake: файл foo_lib.a не знает о необходимости библиотек foo_subproject_*.

...