how do i distribute static library built using cmake - c++

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.

Related

Link external library (steamapi) by path with cmake

I am trying to link Steam API with cmake.
I've tried Googling it, and there is close to nothing on steamapi cmake. In my case the Steam SDK is stored in C:/Users/user/Documents/Visual Studio 2022/Libraries/steam-sdk/.
This is my broken cmake:
# Project
cmake_minimum_required (VERSION 3.14)
project (MyProject)
#target_include_directories(${PROJECT_NAME} PUBLIC "C:/Users/user/Documents/Visual Studio 2022/Libraries/steam-sdk/public/steam/")
#target_link_directories(${PROJECT_NAME} PUBLIC "C:/Users/user/Documents/Visual Studio 2022/Libraries/steam-sdk/redistributable_bin/win64/")
#add_library(steamapi SHARED IMPORTED) # or STATIC instead of SHARED
#set_target_properties(bar PROPERTIES
# IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/lib/libbar.so"
# INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/include/libbar"
#)
find_library(
steamapi
#external_library_dependency
#external_library_name
PATHS "C:/Users/user/Documents/Visual Studio 2022/Libraries/steam-sdk/" # you can manually provide a path to search for your library if its not in the PATH
#PATH_SUFFIXES "/redistributable_bin/win64/" # possible subfolders inside the top directory of your library
PATH_SUFFIXES "/redistributable_bin" "/redistributable_bin/win64" # possible subfolders inside the top directory of your library
)
#find_library(steamapi "steam_api64.lib" PATHS "C:/Users/Rico/Documents/Visual Studio 2022/Libraries/steam-sdk/redistributable_bin/win64/")
#target_include_directories(${PROJECT_NAME} PUBLIC "C:/Users/Rico/Documents/Visual Studio 2022/Libraries/steam-sdk/public/steam/")
include_directories(${steamapi_INCLUDE_DIRS})
# Linking
add_executable(${PROJECT_NAME}
"src/Main.cpp"
)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
target_link_libraries(${PROJECT_NAME}
steamapi
)
How can I do this?
I would end up with a solution similar to CPM.cmake for hands-free installation
# get_steamapi.cmake (located in myproject/cmake)
set(STEAMAPI_ZIP_LOCATION "${CMAKE_BINARY_DIR}/cmake/steamworks_sdk.zip")
set(STEAMAPI_LOCATION "${CMAKE_BINARY_DIR}/cmake/steamworks_sdk")
set(STEAMAPI_BINARY_DIR "${STEAMAPI_LOCATION}/sdk/redistributable_bin/win64/steam_api64.lib")
set(STEAMAPI_SOURCE_DIR "${STEAMAPI_LOCATION}/sdk/public/steam")
set(STEAMAPI_SHARED_DIR "${STEAMAPI_LOCATION}/sdk/redistributable_bin/win64/steam_api64.dll")
#set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
if(NOT (EXISTS ${STEAMAPI_ZIP_LOCATION}))
message(STATUS "Downloading Steam SDK to ${STEAMAPI_ZIP_LOCATION}")
file(DOWNLOAD
https://partner.steamgames.com/downloads/steamworks_sdk.zip
${STEAMAPI_ZIP_LOCATION}
)
endif()
if(NOT(EXISTS ${STEAMAPI_LOCATION}))
message(STATUS "Unzipping Steam SDK to ${STEAMAPI_ZIP_LOCATION}")
file(ARCHIVE_EXTRACT INPUT ${STEAMAPI_ZIP_LOCATION} DESTINATION ${STEAMAPI_LOCATION})
endif()
#include(${STEAMAPI_ZIP_LOCATION})
The CMakeLists.txt
#CMakeLists.txt
# Project
cmake_minimum_required (VERSION 3.14)
project (MyProject)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
include(get_steamapi)
# Libraries
find_package(Threads REQUIRED)
# Linking
add_executable(${PROJECT_NAME}
"src/Main.cpp"
)
target_include_directories(${PROJECT_NAME}
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
PUBLIC ${STEAMAPI_SOURCE_DIR}
#PUBLIC "C:/Users/user/Documents/Visual Studio 2022/Libraries/steam-sdk/public/steam"
)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
target_link_libraries(${PROJECT_NAME}
PRIVATE Threads::Threads
PUBLIC ${STEAMAPI_BINARY_DIR}
#"C:/Users/user/Documents/Visual Studio 2022/Libraries/steam-sdk/redistributable_bin/win64/steam_api64.lib"
)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/steam_appid.txt
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
file(COPY ${STEAMAPI_SHARED_DIR}
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
)
The commented out portions also work but I think the method I came up with is pretty neat.

How does find_package(Boost) work with static and shared libraries?

I am currently working on a project that has multiple libraries and multiple executables. Some of the executables will link some of the libraries statically and others will be linked dynamically. My question is how to go about having the option to link a library statically or dynamically using find_package. I noticed boost has the option to set Boost_USE_STATIC_LIBS before calling find_package but I am not quite sure how Boost goes about using this. Does boost build both shared/static and then Boost_USE_STATIC_LIBS just picks the target? Would I strictly need to use MODULE mode or can I use CONFIG mode and what would be the benefit/costs of using either mode for this kind of behaviour?
My current approach uses config mode as all a library needs to do is just call rn_lib(<target_name>) and everything is set up for them. I am guessing the logic to pick the static vs shared target is done in the config/find file for the library.
Desired Behaviour
set(<lib_name>_USE_STATIC_LIBS TRUE)
find_package(<lib_name> REQUIRED)
Current CMakeLists.txt for libraries
function(rn_lib)
cmake_parse_arguments(RN_LIB
""
"NAME"
"SRCS;HEADERS"
${ARGN}
)
if (RN_LIB_NAME)
set(target_name ${RN_LIB_NAME})
else()
set(target_name ${PROJECT_NAME})
endif()
set(static_target_option ${target_name}_STATIC)
option(${static_target_option} "Force build as static library" OFF)
set(_config_file ${target_name}_config.h)
configure_file("cmake/${_config_file}.in" ${CMAKE_CURRENT_BINARY_DIR}/${_config_file})
if (${PROJECT_VERSION})
set(project_version ${PROJECT_VERSION})
else()
set(project_version 1.0)
endif()
if (static_target_option)
message("Build Static ${target_name}")
add_library(${target_name} STATIC
${RN_LIB_SRCS}
${RN_LIB_HEADERS}
)
else()
message("Build Shared ${target_name}")
# This could still produce a static library
add_library(${target_name}
${RN_LIB_SRCS}
${RN_LIB_HEADERS}
)
endif()
# setup lib variables
set(include_install_dir include)
set(include_install_abs ${RN_INSTALL_PATH}/${include_install_dir})
set(config_install_dir lib/cmake/${target_name})
set(generated_dir ${CMAKE_CURRENT_BINARY_DIR}/generated)
set(version_config ${generated_dir}/${target_name}ConfigVersion.cmake)
set(project_config ${generated_dir}/${target_name}Config.cmake)
set(package_namespace RustyNail::)
set(target_export_name ${target_name}Targets)
install(
FILES ${RN_LIB_HEADERS}
DESTINATION ${include_install_dir}/${base_name}
)
target_include_directories(
${target_name}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:${include_install_dir}>
)
write_basic_package_version_file(
${version_config}
VERSION ${project_version}
COMPATIBILITY AnyNewerVersion
)
configure_package_config_file(
${RN_CMAKE_PATH}/Config.cmake.in
${project_config}
INSTALL_DESTINATION ${config_install_dir}
)
install(
TARGETS ${target_name}
EXPORT ${target_export_name}
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION ${include_install_dir}
)
install(
FILES ${project_config} ${version_config}
DESTINATION ${config_install_dir}
)
install(
EXPORT ${target_export_name}
NAMESPACE ${package_namespace}
DESTINATION ${config_install_dir}
)
endfunction()
Project Tree
- < root>
- output dir
- lib
- bin
- build
- src
- lib
- lib1
- lib2
- apps
- app1
- app2

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 links only to executable

I have a problem that in my project I have multiple libraries who all link eachother differently.
And then there is one executable that links to only one library.
Now there are two problems:
1) The linked libraries are not showing up in the librarian of the static libraries.
2) the librarires are all showing up at the executable.
So here is my project order
Game|- Engine|--Core|---Graphics|----Libpng|---Platform
so Graphic links Libpng
Core links Graphics and Platform
Engine links Core
Game links Engine
Utilities is an interface library so headers only
what happens is that all Additional dependencies are at Game
The librarian of Core, Engine and Graphic containts no additional dependencies.
This is the Root CMake
cmake_minimum_required(VERSION 2.8.9)
set(CMAKE_GENERATOR "Visual Studio 14 2015")
project(EngineOnly)
set(CMAKE_SUPPRESS_REGENERATION true)
if(WIN32)
set(PLATFORM WIN32)
else()
set(PLATFORM LINUX)
endif (WIN32)
add_subdirectory(UTILITIES)
add_subdirectory(PLATFORM)
add_subdirectory(GRAPHICS)
add_subdirectory(CORE)
add_subdirectory(Engine)
add_subdirectory(Game)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Trinity.Game)
This is game
#UTILITIES LIB
include_directories(../UTILITIES/INCLUDE)
include_directories(../ENGINE/INCLUDE)
#SOURCE
file(GLOB SOURCES "SOURCE/*.cpp")
file(GLOB INCLUDES "INCLUDE/*.h")
#PROJECTNAME
set(PROJECTNAME "Trinity.Game")
#LINK ALL
add_executable(${PROJECTNAME} ${SOURCES} ${INCLUDES})
set_target_properties(${PROJECTNAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/../BIN/${PLATFORM}/DEBUG"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/../BIN/${PLATFORM}/RELEASE"
)
target_link_libraries(${PROJECTNAME}
LINK_PRIVATE Trinity.Engine
)
Engine
#UTILITIES LIB
include_directories(../UTILITIES/INCLUDE)
include_directories(../CORE/INCLUDE)
#SOURCE
file(GLOB SOURCES "SOURCE/*.cpp")
file(GLOB INCLUDES "INCLUDE/*.h")
#PROJECTNAME
set(PROJECTNAME "Trinity.Engine")
#LINK ALL
add_library(${PROJECTNAME} ${SOURCES} ${INCLUDES})
set_target_properties(${PROJECTNAME} PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_BINARY_DIR}/LIB/${PLATFORM}/DEBUG"
ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_BINARY_DIR}/LIB/${PLATFORM}/RELEASE"
)
target_link_libraries(${PROJECTNAME}
LINK_PRIVATE Trinity.Core
)
Core
#UTILITIES LIB
include_directories(../UTILITIES/INCLUDE)
include_directories(../GRAPHICS/INCLUDE)
include_directories(../PLATFORM/INCLUDE)
#SOURCE
file(GLOB SOURCES "SOURCE/*.cpp")
file(GLOB INCLUDES "INCLUDE/*.h")
#PROJECTNAME
set(PROJECTNAME "Trinity.Core")
#LINK ALL
add_library(${PROJECTNAME} ${SOURCES} ${INCLUDES})
set_target_properties(${PROJECTNAME} PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_BINARY_DIR}/LIB/${PLATFORM}/DEBUG"
ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_BINARY_DIR}/LIB/${PLATFORM}/RELEASE"
)
target_link_libraries(${PROJECTNAME}
LINK_PRIVATE Trinity.Graphics
LINK_PRIVATE Trinity.Platform
)
The other look the same.
I've tried PRIVATE instead of LINK_PRIVATE but the result is the same.
You can't link static libraries against each other and it is normal that they only showing up at the executable. Does your code not compiling?
For more information about static and dynamic libraries I suggest this article.
If you want to change the library type in cmake specify the add_lib command:
add_library(${LIB_NAME} ${LIB_TYPE} ${SOURCE})
# with ${LIB_TYPE} = STATIC or SHARED
more informations about cmake
If you have more problems or my answer answer to you is not enough please specify your question.

including external libraries in cmakelists.txt file

I'm using cmake to build an executable to run on an Intel Galileo board.
My question is how do I include the external mraa library in the build process.
Mraa library
When I download the library from git (1) do I need to build it as described here
Mraa compiling instructions
What do I need to put in my CMakeLists.txt file to pick up the library?
This is what I have thus far in my CMakeLists.txt file but I believe it is incorrect.
### MRAA ###
add_subdirectory(mraa-master/src)
file(GLOB mraa_SRC
"mraa-master/src/*.c"
)
include_directories( "${PROJECT_SOURCE_DIR}/mraa-master/include" )
add_library( ${MRAA_LIBRARY_NAME} SHARED ${mraa_SRC} )
Thank you
cmake_minimum_required(VERSION 2.8)
MESSAGE( STATUS "Starting build process")
SET( CMAKE_VERBOSE_MAKEFILE on )
if (CMAKE_BUILD_TYPE EQUAL "Debug")
MESSAGE(STATUS "Building in debug mode")
elseif (CMAKE_BUILD_TYPE EQUAL "Release")
MESSAGE(STATUS "Building in release mode")
endif()
SET(PROJECT_NAME "TestProject")
SET(APPLICATION_NAME "TestApplication")
SET(SAFE_STRING_LIBRARY "SafeString")
SET(APPLICATION_LIBRARY "Applibrary")
PROJECT( ${PROJECT_NAME} )
MESSAGE( STATUS "PROJECT: " ${PROJECT_NAME} )
SET(WRSDK_PATH "$ENV{WINDRIVER_SDK_DIR}")
IF (WRSDK_PATH)
else()
SET(WRSDK_PATH /opt/windriver/wrlinux/5.0-intel-quark/)
endif()
SET(APPLIBRARY_NAME ${APPLICATION_LIBRARY} )
SET(SAFE_STRING_LIBRARY_NAME ${SAFE_STRING_LIBRARY} )
### SAFE STRING ###
add_subdirectory(SafeStringStaticLibrary/safeclib)
file(GLOB safestring_SRC
"SafeStringStaticLibrary/safeclib/*.c"
)
include_directories( "${PROJECT_SOURCE_DIR}/SafeStringStaticLibrary /safeclib" )
add_library( ${SAFE_STRING_LIBRARY_NAME} SHARED ${safestring_SRC} )
include(ExternalProject)
ExternalProject_Add(mraa
GIT_REPOSITORY https://github.com/intel-iot-devkit/mraa.git
GIT_TAG v0.8.0
UPDATE_COMMAND ""
INSTALL_COMMAND "" )
file(GLOB app_SRC
"classes/*.cpp"
"Logger.cpp"
"sqlite3.c"
"shell.c"
)
add_library( ${APPLIBRARY_NAME} SHARED ${app_SRC})
include_directories( "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/SafeStringStaticLibrary/include" )
add_executable( ${APPLICATION_NAME} ${APPLICATION_NAME}.cpp
include_directories( "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/SafeStringStaticLibrary/include" "${CMAKE_SOURCE_DIR}/classes")
TARGET_LINK_LIBRARIES( ${APPLICATION_NAME} ${APPLIBRARY_NAME} ${SAFE_STRING_LIBRARY_NAME} -lrt -lpthread -lgcov -ldl)
The simplest way for build your project alongside with 3d party project is add_subdirectory() that subproject. Approach below implies that you (manually) download(git clone) sources of mraa project into mraa-lib subdirectory of your project's sources.
CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
MESSAGE( STATUS "Starting build process")
# Configure subproject before definition of variables for main project.
#
# Subproject defines *mraa* library target.
add_subdirectory(mraa-lib)
# Before installation, public mraa headers are contained under *api* subdirectory.
include_directories(mraa-lib/api)
... # create executable ${APPLICATION_NAME}
# Linking with mraa library is straightforward.
target_link_libraries(${APPLICATION_NAME} mraa)
That way mraa subproject will be configured, built and installed alongside with your project. So, if you cross-compile your project(using toolchain file, or inside IDE), the subproject will also be cross-compiled.