I'm trying to set up the build process of my library using cmake's install/export, for which the built lib and header files will be packaged up as a .deb using dpkg-buildpackage. I was trying to follow the guide on https://cmake.org/cmake/help/git-stage/guide/importing-exporting/index.html for the most part, so as to make my package findable after install using find_package.
The issue I'm facing is when my libraries have nested dependencies from my other folders/projects, which I add using add_subdirectory().
My main CMake for the project I want to build/export is
cmake_minimum_required(VERSION 3.10)
project(exampleLib)
add_subdirectory(../path/to/dependencyA ${PROJECT_BINARY_DIR}/dependencyA)
include(GNUInstallDirs)
add_library(exampleLib SHARED
exampleLib.cpp
)
target_link_libraries(exampleLib PRIVATE
dependencyA
)
target_include_directories(dashStreamerClient PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
# dependencyA has a dependency on "dependencyB"
# which if I don't include here, errors with 'CMake Error: install(EXPORT "exampleLibTargets" ...) includes target "dependencyA" which requires target "dependencyB" that is not in any export set.'
install(TARGETS exampleLib dependencyA dependencyB
EXPORT exampleLibTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
# Note: I only want this exampleLib.h file to be "installed" in CMAKE_INSTALL_INCLUDEDIR, not any of the other dependencies headers as they are unnecessary!
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/exampleLib.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(EXPORT exampleLibTargets
FILE exampleLibTargets.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/exampleLib
)
# Make a package configuration file
include(CMakePackageConfigHelpers)
# Config.cmake.in copied from the importing-exporting cmake guide
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/exampleLibConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/exampleLib
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/exampleLibConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/exampleLibConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/exampleLib
)
export(EXPORT exampleLibTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/exampleLibTargets.cmake"
)
while my dependencyA's CMakeLists.txt is
cmake_minimum_required(VERSION 3.10)
project(dependencyA)
add_subdirectory(path/to/dependencyB ${PROJECT_BINARY_DIR}/dependencyB)
add_library(dependencyA SHARED
dependencyA.cpp
)
target_link_libraries(dependencyA PUBLIC
dependencyB
)
target_include_directories(dependencyA PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
)
The issue is when I try to make install, I get the error
CMake Error at cmake_install.cmake:152 (file):
file INSTALL cannot find
"/home/me/path/to/exampleLib/include/dependencyB.h":
No such file or directory.
where CMake tries to find the header of the nested dependencyB inside my current CMake project's path, but dependencyB.h is actually in e.g. path/to/dependencyB which was defined in the add_subdirectory of dependencyA. I also do not actually want this dependencyB.h to be installed, as it is not necessary, and only want exampleLib.h.
What am I doing wrong?
Thank you!
Related
I am building a C++ project from Github and want to deploy the code to a remote Linux machine. This is all new to me.
The project has a main.cpp, which includes the various headers/sources like a library.
The CMake outputs an executable (to represent main.cpp) AND a separate static library. The project also uses OpenSSL, which I have linked statically.
I presume the OpenSSL functions are included within the static library? So when I deploy, I don't need to copy-over or install any OpenSSL on the remote machine?
Is it possible to modify the CMake so the application and the library are merged in to one file?
I am trying to make deployment as simple as copying over a single file, if this is possible.
Any additional advice/references are most-welcome.
UPDATE the CMake script:
cmake_minimum_required(VERSION 3.20)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
project(helloworld C CXX)
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_BUILD_TYPE Release)
set (BUILD_MAIN TRUE)
set (BUILD_SHARED_LIBS FALSE)
set (OPENSSL_USE_STATIC_LIBS TRUE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set( HELLOWORLD_HEADERS helloworld/File1.h helloworld/File2.h )
set( HELLOWORLD_SOURCES helloworld/File1.cpp helloworld/File2.cpp )
# Static library
add_library( helloworld ${HELLOWORLD_SOURCES} ${HELLOWORLD_HEADERS} )
# Rapidjson
include_directories(/tmp/rapidjson/include/)
# OpenSSL
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
add_definitions(${OPENSSL_DEFINITIONS})
target_include_directories(helloworld PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
target_link_libraries(helloworld PRIVATE ${OPENSSL_LIBRARIES})
set( HELLOWORLD_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
include(GNUInstallDirs)
target_include_directories(helloworld PUBLIC
$<BUILD_INTERFACE:${HELLOWORLD_INCLUDE_DIRS}/>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/helloworld>
)
set_target_properties(helloworld PROPERTIES PUBLIC_HEADER "${HELLOWORLD_HEADERS}")
add_library(helloworld::helloworld ALIAS helloworld)
option(HELLOWORLD_INSTALL "Install HelloWorld" TRUE)
if (HELLOWORLD_INSTALL)
install(TARGETS helloworld
EXPORT helloworld
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/helloworld/
)
configure_file("${CMAKE_CURRENT_LIST_DIR}/helloworld-config.cmake.in" "${CMAKE_BINARY_DIR}/helloworld-config.cmake" #ONLY)
install(FILES "${CMAKE_BINARY_DIR}/helloworld-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/helloworld")
install(EXPORT helloworld
FILE helloworld-targets.cmake
NAMESPACE helloworld::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/helloworld
)
endif()
if (BUILD_MAIN)
add_executable(main main.cpp)
target_link_libraries(main helloworld)
endif()
ITNOA
I it is very helpful to make URL of your GitHub's project, but I write some public notes about that
In generally in CMake for static linking your library to your executable, you can write simple like below (from official CMake example)
add_library(archive archive.cpp zip.cpp lzma.cpp)
add_executable(zipapp zipapp.cpp)
target_link_libraries(zipapp archive)
In above example your executable file is just work without needing .a library file and you can simple copy single file.
if you want to make all of thing static, you make sure all dependencies make static link to your project, like CMake: how to produce binaries "as static as possible"
if you want to prevent library creation, Probably in your CMake file, you can find add_library command, and add_executable command. you can remove add_library command and add all sources to add_executable command.
for example add_executable(a.out main.cpp lib.cpp)
I have a simple cmake setup between two projects, a library project and an executable project. The library project I want to be installed on the system (Linux container), and the executable project I want to link with the library package after finding it with find_package(XXX CONFIG). I successfully build and install the library project just fine, however, when I configure the executable project I get an error in the vein of
CMake Error at MyExecutable/CMakeLists.txt:35 (add_executable):
Target "MyExecutable" links to target "CUDA::toolkit" but the target was
not found. Perhaps a find_package() call is missing for an IMPORTED
target, or an ALIAS target is missing?
CUDA::toolkit is a PRIVATE target linked to the library project.
The CMakeLists.txt for the library project looks like this
add_library(SiftCuda ${SOURCES} ${CUDA_KERNELS})
target_link_libraries(MyLibrary PRIVATE
CUDA::cudart
CUDA::cublas
)
set_target_properties(MyLibrary PROPERTIES
PUBLIC_HEADER "${HEADERS}"
CUDA_SEPARABLE_COMPILATION ON
)
target_include_directories(MyLibrary PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/MyLibrary>
)
target_compile_features(MyLibrary PUBLIC
cxx_std_11
)
# Alias library
add_library(MyLibrary::MyLibrary ALIAS MyLibrary)
###### Installation #####
include(GNUInstallDirs)
# Install library
install(TARGETS MyLibrary
EXPORT MyLibraryConfig
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/MyLibrary
)
# For exposing internal configuration of the package
export(
TARGETS MyLibrary
NAMESPACE MyLibrary::
FILE "${CMAKE_CURRENT_BINARY_DIR}/MyLibraryConfig.cmake"
)
# Install exports
install(EXPORT MyLibraryConfig
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/MyLibrary"
NAMESPACE MyLibrary::
)
The CMakeLists.txt for the executable looks like this
find_package(MyLibrary CONFIG)
add_executable(MyExe
${SOURCES}
)
target_link_libraries(MyExe PUBLIC
MyLibrary::MyLibrary
)
I get the feeling that linking MyExe to the CUDA targets would resolve this issue, however, doing so seems incorrect as I usually don't have to be aware of transitive dependencies for libraries I use.
I am making a custom library that I want to be installable for users. However, when I try to use my own library in a cmake executable, I get a build error saying that the library headers were not found.
The library CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(mylibrary)
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 14)
# Register a library - This will created lib[xxx].so
add_library(mylibrary SHARED src/library.cpp)
configure_file(mylibrary.pc.in mylibrary.pc #ONLY)
# List the /include directory
target_include_directories(mylibrary PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
install(TARGETS mylibrary
EXPORT mylibraryConfig
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
export(TARGETS mylibrary
FILE "${CMAKE_CURRENT_BINARY_DIR}/mylibraryConfig.cmake")
install(EXPORT mylibraryConfig
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mylibrary/cmake"
NAMESPACE mylibraryConfig::)
install(
DIRECTORY include
DESTINATION ${CMAKE_INSTALL_PREFIX})
install(FILES ${CMAKE_BINARY_DIR}/mylibrary.pc
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
Which I successfully build and install with:
$ cmake .. -DCMAKE_INSTALL_PREFIX=~/libraries/local # Use non-standard destination
$ make && make install
The executableCMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(myexecutable)
set(CMAKE_CXX_STANDARD 14)
find_package(mylibrary REQUIRED)
add_executable(myexecutable src/main.cpp)
target_link_libraries(myexecutable PUBLIC mylibrary)
target_include_directories(myexecutable PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
I can prepare cmake for this project:
$ cmake .. -DCMAKE_PREFIX_PATH=~/libraries/local # Use non-standard location
However, building it fails:
$ make
fatal error: mylibrary/library.h: No such file or directory
2 | #include <mylibrary/library.h>
To my understanding the location of the library (binaries and headers) is embedded in the installed package. And through find_package() that information retrieved, so why isn't it working here?
Similar questions:
I largely based my library cmake on: How to create a shared library with cmake?
Same problem but I am already using target_include_directories: Cmake Linking Shared Library: "No such file or directory" when include a header file from library
When a shared library target is namespaced in the config file you need to reference it with the full name in the downstream packages when using find_package, i.e. you need to use
target_link_libraries(myexecutable PUBLIC mylibraryConfig::mylibrary)
Alternatively, remove the namespace from the install by replacing
install(EXPORT mylibraryConfig
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mylibrary/cmake"
NAMESPACE mylibraryConfig::)
...with:
install(EXPORT mylibraryConfig
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mylibrary/cmake")
I'm stuck to import a shared library in another project with CMake.
my-utils-lib
My lib files are generated in my-utils-lib project:
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(my-utils-lib VERSION 1.0.0 DESCRIPTION "Utils for C++.")
set(CMAKE_CXX_STANDARD 14)
add_library(
my-utils-lib SHARED
./src/string_utils/find_matches.cpp
./src/string_utils/split.cpp
./src/string_utils/format.cpp
./src/vector_utils/print_vector.cpp
)
set_target_properties(
my-utils-lib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 1
)
target_include_directories(my-utils-lib PRIVATE src)
include(GNUInstallDirs)
install(
TARGETS my-utils-lib
DESTINATION ${CMAKE_INSTALL_LIBDIR}
EXPORT my-utils-lib
)
configure_file(my-utils-lib.pc.in my-utils-lib.pc #ONLY)
install(
FILES ${CMAKE_BINARY_DIR}/my-utils-lib.pc
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig
)
I run mkdir build && cd ./build/ && cmake .. && sudo make install to generate and install my library files.
Finally, my-utils-lib project tree looks like this:
CMakeLists.txt
my-utils-lib.pc.in
src/
|_main.cpp
|_main.h
|_string_utils/
|_find_matches.cpp
|_find_matches.h
|_format.cpp
|_format.h
|_split.cpp
|_split.h
|_vector_utils/
|_print_vector.cpp
|_print_vector.h
my-project
In another project, I'd like to import "split.h" in one of my local headers without having to enter the full path to the actual file. Something like:
#include "my-utils-lib/string_utils/split.h"
I tried many solution so far, none of them is working. My last attempt is:
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(longer_sub_sequence)
set(CMAKE_CXX_STANDARD 14)
find_package(Qt5Widgets REQUIRED)
find_library(my-utils-lib libmy-utils-lib.1.0.0.dylib)
add_library(my-utils-lib SHARED IMPORTED)
add_executable(
longer_sub_sequence
main.cpp
visualization/draw_matrix.cpp
visualization/draw_matrix.h
)
target_link_libraries(longer_sub_sequence my-utils-lib Qt5::Widgets)
CMake autobuild runs fine (I am using CLion as an IDE) but I can't find how to import any code from my library.
I checked in /usr/local/lib (where CMake generated my lib files), and found the correct lib files :
find . -maxdepth 1 -name "*my-utils-lib*" -print
./libmy-utils-lib.1.0.0.dylib
./libmy-utils-lib.1.dylib
./libmy-utils-lib.dylib
What am I missing here ?
You are missing to install the library header.
You should add an install command for header just like you did with the library binary.
Moreover you should consider using comparison signs (< and >) instead of double quote for include directive :
#include <my-utils-lib/string_utils/split.h>
One last remark, you are generating and installing a .pc file which is used by pkg-config only and you're not using it.
new CMake user here.
I've made a simple header only library, with the following CMake file:
cmake_minimum_required(VERSION 3.7)
project(mylib VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
#add_compile_options(-Wa -aslh)
# Define the library target
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE include/)
add_executable(mytest test/basic_checks.cpp)
target_link_libraries(mytest mylib)
From http://foonathan.net/blog/2016/03/03/cmake-install.html
I've learned there are several steps to getting a library installed and to have it useable from another CMake project. First it must be "installed" and then it must be "exported". Then there's find_package, but I'll cross that bridge after I've gotten over the first two steps.
So following the example, I figure that I could add the following to the bottom of my CMake file:
# Install the header file.
install(FILES include/mylib.hpp DESTINATION "include/mylib-${PROJECT_VERSION}")
But the guide talks about also using install on TARGETS, and then adding EXPORT to the target install commands.
How much of this applies to a header only library in which I have no other compiled code of files other than the header?
How do I apply the steps described in : http://foonathan.net/blog/2016/03/03/cmake-install.html
To an INTERFACE only library? The lack of .cpp files in my project is leading me to question which instructions apply and don't apply.
try this:
cmake_minimum_required(VERSION 3.7)
project(mylib VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
#add_compile_options(-Wa -aslh)
# Define the library target
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/mylibConfigVersion.cmake"
VERSION 0.1
COMPATIBILITY AnyNewerVersion
)
install(TARGETS mylib
EXPORT mylibTargets
LIBRARY DESTINATION lib COMPONENT Runtime
ARCHIVE DESTINATION lib COMPONENT Development
RUNTIME DESTINATION bin COMPONENT Runtime
PUBLIC_HEADER DESTINATION include COMPONENT Development
BUNDLE DESTINATION bin COMPONENT Runtime
)
include(CMakePackageConfigHelpers)
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/mylibConfig.cmake.in"
"${PROJECT_BINARY_DIR}/mylibConfig.cmake"
INSTALL_DESTINATION lib/cmake/mylib
)
install(EXPORT mylibTargets DESTINATION lib/cmake/mylib)
install(FILES "${PROJECT_BINARY_DIR}/mylibConfigVersion.cmake"
"${PROJECT_BINARY_DIR}/mylibConfig.cmake"
DESTINATION lib/cmake/mylib)
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include)
add_executable(mytest test/basic_checks.cpp)
target_link_libraries(mytest mylib)
the content of cmake/mylibConfig.cmake.in should only be this
#PACKAGE_INIT#
include("${CMAKE_CURRENT_LIST_DIR}/mylibTargets.cmake")
check_required_components("#PROJECT_NAME#")
if you do all of this, not only that it makes your header-only library 'installable', but it also makes it 'findable'. users will be able to import your library like so:
find_package(mylib CONFIG REQUIRED)
target_link_libraries(MyApp mylib) # installed include/ path automatically added
Just a small addition to https://stackoverflow.com/a/49143782/2119944
Probably cmake/mylibConfig.cmake.in should look like this:
#PACKAGE_INIT#
include("${CMAKE_INSTALL_PREFIX}/lib/cmake/mylib/mylibTargets.cmake")
check_required_components("#PROJECT_NAME#")
Otherwise it will point to local project dir.