CMake INTERFACE_INCLUDE_DIRECTORIES for IMPORTED target with CONFIG - cmake

I have an IMPORTED target which comes from a config package generated by conan. Since conan creates packages for the build type specified in the build_type of the profile, I have added CMAKE_MAP_IMPORTED_CONFIG_DEBUG to Release, so that even if I compile a Debug build of my project, I can still use Config files generated with conan using a Release profile.
This works fine for all the targets in my project. include directories are set correctly, libraries link correctly. except for one case:
I have an INTERFACE target which I use to use only the include directories of an IMPORTED target if one option is set, or use the whole target.
add_library(deTracing INTERFACE)
# Get include directories of Tracy::TracyClient target
get_target_property(TRACY_INCLUDE_DIRECTORIES Tracy::TracyClient INTERFACE_INCLUDE_DIRECTORIES)
# Include the directories unconditionally
target_include_directories(deTracing INTERFACE ${TRACY_INCLUDE_DIRECTORIES})
# Some debug stuff
message(status ${TRACY_INCLUDE_DIRECTORIES})
add_custom_target(genexdebug COMMAND ${CMAKE_COMMAND} -E echo "${TRACY_INCLUDE_DIRECTORIES}")
# If ENABLE_PROFILING is set, link to the library
if(ENABLE_PROFILING)
target_link_libraries(deTracing INTERFACE Tracy::TracyClient)
target_compile_definitions(deTracing INTERFACE ENABLE_PROFILING)
endif()
The issue is that TRACY_INCLUDE_DIRECTORIES is a generator expression with this content
$<$<CONFIG:Release>:/home/seddi/.conan/data/tracy/0.9/_/_/package/913e5b8c3a7b64b23fdaa63b58e3915a742a1112/include>, which is what I can expect, it just contains the include directory for the release config.
But when this expression is evaluated in a Debug build, it just collapses to an empty string, even though I have set the CMAKE_MAP_IMPORTED_CONFIG_DEBUG to Release, so it doesn't set any include directories.
I am out of ideas of how I set the correct INCLUDE_DIRECTORIES to deTracing target.
Edit:
According to CMake, the MAP_IMPORTED_CONFIG_ property is followed when evaluating properties of IMPORTED targets. I have modified deTracing to be an IMPORTED target, yet still doesn't work.
add_library(deTracing INTERFACE IMPORTED)
get_target_property(TRACY_INCLUDE_DIRECTORIES Tracy::TracyClient INTERFACE_INCLUDE_DIRECTORIES)
set_property(
TARGET deTracing
PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${TRACY_INCLUDE_DIRECTORIES}
APPEND) # ${TRACY_INCLUDE_DIRECTORIES})
get_target_property(deTracing_INCLUDE_DIRECTORIES deTracing INTERFACE_INCLUDE_DIRECTORIES)
message("deTracing INCLUDE_DIRECTORIES: ${deTracing_INCLUDE_DIRECTORIES}")
add_library(TestTarget INTERFACE)
target_link_libraries(TestTarget INTERFACE deTracing)
get_target_property(TestTarget_INCLUDE_DIRECTORIES TestTarget INTERFACE_INCLUDE_DIRECTORIES)
message("TestTarget INCLUDE DIRECTORIES: ${TestTarget_INCLUDE_DIRECTORIES}")
This snippet produces the following output
[cmake] deTracing INCLUDE_DIRECTORIES: $<$<CONFIG:Release>:/home/seddi/.conan/data/tracy/0.9/_/_/package/913e5b8c3a7b64b23fdaa63b58e3915a742a1112/include>
[cmake] TestTarget INCLUDE DIRECTORIES: TestTarget_INCLUDE_DIRECTORIES-NOTFOUND
which shows that deTracing is able to set the correct INCLUDE_DIRECTORIES for the Release config, but then is unable to set them on TestTarget, even though deTracing is an IMPORTED target.

Related

Get cmake to find a custom config file

A cmake project I want to build depends on SDL2, and it comes with the following instruction:
Create a helper called /usr/local/lib/cmake/SDL2/sdl2-config.cmake (because SDL2 doesn't have an SDL2::SDL2 alias) containing:
if(NOT TARGET SDL2::SDL2
AND EXISTS "/usr/lib/x86_64-linux-gnu/cmake/SDL2/sdl2-config.cmake")
include(/usr/lib/x86_64-linux-gnu/cmake/SDL2/sdl2-config.cmake)
add_library(SDL2 SHARED IMPORTED)
set_target_properties(SDL2
PROPERTIES
IMPORTED_LOCATION "${libdir}/libSDL2.so"
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIRS}"
)
add_library(SDL2::SDL2 ALIAS SDL2)
endif()
The problem is that this file isn't found by my cmake (I put a message(...) at the beginning to verify this). If I change the dependency in my CMakeLists.txt from SDL2::SDL2 to just SDL2, it works fine and reads from /usr/lib/x86_64-linux-gnu/cmake/SDL2/sdl2-config.cmake. But this shouldn't be necessary.
How can I get cmake to find this custom file, or how do I find out where to put it?

How to set include directories

I have a directory with various pre-built libraries (mostly headers and libs/dlls, all built without CMake or copied without installing using CMake). For my next project i want to use these libraries with CMake.
I am writing a script that iterates over all the libraries and generates the needed <name>Config.cmake and <name>ConfigVersion.cmake files referencing the existing directory structure.
A minimal config file may look like:
SET(ROOT_DIR_MYLIB "${CMAKE_CURRENT_LIST_DIR}/../../../../MyLib")
add_library(MyLib STATIC IMPORTED)
set_target_properties(MyLib PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${ROOT_DIR_MYLIB}/include"
IMPORTED_LOCATION "${ROOT_DIR_MYLIB}/Lib/MyLib.lib"
IMPORTED_LOCATION_DEBUG "${ROOT_DIR_MYLIB}/Lib/MyLibd.lib"
)
I have copied the line where the property INTERFACE_INCLUDE_DIRECTORIES is set from another project where this worked, but for some reason it does not for MyLib. When i attempt to include its header files i get compile errors that the include files can not be found.
What am i missing here?

Get all include folders from cmake executable

How can I get all include directories of my executable?
I want to introduce cppcheck and I need to forward all include directories to cppcheck (also the include directories of the interface imported libraries).
So for example I have
add_executable(my_exe main.cpp)
target_link_libraries(my_exe PRIVATE RapidJSON::RapidJSON)
How can I get all corresponding include directories of my_exe, including the RapidJSON ones (e.g. ~/.conan/data/RapidJSON/1.1.0/a/b/package/x/include)?
I tried following without success :-(
get_target_property(ALL_INCLUDES my_exe INTERFACE_INCLUDE_DIRECTORIES) # NOTFOUND
get_target_property(ALL_INCLUDES my_exe INCLUDE_DIRECTORIES) # empty
add_executable(my_exe main.cpp)
target_link_libraries(my_exe PRIVATE RapidJSON::RapidJSON)
# the following line should be adapted so that the variable cppcheck_includes also contains the RapidJSON include directories
set(cppcheck_includes ${CMAKE_SOURCE_DIR}/includes)
add_custom_target(cppcheck
COMMAND cppcheck
--enable=all
--std=c++11
--library=/usr/share/cppcheck/cfg/std.cfg
--check-config
${cppcheck_includes}
main.cpp
)
I expect that there is no warning, but there is a warning:
Include file: <rapidjson/document.h> not found.
In my case I just created a target which runs Cppcheck and uses the compilation database of my project as input - see https://github.com/firewave/mame_regtest/blob/master/cppcheck.cmake.
To generate the compilation database you simply need to add
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
to your CMakeLists.txt.
You could also use the integrated support in CMake but I have no experience with that yet.

Are there other means of obtaining the properties of a target in cmake?

I have created a C++ static library, and in order to make it searchable easily, I create the following cmake files:
lib.cmake
# The installation prefix configured by this project.
set(_IMPORT_PREFIX "C:/------/install/win32")
# Create imported target boost
add_library(lib STATIC IMPORTED)
set_target_properties(lib PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "lib_define1;lib_define2"
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/../include"
)
# Load information for each installed configuration.
get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
file(GLOB CONFIG_FILES "${_DIR}/lib-*.cmake")
foreach(f ${CONFIG_FILES})
include(${f})
endforeach()
lib-debug.cmake
# Import target "boost" for configuration "Debug"
set_property(TARGET lib APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
set_target_properties(boost PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "CXX"
IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/Debug/staticlib/lib.lib"
)
When I want to use this library in an executable, I can simply invoke it by calling find_package command:
find_package(lib REQUIRED)
if(lib_FOUND)
message("lib has been found")
else()
message("lib cannot be found")
endif(boost_FOUND)
It works and if I want to know the head file directory of the library, I will have to call it this way:
get_target_property(lib_dir lib INTERFACE_INCLUDE_DIRECTORIES)
I was just wondering whether there are other ways of obtaining the properties of an target. In this case I expect some variable like lib_INCLUDE_DIRECTORIES will exist.
No, CMake does not automatically define variables for the properties of a target (or of anything else). If you need the value of a property, you have to query it explicitly (using get_property or the specific getters like get_target_property etc.).
In your specific case, INTERFACE_INCLUDE_DIRECTORIES is a property which I would expect you would not need to query at all. The whole point of INTERFACE_* properties is to propagate usage requirements automatically; their propagation is implemented in CMake itself.

custom target as a target library in cmake

I have a custom target that is in fact an externally generated library that I want to integrate in my build.
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/liblib2.a
COMMAND make -f ${CMAKE_CURRENT_SOURCE_DIR}/makefile liblib2.a)
add_custom_target(lib2
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/liblib2.a)
How can I tell cmake that this target is in fact a library, where it can be found and where are the headers ?
To be clear : I don't want the upper CMakeList using this library having to manually specify include folders and the library location folder It must be done automatically (from the target properties).
On a standard cmake library I would just have to add the INTERFACE_INCLUDE_DIRECTORIES property in the library CMakeLists to make cmake link my app with the relevant -I and -L gcc parameters :
set_target_properties(lib1
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR})
But in the case of a custom target I don't know how to to it.
Any clue ?
Thanks for your help.
Thanks to zaufi it works!
For others who may be interested in embedded externally build target inside cmake here is what I did :
cmake_minimum_required(VERSION 2.8)
SET(LIB_FILE ${CMAKE_CURRENT_SOURCE_DIR}/bin/liblib2.a)
SET(LIB_HEADER_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/include)
# how to build the result of the library
add_custom_command(OUTPUT ${LIB_FILE}
COMMAND make
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# create a target out of the library compilation result
add_custom_target(lib2_target DEPENDS ${LIB_FILE})
# create an library target out of the library compilation result
add_library(lib2 STATIC IMPORTED GLOBAL)
add_dependencies(lib2 lib2_target)
# specify where the library is and where to find the headers
set_target_properties(lib2
PROPERTIES
IMPORTED_LOCATION ${LIB_FILE}
INTERFACE_INCLUDE_DIRECTORIES ${LIB_HEADER_FOLDER})
Now in a CMakeLists.txt I can do somthing like
add_subdirectory(${ROOT_DIR}/lib1 bin/lib1)
add_subdirectory(${ROOT_DIR}/lib2 bin/lib2)
add_executable(app app.c )
target_link_libraries(app lib1 lib2)
No need to specify where the .a and the .h are.
You can use add_library() and tell that it actually imported. Then, using set_target_properties() you can set required INTERFACE_XXX properties for it. After that, you can use it as an ordinal target like every other built by your project.
Thank you for posting the solution. I have wrapped your snippet in a function:
function(add_external_library)
set(options)
set(oneValueArgs TARGET WORKING_DIRECTORY OUTPUT COMMENT)
set(multiValueArgs COMMAND INCLUDE_DIRS)
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" ${multiValueArgs}" ${ARGN})
# Specify how to build the result of the library
add_custom_command(OUTPUT "${ARGS_OUTPUT}"
COMMAND ${ARGS_COMMAND}
WORKING_DIRECTORY "${ARGS_WORKING_DIRECTORY}"
COMMENT "${ARGS_COMMENT}")
# Create a target out of the library compilation result
add_custom_target(${ARGS_TARGET}_target DEPENDS ${ARGS_OUTPUT})
# Create an library target out of the library compilation result
add_library(${ARGS_TARGET} STATIC IMPORTED GLOBAL)
add_dependencies(${ARGS_TARGET} ${ARGS_TARGET}_target)
# Specify where the library is and where to find the headers
set_target_properties(${ARGS_TARGET}
PROPERTIES
IMPORTED_LOCATION "${ARGS_OUTPUT}"
INTERFACE_INCLUDE_DIRECTORIES "${ARGS_INCLUDE_DIRS}")
endfunction()
# Example
add_external_library(TARGET YourLib
COMMAND /bin/bash compile_your_lib.sh
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT "output/yourlib.a"
INCLUDE_DIRS "include/a" "include/b"
COMMENT "Building YourLib")
add_executable(YourExe)
target_link_libraries(YourExe YourLib)