Get all include folders from cmake executable - cmake

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.

Related

CMake INTERFACE_INCLUDE_DIRECTORIES for IMPORTED target with CONFIG

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.

Cmake add_library ALIAS

I am trying to figure out exactly what this line is for in the cmake file of this github json project,
add_library(${NLOHMANN_JSON_TARGET_NAME} INTERFACE)
add_library(${PROJECT_NAME}::${NLOHMANN_JSON_TARGET_NAME} ALIAS ${NLOHMANN_JSON_TARGET_NAME})
Specifically with this example, what does this allow in this cmake file that otherwise would not be possible?
I see no other references to ${PROJECT_NAME}::${NLOHMANN_JSON_TARGET_NAME} in this CMakeLists.cmake, so I am confused as to what exactly this achieves.
Edit:
The key thing that this achieves, that the comment did not make obvious to me, is that it makes the targets work with the namespaces when the project is used through add_subdirectory()
Without the alias, you can still add the library via add_subdirectory however in the target_link_libraries command you would need to omit the namespace:
project(mySuperApp)
set(mySuperApp_SRC src/main.c)
add_subdirectory(thirdparty/json)
add_executable(${PROJECT_NAME} ${mySuperApp_SRC})
target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json)
If you did that but then decided to use find_package to include the library (as opposed to add_subdirectory), you would need to change target_link_libraries to use the namespaced targets i.e.
project(mySuperApp)
set(mySuperApp_SRC src/main.c)
find_package(nlohmann_json REQUIRED)
add_executable(${PROJECT_NAME} ${mySuperApp_SRC})
target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json)
by adding the alias, the target_link_libraries using the namespaced version (i.e. nlohmann_json::nlohmann_json) will work in either case and not require a change if you later decide to switch from find_package to add_subdirectory).
It allows you to add the library with find_package OR add_subdirectory using the same target name for both:
# creates nlohmann_json::nlohmann_json
find_package(nlohmann_json REQUIRED)
if (nlohmann_json_NOT_FOUND)
# creates nlohmann_json AND nlohmann_json::nlohmann_json
add_subdirectory(thirdparty/json)
endif()
add_executable(your_target_name ${your_target_sources})
target_link_libraries(your_target_name PRIVATE nlohmann_json::nlohmann_json)
Without the alias, you would need:
# creates nlohmann_json::nlohmann_json
find_package(nlohmann_json REQUIRED)
if (NOT nlohmann_json_FOUND)
# creates only nlohmann_json
add_subdirectory(thirdparty/json)
endif()
add_executable(your_target_name ${your_target_sources})
if (nlohmann_json_FOUND)
target_link_libraries(your_target_name PRIVATE nlohmann_json::nlohmann_json)
else()
target_link_libraries(your_target_name PRIVATE nlohmann_json)
endif()
This will allow using nlohmann/json project by adding it into your super project with add_subdirectory(...)
For example simple project structure:
<root project>\
\thirdparty\json <<-- git submodule to https://github.com/nlohmann/json
\include\
\src\
CMakeLists.txt
In your project CMakeLists.txt
...
project(mySuperApp)
set(mySuperApp_SRC src/main.c)
# can under some conditions...
add_subdirectory(thirdparty/json)
add_executable(${PROJECT_NAME} ${mySuperApp_SRC})
target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json)
Using git's blame function shows that line was added in this commit: 33a2154, which has the following comment attached:
CMake convention is to use a project namespace, i.e. Foo::, for imported
targets. When multiple targets are imported from a project, this looks
like Foo::Bar1 Foo::Bar2, etc. This adds the nlohmann_json:: namespace to
the exported target names.
This also allows the generated project config files to be used from the
build directory instead of just the install directory.

Library depend on a header file

Let's assume I have a project with a series of libraries. I also need to generate a header file that will be used by all of these projects. So I created a CMake file, like this:
project(main)
add_subdirectory(sub_1)
add_subdirectory(sub_2)
# ...
add_subdirectory(sub_n)
add_custom_command(
OUTPUT CustomHeader.h
COMMENT "Generating custom header for all the libraries"
COMMAND ...)
add_library(${PROJECT_NAME} STATIC ${OBJECT_LIST})
The problem is, that I don't know how to tell CMake to run my custom command (that generates this CustomHeader.h) before it would try to build the libraries in the subfolders.
I tried add_custom_target(TARGET MyPrebuild PRE_BUILD ...) but I'm running on Linux, and this option only works on Windows platform according to the documentation.
add_dependencies only work between targets, and not a target and a single file.
I could, in theory, add the header to be among the source files of the individual libraries (in the sub_1, .., sub_n folders) but it feels wrong, as the header is not required to be part of those libraries.
So I just have no idea how I can make a library depend on an include file, that is not part of it.
Any tips how I can overcome this problem?
For make header file (re)built before a library in subdirectory is compiled, you may create target, which builds the file, and make the library dependent from the target:
# *CMakeLists.txt*
# ...
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/CustomHeader.h ...)
add_custom_target(generate_custom_header DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/CustomHeader.h)
# *sub/CMakeLists.txt*
# ...
add_library(libA ...)
add_dependencies(libA generate_custom_header)
Instead of using add_dependencies, you may create header-only library which "implements" you header and link with it:
# *CMakeLists.txt*
# ...
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/CustomHeader.h ...)
add_custom_target(generate_custom_header DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/CustomHeader.h)
add_library(libCustom INTERFACE) # Header only library
add_dependencies(libCustom generate_custom_header) # which depends on generated header
# You may even assign include directories for the header-only library
target_include_directories(libCustom INTERFACE ${CMAKE_CURRENT_BINARY_DIR})
# *sub/CMakeLists.txt*
# ...
add_library(libA ...)
target_link_libraries(libA libCustom) # Common linking with a header-only library.
Note, that INTERFACE library is a "fake" - it is never created by itself. Instead, all "features" of INTERFACE library are just propagated to its users.
I would suggest to add another library target that will both keep track of the generated headers and will help to properly configure other libraries to know where to find them (i.e. target_include_directories).
cmake_minimum_required(VERSION 3.0)
project(testable)
set(CustomHeaderInPath ${CMAKE_CURRENT_SOURCE_DIR}/CustomHeader.example)
set(CustomHeaderPath ${CMAKE_CURRENT_BINARY_DIR}/CustomHeader.h)
add_custom_command(
OUTPUT ${CustomHeaderPath}
COMMAND cp ${CustomHeaderInPath} ${CustomHeaderPath}
COMMENT "Generated file"
DEPENDS ${CustomHeaderInPath})
add_library(CustomHeaderLibrary ${CustomHeaderPath})
target_include_directories(CustomHeaderLibrary PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
set_target_properties(CustomHeaderLibrary PROPERTIES LINKER_LANGUAGE C)
add_library(LibA a.c)
target_link_libraries(LibA CustomHeaderLibrary)
add_library(LibB b.c)
target_link_libraries(LibB CustomHeaderLibrary)
add_executable(${PROJECT_NAME} main.c)
target_link_libraries(${PROJECT_NAME} PUBLIC LibA LibB)
Note that I had to explicitly set the LINKER_LANGUAGE of the new target as cmake won't be able to deduce it properly if no c or cpp files are added to the library.

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)

Simple CMakeLists.txt that reflects typical directory structure (/src, /inc, /bin subdirectories)

I am struggling to make a CMakeList.txt file to reflect a simple, typical makefile. The original is here http://pastebin.com/S9Czr1pt .
I tried many things (like SET(SOURCE ... and SET(HEADERS... ) to add /src, /lib and /third-party//include ), but I have no luck.
Can anyone either help me out or point to a tutorial that does this thing?
This is just an out of the blue skeleton - please see the CMake documentation for details of each function:
cmake_minimum_required(VERSION 2.6)
# Project name, can be used as target name too
project(Example)
# Search all .cpp files within src - recursive!
# You can add all source files by hand here too
file(GLOB_RECURSE SRCS "src/*.cpp")
# Add include path (you can also add it's sub directories
include_directories("include")
# Search for packages -- PLEASE NOTE: many libraries provide alternative's to this
# which often provide more functionality
find_package(PkgConfig REQUIRED)
# TODO: add proper pkg modul search info's and change the variable's name
pkg_search_module(PACKAGE_NO1 ...)
# Show some messages (optional)
if( (PACKAGE_NO1 )
include_directories(${(PACKAGE_NO1_INCLUDE_DIRS})
message(STATUS "Using OpenSSL ${(PACKAGE_NO1_VERSION}")
else()
# do error handling
endif()
# Add compiler flags
add_definitions(-std=c++11 -Wall) # -g O3 etc are added according to Release / Debug compilation
# Build a executable target (1st param: Target name; 2nd: source files)
add_executable(${PROJECT_NAME} ${SRCS})