CMake: включить библиотечные зависимости в статическую библиотеку - программирование
Подтвердить что ты не робот

CMake: включить библиотечные зависимости в статическую библиотеку

Я строю статическую библиотеку в CMake, которая зависит от многих других статических библиотек. Я хотел бы, чтобы все они были включены в выходной файл .lib/.a, чтобы я мог просто отправить большой файл lib клиентам. В Visual Studio 2010 есть опция "Зависимости библиотеки ссылок", которая делает именно это.

Но я не могу найти, как это сделать в CMake. Вы можете установить этот флаг через CMake или получить тот же результат другим способом? Я пробовал target_link_libraries (...), а также add_dependencies (...), но CMake, похоже, просто игнорирует эту строку для статических библиотек.

4b9b3361

Ответ 1

Итак, у меня есть решение. Во-первых, важно признать, что статические библиотеки не связывают другие статические библиотеки с кодом. Необходимо создать комбинированную библиотеку, что в Linux можно сделать с помощью ar. См. Связывание статических библиотек с другими статическими библиотеками для получения дополнительной информации.

Рассмотрим два исходных файла:

test1.c:

 int hi()
 {
   return 0;
 }

test2.c:

int bye()
{
  return 1;
}

Файл CMakeLists.txt предназначен для создания двух библиотек, а затем для создания объединенной библиотеки выглядит следующим образом:

project(test)

    add_library(lib1 STATIC test1.c)
    add_library(lib2 STATIC test2.c)

    add_custom_target(combined ALL
      COMMAND ${CMAKE_AR} rc libcombined.a $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>)

Параметры команды ar в этом случае зависят от платформы, хотя переменная CMAKE_AR зависит от платформы. Я постараюсь выяснить, есть ли более общий способ сделать это, но этот подход будет работать на системах, которые используют ar.


Основано на Как мне установить параметры для CMAKE_AR? Похоже, что лучший способ сделать это будет:

add_custom_target(combined ALL
   COMMAND ${CMAKE_CXX_ARCHIVE_CREATE} libcombined.a $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>)

Это должно быть независимым от платформы, потому что это структура команд, используемая для создания архивов внутри CMake. Разумеется, при условии, что единственные параметры, которые вы хотите передать своей команде архивирования, это rc поскольку они встроены в CMake для команды ar.

Ответ 2

Я хотел бы улучшить другие решения, предоставив мой CMakeLists.txt файл, который действительно работает также с точки зрения построения зависимостей.

Решение о неправильном использовании CMake

cmake_minimum_required(VERSION 2.8)

add_library(lib1 test1.cpp)
add_library(lib2 test2.cpp)
include_directories(${CMAKE_CURRENT_DIR})
add_executable(mainexec main.cpp)
target_link_libraries(mainexec combinedLib)  # Important to place before add_custom_target

set(LIBNAME "combinedLib.lib")

add_custom_command(
    OUTPUT ${LIBNAME}
    COMMAND lib.exe /OUT:${LIBNAME} $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>
    DEPENDS lib1 lib2
    COMMENT "Combining libs..."
)

add_custom_target(combinedLib
    DEPENDS ${LIBNAME}
)

Обратите внимание, что это решение работает до сих пор с Visual Studio, но я предполагаю, что он может быть совместим с несколькими платформами. Я могу представить, что следующая версия может работать на платформах Unix:

set(LIBNAME "libCombinedLib.a")

add_custom_command(
    OUTPUT ${LIBNAME}
    COMMAND ar -rcT ${LIBNAME} $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>
    DEPENDS lib1 lib2
    COMMENT "Combining libs..."
)

Обратите внимание, что эти решения каким-то образом неправильно используют CMake, поскольку он будет жаловаться на цель типа UTILITY (вместо STATIC или SHARED), если вы поместите вызов target_link_libraries после объявления add_custom_target.

CMake target-declaration-compliant solution

Чтобы сделать его совместимым с CMake, вы можете заменить вызов `target_link_libraries '

target_link_libraries(mainexec ${LIBNAME})
add_dependencies(mainexec combinedLib)

В моем случае это не совсем удовлетворительно, потому что mainexec должен знать о combinedLib, хотя он ожидает, что все зависимости будут обрабатываться вызовом target_link_libraries.

Альтернативное решение с меньшей связью

Посмотрев немного дальше на импортированные цели, я в итоге нашел решение, которое решает мою последнюю проблему:

cmake_minimum_required(VERSION 2.8)

add_library(lib1 test1.cpp)
add_library(lib2 test2.cpp)
include_directories(${CMAKE_CURRENT_DIR})
add_executable(mainexec main.cpp)

set(LIBNAME "combinedLib.lib")

add_custom_command(
    OUTPUT ${LIBNAME}
    COMMAND lib.exe /OUT:${LIBNAME} $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>
    DEPENDS lib1 lib2
    COMMENT "Combining libs..."
)

add_custom_target(combinedLibGenerator
    DEPENDS ${LIBNAME}
)

add_library(combinedLib STATIC IMPORTED)
set_property(TARGET combinedLib PROPERTY IMPORTED_LOCATION ${LIBNAME})
add_dependencies(combinedLib combinedLibGenerator)

target_link_libraries(mainexec combinedLib)

Если вы планируете модулировать весь add GLOBAL после STATIC IMPORTED, чтобы сделать импортированный целевой объект глобально видимым.

Портативное решение CMake

С текущими версиями CMake CMake обеспечивает полную поддержку транзитивных зависимостей и библиотек интерфейсов. Библиотека интерфейсов может затем "связываться" с другими библиотеками, и эта интерфейсная библиотека, в свою очередь, может быть "связана". Почему кавычки? Хотя это работает хорошо, на самом деле это не создает физическую, комбинированную библиотеку, а скорее создает своего рода псевдоним для набора "sub-libs". Тем не менее это было решением, которое нам в конечном итоге понадобилось, поэтому я хотел добавить его здесь.

add_library(combinedLib INTERFACE)
target_link_libraries(combinedLib INTERFACE lib1 lib2)

target_link_libraries(mainexec combinedLib)

Что это!