Is there an include once command for cmake files? - cmake

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()

Related

CMake: How to make dependence on package optional?

I would like MPI to be optional for using my code.
I currently have a Cmake project that includes MPI as follows in the CMakeLists.txt:
find_package(MPI REQUIRED)
My some of my .cpp and .h files have the following:
#define MPI_available true
On a system where MPI is not available, one can remove the first statement and change the second statement into #define MPI_available false. Then the code is structured such that it doesn't attempt to include ``mpi.h''. However, I would prefer CMAKE to detect the presence/absence of MPI, and set the MPI_available flag for compiling the source files accordingly.
I believe setting
find_package(MPI QUIET)
will ensure that cmake does not choke when it cannot find MPI. Is this the preferred way of getting where I want to be? If so, how to link this to the flag? If not, what should I do to make MPI optional when cmake-> making the code.
Call to find_package(someLib) sets a variable someLib_FOUND. You can use it to check whether the someLib is available, then you can set a compile definitions. Something like:
find_package(MPI)
if(NOT MPI_FOUND)
target_compile_definitions(yourTarget PUBLIC MPI_available=0)
endif()
Calling cmake's default FindMPI.cmake module through find_package(MPI) already ensures that cmake will define the variable MPI_FOUND to reflect whether mpi was found or not.
Afterwards, you can use MPI_FOUND to achieve your goal. For example, you can generate your project's config.h with calls to cmake's config_file to define macros such as MPI_available, and update your code accordingly.

How can I scope a CMake function so it can't be accessed from outside a file?

I'm trying to write some CMake code in a relatively complex project and I have a module that internally includes another module. The problem is, whenever I include my module, all of the functioned defined in the module it internally includes become available at a global level! This effectively is polluting my global namespace with a bunch of functions I didn't explicitly ask for.
For example:
# CMakeLists.txt
# Include my module
include(MyModule)
# Call a function from my module
my_module_function()
# HERE IS THE PROBLEM -- functions from "AnotherModule" are visible here!
# This call works
another_module_function()
Inside my module:
# MyModule.cmake
# Include another module
# - This other module is written and supported by someone else so I can't modify it
# - No functions from "AnotherModule" will be used outside of "MyModule"
include(AnotherModule)
# Define my function
function(my_module_function)
# Call a function from the other module
another_module_function()
endfunction()
Is there any way inside MyModule.cmake that I can import the functions from AnotherModule.cmake without having them be visible outside of my own module? This other module is written by someone else so I don't have control over it and it includes other functions with very generic names like one called parse_arguments that could potentially cause naming conflicts later on.
Making the functions from AnotherModule.cmake fully invisible outside of MyModule.cmake would be ideal, but even if there were a simple way to just simulate a namespace for the imported functions to be in that would be better than nothing.
In CMake macros and functions has global visibility and nothing can change that.
Often a function, "internal" to some module, is defined with underscore (_) prefix. Such prefix plays the role of a signal to outer code "not to use me". But this is only a convention, CMake doesn't enforce anything about underscore-prefixed names.
If including a module has only immediate effects, that is defines custom commands/targets but does not export functions/macros/variables for outer code, you may consider to wrap it with external project (ExternalProject_Add). An external project is a separate CMake project, and none its CMake things like variables or functions are visible outside it.

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 a LINK_DIRECTORIES or equivalent property in 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.