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?
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.
CMake variables are case sensitive. See documentation. the second marked as a warning (default message type).
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.
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.
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
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
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With