使用PARENT_SCOPE设置的变量在相应的子范围中为空。为什么?

时间:2015-04-27 10:15:32

标签: cmake

考虑以下最小例子:

.
├── 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 )

5 个答案:

答案 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()
相关问题