S
S
sudo rm -rf /2021-06-05 01:24:59
CMake
sudo rm -rf /, 2021-06-05 01:24:59

CMake. How to include a library from a file?

There are thousands of articles on the Internet about how to include a prebuilt library in your project using cmake, but none of the articles helped me.

I have a library for representing approximate numbers, which is compiled into a static library.

Library configuration

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)


As you can see from the configuration, it contains the approx_number class, divided into the approx_number.h header file and the approx_number.cpp implementation.
After building the library, we install it along the path /usr/local/lib/libapprox_lib.a

There is an application that uses the library
Application Configuration

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})



After loading the configuration, we see that the library was found
-- Found approx_lib: /usr/local/lib/libapprox_lib.a
-- Configuring done
-- Generating done


For the application to work, we need the approx_number class, which means we need to include the approx_number.h header file from the library.
We register in main.cpp #include <approx_number.h>

But we get an error message:
60baa8c8ea456812711705.png

So how to include the library?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
K
klirichek, 2021-06-05
@MaxLevs

So you have two parts - actually, the .a archive, and the header (or headers).
install(TARGETS approx_lib LIBRARY DESTINATION lib) - installs only the built target (i.e. the static lib file itself), but not the header!
If you do not install, you can connect either as add_subdirectory with the path to the sources. Then it will be part of the main project, it will be assembled with it and everything you need will be found (as long as the target_include_directories is registered and knows where to look).
If you install - then, obviously, you need to install headers too. And somehow tell the project where everything is.
Here is a snippet from our custom build of icu (let the package name be foo, not icu. And the namespace be baz. That's more interesting!)

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}" )

There are a few points here.
First, take a look at the target_include_directories. There are two ways. One will work during assembly, the second - during installation. Those. when either one is assembled or connected via add_subdirectory, the required one will be found along the first path. After installation - according to the second, and this second is relative.
Secondly, the name export is specified during installation. This is something like a named set of different properties that cmake accumulates from targets added with this name and can then be formatted into an export file. In this case, an explicitly defined library gets there, and implicitly - the include-path from it, the latter because it is initially included as public.
Thirdly, in fact, in addition to the target, we also set the headers themselves!
And lastly, we put what the export has "accumulated". The first file is foo-targets.cmake. The second - which will work when searching for a package (I deliberately simplified it, in fact it is longer) foo-config.cmake.
All this is put somewhere. For example, in /root/bar (to be specific) - like `cmake --install . --prefix /root/bar`. You have it directly into the system, but here the left path is special, so that you can see with your eyes how it is forwarded and used in the config.
Then in the project itself - no find_library is needed, it is still known where!
LIST (APPEND CMAKE_PREFIX_PATH "/root/bar")
find_package(foo)

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

Actually, everything.
find_package first tries to find the FindFoo.cmake module. There is no such. Then it tries to find the FooConfig.cmake or foo-config.cmake config. It is searched for by the specified prefix (there is a very spreading algorithm that can look into different folders, everything is in the docks) and turns on. He, in turn, includes foo-targets.cmake generated from the export. And that's where all the magic is.
... # это сгенерированный код!
# 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"
  )

but in general in the cmake docks (no need for any left wiki / SO!) This topic is devoted to a large and detailed section.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question