MaxLevs
@MaxLevs

CMake. Как подключить библиотеку из файла?

Существуют тысячи статей в интернете по поводу того, как же подключить предсобранную библиотеку к своему проекту, использующему cmake, но ни одна из статей мне не помогла.

У меня есть библиотека для представления приближенных чисел, собираемая в статическую библиотеку.
Конфигурация библиотеки

cmake_minimum_required(VERSION 3.19)
project(approx_lib)

set(CMAKE_CXX_STANDARD 20)

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/examples)

add_library(approx_lib STATIC include/approx_number.h src/approx_number.cpp)
target_include_directories(approx_lib PUBLIC include/)
install(TARGETS approx_lib LIBRARY DESTINATION lib)


Как можно заметить по конфигурации, в ней присутствует класс approx_number, разделенный на заголовочный файл approx_number.h и реализацию approx_number.cpp.
После сборки библиотеки производим установку в по пути /usr/local/lib/libapprox_lib.a

Есть приложение, которое библиотеку использует
Конфигурация приложения

cmake_minimum_required(VERSION 3.19)
project(just_calculator VERSION 1.0.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)

find_library(APPROX_LIB approx_lib HINTS /usr/local/lib REQUIRED)

message(STATUS "Found approx_lib: ${APPROX_LIB}")

add_executable(just_calculator main.cpp)
target_link_libraries(just_calculator LINK_PUBLIC ${APPROX_LIB})



После загрузки конфигурации видим, что библиотека нашлась
-- Found approx_lib: /usr/local/lib/libapprox_lib.a
-- Configuring done
-- Generating done


Для работы приложения нам нужен класс approx_number, а значить надо подключить заголовочный файл approx_number.h из библиотеки.
Прописываем в main.cpp #include <approx_number.h>

Но получаем сообщение об ошибке:
60baa8c8ea456812711705.png

Так как же подключить библиотеку?
  • Вопрос задан
  • 2686 просмотров
Решения вопроса 1
@klirichek
Так у вас либа из двух частей - собственно, архив .a, и хедер (или хедеры).

install(TARGETS approx_lib LIBRARY DESTINATION lib) - ставит только собранный target (т.е. непосредственно файл статик либы), но не хедер!

Если не производить установку - можно либу подключить как add_subdirectory с путём к исходникам. Тогда она будет частью основного проекта, будет собираться вместе с ним и всё нужное найдётся (покуда target_include_directories прописан и знает, где искать).

Если устанавливать - то, очевидно, надо установить и хедеры тоже. И ещё как-то сообщить проекту, где что лежит.

Вот кусочек из нашей кастомной сборки icu (пусть имя пакета будет foo, а не icu. А namespace - baz. Так интереснее!)
add_library(icu .....)
target_include_directories(icu PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/source/common>$<INSTALL_INTERFACE:include/icu>)
install ( TARGETS icu EXPORT icuExport RUNTIME DESTINATION lib LIBRARY DESTINATION lib  )
install ( DIRECTORY source/common/unicode DESTINATION include/icu FILES_MATCHING PATTERN "*.h")

# install exports
set ( CMAKE_CMAKE_DIR "lib/cmake/foo" )
install ( EXPORT icuExport FILE foo-targets.cmake DESTINATION "${CMAKE_CMAKE_DIR}" NAMESPACE baz:: )

file ( WRITE foo-config.cmake 
"if(NOT TARGET baz::icu)
    include(\"\${CMAKE_CURRENT_LIST_DIR}/foo-targets.cmake\")
endif()")
install ( FILES foo-config.cmake DESTINATION "${CMAKE_CMAKE_DIR}" )


Здесь несколько моментов.
Во-первых, обратите внимание на target_include_directories. Там два пути. Один будет работать при сборке, второй - при инсталляции. Т.е. когда собирается либа, либо подключается через add_subdirectory - найдётся нужное по первому пути. После установки - по второму, и этот второй - относительный.

Во-вторых, при установке задано имя export. Это что-то вроде именованного набора разных свойств, которые cmake накапливает из добавленных с этим именем целей и потом может оформить в файл экспорта. В данном случае туда попадает явно прописанная библиотека, и неявно - include-path от неё, последний потому что он изначально включён как public.

В-третьих, собственно, ставим помимо таргета и сами хедеры!

И в-последних, ставим то, что "накопил" экспорт. Первый файл foo-targets.cmake. Второй - который будет работать при поиске пакета (я его осознанно упростил, на самом деле он длиннее) foo-config.cmake.

Всё это куда-то ставится. Например, в /root/bar (для определённости) - как `cmake --install . --prefix /root/bar`. У вас оно прямо в систему, но тут спецом левый путь, чтобы глазами было видно, как он в конфиге пробрасывается и используется.

Потом в самом проекте - не надо никаких find_library, всё же известно, где!
LIST (APPEND CMAKE_PREFIX_PATH "/root/bar")
find_package(foo)

add_executable(my_prog main.cpp)
target_link_libraries(my_prog PRIVATE baz::icu)


Собственно, всё.

find_package сперва пытается найти модуль FindFoo.cmake. Такого нет. Потом пытается найти конфиг FooConfig.cmake или foo-config.cmake. Он разыскивается по указанному префиксу (там весьма развесистый алгоритм, который умеет заглядывать в разные папки, в доках всё есть) и включается. Он, в свою очередь, включает. foo-targets.cmake, сгенерированный из экспорта. И вот там и находится вся магия.

... # это сгенерированный код!
# Create imported target baz::icu
add_library(baz::icu STATIC IMPORTED)

set_target_properties(baz::icu PROPERTIES
  INTERFACE_COMPILE_DEFINITIONS "U_STATIC_IMPLEMENTATION;U_CHARSET_IS_UTF8=1;U_NO_DEFAULT_INCLUDE_UTF_HEADERS=1"
  INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include/icu"
)
...
# Load information for each installed configuration.
get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
file(GLOB CONFIG_FILES "${_DIR}/foo-targets-*.cmake")
foreach(f ${CONFIG_FILES})
  include(${f})
endforeach()
...
... а это файл foo-targets-relwithdebinfo.cmake
# Import target "baz::icu" for configuration "RelWithDebInfo"
set_property(TARGET baz::icu APPEND PROPERTY IMPORTED_CONFIGURATIONS RELWITHDEBINFO)
set_target_properties(baz::icu PROPERTIES
  IMPORTED_LINK_INTERFACE_LANGUAGES_RELWITHDEBINFO "CXX"
  IMPORTED_LOCATION_RELWITHDEBINFO "${_IMPORT_PREFIX}/lib/libicu.a"
  )


а вообще в доках cmake (не надо никаких левых вики/SO!) этой теме посвящён большой и подробный раздел.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы