CMake: включить библиотечные зависимости в статическую библиотеку
Я строю статическую библиотеку в CMake, которая зависит от многих других статических библиотек. Я хотел бы, чтобы все они были включены в выходной файл .lib/.a, чтобы я мог просто отправить большой файл lib клиентам. В Visual Studio 2010 есть опция "Зависимости библиотеки ссылок", которая делает именно это.
Но я не могу найти, как это сделать в CMake. Вы можете установить этот флаг через CMake или получить тот же результат другим способом? Я пробовал target_link_libraries (...), а также add_dependencies (...), но CMake, похоже, просто игнорирует эту строку для статических библиотек.
Ответы
Ответ 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)
Что это!