CMake autogenerated export file for relocatable library - c++

I have a very simple library with a header file and a source file. I'm using CMake to compile it, initially like this:
add_library(libOEInfoProvider SHARED
${CMAKE_CURRENT_LIST_DIR}/src/OE/InfoProvider.h
${CMAKE_CURRENT_LIST_DIR}/src/OE/InfoProvider.cpp)
# see: https://stackoverflow.com/a/25681179/276451
target_include_directories(libOEInfoProvider
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:dist/include>)
I've now configured it so that make install generates all necessary files in a dist folder, like so:
install(TARGETS libOEInfoProvider EXPORT libOEInfoProviderConfig
LIBRARY DESTINATION ${CMAKE_SOURCE_DIR}/dist/lib
)
install(DIRECTORY
src/ DESTINATION ${CMAKE_SOURCE_DIR}/dist/include)
install(EXPORT
libOEInfoProviderConfig DESTINATION ${CMAKE_SOURCE_DIR}/dist/cmake)
export(TARGETS libOEInfoProvider FILE libOEInfoProviderConfig.cmake)
The problem I have is that the geneerated .cmake files contain absolute paths to the dist folder, but I need to deploy this library somewhere else with a different directory structure. Therefore I'd need to have relative paths in the .cmake, so wherever I place the library, the moment I use find_package in the client code it should be able to find the code.

Related

Using 3rd Party Shared Object Library and header files in CMake

I am trying to use this ZED Open Capture library for using the ZED Mini camera for my project on RaspberryPi. I succesfully installed the library and the shared object file is at /usr/local/lib/libzed_open_capture.so and the include headers are at the location /usr/local/include/zed-open-capture/.
To include this library I am adding the following lines to my CMakeLists.txt
find_library(ZED_LIB zed_open_capture)
include_directories("/usr/local/include/zed-open-capture/")
add_executable(zed_pub src/zed_pub.cpp)
target_link_libraries(zed_pub ${ZED_LIB})
Now when I use this code , it shows this error "‘sl_oc::video’ has not been declared"
#include "videocapture.hpp" //Library Header File
sl_oc::video::VideoCapture cap;
cap.initializeVideo();
const sl_oc::video::Frame frame = cap.getLastFrame();
Can someone please explain me how a Shared Object Library file along with header files should be used in CMake? The library has already been installed using CMake build and sudo make install on my Linux system.
The github repo of the library is at https://github.com/stereolabs/zed-open-capture
Also I cannot find Find_PKG_name.cmake so I cannot use find_package() option.
videocapture.hpp wraps the definitions you need inside #ifdef VIDEO_MOD_AVAILABLE. It seems likely that this is not defined. The root CMakeLists.txt in the ZED package defaults BUILD_VIDEO to ON, so this was likely all defined for the package build. But as others have pointed out, the package does not persist this information anywhere in the installation. The "right" way for the package to do it would be EITHER to configure/modify the include files at install time to account for the build configuration, probably by generating a "config.hpp" file with the appropriate definitions. OR to include a zed-config.cmake file in the installation, with all the necessary imports and definitions.
Your short-circuit solution should be fine. Just add target_compile_definitions(zed_pub PUBLIC VIDEO_MOD_AVAILABLE). If you want to do it more cleanly for the future, create an IMPORTED target for zed_lib, and set both the include_directories and compile_definitions on that target, so that all users of the library get this defined automatically.
According to the ZED Open Capture's CMakeLists.txt:
...
# Install rules
set_target_properties(${PROJECT_NAME} PROPERTIES
PUBLIC_HEADER "${HDR_FULL}"
)
install(TARGETS ${PROJECT_NAME}
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zed-open-capture)
ref: https://github.com/stereolabs/zed-open-capture/blob/dfa0aee51ccd2297782230a05ca59e697df496b2/CMakeLists.txt#L142-L148
ZED Open Capture seems to not provide any "CMake config" file...
So you must create your own FindZED.cmake module and/or improve the CMakeLists.txt of this project...

Exporting and packaging prebuilt libraries in cmake

instead of asking a question directly, I'll expose my use case and the way I tried (but failed) to solve it.
Say I have:
3 shared libraries A, B and C
A require B and C
A comes with a set of headers
That's it, no extra information, it's provided by a vendor and not possibly subject to any change (modernization/cmake packages, etc).
A should always be packaged with B and C. I should only need to link with A and cmake should transitively link with B and C.
Now, I'd like to make it more "modern cmake" friendly and by able to:
First usecase: Create a repo containing these libs and calling add_subdirectory() from a parent project.
First usecase: Create a package (say debian pkg . deb) containing the relevant AConfig.cmake AConfigVersion.cmake and ATargets.cmake. Then a simple system install of the pkg and a find_package() should to the trick.
What has been done:
I tried using INTERFACE IMPORTED library and INTERFACE.
Because I want to support packaging the libs INTERFACE IMPORTED can't be used (you can't install it as far as I know/tested).
INTERFACE is working fine for the first usecase, using add_subdirectory(), headers are found, everything links, but because the user may not have at this point, the shared lib in is path, he can't run the tests for instance.
Then comes the export part needed to make the shared libs available and to make find_package() work. I succeed to export/package the libs A B C, the headers for A and the files needed for find_package().
But when in a client library D, find_package(A REQUIRED) finds the lib (no messages such as "Could not find a package configuration file provided by "A" ") it doess NOT create any target I can link on. I took a look at the generated ATargets.cmake:
# generated stuff before
add_library(A::A INTERFACE IMPORTED) # This is cannot be used at all, does not create a target I can link on unless I remove IMPORTED
set_target_properties(A::A PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include" # that's fine
INTERFACE_LINK_LIBRARIES "A.so;B.so.1.0;C.so.1.0" # that's not fine I need ${_IMPORT_PREFIX}/${CMAKE_INSTALL_LIBDIR}/ before each libs like it did for the headers
)
# generated stuff after
Note that if I remove the IMPORTED in the add_library of the ATargets.cmake and remove the namespace stuff, the target A is correctly accessible in any client cmake project using find_package but it'll NOT link correctly probably because A.so (and B and C) is not referenced using ${_IMPORT_PREFIX}/lib/A.so. I tried adding ${_IMPORT_PREFIX}/lib/ before all libs in INTERFACE_LINK_LIBRARIES, removed the IMPORTED keyword and namespace stuff and guess what, it works perfectly... Now, how do I do that without the trick.
The content of AConfig.cmake.in is:
#PACKAGE_INIT#
include(CMakeFindDependencyMacro)
# Add the targets file
include("${CMAKE_CURRENT_LIST_DIR}/ATargets.cmake")
# check_required_components(#PROJECT_NAME#) # It is commented because unless I apply the fix specified earlier (IMPORTED and namespace removed), during the find_package call I get:
#################
# CMake Error at /usr/lib/cmake/A/AConfig.cmake:8 # (check_required_components):
# Unknown CMake command "check_required_components".
#################
That's pretty much it, the rest of this post is implementation details. This code below can be used to solve use case 1 but the export package and cmake config/target files are not correct.
add_library(A
INTERFACE)
add_library(A::A ALIAS A)
target_include_directories(A
INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:include>")
target_link_libraries(A
INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib/A.so>"
"$<INSTALL_INTERFACE:A.so>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib/B.so>"
"$<INSTALL_INTERFACE:B.so>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib/C.so>"
"$<INSTALL_INTERFACE:C.so>")
#### install
install(TARGETS A
EXPORT ATargets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT A_Runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT A_Runtime NAMELINK_COMPONENT A_Development
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT A_Development)
install(DIRECTORY "lib/"
DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include"
DESTINATION "include")
write_basic_package_version_file(AConfigVersion.cmake
VERSION "${PACKAGE_VERSION}"
COMPATIBILITY SameMajorVersion)
install(EXPORT ATargets
FILE ATargets.cmake
NAMESPACE A::
DESTINATION "lib/cmake/A")
configure_file(AConfig.cmake.in AConfig.cmake #ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/AConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/AConfigVersion.cmake"
DESTINATION "lib/cmake/A")
# Then some cpack stuff that is not affecting the work done earlier
In a client lib/cmake project after the installation of the generated package (generated by cpack):
find_package(A)
target_link_libraries(my_project PUBLIC/PRIVATE A::A) # Should bring in the headers and link with A B and C
Documentation:
cmake: create a new library target which consists of a prebuilt library
https://gitlab.kitware.com/cmake/community/-/wikis/doc/tutorials/Exporting-and-Importing-Targets
Possible to add an imported library to target_link_libraries that takes care of include directories too?
Exporting an imported library
https://discourse.cmake.org/t/how-to-control-import-prefix-in-exported-targets-cmake-list-file-generated-by-cmake/2291
Create relocatable package with proper autogenerated config cmake
https://discourse.cmake.org/t/exporting-packages-with-a-custom-find-module/3820/2
Thanks for reading/helping

Building a library while maintaining file structure with cmake and conan

I'm building a library containing multiple .h files and 1 .cpp file, in a structure such as this
/include/A/B/f.h
/include/A/B/C/d.h
/include/G/h.h
/src/K/l.h
/src/K/l.cpp
I want to keep the relative path between each of the files after building locally, but they're all placed in a single folder /output/include without retaining any folder structure whatsoever :
/build/output/include/f.h
/build/output/include/d.h
/build/output/include/h.h
/build/output/include/l.h
This is what my install looks like :
install(TARGETS ${CMAKE_PROJECT_NAME}
RUNTIME DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/output/bin"
LIBRARY DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/output/lib"
PUBLIC_HEADER DESTINATION "${CMAKE_CURENT_BINARY_DIR}/output/include"
}
I don't know exactly where to look as there is also the conanbuildinfo.cmake and cmake_install.cmake files generated by the cmake script I'm using (not done by me). If you need any more info please tell me because I'm pretty much new to cmake and conan and don't know which information is essential.

Collect all header files when compiling static library

I am using Cmake to compile a static version of the library from source.
The source code has a structure that looks like this:
src/
module1/
x.cpp
x.h
...
module2/
y.cpp
y.h
...
and so on...
Compiling a static version of the library is not difficult. However for distribution purposes, I just want to distribute the the headers (x.h, y.h, ...) and static libraries (module1.a, module2.a, ...).
Is there some command in GCC or CMAKE to automatically collect all of the headers and put them into a separate folder?
I am aware that I could manually separate the source and headers, or I could simply distribute all of the code (source and headers), but that is wasteful and undesireable for my particular use case. Alternatively, I could write a pretty simple Python script to do it, but it seems to me that this is probably a pretty common scenario. So I would guess that there is something in Gcc or Cmake to do it.
Note: I am not in charge of maintaining the codebase, so I have no say in how the project is structured. If I was, I could have separated the code into src and include folders.
The best thing to do is have cmake glob and install all your artifacts.
# append to your existing CMakeLists.txt
install(TARGETS module1 module2 #adjust to use your names
ARCHIVE DESTINATION lib)
file(GLOB_RECURSE header_list "*.h") #adjust if necessary
install(FILES ${header_list}
DESTINATION include)
Be aware that globbing isn't perfect, and the file list is only updated when cmake is run (i.e., it won't detect added or removed files on its own).

CMake doesn't include header directory of submodule A within submodule B

I have a CMake project that looks like this:
project/
CMakeLists.txt
subprojectA/
CMakeLists.txt
include/
headerA.hpp
src/
libraryA.cpp
subprojectB/
CMakeLists.txt
src/
mainB.cpp
The "library" subproject, A, is compiled as a static library, becoming libsubprojectA.a. The "main" project, B, is compiled as a binary and depends on the library. mainB.cpp includes a reference to headerA.hpp.
Here is subprojectA/CMakeLists.txt:
project(SubProjectA)
include_directories(include)
add_library(subprojectA STATIC src/libraryA.cpp)
set(${PROJECT_NAME}_INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/include
CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
And here is subprojectB/CMakeLists.txt:
project(SubProjectB)
include_directories(${SubProjectA_INCLUDE_DIRS})
add_executable(mainBinary src/mainB.cpp)
target_link_libraries(mainBinary subprojectA)
The main Project CMakeLists.txt looks like:
project(Project)
add_subdirectory(subprojectB)
add_subdirectory(subprojectA)
Note that subprojectB, the main project, is listed before subprojectA.
Here's the problem. When I first run "cmake" on this project, ${SubProjectA_INCLUDE_DIRS} is not set within SubProjectB.
What I think is happening is that the CMakeLists for SubProjectB loads first, when ${SubProjectA_INCLUDE_DIRS} has not yet been set. It sets its own include path to an empty string as a result. However, even though libsubprojectA.a gets built successfully before mainBinary, the include path was already set empty beforehand. As a result, I get this error when trying to make mainBinary:
subprojectB/src/mainB.cpp:1:23: fatal error: headerA.hpp: No such file or directory
#include "headerA.hpp"
^
It's a workaround to put subprojectA before subprojectB in the main Project CMakeLists in the declarative world of CMake. What I really want is to know the proper way to indicate to CMake that the include_directories(${SubProjectA_INCLUDE_DIRS}) line depends on the definitions that exist inside SubProjectA's CMakeLists. Is there a better way to do this?
If you want to express that include directory subprojectA/include is an interface of the library subprojectA, attach this property to the target with target_include_directories command:
subprojectA/CMakeLists.txt:
project(SubProjectA)
add_library(subprojectA STATIC src/libraryA.cpp)
# PUBLIC adds both:
# 1) include directories for compile library and
# 2) include directories for library's interface
target_include_directories(subprojectA PUBLIC include)
So any executable(or other library) which linked with subprojectA will have this include directory automatically:
subprojectB/CMakeLists.txt:
project(SubProjectB)
add_executable(mainBinary src/mainB.cpp)
target_link_libraries(mainBinary subprojectA)
Of course, for use last command properly you need to process directory with library before one with executable:
CMakeLists.txt:
project(Project)
add_subdirectory(subprojectA)
add_subdirectory(subprojectB)