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

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}")

Related

Cmake Replacing add_compile_definitions with target_compile_definitions [duplicate]

I've been told it's bad practice to do things like seting CFLAGS directly in CMake, and that, instead, I should use the target_compile_definitions() command.
Ok, but - what if I want to use similar/identical definitions for multiple (independent) targets? I don't want to repeat myself over and over again.
I see three possible ways:
The preferred one using target_compile_definitions(... INTERFACE/PUBLIC ...) which would self-propagate the compiler definitions to targets depending on it via target_link_libraries() command.
Using the set_property(TARGET target1 target2 ... APPEND PROPERTY COMPILE_DEFINITIONS ...) to set the same definitions to multiple targets.
You may still use the "old commands" of add_definitions() and remove_definitions() to modify COMPILE_DEFINITIONS directory property (which would pre-set all COMPILE_DEFINITIONS target properties in this directories scope).
References
Is Cmake set variable recursive?
CMake: Is there a difference between set_property(TARGET ...) and set_target_properties?
tl;dr: You can iterate the targets in a loop.
If you have a bunch of targets with some common/similar features, you may want to simply manipulate them all in a loop! Remember - CMake is not like GNU Make, it's a full-fledged scripting language (well, sort of). So you could write:
set(my_targets
foo
bar
baz)
foreach(TARGET ${my_targets})
add_executable(${TARGET} "${TARGET}.cu")
target_compile_options(${TARGET} PRIVATE "--some_option=some_value")
target_link_libraries(${TARGET} PRIVATE some_lib)
# and so on
set_target_properties(
${TARGET}
PROPERTIES
C_STANDARD 99
C_STANDARD_REQUIRED YES
C_EXTENSIONS NO )
endforeach(TARGET)
And you could also initialize an empty list of targets, then add to it here-and-there, and only finally apply your common options and settings to all of them, centrally.
Note: In this example I added PRIVATE compile options, but if you need some of them to propagate to targets using your targets, you can make them PUBLIC).
another neat solution is to define an interface library target (a fake target that does not produce any binaries) with all required properties and compiler definitions, then link the other existing targets against it
example:
add_library(myfakelib INTERFACE)
target_compile_definitions(myfakelib INTERFACE MY_NEEDED_DEFINITION)
add_executable(actualtarget1 main1.cpp)
add_executable(actualtarget2 main2.cpp)
set_property(
TARGET actualtarget1 actualtarget2
APPEND PROPERTY LINK_LIBRARIES myfakelib
)
refs:
https://cmake.org/cmake/help/latest/command/add_library.html#interface-libraries

Does the header would be included explicitly when use find_package in CMake?

When I use PyTorch for C++, it's pretty easy to just use find_package to set up the dependency. And here is the CMakeLists.txt:
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(dcgan)
set(CMAKE_PREFIX_PATH /User/root/libtorch) # I added this line, does it effect?
find_package(Torch REQUIRED)
add_executable(dcgan dcgan.cpp)
target_link_libraries(dcgan "${TORCH_LIBRARIES}")
set_property(TARGET dcgan PROPERTY CXX_STANDARD 14)
There isn't any explicit command to include the header, but the header could be found if target_link_libraries(dcgan "${TORCH_LIBRARIES}") exists. I am curious why the header file could be found even there is no target_include_directories(dcgan PUBLIC ${TORCH_INCLUDE_DIRS}).
The code is on the official website of PyTorch and it works on MacOS and Linux. What happened.
ADD:
The package is in a directory where the compiler knows nothing about it.
The include path can be set as propagated setting in the dependency:
target_link_libraries
Specify libraries or flags to use when linking a given target and/or
its dependents. Usage requirements from linked library targets will be
propagated. Usage requirements of a target’s dependencies affect
compilation of its own sources.
https://cmake.org/cmake/help/latest/command/target_link_libraries.html
That means that target_link_libraries will configure the target . It will set target_compile_features, target_compile_options, target_compile_directories, if they're set as INTERFACE or PUBLIC in the dependency.
E.g.
add_library(Lib ${SRCS_LIB})
target_include_directories(Lib INTERFACE ${DIRECTORY})
add_exectuable(Exe ${SRCS_EXE})
target_link_libraries(Exe PRIVATE Lib)
In this example Exe will inherit the include directories from Lib. You don't need to set them explicitly.
That's also how Conan works, e.g. Getting started
cmake_minimum_required(VERSION 2.8.12)
project(MD5Encrypter)
add_definitions("-std=c++11")
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
add_executable(md5 md5.cpp)
target_link_libraries(md5 ${CONAN_LIBS})
and how it's described in Effective Modern CMake
Use exported targets of external packages.
Don’t fall back to the old
CMake style of using variables defined by external packages. Use the
exported targets via target_link_libraries instead.
Best practice is to not use target_include_directories for your dependencies.

Standard way of knowing if find_package defines INCLUDE_DIR or INCLUDE_DIRS

I keep running into this problem with cmake when trying to write build files. the find_package command is supposed to define variables to be used with the include_directories directive, but it does so inconsistently. For example, consider the following code:
find_package(OpenGL REQUIRED)
find_package(glm REQUIRED)
message(STATUS ${GLM_INCLUDE_DIR})
message(STATUS ${GLM_INCLUDE_DIRS})
message(STATUS ${OPENGL_INCLUDE_DIR})
message(STATUS ${OPENGL_INCLUDE_DIRS})
Only the second and third message prints, even though one is "DIR" and the other is "DIRS"
Is there a standard way of determining which one you're supposed to use?
The de-facto standard is *_LIBRARIES and *_DIRS, i.e. the plural variable names are result variables. So, they are only meant to be read from not written to.
Also, with a well-written modern FindModule even those variables are rarely useful to users because the imported targets will contain all the relevant information. So, users can just do the following without using result variables directly:
add_executable(myexe OpenGL::GL)
add_library(mylibrary PUBLIC OpenGL::GL)
However, the correct way to know how to use a FindModule is to just read the documentation which will either be in docs explicitly or in CMake comments at the beginning of the FindModule.cmake file.
All the standard module files are in the CMake installation subdirectory 'Modules' and most (all?) have CMake documentation.
The location hint variables will often be named *_INCLUDE_DIR AND *_LIBRARY, which you can set before calling find_package() in your CMakeLists.txt or you can set with cmake -D or cmake-gui or ccmake.

Cmake: check if system include directory

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.

SWIG and CMake: make use of information provided by `target_include_directories()`

With CMake 2.8+ you can avoid setting include directories redundantly by using target_include_directories().
E.g. by writing
add_libary(mylib SHARED ${SOURCES})
target_include_directories(mylib PUBLIC ./include)
.. you just have to link against mylib to add the needed include folder to your target.
But how can I make use of this information when I have to use CMake modules which don't make use of this capability yet? (in my case SWIG)
When I configure a SWIG project I currently have to hard code a lot of information:
set(SWIG_MODULE_${PYTHON_MODULE_NAME}_EXTRA_DEPS
"../long/relative/path/1/include/some/header1.h"
"../long/relative/path/1/include/some/header2.h"
"../long/relative/path/2/include/some/header1.h"
"../long/relative/path/2/include/some/header2.h")
I also have to use the old fashioned include_directories() to make the swig generator know what it needs to know:
include_directories(
"../long/relative/path/1/include
"../long/relative/path/2/include)
Otherwise the %include statements inside .i files won't work any more.
Of course I could set variables containing the paths but then I would provide the information I wanted to get rid of..
Is there a way to either extract the directory information from a target or (better of course) make the SWIG CMake module correctly use it?
My current solution:
With some (very beautiful) CMake magic you can automate listing all header files from the interface part of a library and set the include directories:
function(swig_add_library_dependencies swig_module library_names)
foreach(library_name ${library_names})
get_property(LIBRARY_INCLUDES
TARGET ${library_name}
PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
foreach(INCLUDE_PATH ${LIBRARY_INCLUDES})
include_directories(${INCLUDE_PATH})
file(GLOB_RECURSE header_files "${INCLUDE_PATH}/*.h")
list(APPEND SWIG_MODULE_${swig_module}_EXTRA_DEPS ${header_files})
# export variable to parent scope
set(SWIG_MODULE_${swig_module}_EXTRA_DEPS
${SWIG_MODULE_${swig_module}_EXTRA_DEPS} PARENT_SCOPE)
endforeach()
endforeach()
endfunction()
to be used like this:
swig_add_library_dependencies(<swig_module_name> "library1;library2")
or discretely like this:
swig_add_library_dependencies(<swig_module_name> library1)
swig_add_library_dependencies(<swig_module_name> library2)
Disadvantages:
uses GLOB_RECURSE
only works if target_include_directories had been used correctly
creates dependencies to all header files found in include directories
Have a look at the documentation for get_property:
https://cmake.org/cmake/help/v3.0/command/get_property.html?highlight=get_property
you would do something like this:
get_property(MY_INCLUDES TARGET my_target PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
to get the interface include directories from the target my_target and store them in the variable MY_INCLUDES