Consider the following minimal example:
.
├── bar
│ └── CMakeLists.txt
└── CMakeLists.txt
where ./CMakeLists.txt
is
Context: my project consists of several executables and libraries. For a library, e.g. bar, I'd like to set a variable bar_INCLUDE_DIR which is added to the include paths of any depending executable.
There is a much better way to do this than to set variables in the parent scope. CMake allows a target to specify include directories, preprocessor symbols etc. that depending targets can use. In your case, you can use target_include_directories.
For example:
target_include_directories(my_target PUBLIC my_inc_dir)
Peter explained well the reason for this behaviour.
A workaround I usually use in this case is to set a cached variable, which will be visible everywhere:
set(BAR "Visible everywhere"
CACHE INTERNAL ""
)
INTERNAL
is to make it not visible from cmake-gui. INTERNAL implies FORCE, making sure it gets updated if you change something for example in your folder structure. The empty string is a description string, that you might want to fill if you believe it's necessary.
Note, though, that the correct approach is attaching properties to targets whenever possible, like using target_incude_directories, and propagate them to other targets by setting dependencies.
Each variable in the cmake has it's own scope so it is dangerous use case where a variable automatically propagates in a child context, because it can interfere with it from a parent scope!
But you can set just another variable in a child scope to test it later instead of rereading a parent one:
./bar/CMakeLists.txt
:
set( BAR "Exists in parent scope only." PARENT_SCOPE )
set( _somerandomid_BAR "Exists in child scope only.")
message( STATUS "Variable BAR in ./bar/ = ${_somerandomid_BAR}" )
Now, if you have loops in your code, then you can test both variables:
foreach(...)
...
# read a variable token name and token value, for example, from a configuration file
set(my_var_name_token ...)
set(my_var_value_token ...)
...
# parse a variable name and value tokens into a real name and value
set(my_var_name ...)
set(my_var_value ...)
...
if (DEFINED ${my_var_name})
if (DEFINED local_${my_var_name})
message("has been defined before and was resetted");
else()
message("has been defined before and was not resetted");
endif()
else()
if (DEFINED local_${my_var_name})
message("has not been defined before and was setted");
else()
message("has not been defined before and was not touched");
endif()
endif()
...
# sets parsed name and value into cmake context
set(${my_var_name} "..." PARENT_SCOPE)
# Do save all values has been setting from this function to differently compare and
# validate them versus already existed before:
# 1. If has been set before, then must be set to the same value, otherwise - error
# 2. If has not been set before, then should set only once, otherwise - ignore new
# value (a constant variable behaviour)
set(local_${my_var_name} "...")
...
endforeach()
I do not see anything that is not consistent with the SET command documentation
If PARENT_SCOPE is present, the variable will be set in the scope above the current scope. Each new directory or function creates a new scope. This command will set the value of a variable into the parent directory or calling function (whichever is applicable to the case at hand).
./bar/CMakeLists.txt
set( BAR "This is bar." PARENT_SCOPE ) #<-- Variable is set only in the PARENT scope
message( STATUS "Variable BAR in ./bar/ = ${BAR}" ) #<--- Still undefined/empty
You can always do:
set( BAR "This is bar." ) #<-- set in this scope
set( BAR ${BAR} PARENT_SCOPE ) #<-- set in the parent scope too
Grep for PARENT_SCOPE
in the delivered modules in your installation, for example FindGTK2
if(GTK2_${_var}_FOUND)
set(GTK2_LIBRARIES ${GTK2_LIBRARIES} ${GTK2_${_var}_LIBRARY})
set(GTK2_LIBRARIES ${GTK2_LIBRARIES} PARENT_SCOPE)
endif()