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()