Include headers with same name but from different libraries using CMake - c++

I have two (or more) libraries containing header files that have the same name (e.g., "point.h"). In my project(s), I need to use attributes from the classes defined in those headers in different contexts, but of course, is not possible to include the correct file in the common way, having the ambiguity of same name.
Many libraries solve this issue by "namespacing" their headers (e.g., "#include <rapidjson/document.h>"). I would like to do this. How can I configure CMake to achieve this? Is there a common (and possibly simple) way to do this?
Here is my CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project(GISManager LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(OSMManager REQUIRED) #First libray
find_package(GeoJSONParser REQUIRED) #Second library
set(GISManager_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src)
set(GISManager_H ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(Hdrs ${GISManager_H}/gismanager.h )
set(Srcs ${GISManager_SRC}/gismanager.cpp)
add_library(GISManager ${Hdrs} ${Srcs})
target_include_directories(GISManager PUBLIC
$<BUILD_INTERFACE:${GISManager_H}/>
${OSMManager_INCLUDE_DIRS}/
${GeoJSONParser_INCLUDE_DIRS}/
)
target_link_libraries(GISManager ${OSMManager_LIBRARIES} ${GeoJSONParser_LIBRARIES})
add_executable(GISManager_Test ${Hdrs} ${Srcs} ${GISManager_SRC}/main.cpp)
target_include_directories(GISManager_Test PUBLIC
$<BUILD_INTERFACE:${GISManager_H}/>
${OSMManager_INCLUDE_DIRS}/
${GeoJSONParser_INCLUDE_DIRS}/
)
target_link_libraries(GISManager_Test ${OSMManager_LIBRARIES} ${GeoJSONParser_LIBRARIES})
install(FILES ${Hdrs} DESTINATION include/GISManager-${version})
install(TARGETS GISManager
DESTINATION lib/GISManager-${version}
EXPORT GISManager-targets)
install(EXPORT GISManager-targets
DESTINATION lib/GISManager-${version})
configure_file(
${GISManager_SOURCE_DIR}/pkg/gismanager-config.cmake.in
${GISManager_BINARY_DIR}/pkg/gismanager-config.cmake #ONLY)
configure_file(
${GISManager_SOURCE_DIR}/pkg/gismanager-config-version.cmake.in
${GISManager_BINARY_DIR}/pkg/gismanager-config-version.cmake #ONLY)
install(FILES ${GISManager_BINARY_DIR}/pkg/gismanager-config.cmake
${GISManager_BINARY_DIR}/pkg/gismanager-config-version.cmake
DESTINATION lib/GISManager-${version})
The libraries are generated using the next two CMakeLists.txt files:
cmake_minimum_required(VERSION 3.5)
project(GeoJSONParser LANGUAGES CXX)
set(version 1.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(GeoJSONParser_H ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(GeoJSONParser_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src)
find_path(RapidJSON_INCLUDE_DIRS rapidjson)
set(HDRS
${GeoJSONParser_H}/object.h
${GeoJSONParser_H}/featurecollection.h
${GeoJSONParser_H}/feature.h
${GeoJSONParser_H}/geometry.h
${GeoJSONParser_H}/point.h
${GeoJSONParser_H}/multipoint.h
${GeoJSONParser_H}/linestring.h
${GeoJSONParser_H}/multilinestring.h
${GeoJSONParser_H}/polygon.h
${GeoJSONParser_H}/multipolygon.h
${GeoJSONParser_H}/geometrycollection.h
${GeoJSONParser_H}/utils.h
${GeoJSONParser_H}/types.h
)
set(SRCS
${GeoJSONParser_SRC}/featurecollection.cpp
${GeoJSONParser_SRC}/feature.cpp
${GeoJSONParser_SRC}/point.cpp
${GeoJSONParser_SRC}/multipoint.cpp
${GeoJSONParser_SRC}/linestring.cpp
${GeoJSONParser_SRC}/multilinestring.cpp
${GeoJSONParser_SRC}/polygon.cpp
${GeoJSONParser_SRC}/multipolygon.cpp
${GeoJSONParser_SRC}/geometrycollection.cpp
${GeoJSONParser_SRC}/utils.cpp
)
add_library(GeoJSONParser ${HDRS} ${SRCS})
target_include_directories(GeoJSONParser PUBLIC $<BUILD_INTERFACE:${GeoJSONParser_H}/> ${RapidJSON_INCLUDE_DIRS})
add_executable(GeoJSONParser_Test ${HDRS} ${SRCS} ${GeoJSONParser_SRC}/main.cpp)
target_include_directories(GeoJSONParser_Test PUBLIC $<BUILD_INTERFACE:${GeoJSONParser_H}/> ${RapidJSON_INCLUDE_DIRS})
install(FILES ${HDRS} DESTINATION include/GeoJSONParser-${version})
install(TARGETS GeoJSONParser
DESTINATION lib/GeoJSONParser-${version}
EXPORT GeoJSONParser-targets)
install(EXPORT GeoJSONParser-targets
DESTINATION lib/GeoJSONParser-${version})
configure_file(
${GeoJSONParser_SOURCE_DIR}/pkg/geojsonparser-config.cmake.in
${GeoJSONParser_BINARY_DIR}/pkg/geojsonparser-config.cmake #ONLY)
configure_file(
${GeoJSONParser_SOURCE_DIR}/pkg/geojsonparser-config-version.cmake.in
${GeoJSONParser_BINARY_DIR}/pkg/geojsonparser-config-version.cmake #ONLY)
install(FILES ${GeoJSONParser_BINARY_DIR}/pkg/geojsonparser-config.cmake
${GeoJSONParser_BINARY_DIR}/pkg/geojsonparser-config-version.cmake
DESTINATION lib/GeoJSONParser-${version})
cmake_minimum_required(VERSION 3.5)
project(OSMManager LANGUAGES CXX)
set(version 1.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(OSMManager_H ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(OSMManager_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src)
find_path(RapidJSON_INCLUDE_DIRS rapidjson)
find_package(PROJ)
set(HDRS
${OSMManager_H}/root.h
${OSMManager_H}/node.h
${OSMManager_H}/way.h
${OSMManager_H}/relation.h
${OSMManager_H}/coordinatesconverter.h
${OSMManager_H}/utils.h
${OSMManager_H}/types.h
${OSMManager_H}/tinyxml2.h
)
set(SRCS
${OSMManager_SRC}/root.cpp
${OSMManager_SRC}/node.cpp
${OSMManager_SRC}/way.cpp
${OSMManager_SRC}/relation.cpp
${OSMManager_SRC}/coordinatesconverter.cpp
${OSMManager_SRC}/tinyxml2.cpp
)
add_library(OSMManager ${HDRS} ${SRCS})
target_include_directories(OSMManager PUBLIC $<BUILD_INTERFACE:${OSMManager_H}/> ${RapidJSON_INCLUDE_DIRS} ${PROJ_INCLUDE_DIRS})
target_link_libraries(OSMManager ${PROJ_LIBRARIES})
add_executable(OSMManager_Test ${HDRS} ${SRCS} ${OSMManager_SRC}/main.cpp)
target_include_directories(OSMManager_Test PUBLIC $<BUILD_INTERFACE:${OSMManager_H}/> ${RapidJSON_INCLUDE_DIRS} ${PROJ_INCLUDE_DIRS})
target_link_libraries(OSMManager_Test ${PROJ_LIBRARIES})
install(FILES ${HDRS} DESTINATION include/OSMManager-${version})
install(TARGETS OSMManager
DESTINATION lib/OSMManager-${version}
EXPORT OSMManager-targets)
install(EXPORT OSMManager-targets
DESTINATION lib/OSMManager-${version})
configure_file(
${OSMManager_SOURCE_DIR}/pkg/osmmanager-config.cmake.in
${OSMManager_BINARY_DIR}/pkg/osmmanager-config.cmake #ONLY)
configure_file(
${OSMManager_SOURCE_DIR}/pkg/osmmanager-config-version.cmake.in
${OSMManager_BINARY_DIR}/pkg/osmmanager-config-version.cmake #ONLY)
install(FILES ${OSMManager_BINARY_DIR}/pkg/osmmanager-config.cmake
${OSMManager_BINARY_DIR}/pkg/osmmanager-config-version.cmake
DESTINATION lib/OSMManager-${version})
The files library-config.cmake.in have more or less the same structure in the two libraries, here is the one for GeoJSONParser:
# Compute installation prefix relative to this file.
get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(_prefix "${_dir}/../.." ABSOLUTE)
# Import the targets.
include("${_prefix}/lib/GeoJSONParser-#version#/GeoJSONParser-targets.cmake")
# Report other information.
set(GeoJSONParser_INCLUDE_DIRS "${_prefix}/include/GeoJSONParser-#version#/")
file(GLOB GeoJSONParser_LIBRARIES "${_prefix}/lib/GeoJSONParser-#version#/lib*")
As you can see above, the two libraries have both a "utils.h", including some functions that are specific to the library in question.

Just make it so that the include directories passed to the compiler are directories that contain such an intermediate directory that does the namespacing that you want to have for the #include directives.
You'll want to make it so for both the source tree and the install tree.
If you'd like an answer with config code, please provide a minimal, respresentative example setup.

Related

How cmake find_package automatically find all the dependencies?

I have two static libraries: Foo and Bar. Bar is a high level component which encapsulates Foo. Both libraries have Config.cmake file.
After installing the libraries on the
system (so they are at /usr/local/lib) I'm using find_package to find Bar but the problem is that I also have to link target with Foo.
Is there any way that Foo can be found and linked automatically when Bar is linked?
[CMakeLists.txt for Foo library]:
cmake_minimum_required(VERSION 3.12.0)
project(foo)
add_library(foo ${SOURCES})
target_include_directories(foo PUBLIC "include")
set(INCLUDE_INSTALL_DIR "include/foo/")
set(LIBRARY_INSTALL_DIR "lib/foo/")
set(INSTALL_DESTINATION_DIR "${LIBRARY_INSTALL_DIR}/cmake")
include(CMakePackageConfigHelpers)
configure_package_config_file(FooConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
INSTALL_DESTINATION ${INSTALL_DESTINATION_DIR}
PATH_VARS INCLUDE_INSTALL_DIR LIBRARY_INSTALL_DIR)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/FooConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion)
install(TARGETS foo DESTINATION ${LIBRARY_INSTALL_DIR})
install(FILES ${HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/foo)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/FooConfigVersion.cmake
DESTINATION ${INSTALL_DESTINATION_DIR})
[CMakeLists.txt for Bar library]:
cmake_minimum_required(VERSION 3.12.0)
project(bar)
add_library(bar ${SOURCES})
target_include_directories(bar PUBLIC "include")
target_link_libraries(bar PUBLIC foo)
set(INCLUDE_INSTALL_DIR "include/bar/")
set(LIBRARY_INSTALL_DIR "lib/bar/")
set(INSTALL_DESTINATION_DIR "${LIBRARY_INSTALL_DIR}/cmake")
include(CMakePackageConfigHelpers)
configure_package_config_file(BarConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/BarConfig.cmake
INSTALL_DESTINATION ${INSTALL_DESTINATION_DIR}
PATH_VARS INCLUDE_INSTALL_DIR LIBRARY_INSTALL_DIR)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/BarConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion)
install(TARGETS bar DESTINATION ${LIBRARY_INSTALL_DIR})
install(FILES ${HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/bar)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/BarConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/BarConfigVersion.cmake
DESTINATION ${INSTALL_DESTINATION_DIR})
[User's CMakeLists.txt]:
cmake_minimum_required(VERSION 3.12.0)
project(test)
find_package(bar CONFIG REQUIRED PATHS "/usr/local/lib/bar")
# I want the find_package above to find foo as well...
add_executable(test main.cpp)
target_include_directories(test PRIVATE ${BAR_INCLUDE_DIR})
target_link_directories(test PRIVATE ${BAR_LIBRARY_DIR})
# ...and link it here automatically.
target_link_libraries(test PUBLIC bar)
Add cmake "interface library" to the BarConfig.cmake.in file:
#PACKAGE_INIT#
set_and_check(BAR_INCLUDE_DIR "#PACKAGE_INCLUDE_INSTALL_DIR#")
set_and_check(BAR_LIBRARY_DIR "#PACKAGE_LIBRARY_INSTALL_DIR#")
check_required_components(bar)
add_library(barlib INTERFACE)
target_include_directories(barlib INTERFACE ${BAR_INCLUDE_DIR})
target_link_directories(barlib INTERFACE ${BAR_LIBRARY_DIR})
target_link_libraries(barlib INTERFACE bar foo)
It creates a new library "barlib" which does not contain it's own code, but it has the libraries "bar" and "foo" as dependencies.
User's CMakeLists.txt now may look like this:
cmake_minimum_required(VERSION 3.12.0)
project(test)
find_package(bar CONFIG REQUIRED PATHS "/usr/local/lib/bar")
add_executable(test main.cpp)
target_link_libraries(test PRIVATE barlib)
The "test" binary here inherits all the dependencies of the "barlib" library.

how do i distribute static library built using cmake

I have a cmake project which is a static library that i have to share with other teams in my company but just the .lib file and not the source code.
This library depends on Boost.
Now i build the library and transfer the install folder onto another pc (exe_pc) and use it in a exe project (this exe also depends on boost).
Now at compile time i get a linking error saying that E:/Development/vcpkg/installed/x64-windows/lib/boost_system-vc140-mt.lib cannot be opened.
so i open and check the generated LibTargets.cmake and it contains absolute paths of boost library for pc on which the library was built.
# The installation prefix configured by this project.
set(_IMPORT_PREFIX "C:/Program Files/cpp_licensing")
# Create imported target Licensing::liblicensing
add_library(Licensing::liblicensing STATIC IMPORTED)
set_target_properties(Licensing::liblicensing PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "_LICENSING_DEBUG=0"
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
INTERFACE_LINK_LIBRARIES "\$<\$<NOT:\$<CONFIG:DEBUG>>:E:/Development/vcpkg/installed/x64-windows/lib/boost_system-vc140-mt.lib>;\$<\$<CONFIG:DEBUG>:E:/Development/vcpkg/installed/x64-windows/debug/lib/boost_system-vc140-mt-gd.lib>;\$<\$<NOT:\$<CONFIG:DEBUG>>:E:/Development/vcpkg/installed/x64-windows/lib/boost_filesystem-vc140-mt.lib>;\$<\$<CONFIG:DEBUG>:E:/Development/vcpkg/installed/x64-windows/debug/lib/boost_filesystem-vc140-mt-gd.lib>;cryptopp-static;wbemuuid"
)
LibLicensingTargets-release.cmake
et_target_properties(Licensing::liblicensing PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "CXX"
IMPORTED_LOCATION_RELEASE "C:/Program Files/cpp_licensing/lib/licensing.lib"
)
list(APPEND _IMPORT_CHECK_TARGETS Licensing::liblicensing )
list(APPEND _IMPORT_CHECK_FILES_FOR_Licensing::liblicensing "C:/Program Files/cpp_licensing/lib/licensing.lib" )
The user is supposed to have boost installed, the only problem i have is that because absolute paths are used in targets file user has to manually change them on his pc in the targets file.
what changes do i make to cmake so that the libraries built folder can be shared. How do i distribute a library built using cmake ??
Here is the cmake for the library project
cmake_minimum_required(VERSION 3.10)
project(liblicensing)
message("~~ Project: " ${PROJECT_NAME})
set(LIB_NAME "${PROJECT_NAME}")
set(PROJECT_VERSION 1.0)
include_directories(
${PROJECT_SOURCE_DIR}/include
)
if (WIN32)
find_package(Boost REQUIRED system filesystem)
find_package(cryptopp REQUIRED)
find_package(unofficial-date REQUIRED)
include_directories(
${Boost_INCLUDE_DIR}
)
else()
find_package(date REQUIRED)
endif ()
file(GLOB_RECURSE HEADER_FILES include/*.h include/*.hpp)
file(GLOB_RECURSE SOURCES src/*.cpp)
add_library(${LIB_NAME} STATIC ${SOURCES} ${HEADER_FILES} )
set_target_properties(
${LIB_NAME}
PROPERTIES
OUTPUT_NAME "licensing"
POSITION_INDEPENDENT_CODE ON
CXX_STANDARD 14
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
LINKER_LANGUAGE CXX
)
if (MSVC)
set_target_properties(${LIB_NAME} PROPERTIES DEBUG_POSTFIX "_d")
endif()
add_library(Licensing::liblicensing ALIAS ${LIB_NAME})
set(INSTALLATION_DIR "${CMAKE_INSTALL_PREFIX}")
set(CMAKE_INSTALL_LIBDIR "${INSTALLATION_DIR}/lib")
set(CMAKE_INSTALL_BINDIR "${INSTALLATION_DIR}/bin")
set(CMAKE_INSTALL_INCLUDEDIR "include")
set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${LIB_NAME})
target_include_directories(${LIB_NAME}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
if (WIN32)
target_link_libraries(
${LIB_NAME}
${Boost_LIBRARIES}
cryptopp-static
wbemuuid
)
else()
target_link_libraries(
${LIB_NAME}
cryptopp
boost_system
boost_filesystem
)
endif ()
if(CMAKE_BUILD_TYPE MATCHES Debug)
target_compile_definitions(${LIB_NAME} PUBLIC _LICENSING_DEBUG=1)
elseif(CMAKE_BUILD_TYPE MATCHES Release)
target_compile_definitions(${LIB_NAME} PUBLIC _LICENSING_DEBUG=0)
endif()
# Expose Projects's public includes to other subprojects through cache variable.
set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
###############
# Installation
##
install(TARGETS ${LIB_NAME}
EXPORT LibLicensingTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
# INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${LIB_NAME}
FILES_MATCHING PATTERN "*.h*")
install(EXPORT LibLicensingTargets
FILE LibLicensingTargets.cmake
NAMESPACE Licensing::
DESTINATION ${INSTALL_CONFIGDIR}
)
#####################
# ConfigVersion file
##
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/LibLicensingConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
configure_package_config_file(
${CMAKE_CURRENT_LIST_DIR}/CMake/LibLicensingConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/LibLicensingConfig.cmake
INSTALL_DESTINATION ${INSTALL_CONFIGDIR}
)
## Install all the helper files
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/LibLicensingConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/LibLicensingConfigVersion.cmake
DESTINATION ${INSTALL_CONFIGDIR}
)
Not an entire answer but too much for a comment:
If you inspect your file LibLicensingTargets.cmake closely, you see that cryptopp-static and wbemuuid are not resolved down to the library level.
Looking at how you reference Boost you use the oldstyle version (${Boost_LIBRARIES}) that resolves down to the library level.
Try instead to use the targets provided by FindBoost.cmake module as follows:
if (WIN32)
target_link_libraries(
${LIB_NAME}
Boost::system
Boost::filesystem
cryptopp-static
wbemuuid
)
else()
target_link_libraries(
${LIB_NAME}
cryptopp
Boost::system
Boost::filesystem
)
endif()
Another thing that puzzles me is why you added the ALIAS:
add_library(Licensing::liblicensing ALIAS ${LIB_NAME})
This ALIAS is properly created in the LibLicensingTargets.cmake file, so you do not need to create it on your own.
Another thing is, that you are giving your INSTALL command absolute paths. To make the package entirely relocatable you need to use relative paths (relative to the CMAKE_INSTALL_PREFIX, the installation directory).
The following commands should therefore be changed to:
set(CMAKE_INSTALL_LIBDIR "lib")
set(CMAKE_INSTALL_BINDIR "bin")
set(CMAKE_INSTALL_INCLUDEDIR "include")
set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${LIB_NAME})
If you further add the line:
export(TARGETS liblicensing NAMESPACE LibLicensing:: FILE LibLicensingTargets.cmake)
to your CMakeLists.txt, outside projects are able to import target from your build tree folder as of liblicensing was installed therein. See the CMake documentation of the export command.

How to install a cmake package in a custom directory and link it with target_link_libraries() by name?

I have a custom library which exports include/, lib/ and lib/cmake/ which contains MyProjectConfig.cmake with the following contents:
set(MyProject_INCLUDE_DIRS "/home/.../cmake-build-debug/thirdparty/myproj/include")
set(MyProject_LIBRARY_DIRS "/home/.../cmake-build-debug/thirdparty/myproj/lib")
set(MyProject_LIBRARIES "MyProject")
message(STATUS "MyProject found. Headers: ${MyProject_INCLUDE_DIRS}")
I use it in another project like this:
include(ExternalProject)
ExternalProject_Add(MyProjectExternal
PREFIX "${CMAKE_BINARY_DIR}/external/myproject"
GIT_REPOSITORY "git#bitbucket.org:myproject.git"
GIT_TAG "master"
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/thirdparty/myproject
)
add_dependencies(${PROJECT_NAME} MyProjectExternal)
# prevent error on first project use when dir doesn't exist
if(EXISTS ${CMAKE_BINARY_DIR}/thirdparty/myproject/lib)
find_package(MyProject REQUIRED HINTS ${CMAKE_BINARY_DIR}/thirdparty/myproject/lib/cmake)
find_library(MyProject_LIB MyProject HINTS ${MyProject_LIBRARY_DIRS})
target_link_libraries(${PROJECT_NAME} PUBLIC ${MyProject_LIB})
target_include_directories(${PROJECT_NAME} PRIVATE ${MyProject_INCLUDE_DIRS})
endif()
I expected that the variables set in MyProjectConfig.cmake would be picked up automatically by cmake to find the library by name and this would work:
find_package(MyProject REQUIRED HINTS ${CMAKE_BINARY_DIR}/thirdparty/myproject/lib/cmake)
target_link_libraries(${PROJECT_NAME} PUBLIC MyProject)
But it doesn't:
[ 87%] Built target MyProjectExternal
[ 90%] Linking CXX executable RootProject
/usr/bin/ld: cannot find -lMyProject
collect2: error: ld returned 1 exit status
Part II (as requested here) The full code and the steps to reproduce the problem
Library Code (mylib branch)
CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(MyLib VERSION 1.0.0 LANGUAGES CXX)
add_library(${PROJECT_NAME} SHARED
src/mylib/hello.cpp
)
target_include_directories(${PROJECT_NAME}
PUBLIC $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 11
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
include(cmake/install.cmake)
cmake/install.cmake
include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
include(GenerateExportHeader)
generate_export_header(${PROJECT_NAME}
EXPORT_MACRO_NAME EXPORT
NO_EXPORT_MACRO_NAME NO_EXPORT
PREFIX_NAME MYLIB_
EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/include-exports/mylib/export.h)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include-exports>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
install(DIRECTORY ${CMAKE_BINARY_DIR}/include-exports/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
include(CMakePackageConfigHelpers)
set_property(TARGET ${PROJECT_NAME} PROPERTY VERSION ${PROJECT_VERSION})
set_property(TARGET ${PROJECT_NAME} PROPERTY SOVERSION ${PROJECT_VERSION_MAJOR})
set_property(TARGET ${PROJECT_NAME} PROPERTY INTERFACE_${PROJECT_NAME}_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY COMPATIBLE_INTERFACE_STRING ${PROJECT_VERSION_MAJOR})
write_basic_package_version_file(
"${CMAKE_BINARY_DIR}/CMakePackage/${PROJECT_NAME}ConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
export(EXPORT ${PROJECT_NAME}Targets
FILE "${CMAKE_BINARY_DIR}/CMakePackage/${PROJECT_NAME}.cmake"
)
SET(CONFIG_SOURCE_DIR ${CMAKE_SOURCE_DIR})
SET(CONFIG_DIR ${CMAKE_BINARY_DIR})
SET(${PROJECT_NAME}_INCLUDE_DIR "\${${PROJECT_NAME}_SOURCE_DIR}/include")
configure_package_config_file(${CMAKE_SOURCE_DIR}/cmake/Config.cmake.in
"${CMAKE_BINARY_DIR}/CMakePackage/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION lib/cmake/${PROJECT_NAME}
PATH_VARS ${PROJECT_NAME}_INCLUDE_DIR)
install(EXPORT ${PROJECT_NAME}Targets
FILE ${PROJECT_NAME}.cmake
DESTINATION lib/cmake/${PROJECT_NAME}
)
install(
FILES
"${CMAKE_BINARY_DIR}/CMakePackage/${PROJECT_NAME}Config.cmake"
"${CMAKE_BINARY_DIR}/CMakePackage/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION lib/cmake/${PROJECT_NAME}
COMPONENT Devel
)
cmake/Config.cmake.in
#PACKAGE_INIT#
set_and_check(#PROJECT_NAME#_INCLUDE_DIRS "#CMAKE_INSTALL_PREFIX#/#CMAKE_INSTALL_INCLUDEDIR#")
set_and_check(#PROJECT_NAME#_LIBRARY_DIRS "#CMAKE_INSTALL_PREFIX#/#CMAKE_INSTALL_LIBDIR#")
set(#PROJECT_NAME#_LIBRARIES "#PROJECT_NAME#")
check_required_components(#PROJECT_NAME#)
message(STATUS "#PROJECT_NAME# found. Headers: ${#PROJECT_NAME#_INCLUDE_DIRS}")
include/mylib/hello.h
#ifndef MYLIB_HELLO_H
#define MYLIB_HELLO_H
#include <mylib/export.h>
namespace mylib {
extern MYLIB_EXPORT void hello();
}
#endif
src/mylib/hello.cpp
#include <mylib/hello.h>
#include <iostream>
namespace mylib {
void hello() {
std::cout << "Hello, MyLib!" << std::endl;
}
}
Application Code (master branch)
CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(MyLibConsumer VERSION 1.0.0 LANGUAGES CXX)
add_executable(${PROJECT_NAME}
main.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 11
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
include(ExternalProject)
set(LIB_INSTALL_DIR ${CMAKE_BINARY_DIR}/thirdparty/mylib)
ExternalProject_Add(MyLibExternal
PREFIX "${CMAKE_BINARY_DIR}/external/mylib"
GIT_REPOSITORY "https://github.com/arteniioleg/stackoverflow-question-46772541.git"
GIT_TAG "mylib"
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIB_INSTALL_DIR}
)
add_dependencies(${PROJECT_NAME} MyLibExternal)
if(EXISTS ${LIB_INSTALL_DIR}/lib) # prevent error on first cmake load
find_package(MyLib REQUIRED HINTS ${LIB_INSTALL_DIR}/lib/cmake)
# fixme: make this work
target_link_libraries(${PROJECT_NAME} PUBLIC MyLib)
# to test: comment the above line and uncomment the below lines
#find_library(MyLib_LIB MyLib HINTS ${MyLib_LIBRARY_DIRS})
#target_link_libraries(${PROJECT_NAME} PRIVATE ${MyLib_LIB})
#target_include_directories(${PROJECT_NAME} PRIVATE ${MyLib_INCLUDE_DIRS})
endif()
main.cpp
#include <mylib/hello.h>
int main()
{
mylib::hello();
return 0;
}
The Error
main.cpp:1:25: fatal error: mylib/hello.h: No such file or directory
#include <mylib/hello.h>
^
Using the MyLib_* variables created by find_package() fixes the problem but it's too verbose.
As suggested here:
However, recommended way is to use CMake functionality for create configuration file. Such way, fully-fledged target will be created, and can be used for linking.
How to do that?
I want to use my library in 2 steps, like Qt:
find_package(Qt5Widgets)
target_link_libraries(myApp Qt5::Widgets)
Just include MyProject.cmake in MyProjectConfig.cmake (it is not included by default)
include(${CMAKE_CURRENT_LIST_DIR}/MyProject.cmake)
The final version of key files:
LIBRARY/cmake/Config.cmake.in
Removed redundant variables: *_INCLUDE_DIRS, *_LIBRARY_DIRS, *_LIBRARIES
#PACKAGE_INIT#
include(${CMAKE_CURRENT_LIST_DIR}/#PROJECT_NAME#.cmake)
check_required_components(#PROJECT_NAME#)
message(STATUS "#PROJECT_NAME# found.")
EXECUTABLE/CMakeLists.txt
Moved external projects before executable and return() if external projects are not built.
cmake_minimum_required(VERSION 3.8)
project(MyLibConsumer VERSION 1.0.0 LANGUAGES CXX)
set(LIB_INSTALL_DIR ${CMAKE_BINARY_DIR}/thirdparty/mylib)
include(ExternalProject)
ExternalProject_Add(MyLibExternal
PREFIX "${CMAKE_BINARY_DIR}/external/mylib"
GIT_REPOSITORY "https://github.com/arteniioleg/stackoverflow-question-46772541.git"
GIT_TAG "mylib"
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIB_INSTALL_DIR}
)
if(NOT EXISTS ${LIB_INSTALL_DIR}/lib)
# Happens on first CMake run.
# Can't continue because the below `find_package(REQUIRED)` will fail.
# Build all external project targets then rerun CMake and build the project target.
message(AUTHOR_WARNING "Build all external projects then reload cmake.")
return()
endif()
add_executable(${PROJECT_NAME} main.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 11
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
add_dependencies(${PROJECT_NAME} MyLibExternal)
find_package(MyLib REQUIRED HINTS ${LIB_INSTALL_DIR})
target_link_libraries(${PROJECT_NAME} PRIVATE MyLib)
I expected that the variables set in MyProjectConfig.cmake would be picked up automatically by cmake to find the library by name.
The most "magic" in find_package command is how it searches *Config.cmake script. After the script is found, it is simply executed in the context of the caller.
In you case, CMake sets variables MyProject_INCLUDE_DIRS, MyProject_LIBRARY_DIRS and MyProject_LIBRARIES. Nothing more. It doesn't create MyProject target and so.
If you want find_package() preparing linking with MyProject library, you need to write your MyProjectConfig.cmake script accordingly (e.g., call link_directories() from it).
However, recommended way is to use CMake functionality for create configuration file. Such way, fully-fledged target will be created, and can be used for linking. See cmake-packages documentation for more info.

Cmake shared library - include doesn't exist

I am trying to write a cmake file to create a shared library and use it in the executable file in my project. In the main I include heades as <pattern_follower/header_file_name.h>, but when I run make install it says pattern_follower/header_file_name.h does not exist.
cmake_minimum_required(VERSION 2.8)
project(pattern_follower)
add_definitions(-std=c++14)
file(GLOB SOURCES src/*)
file(GLOB HEADERS include/*)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_RELEASE} -Wall -O4 -march=native -Wfatal-errors")
find_package(OpenCV REQUIRED)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(${PROJECT_NAME} SHARED ${SOURCES})
target_link_libraries( ${PROJECT_NAME} ${OpenCV_LIBS})
add_executable(${PROJECT_NAME}-main main.cpp)
set_target_properties(${PROJECT_NAME}-main PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
target_link_libraries( ${PROJECT_NAME}-main ${PROJECT_NAME})
install(TARGETS ${PROJECT_NAME} DESTINATION lib)
install(TARGETS ${PROJECT_NAME}-main DESTINATION bin)
install(DIRECTORY include DESTINATION include/${PROJECT_NAME})

include_directories and CMAKE_INSTALL_FULL_INCLUDEDIR

I am building two C++ libraries(2 CMake projects). library2 depends on library1. I am installing my first library header files at ${CMAKE_INSTALL_INCLUDEDIR}/mylibraries. Now to include library1 header files in my library2, I am doing include_directories(${CMAKE_INSTALL_FULL_INCLUDEDIR}/mylibraries) in the library2's CMakeLists.txt.
But the make is failing with no such file or directory error.
library1.h no such file or directory from library2.cpp
library1's CmakeLists.txt
cmake_minimum_required (VERSION 3.5)
include(GNUInstallDirs)
project (logger)
set (VERSION_MAJOR 1)
set (VERSION_MINOR 0)
set (Umbrella "ferryfair")
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
configure_file(
"${PROJECT_SOURCE_DIR}/pkgconfig.pc.in"
"${PROJECT_BINARY_DIR}/pkgconfig.pc"
)
set (GCC_COVERAGE_COMPILE_FLAGS "-std=c++14")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
file(GLOB HEADERS *.h)
file(GLOB SOURCES *.cpp)
include_directories(${PROJECT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_INSTALL_FULL_INCLUDEDIR}/${Umbrella})
add_library(${PROJECT_NAME}Static STATIC ${SOURCES} ${HEADERS})
add_library(${PROJECT_NAME} SHARED ${SOURCES} ${HEADERS})
set_target_properties(${PROJECT_NAME}Static PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
set_target_properties(${PROJECT_NAME}
PROPERTIES
VERSION ${VERSION_MAJOR}.${VERSION_MINOR}
SOVERSION ${VERSION_MAJOR}
)
set_target_properties(${PROJECT_NAME}Static
PROPERTIES
VERSION ${VERSION_MAJOR}.${VERSION_MINOR}
SOVERSION ${VERSION_MAJOR}
)
target_link_libraries(${PROJECT_NAME}Static ferrybase)
target_link_libraries(${PROJECT_NAME} ferrybase)
install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(TARGETS ${PROJECT_NAME}Static DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${Umbrella})
install(FILES "${PROJECT_BINARY_DIR}/config.h"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${Umbrella}
RENAME ${PROJECT_NAME}Config.h)
install(FILES "${PROJECT_BINARY_DIR}/pkgconfig.pc"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
RENAME "${PROJECT_NAME}.pc")
install(FILES "${PROJECT_BINARY_DIR}/pkgconfig.pc"
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig
RENAME "${PROJECT_NAME}.pc")
library2's CMakeLists.txt
cmake_minimum_required (VERSION 3.5)
include(GNUInstallDirs)
project (FFJSON)
IF (DEFINED _DEBUG)
ADD_DEFINITIONS(-D_DEBUG=${_DEBUG})
ENDIF()
set (VERSION_MAJOR 1)
set (VERSION_MINOR 0)
set (Umbrella "ferryfair")
IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
# Mac OS X specific code
SET(macOS ON)
ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
configure_file(
"${PROJECT_SOURCE_DIR}/pkgconfig.pc.in"
"${PROJECT_BINARY_DIR}/pkgconfig.pc"
)
set (GCC_COVERAGE_COMPILE_FLAGS "-std=c++14")
set (GCC_COVERAGE_LINK_FLAGS "-Wl,-unresolved-symbols=ignore-in-shared-libs")
IF (DEFINED _DEBUG)
set (GCC_COVERAGE_COMPILE_FLAGS "${GCC_COVERAGE_COMPILE_FLAGS} -g -O0")
set (GCC_COVERAGE_LINK_FLAGS "${GCC_COVERAGE_LINK_FLAGS} -g -O0")
ENDIF()
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
file(GLOB HEADERS *.h)
file(GLOB SOURCES *.cpp)
include_directories(${PROJECT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_INSTALL_FULL_INCLUDEDIR}/${Umbrella})
add_library(${PROJECT_NAME}Static STATIC ${SOURCES} ${HEADERS})
add_library(${PROJECT_NAME} SHARED ${SOURCES} ${HEADERS})
set_target_properties(${PROJECT_NAME}Static PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
set_target_properties(${PROJECT_NAME}
PROPERTIES
VERSION ${VERSION_MAJOR}.${VERSION_MINOR}
SOVERSION ${VERSION_MAJOR}
)
set_target_properties(${PROJECT_NAME}Static
PROPERTIES
VERSION ${VERSION_MAJOR}.${VERSION_MINOR}
SOVERSION ${VERSION_MAJOR}
)
target_link_libraries(${PROJECT_NAME}Static logger ferrybase)
target_include_directories(TestFFJSON PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include/${Umbrella}>
)
add_executable(IteratorIncrement tests/iteratorincrement.cpp)
target_link_libraries(IteratorIncrement FFJSON logger ferrybase)
target_include_directories(IteratorIncrement PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include/${Umbrella}>
)
add_test(test1 TestFFJSON)
add_test(test2 IteratorIncrement)
ENDIF()
Its weird! ridiculous! pathetic!
executing cmake twice fixed the issue. Its an another entry into accidental discoveries.