CMake中变量与option的回退(fallback)机制及相关问题咨询
一、if()的回退机制怎么理解?
CMake里的option和普通变量是两个不同的概念:option本质是缓存中的布尔选项,会被持久化到CMakeCache.txt里;而普通变量是通过set()定义的、仅存在于当前作用域的变量。
当你用if(WITH_FOO)做判断时,CMake会按以下顺序查找取值:
- 先找当前作用域中是否存在同名的普通变量(用
set()定义的,未加CACHE关键字); - 如果找不到普通变量,就会回退到查找同名的
option(也就是缓存中的布尔选项)的值来做判断。
你提供的代码里,WITH_FOO是用option定义的缓存选项,unset(WITH_FOO)只能删除当前作用域的普通变量——但本来就没有这个普通变量,所以unset等于没生效。之后的if(WITH_FOO)找不到普通变量,就会用缓存里option(WITH_FOO)的默认值ON,这就是为什么明明执行了unset,判断结果还是真。
二、命令行cmake .. -DMacro_name=value是否始终定义变量?
是的。通过-D参数传递的参数,会直接创建一个缓存变量,优先级高于同名的option。也就是说,如果先用-D定义了某个变量,后续的option指令不会覆盖这个值,只会用缓存里已有的值。
比如你脚本里的cmake .. -DPULISH_VERSION=ON,会直接在缓存中定义PULISH_VERSION=ON,后续的option(PULISH_VERSION ...)不会修改这个值,而是直接沿用缓存里的设置。
三、没有同名变量时,option会不会定义变量?
option会定义一个缓存变量,但这个缓存变量和普通set()定义的变量不是一回事。unset()默认只删除当前作用域的普通变量,不会删除缓存中的option变量。这也是为什么你代码里unset(WITH_FOO)后,if(WITH_FOO)还能拿到option的默认值——缓存里的option根本没被删掉。
四、结合你的代码看问题所在
你执行cmake .. -DPULISH_VERSION=ON后,代码流程是这样的:
option(PULISH_VERSION ...)直接用命令行传入的ON值;- 进入
if(PULISH_VERSION)分支,因为WITH_FOO的option默认是ON,所以if(WITH_FOO)成立,执行unset(WITH_FOO)——但这步没删掉缓存里的option; - 下一个
if(WITH_FOO)找不到普通变量,回退到缓存里的option(WITH_FOO)=ON,所以又会执行message("Attention! FOO is enabled!")和添加编译定义,这和你想要的“发布版本关闭FOO”的逻辑不符。
要解决这个问题,把unset(WITH_FOO)改成set(WITH_FOO OFF),这样就定义了一个普通变量WITH_FOO=OFF,后续的if(WITH_FOO)会优先用这个普通变量的值,不会再回退到option的默认值。
内容的提问来源于stack exchange,提问作者John

