Is there a LINK_DIRECTORIES or equivalent property in CMake? - cmake

I have created a project with a large number of link_directories() commands. I'd now like to store the resulting string of directories into a variable. For include_directories() this is easy using
get_property( test_INCLUDE_DIRECTORIES TARGET test PROPERTY INCLUDE_DIRECTORIES )
however there seems to be no LINK_DIRECTORIES property to do
get_property( test_LINK_DIRECTORIES TARGET test PROPERTY LINK_DIRECTORIES )
Is there a way to get a list of link directories used for a target?
(Note: I realize I could manually track the link directories in a variable myself and then use a single link_directories() but it doesn't seem very clean)

Take a look at the LINK_DIRECTORIES directory property.
The point is that link_directories operates on a per-directory basis (the command affects all targets defined in the same CMakeLists, as well as targets from all of its subdirectories), unlike, for instance, target_include_directories which works on a per-target basis.
You can query the value of the property with:
get_property(test_LINK_DIRECTORIES DIRECTORY PROPERTY LINK_DIRECTORIES)
if called from the same directory as the link_directories call. Otherwise, you need to give the (full or relative) path as an additional argument after DIRECTORY. Unfortunately, I know of no way to obtain the matching directory for an existing target.
Note that in general the use of link_directories is discouraged in CMake, which is probably the main reason why it's not supported very well. Unless you have very good reasons not to, you should always stick with full library paths passed to target_link_libraries. It will save you lots of headaches in the long run.

Related

In CMake, is there a way to set properties on all target dependencies?

In CMake, we can set target properties as either PRIVATE, PUBLIC, or INTERFACE. Both PUBLIC and INTERFACE properties are inherited by any targets that depend on the current target. However, unless I'm missing something, there doesn't seem to be an easy way to define a property that must propagate in the other direction (i.e., inherited by dependencies of the current target).
Most linkers/compilers require that all linked targets have the same value for certain properties (e.g., the exception handling model). If we want to change one of these properties for an executable it requires that it be set on all of its dependencies. Often these dependencies are submodules in our code where we can't modify their CMakeLists.txt files for our specific use-case. This leaves us with two options:
Set a global property (e.g., CMAKE_CXX_FLAGS or add_compile_options) that propagates to all targets in any subdirectories regardless of whether they are dependencies or not.
Explicitly set the properties on each dependent target using target_compile_options. This gets excessive and repetitive depending on the number of dependencies.
It would be nice if there was a functionality that would pass properties down only to dependency targets without having to specify them all individually. Does anyone know how to do this?
For the case of compiler flags that must be consistent for an entire program (including parts that are dynamically linked), such as MSVC's exception handling model, I think the set-something-global approach is suitable. To me, it seems pragmatic and slightly more robust than adding flags to each third-party target one-by-one (ie. what if you forget to handle to one? or what if third-party targets are added or removed in a new version? it seems like a ripe opportunity for human error).
Setting the environment variable [CMAKE_<LANG>_FLAGS] is a good start. You may need to do more if you are building external projects via ExternalProject.
A word of caution for such settings like the exception handling model: You might be tempted to hardcode this global setting inthe CMake files for your project. If your project is used by people other than just you or your company, and especially if its main component is a library and not an executable, it's good practice not to do that. Don't take away your user's ability to choose something like this (unless for some reason your library requires a certain exception handling model, in which case I would still leave this global setting up to them to set, provide documentation stating this, and look into emitting a CMake warning if a user doesn't comply). Instead, use a feature like CMake presets, or only set it if the project is the top-level project
An intersting side note: CMake currently globally "hard-codes" /EHsc for MSVC builds by default. Here's the ticket discussing this

Include one target's include directories in another target

How can I include target1's include directories (made by target_include_directories(target1 t1/include)) into target2?
I know only one way, which is to create a variable and set() include directories in it, and then reuse the variable for another target. But this looks too non-flexible, and I think CMake must have an much easier way.
My targets are shared libraries.
"$<TARGET_PROPERTY:TargetName,INTERFACE_INCLUDE_DIRECTORIES>"
is what I looked for
using like that:
target_include_directories(MyTarget
PRIVATE
"$<TARGET_PROPERTY:OtherTarget,INTERFACE_INCLUDE_DIRECTORIES>"
)
it allows me to include PUBLIC and INTERFACE include directories of target OtherTarget into MyTarget

CMake generator expression dependent on source property

I'm trying to add a compiler flag to all source files that don't have a specific property set.
The first use case is adding -Wshadow -Wuseless-cast to the command line for all non-GENERATED files, later on I'd like to add custom properties for other compiler flags.
I'd like to avoid the "trick" of countermanding default compile flags with per-source flags, because that requires subdirectory CMakeLists.txt to be aware of different options, the need to override them and the proper way to extend a list of flags with
set_property(
SOURCE ...
APPEND_STRING
PROPERTY COMPILE_FLAGS "-Wno-shadow ")
which IMO is a lot of boilerplate that needs to be duplicated often.
The documentation for generator expressions is awfully silent on checking source properties. Can this be done at all?

"config-style" cmake find_package unusable in parent scope

I have the following structure
project_root/
CMakeLists.txt (A)
ext/
CMakeLists.txt (B)
apps/
CMakeLists.txt (C)
The setup seems to be the fundamental issue, only when adding this new "config-style" library.
TL;DR: when find_package(foo) in (B) defines foo::foo as the library, how can I make foo::foo available in the parent scope so that target_link_libraries(tgt foo) will work for both (A) and (C)?
List (A) defines my project's options, such as what drivers to compile support for.
add_subdirectory(ext) takes place, and the needed external libraries are found. They are a mixture of add_subdirectory and find_package. List (B) populates lists for extra include directories, libraries, and compile time definitions, making them available to (A) (and subsequently (C)) with
set(MYPROJ_EXTRA_INC_DIRS "${MYPROJ_EXTRA_INC_DIRS}" PARENT_SCOPE)
set(MYPROJ_EXTRA_LIBS "${MYPROJ_EXTRA_LIBS}" PARENT_SCOPE)
set(MYPROJ_EXTRA_DEFINES "${MYPROJ_EXTRA_DEFINES}" PARENT_SCOPE)
List (A) now adds my library, including these extra directories, adding these extra definitions, and ultimately
target_link_libraries(${MYPROJ_LIB_NAME} ${MYPROJ_EXTRA_LIBS})
When the applications are requested to be built, add_subdirectory(apps) takes place, and list (C) defines a simple macro that creates an executable using the specified dependencies. The relevant part
target_link_libraries(${appName} ${MYPROJ_LIB_NAME} ${MYPROJ_EXTRA_LIBS})
This has been working very well for a long time. However, I added support for a new library that uses config-style find_package definitions, and I can't figure out how to use it correctly.
Call this new library dependency foo. It ultimately defines a single foo_LIBRARY which is foo::foo. My understanding was that I would need to do target_link_libraries(tgt foo), which works in list (A) for my library. However, it does not work for the applications, and in the macro I have to do find_package(foo) again for every executable.
Is there a way to use the existing approach (list(APPEND MYPROJ_EXTRA_LIBS <something>)) that does not require running find_package every time?
I've exhausted every reasonable option, and either get that -lfoo is not defined (if I just append foo to the list like I thought I should be), or find_package() is missing for an IMPORTED or ALIAS target. AKA since find_package(foo) happens in (B), by the time we reach (C) this target is not available. I tried making an ALIAS, but the error was then something that amounts to ALIAS cannot be created to an IMPORTED library.
Results of find_package call(both CONFIG and MODULE) are intended be used in the same directory or below. You are just lucky in that simple propagating of variables into PARENT_SCOPE makes results of find_package usable by the parent.
add_subdirectory(ext) takes place, and the needed external libraries are found.
Instead of ext/CMakeLists.txt included with add_subdirectory create CMake file (e.g. external.cmake) for being included via include. Because include command doesn't introduce new variable's scope, its find_package calls works for the main CMakeLists.txt.
Many existed projects process their dependencies in include files.
Another approach would be propagating results of find_package calls from subdirectory to the parent by creating INTERFACE library target which itself uses these results:
add_library(MyLibExtra INTERFACE)
target_link_libraries(MyLibExtra INTERFACE ${MYPROJ_EXTRA_LIBS})
target_include_directories(MyLibExtra INTERFACE ${MYPROJ_EXTRA_INC_DIRS})
target_compile_definitions(MyLibExtra INTERFACE ${MYPROJ_EXTRA_DEFINES})

Is there an include once command for cmake files?

Does anyone know a simple way to apply the "include once" pattern in CMake files? In C/C++ it used to require a #ifdef / #endif pair in the beginning and end of the header file until #pragma once became common. Of course, it's possible to do the same in CMake, but I thought it'd be nice if it didn't require an explicit conditional statement.
Re-edition: It seems that the return() command should do it. And I'd define a macro like this:
macro(include_once)
if (INCLUDED_${CMAKE_CURRENT_LIST_FILE})
return()
endif()
set(INCLUDED_${CMAKE_CURRENT_LIST_FILE} true)
endmacro()
Use the macro in the beginning of your file, without any arguments. Because it's a macro, the return is from the include command for the file, not from the macro itself.
Notice that the created variable has an odd name, but CMake seems to accept this.
I used to use the logic that others have suggested as the solution. Then I learned that cmake has this functionality built in.
Take a look at the include_guard() command. I believe this will do what you want.
New in version 3.10.
Provides an include guard for the file currently being processed by CMake.
include_guard([DIRECTORY|GLOBAL])
Sets up an include guard for the current CMake file (see the CMAKE_CURRENT_LIST_FILE variable documentation).
CMake will end its processing of the current file at the location of the include_guard() command if the current file has already been processed for the applicable scope (see below). This provides functionality similar to the include guards commonly used in source headers or to the #pragma once directive. If the current file has been processed previously for the applicable scope, the effect is as though return() had been called. Do not call this command from inside a function being defined within the current file.
An optional argument specifying the scope of the guard may be provided. Possible values for the option are:
DIRECTORY
The include guard applies within the current directory and below. The file will only be included once within this directory scope, but may be included again by other files outside of this directory (i.e. a parent directory or another directory not pulled in by add_subdirectory() or include() from the current file or its children).
GLOBAL
The include guard applies globally to the whole build. The current file will only be included once regardless of the scope.
If no arguments given, include_guard has the same scope as a variable, meaning that the include guard effect is isolated by the most recent function scope or current directory if no inner function scopes exist. In this case the command behavior is the same as:
if(__CURRENT_FILE_VAR__)
return()
endif()
set(__CURRENT_FILE_VAR__ TRUE)
The simplest guard pattern against multiple module's inclusion would be
if(<something-which-is-defined-in-your-module>)
return()
endif()
E.g., if your CMake module defines a function foo_func, you may use this guard:
if(COMMAND foo_func)
return()
endif()
Do you actually need a guard?
Depended on things, defined in the module, the module may require protection agains multiple inclusion or not.
In many simple cases a guard is not needed: the module's code will work even when included multiple times.
But in some other cases, wrong protection may break the usage of the proptected module.
A module defines a function or a macro: guard is not needed.
CMake allows to define the functions and macros many times.
A module defines a constant variable: guard is not needed.
Like with function, CMake allows to define the variable many times.
But if you use guard in that case, it should check variable, not a function:
if(foo_var)
return()
endif()
This is because functions have global visibility, but variables have local visibility. That is, if you module will be included into other subtree, the function will be already visible in that subtree, but the variable is not.
A module defines a global variable via set(CACHE): guard is needed only if variable is defined as set(CACHE INTERNAL).
Variables defined via, e.g., set(CACHE STRING) or find_library don't require guards.
A module defines global property: guard is needed.
Note, that if your module uses simple (not CACHE) variable as global, it cannot work in multiple subtrees, so guard should be
if(foo_var)
message(SEND_ERROR "Module <...> cannot be included twice")
endif()