pkg_check_modules from FindPkgConfig gives MYLIBRARY_LDFLAGS and MYLIBRARY_CFLAGS that are ordinary CMake lists (with semicolon-separator).
The set_target_properties and set_property accept just one string.
So that doesn't work because it doesn't expand the list:
set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY LINK_FLAGS ${MYLIBRARY_LDFLAGS})
This gives same thing with the semicolons inside:
set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY LINK_FLAGS "${MYLIBRARY_LDFLAGS}")
set_target_properties expands into multiple strings when unquoted and into one string with semicolons when quoted.
How am I supposed to use that?
Common workflow with libraries searched by pkg-config within CMake:
# Use pkg-config for search library `xxx`.
pkg_check_modules(XXX xxx)
# Build target which uses given library
# The only command which has no target-oriented equivalent.
# But see comments after the code.
link_directories(${XXX_LIBRARY_DIRS})
# Two global commands belows can be replaced by target-oriented equivalent
# after creation of the target.
include_directories(${XXX_INCLUDE_DIRS})
add_compile_options(${XXX_CFLAGS_OTHER})
# Create target
add_executable(my_exe ...) # Or add_library()
# The only target-oriented command, which has no global equivalent.
target_link_libraries(my_exe ${XXX_LDFLAGS_OTHER}) # It is OK to have link flags here
target_link_libraries(my_exe ${XXX_LIBRARIES}) # Can be combined with previous call.
Note, that we use XXX_LDFLAGS_OTHER variable instead of XXX_LDFLAGS one. This is because XXX_LDFLAGS includes -l and -L options for which CMake has more suitable commands. Similar reason about usage of XXX_CFLAGS_OTHER.
Currently, CMake doesn't recommend to use link_directories command but use absolute paths to libraries in target_link_libraries call. One can extract absolute paths to libraries, listed by pkg-config, with use of find_library command:
...
# Instead of using `link_directories`, collect absolute paths to libraries.
set(XXX_LIBS_ABSOLUTE)
foreach(lib ${XXX_LIBRARIES})
# Choose name of variable, which will contain result of `find_library`
# for specific library.
set(var_name XXX_${lib}_ABS)
# Search library under dirs, returned by pkg-config.
find_library(${var_name} ${lib} ${XXX_LIBRARY_DIR})
list(APPEND XXX_LIBS_ABSOLUTE ${${var_name}})
endforeach()
# Instead of `target_link_libraries(my_exe ${XXX_LIBRARIES})`
target_link_libraries(my_exe ${XXX_LIBS_ABSOLUTE})
I'm not sure if this is how it is intended to be used, but this should work:
string(REPLACE ";" " " MYLIBRARY_LDFLAGS "${MYLIBRARY_LDFLAGS}")
set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY LINK_FLAGS " ${MYLIBRARY_LDFLAGS}")
The string(REPLACE) command will change the list in a string separated by spaces, and that you can therefore use for the LINK_FLAGS property
The prepended white space in " ${MYLIBRARY_LDFLAGS}" and the APPEND_STRING instead of APPEND will ensure that the resulting string is usable as LINK_FLAGS even if the property is not empty.
Related
I have a cpp file from a program that I want to open separately from the whole file structure. I need to do that in order to use this cpp file in ros. I have the header files included but I need to include the source files as well if I am correct.
my cpp file is called open_camera.cpp and includes a header file /usr/include/ids_peak-1.3.0/peak/backend/peak_backend.h
the peak_backend.h file contains declarations like this:
PEAK_C_API PEAK_Library_GetLastError(
PEAK_RETURN_CODE* lastErrorCode, char* lastErrorDescription, size_t* lastErrorDescriptionSize);
My Cmake File looks like this:
cmake_minimum_required(VERSION 3.0.2)
project(ros_package)
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
)
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES ros_package
# CATKIN_DEPENDS roscpp rospy std_msgs
# DEPENDS system_lib
)
###########
## Build ##
###########
## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
${catkin_INCLUDE_DIRS}
)
add_executable(open_camera_node src/open_camera.cpp)
#############
## Install ##
#############
include_directories(/usr/include/ids_peak-1.3.0)
if I run catkin_make I get errors like:
/usr/bin/ld: CMakeFiles/open_camera_node.dir/src/open_camera.cpp.o: in function `void peak::core::ExecuteAndMapReturnCodes<(anonymous namespace)::CallAndCheckCInterfaceFunction(std::function<int ()> const&)::{lambda()#1}>((anonymous namespace)::CallAndCheckCInterfaceFunction(std::function<int ()> const&)::{lambda()#1} const&)':
open_camera.cpp:(.text+0x516): undefined reference to `PEAK_Library_GetLastError'
From my understanding the problem is that I need to link the source files for the header. How can I do that and where do I find the source files for my headers? I searched for quiet some time but could not locate them.
The open_camera.cpp has its own CMake file looking like this:
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
project ("open_camera_cpp")
message (STATUS "[${PROJECT_NAME}] Processing ${CMAKE_CURRENT_LIST_FILE}")
set (SAMPLE_TARGET_NAME ${PROJECT_NAME})
set (CMAKE_SCRIPTS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../_cmake_scripts" CACHE STRING "The path of the cmake scripts directory.")
set (SAMPLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/output/bin")
include (${CMAKE_SCRIPTS_PATH}/cmake_detect_architecture.cmake)
detect_target_architecture (ARCH)
add_executable (${SAMPLE_TARGET_NAME}
open_camera.cpp
)
set (LIBRARY_NAME_VISION_API "ids_peak")
string (TOUPPER ${LIBRARY_NAME_VISION_API} LIBRARY_NAME_UPPER_VISION_API)
if (NOT TARGET ids_peak)
file (TO_CMAKE_PATH "$ENV{IDS_PEAK_SDK_PATH}/api" ${LIBRARY_NAME_UPPER_VISION_API}_PACKAGE_DIR)
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${${LIBRARY_NAME_UPPER_VISION_API}_PACKAGE_DIR}/cmake_finder")
message (STATUS "[${PROJECT_NAME}] Will find IDS peak API library.. CMAKE_MODULE_PATH: ${CMAKE_MODULE_PATH}")
find_package (ids_peak REQUIRED)
endif ()
target_include_directories (${SAMPLE_TARGET_NAME}
PRIVATE ${${LIBRARY_NAME_UPPER_VISION_API}_INCLUDE_DIR}
)
find_package (Threads REQUIRED)
target_link_libraries (${SAMPLE_TARGET_NAME}
ids_peak
${CMAKE_THREAD_LIBS_INIT}
)
if ((CMAKE_CXX_COMPILER_ID MATCHES "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
target_link_libraries (${SAMPLE_TARGET_NAME}
atomic
)
endif ()
# Set output directories for all configuration types (Debug, Release, etc.)
if (NOT CMAKE_BUILD_TYPE)
set (CMAKE_BUILD_TYPE "Debug")
endif()
if (NOT CMAKE_CONFIGURATION_TYPES)
set (CMAKE_CONFIGURATION_TYPES ${CMAKE_BUILD_TYPE})
endif ()
if (CMAKE_CONFIGURATION_TYPES)
foreach (CONFIGURATION_TYPE ${CMAKE_CONFIGURATION_TYPES})
string (TOUPPER ${CONFIGURATION_TYPE} CONFIGURATION_TYPE_UPPER)
if (CONFIGURATION_TYPE_UPPER STREQUAL "RELEASE")
set (SAMPLE_RUNTIME_OUTPUT_NAME ${SAMPLE_TARGET_NAME})
set (SAMPLE_RUNTIME_OUTPUT_DIRECTORY ${SAMPLE_OUTPUT_PATH}/${ARCH})
else ()
string (TOLOWER ${CONFIGURATION_TYPE} CONFIGURATION_TYPE_LOWER)
set (SAMPLE_RUNTIME_OUTPUT_NAME "${SAMPLE_TARGET_NAME}_${CONFIGURATION_TYPE_LOWER}")
set (SAMPLE_RUNTIME_OUTPUT_DIRECTORY ${SAMPLE_OUTPUT_PATH}/${ARCH}/${CONFIGURATION_TYPE})
endif ()
set_target_properties (${SAMPLE_TARGET_NAME} PROPERTIES
RUNTIME_OUTPUT_NAME_${CONFIGURATION_TYPE_UPPER} ${SAMPLE_RUNTIME_OUTPUT_NAME}
RUNTIME_OUTPUT_DIRECTORY_${CONFIGURATION_TYPE_UPPER} ${SAMPLE_RUNTIME_OUTPUT_DIRECTORY}
)
message (STATUS "[${PROJECT_NAME}] Cfg ${CONFIGURATION_TYPE} -> Output directory: ${SAMPLE_RUNTIME_OUTPUT_DIRECTORY}, Output name: ${SAMPLE_RUNTIME_OUTPUT_NAME}")
endforeach ()
endif ()
set_target_properties(${SAMPLE_TARGET_NAME} PROPERTIES
CXX_STANDARD 14
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS NO
)
if (MSVC)
target_compile_options (${SAMPLE_TARGET_NAME}
PRIVATE "/bigobj"
PRIVATE "/MP"
)
endif ()
GET_PROPERTY(${LIBRARY_NAME_UPPER_VISION_API}_LIBRARIES_COPIED_LOCAL GLOBAL PROPERTY ${LIBRARY_NAME_UPPER_VISION_API}_LIBRARIES_COPIED)
if(NOT ${LIBRARY_NAME_UPPER_VISION_API}_LIBRARIES_COPIED_LOCAL)
file (GLOB ids_peak_LIBS
"${${LIBRARY_NAME_UPPER_VISION_API}_LIBRARY_DIR}/*${CMAKE_SHARED_LIBRARY_SUFFIX}"
)
foreach (ids_peak_LIBRARY ${ids_peak_LIBS})
message (STATUS "[${PROJECT_NAME}] Add PostBuildStep for copy of ${ids_peak_LIBRARY}.")
add_custom_command (TARGET ${SAMPLE_TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${ids_peak_LIBRARY}
$<TARGET_FILE_DIR:${SAMPLE_TARGET_NAME}>
COMMENT "Post build copy of ${ids_peak_LIBRARY} to output dir." VERBATIM
)
endforeach ()
SET_PROPERTY(GLOBAL PROPERTY ${LIBRARY_NAME_UPPER_VISION_API}_LIBRARIES_COPIED ON)
endif()
# For Unix Build we need the environment variable GENICAM_GENTL32_PATH respectivily GENICAM_GENTL64_PATH to find the GenTL producer libraries.
# To set these environment variables a shell script is used which can be found in the samples root folder in _cmake_scripts.
# To run the samples run this script not the binary.
if (UNIX)
string (TOLOWER ${CMAKE_BUILD_TYPE} CONFIGURATION_TYPE_LOWER)
if(${CONFIGURATION_TYPE_LOWER} STREQUAL "release")
set(VSSL_SAMPLE_BINARY_NAME ${PROJECT_NAME})
else()
set(VSSL_SAMPLE_BINARY_NAME ${PROJECT_NAME}_${CONFIGURATION_TYPE_LOWER})
endif()
configure_file(${CMAKE_SCRIPTS_PATH}/sample_starter.in ${CMAKE_CURRENT_BINARY_DIR}/${VSSL_SAMPLE_BINARY_NAME}.sh)
file(COPY ${CMAKE_CURRENT_BINARY_DIR}/${VSSL_SAMPLE_BINARY_NAME}.sh
DESTINATION ${SAMPLE_RUNTIME_OUTPUT_DIRECTORY}
FILE_PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
)
endif (UNIX)
I don't understand much of the original cmake file since I am quiet new to the topic.
The path of the cpp file is: /usr/local/src/ids/samples/peak/cpp/open_camera/open_camera.cpp
According to CMake documentation we shouldn't use the global settings for include directories or such anymore, but use the target_ versions. And to be honest, I don't think, that the second, complicated CMakeLists.txt is used (or needed), it doesn't seem to be included with the first one (but it is hard to say without knowing the directory structure.
But, never the less, if you want to use some library, you need two things: the header file(s) with the declaration of the provided items and usually the compiled library containing the definition/implementation of the items (static or dynamic library). In principle, you can also compile the library on your own, if you have access to the libraries source files. In this case my suggestion would be:
add_library(ids_peak
${IDS_PEAK_SOURCE_FILES}
)
target_include_directories(ids_peak PUBLIC /usr/include/ids_peak-1.3.0)
...
add_executable(open_camera
src/open_camera.cpp
)
target_include_directories(open_camera PRIVATE ${catkin_INCLUDE_DIRS})
target_link_libraries(open_camera PRIVATE ids_peak)
This will define two targets to compile:
a library target, compiling any source files which are in the list ${ID_PEAK_SOURCE_FILES} and with the corresponding include directories attached
an executable target, compiling your open_camera.cpp source file. There is this catkin include directory attached (perhaps we should opt for an other library target here? Are there sources to compile or is there only a lib+headers?). Last but not least a dependency is added to this target.
Since the include directories of the library target are declared public, they are forwarded to all targets, that depend on it (same happens with target_compile_definitions, target_link_libraries, target_link_options, etc.).
These links could be of interest to you:
https://cmake.org/cmake/help/latest/command/add_library.html
https://cmake.org/cmake/help/latest/command/add_executable.html
https://cmake.org/cmake/help/latest/command/target_include_directories.html
https://cmake.org/cmake/help/latest/command/target_link_libraries.html
What do linkers do?
And, if you'd be so kind as to drop the FILE(GLOB...) call. I was told by some CMake contributor once, that this feature wasn't released, but escaped and shouldn't be used at all being pretty error prone. I know it comes in handy, but you can't really control, what your build is really doing. It is better to name the files explicitly. Or, in case of install (https://cmake.org/cmake/help/latest/command/install.html#directory) or copy, you can apply to whole directories.
Either this is really easy and I'm just not able to find the correct way to do it, or I've wildly misunderstood something. I'm attempting to add a conditional to a CMakeLists.txt file to include the proper .lib file depending on which build configuration type is being used (within visual studio at the moment). So for example, if configuration in visual studio is set to Debug then use file zlibstaticd.lib vs zlibstatic.lib. Below is what I have that's not working:
add_library(ZLIB_LIBRARY OBJECT IMPORTED)
# zlib added via assimp, and I can't get CMAKE_DEBUG_POSTFIX value to overwrite (because it's set within zlibs cmake file when using MSVC)
# so we have to do this check
if($<CONFIG:Debug>)
set_target_properties(ZLIB_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/zlibstaticd.lib)
else()
set_target_properties(ZLIB_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/zlibstatic.lib)
endif()
I've also tried CMAKE_BUILD_TYPE but it's always an empty string. Below is my entire CMakeLists.txt file so you can see what it is I'm doing (building a singular static library which contains many other static libraries):
cmake_minimum_required(VERSION 3.20.0)
# Define our project name
set(PROJECT_NAME myProjectName)
project(${PROJECT_NAME})
# Make sure binary directory is not the same as source directory
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(
FATAL_ERROR
"In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there."
)
endif()
# This Project Depends on External Project(s)
include(ExternalProject)
set(libGLFW glfw)
ExternalProject_Add(${libGLFW}
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLFW}
GIT_REPOSITORY https://github.com/glfw/glfw.git
GIT_TAG 3.3.4
GIT_SHALLOW ON
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLFW}/install
-DGLFW_BUILD_DOCS:BOOL=OFF
-DGLFW_BUILD_EXAMPLES:BOOL=OFF
-DGLFW_BUILD_TESTS:BOOL=OFF
)
set(libGLAD glad)
ExternalProject_Add(${libGLAD}
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLAD}
GIT_REPOSITORY https://github.com/Dav1dde/glad.git
GIT_TAG origin/master
GIT_SHALLOW ON
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLAD}/install
-DGLAD_INSTALL:BOOL=ON
-DGLAD_PROFILE:STRING="core"
-DGLAD_ALL_EXTENSIONS:BOOL=ON
-DUSE_MSVC_RUNTIME_LIBRARY_DLL:BOOL=OFF
)
set(libGLM glm)
ExternalProject_Add(${libGLM}
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLM}
GIT_REPOSITORY https://github.com/g-truc/glm.git
GIT_TAG origin/master
GIT_SHALLOW ON
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLM}/install
-DBUILD_SHARED_LIBS:BOOL=OFF
-DBUILD_STATIC_LIBS:BOOL=OFF
-DGLM_TEST_ENABLE:BOOL=OFF
)
set(libAssimp assimp)
ExternalProject_Add(${libAssimp}
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dep/${libAssimp}
GIT_REPOSITORY https://github.com/assimp/assimp.git
GIT_TAG v5.0.1
GIT_SHALLOW ON
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/dep/${libAssimp}/install
-DASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT:BOOL=OFF
-DASSIMP_BUILD_ALL_EXPORTERS_BY_DEFAULT:BOOL=OFF
-DBUILD_SHARED_LIBS:BOOL=OFF
-DASSIMP_BUILD_ASSIMP_TOOLS:BOOL=OFF
-DASSIMP_BUILD_TESTS:BOOL=OFF
-DASSIMP_BUILD_FBX_IMPORTER:BOOL=ON
-DASSIMP_BUILD_OBJ_IMPORTER:BOOL=ON
-DASSIMP_BUILD_OBJ_EXPORTER:BOOL=ON
-DASSIMP_LIBRARY_SUFFIX:STRING=
-DLIBRARY_SUFFIX:STRING=
-DCMAKE_DEBUG_POSTFIX:STRING=
-DASSIMP_INJECT_DEBUG_POSTFIX:BOOL=OFF
)
# Note set_target_properties will need conditionals for windows/linux since extensions differ
# Create the oject files we will join together to create our singular static library, using the projects
# that were previously added above via ExternalProject_Add
# INSTALL_DIR not being set to value of CMAKE_INSTALL_PREFIX, so manuallysetting
#ExternalProject_Get_Property(${libGLFW} INSTALL_DIR)
# SETUP GLFW
set(GLFW_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLFW}/install)
add_library(GLFW_LIBRARY OBJECT IMPORTED)
set_target_properties(GLFW_LIBRARY PROPERTIES IMPORTED_OBJECTS ${GLFW_INSTALL_DIR}/lib/glfw3.lib)
# SETUP GLAD
set(GLAD_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLAD}/install)
add_library(GLAD_LIBRARY OBJECT IMPORTED)
set_target_properties(GLAD_LIBRARY PROPERTIES IMPORTED_OBJECTS ${GLAD_INSTALL_DIR}/lib/glad.lib)
# SETUP GLM
# GLM is header only library, so we simply include it's include directory in target_include_directories below
set(GLM_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/dep/${libGLM}/install)
# SETUP ASSIMP and it's dependencies
set(ASSIMP_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/dep/${libAssimp}/install)
add_library(ASSIMP_LIBRARY OBJECT IMPORTED)
set_target_properties(ASSIMP_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/assimp.lib)
add_library(IRRXML_LIBRARY OBJECT IMPORTED)
set_target_properties(IRRXML_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/IrrXML.lib)
add_library(ZLIB_LIBRARY OBJECT IMPORTED)
# zlib added via assimp, and I can't get CMAKE_DEBUG_POSTFIX value to overwrite (because it's set within zlibs cmake file when using MSVC)
# so we have to do this check
if($<CONFIG:Debug>)
set_target_properties(ZLIB_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/zlibstaticd.lib)
else()
set_target_properties(ZLIB_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/zlibstatic.lib)
endif()
# Documentation states not to do this, but do it anyway for the time being since it prevents us from having
# to manually list all project files
file(GLOB_RECURSE headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/inc/*.h")
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# Create a single .lib file containing our compiled objects, and the compiled objects of all other dependencies
add_library(${PROJECT_NAME} STATIC ${headers} ${sources}
$<TARGET_OBJECTS:GLFW_LIBRARY>
$<TARGET_OBJECTS:GLAD_LIBRARY>
$<TARGET_OBJECTS:ASSIMP_LIBRARY>
$<TARGET_OBJECTS:IRRXML_LIBRARY>
$<TARGET_OBJECTS:ZLIB_LIBRARY>
)
# Add all include file paths
target_include_directories(${PROJECT_NAME}
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc
PUBLIC ${GLFW_INSTALL_DIR}/include
PUBLIC ${GLAD_INSTALL_DIR}/include
PUBLIC ${GLM_INSTALL_DIR}/include
PUBLIC ${ASSIMP_INSTALL_DIR}/include
)
# Specify the order in which libs depend on each other, use the name of the ExternalProject, not the name of the
# library object you create and use with add_library
add_dependencies(${PROJECT_NAME} ${libGLFW} ${libGLAD} ${libGLM} ${libAssimp})
add_library(ZLIB_LIBRARY OBJECT IMPORTED)
# zlib added via assimp, and I can't get CMAKE_DEBUG_POSTFIX value to overwrite (because it's set within zlibs cmake file when using MSVC)
# so we have to do this check
if($<CONFIG:Debug>)
set_target_properties(ZLIB_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/zlibstaticd.lib)
else()
set_target_properties(ZLIB_LIBRARY PROPERTIES IMPORTED_OBJECTS ${ASSIMP_INSTALL_DIR}/lib/zlibstatic.lib)
endif()
Generator expressions are evaluated after the configuration step has run, so they're just literal strings when the if() statement sees them. Basically, the CMake configure step is meta-programming a declarative language of targets and generator expressions that gets compiled into Ninja build files (or whatever) by the generator.
You can set the library up as follows:
add_library(zlib OBJECT IMPORTED)
set_target_properties(
zlib
PROPERTIES
IMPORTED_OBJECTS_RELEASE "${ASSIMP_INSTALL_DIR}/lib/zlibstatic.lib"
IMPORTED_OBJECTS_DEBUG "${ASSIMP_INSTALL_DIR}/lib/zlibstaticd.lib"
)
CMake first tries IMPORTED_OBJECTS_$<CONFIG> before trying IMPORTED_OBJECTS when resolving a library path.
All that said, I have to wonder why you don't just use find_package, vcpkg, Conan, or maybe add_subdirectory / FetchContent to manage your dependencies. This seems like a lot of pain given that all of those libraries (I think) either provide their own find_package config packages or CMake provides a find module for them.
I've wrote a C++ library MyLib and I'd like to integrate it with another project ExternPro. So in ExternPro I wrote CMakeLists.txt like this:
add_subdirectory(MyLib)
ADD_EXECUTABLE(test test.cpp)
include_directories(${MyLib_INCLUDE_DIRS})
target_link_libraries(test ${MyLib_LIBRARIES})
To set variables MyLib_LIBRARIES and MyLib_INCLUDE_DIRS I wrote:
set(MyLib_LIBRARIES ${PROJECT_SOURCE_DIR}/src/MyLib.a CACHE INTERNAL "")
set(MyLib_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include CACHE INTERNAL "")
But something wrong "No rule to make target MyLib/src/MyLib.a, needed by test. Stop."
So my question is, how should I wrote CMakeLists.txt correctly so cmake could help me build MyLib first and then take care of dependencies of ExternPro?
If those are two separate projects, I normally use "CMake find scripts" to reference one library from the other: http://www.vtk.org/Wiki/CMake:How_To_Find_Libraries#Writing_find_modules
But I normally use slightly different find script than described there (FindMyLibrary.cmake):
# Find MyLibrary installation
#
# This module needs following variables specified (e.g. through cmake -Dvar=)
# MyLibrary_ROOT_DIR - root directory of the library installation
#
# This module defines the following variables:
# MyLibrary_INCLUDE_DIRS - Where to find the public headers
# MyLibrary_LIBRARIES - List of mandatory and optional libraries
# MyLibrary_FOUND - True if an installation was found
#
# Configuration variables for tis module:
# MyLibrary_USE_STATIC_LIBS - Set to ON to force the use of the static
# libraries. Default is OFF.
# If MyLibrary_ROOT_DIR was defined in the environment, use it.
if(NOT MyLibrary_ROOT_DIR AND NOT $ENV{MyLibrary_ROOT_DIR} STREQUAL "")
set(MyLibrary_ROOT_DIR $ENV{MyLibrary_ROOT_DIR})
endif()
if(NOT MyLibrary_ROOT_DIR)
set(MyLibrary_ROOT_DIR /usr)
endif()
message(STATUS "Using MyLibrary_ROOT_DIR: ${MyLibrary_ROOT_DIR}")
find_path(MyLibrary_INCLUDE_DIRS
NAMES mylib/mylib.hpp
PATHS ${MyLibrary_ROOT_DIR}
PATH_SUFFIXES include)
# Here we set the default components
if(NOT MyLibrary_FIND_COMPONENTS)
set(MyLibrary_FIND_COMPONENTS mylibrary)
endif()
# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
if(MyLibrary_USE_STATIC_LIBS)
set(_mylib_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
if(WIN32)
set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
else()
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
endif()
endif()
foreach(COMPONENT ${MyLibrary_FIND_COMPONENTS})
find_library(MyLibrary_${COMPONENT}_LIBRARY
NAMES ${COMPONENT}
HINTS ${MyLibrary_ROOT_DIR}
PATH_SUFFIXES lib64 lib
NO_DEFAULT_PATH)
set(MyLibrary_LIBRARIES ${MyLibrary_LIBRARIES} ${MyLibrary_${COMPONENT}_LIBRARY})
endforeach()
# Restore the original find library ordering
if(MyLibrary_USE_STATIC_LIBS)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_mylib_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
endif()
# handle the QUIETLY and REQUIRED arguments and set MyLibrary_FOUND to
# TRUE if all listed variables are TRUE
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
MyLibrary "Could NOT find MyLibrary: set MyLibrary_ROOT_DIR to a proper location"
MyLibrary_LIBRARIES
MyLibrary_INCLUDE_DIRS)
mark_as_advanced(MyLibrary_INCLUDE_DIRS MyLibrary_LIBRARIES)
Then used like this:
find_package(MyLibrary REQUIRED)
include_directories(SYSTEM ${MyLibrary_INCLUDE_DIRS})
target_link_libraries(${TARGET}
${MyLibrary_LIBRARIES}
)
Basically, it goes like this:
The library is built and installed (make install) to either the default location (e.g. /usr), or some other location (usual in development).
The FindMyLibrary.cmake is part of the library installation (for RPM the library devel package) and installs as well (into ${instdir}/share/cmake/Modules, for example).
The dependent project then adds the path to the CMAKE_MODULE_PATH and uses the find script to find the public headers and libraries as installed.
The advantage is that this way you can either use it during the development (when you have the library sources and build the library), or without the library sources as well (with just the library and headers - the devel package - installed in the system).
Similar technique is normally used by Boost etc. (the find scripts provided already by the CMake).
Instead of path to the library, use library target for target_link_libraries.
Assuming your library project contains
add_library(MyLib ...)
Linking of executable in main project should be performed with
target_link_libraries(test MyLib)
First of all this doesn't work because variables set in a subdirectory are not set for the parent directory.
So to solve this properly you should define MyLib like:
add_library(MyLib ...)
target_include_directories(MyLib INTERFACE ${PROJECT_SOURCE_DIR}/include)
And for the ExternPro you just need to link to MyLib:
target_link_libraries(test MyLib)
This will automatically add the include directory to test and link MyLib properly.
This question already has an answer here:
CMake use foreach and find_library to return full path of libraries
(1 answer)
Closed 6 years ago.
I have some helper functions which help me locate shared and static libraries in the filesystem:
I have a helper function which performs the actual search, given a name and a suffix:
function(do_find_lib LIB_NAME SUFFIX OUT)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${SUFFIX})
find_library(FOUND ${LIB_NAME})
if(NOT FOUND)
message(SEND_ERROR "unable to find library ${LIB_NAME}")
endif()
message(STATUS "search: ${LIB_NAME}.${SUFFIX} result: ${FOUND}")
set(${OUT} ${FOUND} PARENT_SCOPE)
endfunction()
The function find_static_lib sets the appropriate suffix, and calls do_find_lib:
function(find_static_lib LIB_NAME OUT)
if (WIN32 OR MSVC)
set(SUFFIX ".lib")
elseif (UNIX)
set(SUFFIX ".a")
endif()
do_find_lib(${LIB_NAME} ${SUFFIX} FOUND)
message(STATUS "static lib search: ${LIB_NAME} result: ${FOUND}")
set(${OUT} ${FOUND} PARENT_SCOPE)
endfunction()
Similarly, the function find_shared_lib sets the appropriate suffix, and calls do_find_lib:
function(find_shared_lib LIB_NAME OUT)
if (WIN32 OR MSVC)
set(SUFFIX ".dll")
elseif (UNIX)
set(SUFFIX ".so")
endif()
do_find_lib(${LIB_NAME} ${SUFFIX} FOUND)
message(STATUS "shared lib search: ${LIB_NAME} result: ${FOUND}")
set(${OUT} ${FOUND} PARENT_SCOPE)
endfunction()
As required, elsewhere in my cmake hierarchy I use these functions to find various libraries
The first time I use it, it works:
First I search for tcmalloc static library.
find_shared_library(tcmalloc_minimal TCMALLOC)
Here is the resulting output:
-- search: tcmalloc_minimal.a result: /usr/local/lib/libtcmalloc_minimal.a
-- static lib search: tcmalloc_minimal result: /usr/local/lib/libtcmalloc_minimal.a
The second time I use it, it breaks:
Next I search for protobuf shared library.
find_shared_library(protobuf PROTOBUF)
Here is the resulting output:
-- search: protobuf.so result: /usr/local/lib/libtcmalloc_minimal.a
-- shared lib search: protobuf result: /usr/local/lib/libtcmalloc_minimal.a
Error:
Wah? Searching for protobuf with suffix .so finds the previous search result, /usr/local/lib/libtcmalloc_minimal.a
Question:
Why is this happening?
Is there a bug in my functions?
What do I need to do to correctly search for the requisite library?
Notes:
I have completed deleted my build directory and rebuild the makefiles, so don't believe this is an issue with a cache.
From the documentation on find_library:
A cache entry named by <VAR> is created to store the result of this command.
If the library is found the result is stored in the variable and the search will not be repeated unless the variable is cleared.
This cmake mailing list answer shows you have to set the variable to FOUND-NOTFOUND in order to clear the cache.
set(FOUND FOUND-NOTFOUND)
However, it could be argued that this is an anti-pattern, as doing this defeats the caching behaviour of find_library
It would be be better to use a unique variable name for each unique search.
Here the variable is called FOUND_${LIB_NAME}${SUFFIX}:
function(do_find_lib LIB_NAME SUFFIX OUT)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${SUFFIX})
find_library(FOUND_${LIB_NAME}${SUFFIX} ${LIB_NAME})
if(NOT FOUND_${LIB_NAME}${SUFFIX})
message(SEND_ERROR "unable to find library ${LIB_NAME}")
endif()
set(${OUT} ${FOUND_${LIB_NAME}${SUFFIX}} PARENT_SCOPE)
endfunction()
I have some debug info in CMake to check if I have added no needed info compiling a project. This piece of code works propertly:
# Include DIRECTORIES
GET_PROPERTY(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
FOREACH(dir ${dirs})
MESSAGE(STATUS " * Include directory: '${dir}'")
ENDFOREACH()
But when I try to check all libraries linked in current project, I get an empty variable:
# Linking against
GET_PROPERTY(libtargets DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY LINK_LIBRARIES)
MESSAGE("Libs: ${libtargets}")
FOREACH(libtarget ${libtargets})
MESSAGE(STATUS " * Target Link library: '${libtarget}'")
ENDFOREACH()
Checking CMake documentation 3.0 it seems ok, but I don't know what values are available to read. I print the info AFTER make ADD_LIBRARY/ADD_EXECUTABLE
Is the LINK_LIBRARIES an incorrect value to GET_PROPERTY? How could I get that information?
Directories don't have the LINK_LIBRARIES property according to this page:
http://www.cmake.org/cmake/help/v3.0/manual/cmake-properties.7.html?highlight=properties%20targets#properties-on-directories
Also I think target_link_libraries would have effect only on the target on which it was called, not on the directory. Try using get_target_property instead.