Cmake: check if system include directory - c++

Is it possible with cmake 2.6 (or higher, if not possible in this version) to check whether an include directory is marked as SYSTEM (e.g. compile with the isystem gcc flag, see 2.8 System Headers)?
For example, I get the include directories of the current target with:
GET_PROPERTY(_target_include_dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
How do I know which ones are marked as isystem?
Thanks

Looking at the CMake source, whether an include directory added with include_directories is marked as SYSTEM seems to be tracked in CMake internals, and is not available for consumption inside your CMakeLists.txt¹.
However, for the target property INTERFACE_INCLUDE_DIRECTORIES (populated by target_include_directories with either PUBLIC or INTERFACE), there is another target property named INTERFACE_SYSTEM_INCLUDE_DIRECTORIES:
add_library(testlib test.cc)
target_include_directories(testlib SYSTEM INTERFACE /target_system)
get_property(_system_include_dirs TARGET testlib PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
message("System: ${_system_include_dirs}")
So maybe you can use that instead.
¹ There is mention of a SYSTEM_INCLUDE_DIRECTORIES property available through a generator expression, but I couldn't get it to work.

Related

Access CMake's target/library name at compile time globally

We implement a logger which automatically prints out the project name of a log entry among other infos.
We recently change our build system from using native Microsoft visual c++ to cmake generated.
With native Microsoft c++ build files, we were just defining a macro "PROJECT_NAME" in a global ".props" as such:
<PreprocessorDefinitions>%(PreprocessorDefinitions);PROJECT_NAME=R"($(ProjectName))"</PreprocessorDefinitions>
However, now that we use CMake, I struggle to find a good way to access the project name at compile time. I can use configure_file or target_compile_definitions to access a CMake variable at compile time but I do not know any variable holding a string with the target name. Is there such a variable or can it be defined?
Also, can it be defined only in one place? I don't want to copy paste in every CMakeList.txt a line of the such:
target_compile_definitions(MYTARGET PRIVATE PROJECT_NAME="$mytarget_name")
If you've got multiple options to apply to all targets, you could link a INTERFACE library which allows you to inherit multiple properties of this cmake target via a single target_link_libraries use.
For using the name of the linking target, generator expressions that do not require specifying the target can be used:
add_library(OptionsForAll INTERFACE)
# note: using the target name not the cmake project name here
target_compile_definitions(OptionsForAll INTERFACE PROJECT_NAME=\"$<TARGET_PROPERTY:NAME>\")
target_link_libraries(MYTARGET PRIVATE OptionsForAll)
Another option mentioned in #Tsyvarevs comment would be to use add_compile_definitions to apply the definition for all targets defined in the current directory and subdirectory, but this makes it harder to remove it from some targets...
fabian's answer is IMO the good approach.
However, i wanted to offer an alternative based on BUILDSYSTEM_TARGETS inspired from this answer.
Thus, there is no need to modify all target's call to target_link_libraries.
function(get_all_targets var)
set(targets)
get_all_targets_recursive(targets ${CMAKE_CURRENT_SOURCE_DIR})
set(${var} ${targets} PARENT_SCOPE)
endfunction()
macro(get_all_targets_recursive targets dir)
get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES)
foreach(subdir ${subdirectories})
get_all_targets_recursive(${targets} ${subdir})
endforeach()
get_property(current_targets DIRECTORY ${dir} PROPERTY BUILDSYSTEM_TARGETS)
list(APPEND ${targets} ${current_targets})
endmacro()
function(define_targetname all_targets)
foreach(target ${all_targets})
get_target_property(type ${target} TYPE)
if (NOT ${type} STREQUAL "INTERFACE_LIBRARY" AND NOT ${target} STREQUAL "PCHTarget") # INTERFACE library cannot target_compile_definitions && precompiled headers will redefine the MACRO
target_compile_definitions(${target} PRIVATE PROJECT_NAME="${target}")
endif()
endforeach()
endfunction()
get_all_targets(all_targets)
define_targetname("${all_targets}")

What's a simple straightforward find_package set of files for CMake

I'm new(ish) to CMake (meaning off and on over a few years I've been forced to use it even though it's something that's made me question my career path).
I have a folder with an include folder, and a lib folder containing .lib files and their corresponding .dlls for a dependency I'll call "mydep". I need to provide the infrastructure files, mydep-config.cmake, mydep-target.cmake, etc, which will add the includes folders and .lib files to the command line for compiling and linking and then move the .dlls to a specific location.
Can anyone point me to a simple example anywhere on the net that illustrates the way to do this? The CMake documentation is utterly useless.
Thanks!
There are basically 2 files you need to put in the correct location on the file system. Let's assume the library is to import is called ExternLib with version 1.2.3 and compiled for 64 bit. With the and you've got it stored in your repo as
external_dependencies/lib/ExternLib.lib
external_dependencies/bin/ExternLib.dll
external_dependencies/include/ExternLib/ExternHeader.hpp
external_dependencies/include/ExternLib/...
...
and you want the include paths used in your project to be
#include "ExternLib/ExternHeader.hpp"
First of all we'll use the file names externlib-config.cmake and externlib-version-config.cmake to allow the user to use arbitrary case when using find_package.
externlib-version-config.cmake
This file is used to check, if the version of the config script is compatible with the cmake configuration trying to find the package. Adding this file allows you to place multiple versions of the library to be found for different target architectures (e.g. Windows x64, Win32, linux x86_64, ...). find_package won't read the externlib-config.cmake in the same directory, if the version is marked as non-compatible via the version file.
set(PACKAGE_VERSION "1.2.3")
# basically ignore the version passed
set(PACKAGE_VERSION_COMPATIBLE TRUE) # consider every version compatible
#usually you'd add logic for ignoring any unspecified parts of the version here instead of treating unspecified version parts as 0
# we'll keep it simple though
if(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION)
set(PACKAGE_VERSION_EXACT TRUE) # make version as exact match
endif()
if(NOT WIN32)
set(PACKAGE_VERSION_COMPATIBLE FALSE) # disallow older version
else()
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") # note: we'll ignore the possibility of find_package before the first project() command
set(PACKAGE_VERSION_UNSUITABLE TRUE)
endif()
if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8") # check for 64 bit
set(PACKAGE_VERSION_UNSUITABLE TRUE)
endif()
externlib-config.cmake
Here we'll put the creation of the actual library in addition to providing any functions/macros/variables the user should have access to. We'll use ExternLib::ExternLib as target name, but you can simply replace this by any name of your choosing unique in your project.
if(NOT TARGET ExternLib::ExternLib) # prevent recreation on multiple uses of find_package
add_library(ExternLib::ExternLib SHARED IMPORTED)
get_filename_component(EXTERNLIB_BASE_DIR ${CMAKE_CURRENT_LISTS_DIR}/../../.. ABSOLUTE)
set(EXTERNLIB_BINARY_DIR ${EXTERNLIB_BASE_DIR} CACHE INTERNAL "Directory containing the dlls for ExternLib")
# add info about locations of the dll/lib files
set_target_properties(ExternLib::ExternLib PROPERTIES
IMPORTED_LOCATION "$CACHE{EXTERNLIB_BINARY_DIR}/ExternLib.dll"
IMPORTED_IMPLIB "${EXTERNLIB_BASE_DIR}/lib/ExternLib.lib"
)
# add include directory information
target_include_directories(ExternLib::ExternLib INTERFACE "${EXTERNLIB_BASE_DIR}/include")
# add dependencies, if required; you may need to use find_package to locate those
# target_link_libraries(ExternLib::ExternLib INTERFACE ...)
endif()
# any helper function/macro definitions should go here, since they may need to get reintroduced
Note: I did ignore the possibility of components of a package being specified here. Any components specifying components for find_package are simply ignored for the script above.
Placement of the files
You'll need to place the files in one of some possible paths below one of the directories mentioned in CMAKE_PREFIX_PATH or in one of the default dirs, see find_package Config Mode Search Procedure.
Note: we'll use a path that isn't documented for windows, since this path would also work for linux. (Boost is doing this too.)
lib/cmake/ExternLib-x64-1.2.3
(You could choose a different suffix to lib/cmake/ExternLib or just use lib/cmake/ExternLib. The search procedure picks up on any directory names which starts with the package name ignoring case, if it expects the lib name.)
Place both the files in this directory. externlib-config.cmake assumes lib is external_dependencies/lib here. Otherwise you may need to adjust EXTERNLIB_BASE_DIR accordingly.
Usage
We'll assume the CMakeLists.txt file is placed in the same directory as external_dependencies
project(...)
...
add_executable(my_exe ...)
...
# this allows the user to pass directories to be searched first via -D option during configuration
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies)
find_package(ExternLib REQUIRED)
target_link_libraries(my_exe PRIVATE ExternLib::ExternLib)
# allow vs debugger to find the dll without copying the dlls around
set_target_properties(my_exe PROPERTIES
VS_DEBUGGER_ENVIRONMENT "PATH=${EXTERNLIB_BINARY_DIR};$ENV{PATH}"
)

Force CMake target_link_libraries to fail when adding nonexistent target

CMake has an irritating default (I presume, I see nothing magical in my CMake config, but I could be wrong since I know very little about CMake) behavior that he silently ignores when you add a target to your project even if that target does not exist, for example:
project(StackOverflow)
// another CMakeLists.txt
project (Stuff)
target_link_libraries(Stuff
PUBLIC StackOverlow )
Is there a way to force CMake to check that all projects you link in target_link_libraries must exist?
It is possible for CMake to fail if you link ALIAS targets. For example
In first CMakeLists.txt
add_library(StackOverflow STATIC lib.cpp)
add_library(StackOverflow::StackOverflow ALIAS StackOverflow)
In second CMakeLists.txt
target_link_libraries(Stuff PUBLIC StackOverflow::StackOverflow)
CMake will fail with an error if StackOverflow::StackOverflow is not defined.
https://cmake.org/cmake/help/v3.0/manual/cmake-buildsystem.7.html#alias-targets
In CMake, you do not link projects to other projects. Instead, you link targets to other targets.
CMake targets are only created via a few commands (such as add_library, add_executable, and add_custom_target). The project command does not create a CMake target, it merely declares a project.
Furthermore, the target_link_libraries() command accepts the following arguments after the scoping keyword:
A library target name
A full path to a library file
A plain library name
A link flag
A generator expression
A debug, optimized, or general keyword
It does not accept project names, although if you put a project name, it will instead look for a CMake target or library file on your system with that name.
To get to the root of what I believe you're asking: If you provide link-item name to target_link_libraries() that does not match an existing target, the command will simply search for a library file of that name instead.
To check if a target exists before trying to link it, you can do:
if (TARGET StackOverflow)
target_link_libraries(Stuff PUBLIC StackOverflow)
endif()
I suggest reading through the linked target_link_libraries() documentation if you want more details about what this command does.

How to link external C++ library to my project on Mac OSX [duplicate]

I have 2 folders "inc" and "lib" in my project which have headers and static libs respectively. How do I tell cmake to use those 2 directories for include and linking respectively?
The simplest way of doing this would be to add
include_directories(${CMAKE_SOURCE_DIR}/inc)
link_directories(${CMAKE_SOURCE_DIR}/lib)
add_executable(foo ${FOO_SRCS})
target_link_libraries(foo bar) # libbar.so is found in ${CMAKE_SOURCE_DIR}/lib
The modern CMake version that doesn't add the -I and -L flags to every compiler invocation would be to use imported libraries:
add_library(bar 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"
)
set(FOO_SRCS "foo.cpp")
add_executable(foo ${FOO_SRCS})
target_link_libraries(foo bar) # also adds the required include path
If setting the INTERFACE_INCLUDE_DIRECTORIES doesn't add the path, older versions of CMake also allow you to use target_include_directories(bar PUBLIC /path/to/include). However, this no longer works with CMake 3.6 or newer.
You had better use find_library command instead of link_directories. Concretely speaking there are two ways:
designate the path within the command
find_library(NAMES gtest PATHS path1 path2 ... pathN)
set the variable CMAKE_LIBRARY_PATH
set(CMAKE_LIBRARY_PATH path1 path2)
find_library(NAMES gtest)
the reason is as flowings:
Note This command is rarely necessary and should be avoided where there are other choices. Prefer to pass full absolute paths to
libraries where possible, since this ensures the correct library will
always be linked. The find_library() command provides the full path,
which can generally be used directly in calls to
target_link_libraries(). Situations where a library search path may be
needed include: Project generators like Xcode where the user can
switch target architecture at build time, but a full path to a library
cannot be used because it only provides one architecture (i.e. it is
not a universal binary).
Libraries may themselves have other private library dependencies that
expect to be found via RPATH mechanisms, but some linkers are not able
to fully decode those paths (e.g. due to the presence of things like
$ORIGIN).
If a library search path must be provided, prefer to localize the
effect where possible by using the target_link_directories() command
rather than link_directories(). The target-specific command can also
control how the search directories propagate to other dependent
targets.
might fail working with link_directories, then add each static library like following:
target_link_libraries(foo /path_to_static_library/libbar.a)

What is the difference between include_directories and target_include_directories in CMake?

I have a directory structure for my C++ code which goes like this :
|
|->include
|->src
I am writing a CMakeLists.txt file for my code. I want to understand the difference between include_directories and target_include_directories in CMake.
What is the difference between their usage and in order to add my include file path which one should I be using?
include_directories(x/y) affects directory scope. All targets in this CMakeList, as well as those in all subdirectories added after the point of its call, will have the path x/y added to their include path.
target_include_directories(t x/y) has target scope—it adds x/y to the include path for target t.
You want the former one if all of your targets use the include directories in question. You want the latter one if the path is specific to a target, or if you want finer control of the path's visibility. The latter comes from the fact that target_include_directories() supports the PRIVATE, PUBLIC, and INTERFACE qualifiers.
Beside what Angew's answer correctly says, another very important difference between include_directories and target_include_directories is that, when used with PUBLIC or INTERFACE, the latter populate the INTERFACE_INCLUDE_DIRECTORIES property of the target. This property is useful when another target uses target_link_libraries to link to the original target, as the linking target will have automatically those include directories added. See example.
This important feature is pretty well hidden in the documentation: target_include_directories mention populating INTERFACE_INCLUDE_DIRECTORIES, whose documentation says:
When target dependencies are specified using target_link_libraries(),
CMake will read this property from all target dependencies to
determine the build properties of the consumer.
As #Angew said, the very difference is :
1, include_directories() is accessible for all the files in the source-tree
2, target_include_directories() is-only accessible for a specific target when compile.