考虑以下最小例子:
.
├── bar
│ └── CMakeLists.txt
└── CMakeLists.txt
其中./CMakeLists.txt
是
project( foo )
cmake_minimum_required( VERSION 2.8 )
set( FOO "Exists in both, parent AND in child scope." )
add_subdirectory( bar )
message( STATUS "Variable BAR in ./ = ${BAR}" )
message( STATUS "Variable FOO in ./ = ${FOO}" )
和./bar/CMakeLists.txt
是
set( BAR "Exists in parent scope only." PARENT_SCOPE )
message( STATUS "Variable BAR in ./bar/ = ${BAR}" )
cmake
输出的相关部分是:
...
-- Variable BAR in ./bar/ =
-- Variable FOO in ./bar/ = Exists in both, parent AND in child scope.
-- Variable BAR in ./ = Exists in parent scope only.
-- Variable FOO in ./ = Exists in both, parent AND in child scope.
...
由于变量BAR
被放置在父作用域中,我希望它在当前子作用域中(以及随后的作用域)中可用 - 就像变量FOO
一样,这是定义父开头的范围。但是从上面的线条可以看出
变量BAR
在./bar/CMakeLists.txt
中为空,这导致我
以下问题:
为什么修改后的父作用域不能在子级中立即访问
范围,./bar/
?这可以减轻吗?如果有,怎么样?如果不是,那是什么
变通?还是我完全错过了一些明显的东西?
上下文:我的项目由几个可执行文件和库组成。为一个
库,例如bar
,我想设置一个变量bar_INCLUDE_DIR
被添加到任何依赖可执行文件的包含路径,即target_include_directories( my_target PUBLIC bar_INCLUDE_DIR )
。
答案 0 :(得分:7)
我没有看到任何与SET command documentation
不一致的内容如果存在PARENT_SCOPE,则变量将在当前范围上方的范围内设置。每个新目录或函数都会创建一个新范围。此命令将变量的值设置为父目录或调用函数(适用于手头的情况)。
<强> ./酒吧/的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
你总是可以这样做:
set( BAR "This is bar." ) #<-- set in this scope
set( BAR ${BAR} PARENT_SCOPE ) #<-- set in the parent scope too
安装中已交付模块中PARENT_SCOPE
的Grep,例如FindGTK2
if(GTK2_${_var}_FOUND)
set(GTK2_LIBRARIES ${GTK2_LIBRARIES} ${GTK2_${_var}_LIBRARY})
set(GTK2_LIBRARIES ${GTK2_LIBRARIES} PARENT_SCOPE)
endif()
答案 1 :(得分:7)
我通常在这种情况下使用的解决方法是设置一个缓存变量,该变量将在任何地方可见:
set(BAR "Visible everywhere"
CACHE INTERNAL "" FORCE
)
INTERNAL
是让cmake-gui无法看到它。 FORCE
是为了确保在您的文件夹结构中更改某些内容时更新它。空字符串是一个描述字符串,如果您认为有必要,可以填写该字符串。
请注意,正确的方法是尽可能将属性附加到目标,例如使用target_incude_directories
,并通过设置依赖项将它们传播到其他目标。
答案 2 :(得分:4)
上下文:我的项目由几个可执行文件和库组成。对于图书馆,例如吧,我想设置一个变量bar_INCLUDE_DIR,它被添加到任何依赖可执行文件的包含路径中。
有一种更好的方法可以在父范围内设置变量。 CMake允许目标指定依赖目标可以使用的包含目录,预处理器符号等。在您的情况下,您可以使用target_include_directories。
例如:
target_include_directories(my_target PUBLIC my_inc_dir)
答案 3 :(得分:1)
如果您只需要在本地和父作用域中设置一个变量,macro 可以帮助减少重复:
macro(set_local_and_parent NAME VALUE)
set(${ARGV0} ${ARGV1})
set(${ARGV0} ${ARGV1} PARENT_SCOPE)
endmacro()
答案 4 :(得分:0)
cmake中的每个变量都有其自己的作用域,因此在危险的用例中,变量会在子上下文中自动传播,因为它可能会干扰父作用域!
但是您可以在子范围内仅设置另一个变量以稍后进行测试,而不必重新读取父变量:
./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}" )
现在,如果代码中包含循环,则可以测试两个变量:
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()