Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between CMake variables and properties?

Tags:

cmake

CMake variables and properties seem to accomplish very similar things, and I have been unable to understand the difference between them.

They each have their own sections of documentation, but both can affect the build system, both "pre-exist", and both can be dynamically generated based on other CMake commands. It seems like they should have separate purposes. What are they?

like image 624
user1489829 Avatar asked Apr 21 '18 17:04

user1489829


People also ask

How do you define a variable in CMake?

Options and variables are defined on the CMake command line like this: $ cmake -DVARIABLE=value path/to/source You can set a variable after the initial `CMake` invocation to change its value. You can also undefine a variable: $ cmake -UVARIABLE path/to/source Variables are stored in the `CMake` cache.

Are CMake variables case sensitive?

CMake variables are case sensitive. See documentation. the second marked as a warning (default message type).

What is CMake cache variable?

The CMake cache may be thought of as a configuration file. The first time CMake is run on a project, it produces a CMakeCache. txt file in the top directory of the build tree. CMake uses this file to store a set of global cache variables, whose values persist across multiple runs within a project build tree.

How do I view environment variables in CMake?

Use the syntax $ENV{VAR} to read environment variable VAR . To test whether an environment variable is defined, use the signature if(DEFINED ENV{<name>}) of the if() command.


2 Answers

I think Stephen Newell's answer captures the main motivation for properties (and the c++ analogy of member variables vs non-member variables is very helpful).

However, in addition to target properties, which are scoped to targets there are various other kinds which can instead be scoped to eg source, install, directory, global etc.

Its possible to have a ('global') variable and (global) property with the same name exist simultaneously (but the usefulness of such is not obvious).

In summary, properties are primarily useful for their being "scoped" to something like a target. Global properties are also scoped to a single "global object". (In Stephen's analogy they might be member variables of a singleton). Properties are set/get with a different syntax to variables. And a property with a given scope can simultaneously exist with others of the same name in any other scope (including global) as well as a regular variable of the same name.

One possible usefulness of combining global properties and variables is given here:

A global property can be a useful uncached global variable. Many target properties are initialised from a matching variable with CMAKE_ at the front. So setting CMAKE_CXX_STANDARD, for example, will mean that all new targets created will have CXX_STANDARD set to that when they are created

like image 114
Declan Avatar answered Oct 11 '22 13:10

Declan


A very short and simple way to think about it is that properties are variables scoped to a target. For example:

add_executable(foo foo.cpp)
set_target_properties(foo PROPERTIES
    CXX_STANDARD 14
    CXX_EXTENSIONS OFF
)
# Build foo with c++11 for some reason
add_executable(foo11 foo.cpp)
set_target_properties(foo11 PROPERTIES
    CXX_STANDARD 11
    CXX_EXTENSIONS OFF
)

If CMakeLists.txt was written in C++, that might look something like this:

const char * src_files[] = { "foo.cpp" };
executable foo{src_files};
foo.setCxxStandard(14);
foo.setCxxExtensions(false);

executable foo11{src_files};
foo.setCxxStandard(11);
foo.setCxxExtensions(false);

If we used variables for these things, it'd look more like this:

// globals
int CMAKE_CXX_STANDARD = 14;
bool CMAKE_CXX_EXTENSIONS = false;

// later, in a function
const char * src_files[] = { "foo.cpp" };
executable foo{src_files}; // foo copies global settings

CMAKE_CXX_STANDARD = 11;
executable foo11{src_files};

Because properties are part of a target instead of globals, this also means they can be exported. Sanitized rom one of my projects:

set_target_properties(Foo::bar PROPERTIES
    INTERFACE_COMPILE_FEATURES "cxx_std_14"
    INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include/"
    INTERFACE_SOURCES "${_IMPORT_PREFIX}/include/foo/bar.hpp"
)

This means if you import Foo::bar (probably through something like find_package(Foo)), your project already knows that things linking against Foo::bar need to use C++14 (INTERFACE_COMPILE_FEATURES), it needs to add something to the include path (INTERFACE_INCLUDE_DIRECTORIES), and there are some source files it cares about (my headers, INTERFACE_SOURCES).

like image 26
Stephen Newell Avatar answered Oct 11 '22 14:10

Stephen Newell