Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/cmake/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Cmake 在相应的子作用域中,具有父作用域的变量集为空。为什么?_Cmake - Fatal编程技术网

Cmake 在相应的子作用域中,具有父作用域的变量集为空。为什么?

Cmake 在相应的子作用域中,具有父作用域的变量集为空。为什么?,cmake,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
│   └── 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}" )
set( BAR "Exists in parent scope only." PARENT_SCOPE )
message( STATUS "Variable BAR in ./bar/ = ${BAR}" )
并且
/bar/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}" )
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\u INCLUDE\u DIR
,该变量
添加到任何依赖的可执行文件的包含路径,即
目标\u包含\u目录(我的目标公共栏\u包含\u目录)

我没有看到任何与

如果存在父作用域,则变量将设置在当前作用域上方的作用域中。每个新目录或函数都会创建一个新范围。此命令将变量的值设置到父目录或调用函数中(以适用于当前情况的为准)

/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
上下文:我的项目由几个可执行文件和库组成。对于一个库,例如bar,我想设置一个变量bar_INCLUDE_DIR,它被添加到任何依赖的可执行文件的INCLUDE路径中

有一种比在父范围中设置变量更好的方法。CMake允许目标指定包含目录、预处理器符号等,这取决于目标可以使用。在您的情况下,您可以使用

例如:

target_include_directories(my_target PUBLIC my_inc_dir)

彼得很好地解释了这种行为的原因

在这种情况下,我通常使用的一种解决方法是设置一个缓存变量,该变量在任何地方都可见:

内部
是为了使其在cmake gui中不可见,如果您更改了某些内容(例如文件夹结构中的内容),请确保它得到更新。空字符串是一个描述字符串,如果您认为有必要,可能需要填充它


但是请注意,正确的方法是尽可能地将属性附加到目标,如使用,并通过设置依赖项将它们传播到其他目标。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()

如果只需要在本地和父范围内设置变量,则a可以帮助减少重复:

macro(set_local_and_parent NAME VALUE)
  set(${ARGV0} ${ARGV1})
  set(${ARGV0} ${ARGV1} PARENT_SCOPE)
endmacro()

事实上,我用的就是这个。但是由于
my_target
位于相对于所需库
bar
的同级作用域中,因此我使用通过父作用域的迂回来转发包含目录的位置。传递该信息不需要变量。你所需要做的就是在库中应用
target\u include\u目录
,所有其他使用它的目标将自动在其include路径中获取相关目录。啊,现在我明白你的意思了。确实好得多。谢谢你的坚持@nils表示
注意,不建议使用依赖项路径填充目标的INTERFACE_INCLUDE_目录的INSTALL_接口。这将硬编码到已安装的软件包中,包括在制作软件包的计算机上找到的依赖项的目录路径。
So,您还应该明确指定
BUILD\u INTERFACE
@nils如果您能够让它在示例项目中工作,请将代码发布到某个地方:我在一个项目中尝试过,但它不起作用,但这肯定是因为它是一个复杂的投影,而且我的CMake文件正在做其他一些事情。嗯,考虑一个变量<代码> Foo,它是在父范围中定义的,然后这个变量在子范围中也是可用的(参见更新的示例)。那么,如果两者都位于同一(父)作用域中,为什么
BAR
不能在子作用域中使用呢?我希望文档中会提到这样的行为。这可能会被视为感谢您指出了
缓存
选项。然而,在理解了@Lindydancer的意思之后,我倾向于使用这种方法。这绝对是错误的方法,因为它可能会以随机方式影响每个函数和上下文<代码>缓存将清除原始值,
FORCE
将覆盖相应的
-D
选项。代替使用缓存,您必须考虑在上下文中设置值,在父级和子级中设置值,而不是在所有位置设置值。否则,不要使用变量,而是使用附加到目标或其他对象的属性。@andry最好的方法确实是尽可能多地使用与目标关联的属性,并通过指定依赖项来传播它们。这个答案解决了设置全局变量的需要(如果存在的话,我说这确实是一个解决方法),该变量在任何地方都可见,尤其是在兄弟目录中。FORCE在这里是多余的,因为INTERNAL意味着FORCE。见:
macro(set_local_and_parent NAME VALUE)
  set(${ARGV0} ${ARGV1})
  set(${ARGV0} ${ARGV1} PARENT_SCOPE)
endmacro()