CMake语法—缓存变量(Cache Variable)

1 CMake缓存变量

Normal Variable,普通变量,相当于一个局部变量。在同一个CMake工程中使用,会有作用域限制或区分。

Cache Variable,缓存变量,相当于一个全局变量。在同一个CMake工程中任何地方都可以使用。

2 定义缓存变量

2.1 定义格式

set(<variable> <value>... CACHE <type> <docstring> [FORCE])
  • variable:变量名称
  • value:变量值列表
  • CACHE:cache变量的标志
  • type:变量类型,取决于变量的值。类型分为:BOOL、FILEPATH、PATH、STRING、INTERNAL
  • docstring:必须是字符串,作为变量概要说明
  • FORCE:强制选项,强制修改变量值

2.2 定义示例代码

  1. 代码结构

    • learn_cmake:为根目录
    • build:为CMake配置输出目录(在此例中即生成sln解决方案的地方)
    • cmake_config.bat:执行CMake配置过程的脚本(双击直接运行)
    • CMakeLists.txt:CMake脚本
  2. 示例代码(CMakeLists.txt文件内容)

    cmake_minimum_required(VERSION 3.18)
    
    # 设置工程名称
    set(PROJECT_NAME KAIZEN) # 设置工程版本号
    set(PROJECT_VERSION "1.0.0.10" CACHE STRING "默认版本号") # 工程定义
    project(${PROJECT_NAME}
    LANGUAGES CXX C
    VERSION ${PROJECT_VERSION}
    ) # 打印开始日志
    message(STATUS "\n########## BEGIN_TEST_CACHE_VARIABLE") # 定义一个STRIING类型缓存变量(不加FORCE选项)
    set(MY_GLOBAL_VAR_STRING_NOFORCE "abcdef" CACHE STRING "定义一个STRING缓存变量")
    message("MY_GLOBAL_VAR_STRING_NOFORCE: ${MY_GLOBAL_VAR_STRING_NOFORCE}") # 定义一个STRIING类型缓存变量(加FORCE选项)
    set(MY_GLOBAL_VAR_STRING "abc" CACHE STRING "定义一个STRING缓存变量" FORCE)
    message("MY_GLOBAL_VAR_STRING: ${MY_GLOBAL_VAR_STRING}") # 定义一个INTERNAL类型缓存变量
    set(MY_GLOBAL_VAR_INTERNAL "aaabbb" CACHE INTERNAL "定义一个INTERNAL类型的缓存变量")
    message("MY_GLOBAL_VAR_INTERNAL: ${MY_GLOBAL_VAR_INTERNAL}") # 定义一个BOOL类型缓存变量
    set(MY_GLOBAL_VAR_BOOL OFF CACHE BOOL "定义一个BOOL类型的缓存变量")
    message("MY_GLOBAL_VAR_BOOL: ${MY_GLOBAL_VAR_BOOL}") # 定义一个FILEPATH类型缓存变量
    set(MY_GLOBAL_VAR_FILEPATH "F:/learn_cmake/CMakeLists.txt" CACHE FILEPATH "定义一个FILEPATH类型的缓存变量")
    message("MY_GLOBAL_VAR_FILEPATH: ${MY_GLOBAL_VAR_FILEPATH}") # 定义一个PATH类型缓存变量
    set(MY_GLOBAL_VAR_PATH "F:/learn_cmake" CACHE PATH "定义一个PATH类型的缓存变量")
    message("MY_GLOBAL_VAR_PATH: ${MY_GLOBAL_VAR_PATH}") # 缓存变量3个值(验证值列表)
    set(MY_GLOBAL_VAR_THRESS "aaa" "bbb" "ccc" CACHE STRING "定义一个缓存变量(3个值)")
    message("MY_GLOBAL_VAR_THRESS: ${MY_GLOBAL_VAR_THRESS}") # 修改STRING类型缓存变量(不加FORCE选项)
    set(MY_GLOBAL_VAR_STRING "modifyabc" CACHE STRING "修改STRING缓存变量")
    message("MY_GLOBAL_VAR_STRING: ${MY_GLOBAL_VAR_STRING}") # 修改STRING类型缓存变量(加FORCE选项)
    set(MY_GLOBAL_VAR_STRING "modifyabcforce" CACHE STRING "修改STRING缓存变量" FORCE)
    message("MY_GLOBAL_VAR_STRING: ${MY_GLOBAL_VAR_STRING}") # 打印结束日志
    message(STATUS "########## END_TEST_CACHE_VARIABLE\n")
  3. cmake_config.bat

    @echo off
    set currentDir=%~dp0
    set buildDir=%currentDir%
    set cmakeOutputDir=%currentDir%\build
    cmake -S %buildDir% -B %cmakeOutputDir% -G"Visual Studio 16 2019" -T v140 -A x64
    pause

2.3 运行结果

  1. 本地环境

    本地安装VS版本:Visual Studio 2019(2015工具集)

    CMake版本:3.18.2

    F:\learn_cmake
    λ cmake --version
    cmake version 3.18.2 CMake suite maintained and supported by Kitware (kitware.com/cmake).
  2. 输出结果

    -- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.17763.
    -- The CXX compiler identification is MSVC 19.0.24245.0
    -- The C compiler identification is MSVC 19.0.24245.0
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting C compile features
    -- Detecting C compile features - done
    --
    ########## BEGIN_TEST_CACHE_VARIABLE
    MY_GLOBAL_VAR_STRING_NOFORCE: abcdef
    MY_GLOBAL_VAR_STRING: abc
    MY_GLOBAL_VAR_INTERNAL: aaabbb
    MY_GLOBAL_VAR_BOOL: OFF
    MY_GLOBAL_VAR_FILEPATH: F:/learn_cmake/CMakeLists.txt
    MY_GLOBAL_VAR_PATH: F:/learn_cmake
    MY_GLOBAL_VAR_THRESS: aaa;bbb;ccc
    MY_GLOBAL_VAR_STRING: abc
    MY_GLOBAL_VAR_STRING: modifyabcforce
    -- ########## END_TEST_CACHE_VARIABLE -- Configuring done
    -- Generating done
    -- Build files have been written to: F:/learn_cmake/build
    请按任意键继续. . .

2.4 小结

  1. 定义缓存变量时,可以不加FORCE选项:

    示例程序中第19行添加FORCE,第23行未加FORCE选项,结果没有发现两者差异。后来其它类型都没有加FORCE选项。

  2. 修改缓存变量时,一定要加FORCE选项,否则修改无效:

    示例程序中第47行,试图修改缓存变量的值,没有添加FORCE选项,结合修改运行结果第23行可知,修改无效。

    相反,程序中第51行,企图修改值时添加FORCE选项,结合修改运行结果第24行可知,修改生效。

  3. Cache变量都会保存在CMakeCache.txt文件中。

  4. 不论定义或修改缓存变量时,建议都加上FORCE选项。结合1、2项所得。

3 CMakeCache.txt文件

CMake中的缓存变量都会保存在CMakeCache.txt文件中。

至于为何这样干,可以逆向理解,如若不保存,想作为全局变量根本没法实现。

限于篇幅,在此不展示CMakeCache.txt中的内容。因为其中包括很多默认的缓存变量,比较多。可在本地自行运行示例程序后查看即可。

如上示例中定义的缓存变量,可以从CMakeCache.txt中找到保存记录,如下图:

细心人估计已经从上图查找结果中看出差异:为什么前6个缓存变量与第7个缓存变量行数竟然差距100多行?

因为:第7个变量被定义为INTERNAL类型,CMakeCache.txt文件中会把INTERNAL类型与EXTERNAL类型分开记录。

CMakeCache.txt文件中把缓存变量分为两种:

  • INTERNAL cache entries 内部缓存条目
  • EXTERNAL cache entries 外部缓存条目

另外,可能还有人会问:为什么运行结果打印输出共9行(16~24),查找结果中只有7行?

因为:示例程序中有两行(47、51)是修改缓存变量,所以修改后缓存变量值只是变化,变量名称(MY_GLOBAL_VAR_STRING)不变化。

4 缓存变量在函数(function)、宏(macro)、子目录(subdirectory)、包含模块(include)中应用

4.1 应用示例

  1. 代码结构

    • learn_cmake:为根目录
    • build:为CMake配置输出目录(在此例中即生成sln解决方案的地方)
    • cmake_config.bat:执行CMake配置过程的脚本(双击直接运行)
    • CMakeLists.txt:父目录的CMake脚本
    • src:子目录,包含测试子目录的CMakeLists.txt文件
    • cmake:子目录,包含测试include模块的test_include_cache.cmake文件
  2. 父目录CMakeLists.txt

    cmake_minimum_required(VERSION 3.18)
    
    # 设置工程名称
    set(PROJECT_NAME KAIZEN) # 设置工程版本号
    set(PROJECT_VERSION "1.0.0.10" CACHE STRING "默认版本号") # 工程定义
    project(${PROJECT_NAME}
    LANGUAGES CXX C
    VERSION ${PROJECT_VERSION}
    ) # 打印开始日志
    message(STATUS "\n########## BEGIN_TEST_CACHE_VARIABLE") # 定义一个STRIING类型缓存变量
    set(MY_GLOBAL_VAR_STRING "abcdef" CACHE STRING "定义一个STRING缓存变量" FORCE)
    message("MY_GLOBAL_VAR_STRING_1: ${MY_GLOBAL_VAR_STRING}") function(test_cache_func)
    # 访问函数外定义的缓存变量
    message("MY_GLOBAL_VAR_STRING_2: ${MY_GLOBAL_VAR_STRING}") # 修改函数外定义的缓存变量
    set(MY_GLOBAL_VAR_STRING "abcdef modify by func" CACHE STRING "修改缓存变量" FORCE) # 访问修改后的函数外缓存变量
    message("MY_GLOBAL_VAR_STRING_3: ${MY_GLOBAL_VAR_STRING}") # 在函数中新定义一个STRIING类型缓存变量
    set(MY_GLOBAL_VAR_STRING_FUNC "I am a func inner cache variable" CACHE STRING "定义一个STRING缓存变量" FORCE)
    message("MY_GLOBAL_VAR_STRING_FUNC_1: ${MY_GLOBAL_VAR_STRING_FUNC}") endfunction() message(STATUS "\n##### BEGIN_TEST_FUNC_CACHE_VAR") # 调用函数
    test_cache_func() # 访问缓存变量的值
    message("MY_GLOBAL_VAR_STRING_4: ${MY_GLOBAL_VAR_STRING}")
    message("MY_GLOBAL_VAR_STRING_FUNC_2: ${MY_GLOBAL_VAR_STRING_FUNC}") message(STATUS "##### BEGIN_TEST_FUNC_CACHE_VAR") macro(test_cache_macro)
    # 访问宏外定义的缓存变量
    message("MY_GLOBAL_VAR_STRING_5: ${MY_GLOBAL_VAR_STRING}") # 修改宏外定义的缓存变量
    set(MY_GLOBAL_VAR_STRING "abcdef modify by macro" CACHE STRING "修改缓存变量" FORCE) # 访问修改后的宏外缓存变量
    message("MY_GLOBAL_VAR_STRING_6: ${MY_GLOBAL_VAR_STRING}") # 在宏中新定义一个STRIING类型缓存变量
    set(MY_GLOBAL_VAR_STRING_MACRO "I am a macro inner cache variable" CACHE STRING "定义一个STRING缓存变量" FORCE)
    message("MY_GLOBAL_VAR_STRING_MACRO_1: ${MY_GLOBAL_VAR_STRING_MACRO}")
    endmacro() message(STATUS "\n##### BEGIN_TEST_MACRO_CACHE_VAR") # 调用宏
    test_cache_macro() # 访问缓存变量的值
    message("MY_GLOBAL_VAR_STRING_7: ${MY_GLOBAL_VAR_STRING}")
    message("MY_GLOBAL_VAR_STRING_MACRO_2: ${MY_GLOBAL_VAR_STRING_MACRO}") message(STATUS "##### END_TEST_MACRO_CACHE_VAR") message(STATUS "\n##### BEGIN_TEST_SUBDIR_CACHE_VAR") add_subdirectory(src) # 访问缓存变量的值
    message("MY_GLOBAL_VAR_STRING_10: ${MY_GLOBAL_VAR_STRING}")
    message("MY_GLOBAL_VAR_STRING_SUBDIR_2: ${MY_GLOBAL_VAR_STRING_SUBDIR}") message(STATUS "##### END_TEST_SUBDIR_CACHE_VAR") message(STATUS "\n##### BEGIN_TEST_INCLUDE_CACHE_VAR") list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
    include(test_include_cache) # 访问缓存变量的值
    message("MY_GLOBAL_VAR_STRING_13: ${MY_GLOBAL_VAR_STRING}")
    message("MY_GLOBAL_VAR_STRING_INCLUDE_2: ${MY_GLOBAL_VAR_STRING_INCLUDE}") message(STATUS "##### END_TEST_INCLUDE_CACHE_VAR") # 打印结束日志
    message(STATUS "########## END_TEST_CACHE_VARIABLE\n")
  3. src子目录CMakeLists.txt

    cmake_minimum_required(VERSION 3.18)
    
    # 访问父目录的缓存变量
    message("MY_GLOBAL_VAR_STRING_8: ${MY_GLOBAL_VAR_STRING}") # 修改父目录的缓存变量
    set(MY_GLOBAL_VAR_STRING "abcdef modify by subdir" CACHE STRING "修改缓存变量" FORCE) # 访问修改后的父目录缓存变量
    message("MY_GLOBAL_VAR_STRING_9: ${MY_GLOBAL_VAR_STRING}") # 在子目录中新定义一个STRIING类型缓存变量
    set(MY_GLOBAL_VAR_STRING_SUBDIR "I am a sub directory inner cache variable" CACHE STRING "定义一个STRING缓存变量" FORCE)
    message("MY_GLOBAL_VAR_STRING_SUBDIR_1: ${MY_GLOBAL_VAR_STRING_SUBDIR}")
  4. cmake子目录test_include_cache.cmake

    cmake_minimum_required(VERSION 3.18)
    
    # 访问调用者的缓存变量
    message("MY_GLOBAL_VAR_STRING_11: ${MY_GLOBAL_VAR_STRING}") # 修改调用者的缓存变量
    set(MY_GLOBAL_VAR_STRING "abcdef modify by include mudule" CACHE STRING "修改缓存变量" FORCE) # 访问修改后的调用者缓存变量
    message("MY_GLOBAL_VAR_STRING_12: ${MY_GLOBAL_VAR_STRING}") # 在此模块中新定义一个STRIING类型缓存变量
    set(MY_GLOBAL_VAR_STRING_INCLUDE "I am a include module inner cache variable" CACHE STRING "定义一个STRING缓存变量" FORCE)
    message("MY_GLOBAL_VAR_STRING_INCLUDE_1: ${MY_GLOBAL_VAR_STRING_INCLUDE}")
  5. cmake_config.bat

    与上文示例一样,不做赘述。

4.2 运行结果

  1. 本地环境

    同上文示例中的环境,不做赘述。

  2. 输出结果

    -- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.17763.
    -- The CXX compiler identification is MSVC 19.0.24245.0
    -- The C compiler identification is MSVC 19.0.24245.0
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting C compile features
    -- Detecting C compile features - done
    --
    ########## BEGIN_TEST_CACHE_VARIABLE
    MY_GLOBAL_VAR_STRING_1: abcdef
    --
    ##### BEGIN_TEST_FUNC_CACHE_VAR
    MY_GLOBAL_VAR_STRING_2: abcdef
    MY_GLOBAL_VAR_STRING_3: abcdef modify by func
    MY_GLOBAL_VAR_STRING_FUNC_1: I am a func inner cache variable
    MY_GLOBAL_VAR_STRING_4: abcdef modify by func
    MY_GLOBAL_VAR_STRING_FUNC_2: I am a func inner cache variable
    -- ##### BEGIN_TEST_FUNC_CACHE_VAR
    --
    ##### BEGIN_TEST_MACRO_CACHE_VAR
    MY_GLOBAL_VAR_STRING_5: abcdef modify by func
    MY_GLOBAL_VAR_STRING_6: abcdef modify by macro
    MY_GLOBAL_VAR_STRING_MACRO_1: I am a macro inner cache variable
    MY_GLOBAL_VAR_STRING_7: abcdef modify by macro
    MY_GLOBAL_VAR_STRING_MACRO_2: I am a macro inner cache variable
    -- ##### END_TEST_MACRO_CACHE_VAR
    --
    ##### BEGIN_TEST_SUBDIR_CACHE_VAR
    MY_GLOBAL_VAR_STRING_8: abcdef modify by macro
    MY_GLOBAL_VAR_STRING_9: abcdef modify by subdir
    MY_GLOBAL_VAR_STRING_SUBDIR_1: I am a sub directory inner cache variable
    MY_GLOBAL_VAR_STRING_10: abcdef modify by subdir
    MY_GLOBAL_VAR_STRING_SUBDIR_2: I am a sub directory inner cache variable
    -- ##### END_TEST_SUBDIR_CACHE_VAR
    --
    ##### BEGIN_TEST_INCLUDE_CACHE_VAR
    MY_GLOBAL_VAR_STRING_11: abcdef modify by subdir
    MY_GLOBAL_VAR_STRING_12: abcdef modify by include mudule
    MY_GLOBAL_VAR_STRING_INCLUDE_1: I am a include module inner cache variable
    MY_GLOBAL_VAR_STRING_13: abcdef modify by include mudule
    MY_GLOBAL_VAR_STRING_INCLUDE_2: I am a include module inner cache variable
    -- ##### END_TEST_INCLUDE_CACHE_VAR
    -- ########## END_TEST_CACHE_VARIABLE -- Configuring done
    -- Generating done
    -- Build files have been written to: F:/learn_cmake/build
    请按任意键继续. . .

4.3 小结

缓存变量在函数、宏、子目录、包含模块中使用,没有任何差别。

全局变量,全工程项目使用方式相同。可以理解与C、C++全局变量的“格局”一样。

5 缓存变量与普通变量相互转换

5.1 关系示例

  1. 代码结构

  2. 示例程序(CMakeLists.txt)

    cmake_minimum_required(VERSION 3.18)
    
    # 设置工程名称
    set(PROJECT_NAME KAIZEN) # 设置工程版本号
    set(PROJECT_VERSION "1.0.0.10" CACHE STRING "默认版本号") # 工程定义
    project(${PROJECT_NAME}
    LANGUAGES CXX C
    VERSION ${PROJECT_VERSION}
    ) # 打印开始日志
    message(STATUS "\n########## BEGIN_TEST_CACHE_VARIABLE") # 1.1 定义一个STRIING类型缓存变量
    set(MY_GLOBAL_VAR_STRING "abcdef" CACHE STRING "定义一个STRING缓存变量" FORCE)
    message("MY_GLOBAL_VAR_STRING_1: ${MY_GLOBAL_VAR_STRING}") function(test_normal_var suffix)
    # 访问
    message("MY_GLOBAL_VAR_STRING_3: ${MY_GLOBAL_VAR_STRING}")
    # 修改
    set(MY_GLOBAL_VAR_STRING "modify by in normal func inner :: ${suffix}" PARENT_SCOPE)
    message("MY_GLOBAL_VAR_STRING_4: ${MY_GLOBAL_VAR_STRING}") # 定义新的普通变量
    set(MY_LOCAL_VAR_NORMAL_FUNC "my local var define in normal func :: ${suffix}")
    message("MY_LOCAL_VAR_NORMAL_FUNC_1: ${MY_LOCAL_VAR_NORMAL_FUNC}")
    endfunction() function(test_global_var suffix)
    # 访问
    message("MY_GLOBAL_VAR_STRING_5: ${MY_GLOBAL_VAR_STRING}")
    # 修改
    set(MY_GLOBAL_VAR_STRING "modify by in global func inner :: ${suffix}" CACHE STRING "修改STRING缓存变量" FORCE)
    message("MY_GLOBAL_VAR_STRING_6: ${MY_GLOBAL_VAR_STRING}") # 定义新的缓存变量
    set(MY_GLOBAL_VAR_GLOBAL_FUNC "my global var define in global func :: ${suffix}" CACHE STRING "定义一个STRING缓存变量" FORCE)
    message("MY_GLOBAL_VAR_GLOBAL_FUNC_1: ${MY_GLOBAL_VAR_GLOBAL_FUNC}")
    endfunction() message("\n###### BEGIN_Test_From_Cache_To_Normal\n") # 1.2 定义一个同名的普通变量(全局变量沦为普通变量示例)
    set(MY_GLOBAL_VAR_STRING "I am a normal variable but name same as cache variable")
    message("MY_GLOBAL_VAR_STRING_2: ${MY_GLOBAL_VAR_STRING}\n") test_normal_var("aaa")
    message("\nMY_GLOBAL_VAR_STRING_7: ${MY_GLOBAL_VAR_STRING}")
    message("MY_LOCAL_VAR_NORMAL_FUNC_2: ${MY_LOCAL_VAR_NORMAL_FUNC}\n") test_global_var("bbb")
    message("\nMY_GLOBAL_VAR_STRING_8: ${MY_GLOBAL_VAR_STRING}")
    message("MY_GLOBAL_VAR_GLOBAL_FUNC_2: ${MY_GLOBAL_VAR_GLOBAL_FUNC}") message("\n###### END_Test_From_Cache_To_Normal") message("\n###### BEGIN_Test_From_Normal_To_Cache\n") # 1.3 修改同名缓存变量(普通变量晋升为全局变量示例)
    set(MY_GLOBAL_VAR_STRING "abcdef modify after set normal variable" CACHE STRING "修改STRING缓存变量" FORCE)
    message("MY_GLOBAL_VAR_STRING_9: ${MY_GLOBAL_VAR_STRING}\n") test_global_var("111")
    message("\nMY_GLOBAL_VAR_STRING_11: ${MY_GLOBAL_VAR_STRING}")
    message("MY_GLOBAL_VAR_GLOBAL_FUNC_3: ${MY_GLOBAL_VAR_GLOBAL_FUNC}\n") test_normal_var("222")
    message("\nMY_GLOBAL_VAR_STRING_10: ${MY_GLOBAL_VAR_STRING}")
    message("MY_LOCAL_VAR_NORMAL_FUNC_3: ${MY_LOCAL_VAR_NORMAL_FUNC}") message("\n###### END_Test_From_Normal_To_Cache") # 打印结束日志
    message(STATUS "########## END_TEST_CACHE_VARIABLE\n")

5.2 运行结果

  1. 本地环境

    同上文示例中的环境,不做赘述。

  2. 输出结果

    -- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.17763.
    -- The CXX compiler identification is MSVC 19.0.24245.0
    -- The C compiler identification is MSVC 19.0.24245.0
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting C compile features
    -- Detecting C compile features - done
    --
    ########## BEGIN_TEST_CACHE_VARIABLE
    MY_GLOBAL_VAR_STRING_1: abcdef ###### BEGIN_Test_From_Cache_To_Normal MY_GLOBAL_VAR_STRING_2: I am a normal variable but name same as cache variable MY_GLOBAL_VAR_STRING_3: I am a normal variable but name same as cache variable
    MY_GLOBAL_VAR_STRING_4: I am a normal variable but name same as cache variable
    MY_LOCAL_VAR_NORMAL_FUNC_1: my local var define in normal func :: aaa MY_GLOBAL_VAR_STRING_7: modify by in normal func inner :: aaa
    MY_LOCAL_VAR_NORMAL_FUNC_2: MY_GLOBAL_VAR_STRING_5: modify by in normal func inner :: aaa
    MY_GLOBAL_VAR_STRING_6: modify by in global func inner :: bbb
    MY_GLOBAL_VAR_GLOBAL_FUNC_1: my global var define in global func :: bbb MY_GLOBAL_VAR_STRING_8: modify by in normal func inner :: aaa
    MY_GLOBAL_VAR_GLOBAL_FUNC_2: my global var define in global func :: bbb ###### END_Test_From_Cache_To_Normal ###### BEGIN_Test_From_Normal_To_Cache MY_GLOBAL_VAR_STRING_9: abcdef modify after set normal variable MY_GLOBAL_VAR_STRING_5: abcdef modify after set normal variable
    MY_GLOBAL_VAR_STRING_6: modify by in global func inner :: 111
    MY_GLOBAL_VAR_GLOBAL_FUNC_1: my global var define in global func :: 111 MY_GLOBAL_VAR_STRING_11: modify by in global func inner :: 111
    MY_GLOBAL_VAR_GLOBAL_FUNC_3: my global var define in global func :: 111 MY_GLOBAL_VAR_STRING_3: modify by in global func inner :: 111
    MY_GLOBAL_VAR_STRING_4: modify by in global func inner :: 111
    MY_LOCAL_VAR_NORMAL_FUNC_1: my local var define in normal func :: 222 MY_GLOBAL_VAR_STRING_10: modify by in normal func inner :: 222
    MY_LOCAL_VAR_NORMAL_FUNC_3: ###### END_Test_From_Normal_To_Cache
    -- ########## END_TEST_CACHE_VARIABLE -- Configuring done
    -- Generating done
    -- Build files have been written to: F:/learn_cmake/build
    请按任意键继续. . .

5.3 小结

  1. 普通变量若与缓存变量同名(PS:注意顺序,先有缓存变量,后定义普通变量),缓存变量会变为普通变量。

    听不懂?说人话?那就是:CMake 语法规定,当有一个与 Cache 变量同名的 Normal 变量出现时,后面使用这个变量都被当做 Normal 变量;如果一直没有同名的 Normal 变量,CMake 会自动认定这个变量一直为 Cache 变量。

    如示例中第19行,定义了一个Cache变量;第49行,又定义了一个同名的普通变量,从这里此变量“沦为”为了普通变量。

    为了验证,我们使用测试普通变量的函数进行了校核(第52行),的确与普通变量在函数作用域中应用表现一致。

    另外,注意示例程序第56行,当我们用测试缓存变量的函数进行校核时(示例程序第39、57行的运行结果在第30、33行),发现在函数作用域中将其修改为缓存变量后,只在函数作用域中有效。

    当退出函数作用域后,此变量依然是原旧值,即仍旧是普通变量。但是,与之形成鲜明对比的,在函数作用域中新定义的缓存变量第42行,从示例程序中第43、58行的运行结果31、34行分析,的确是全局有效的,因为值完全相同。

    重要推论:父作用域的普通变量,在子作用域中即使修改为缓存变量,仅仅在子作用域中有效,退出子作用域后失效,即仍旧是普通变量。

  2. 相反:缓存变量若与普通变量同名,普通变量会"晋升"为缓存变量。

    如示例中第65行,再定义了一个同名的缓存变量,从这里,此变量又“晋升”为了缓存变量。

    为了验证,我们使用测试缓存变量的函数进行了校核时(第68行),的确与缓存变量在函数作用域中应用表现一致。

    另外,注意示例程序第72行,当我们用测试普通变量的函数进行校核时(示例程序第27、73行的运行结果在第50、53行),发现在函数作用域中将其修改为普通变量后,当退出函数作用域后,此变量的值才改变为修改后的值。

    在函数内部之所以没有改变(如运行结果第50行),因为进入函数的瞬时,变量的值为运行结果第49行打印的值,而示例程序第26行修改时添加PARENT_SCOPE选项,明确修改的是父作用域的值,所以对当前函数作用域”无关痛痒“。

    最后,再明确一点:当72行调用函数test_normal_var结束后,该变量已变为普通变量,不再是缓存变量。

    重要推论:父作用域的缓存变量,在子作用域中修改为普通变量(加PARENT_SCOPE选项),退出子作用域后仍旧是普通变量。

  3. 建议尽量避免两者同名,容易混淆。

    学习这个目的:一方面,满足好奇心,探究一下;二方面,为了假如遇到类似问题,排查分析时有个基本规则认知。但强烈建议不要这样使用,个人拙见,仅供参考。

6 通过终端预设缓存变量值

6.1 应用示例

  1. 代码结构

    • learn_cmake:为根目录
    • build:为CMake配置输出目录(在此例中即生成sln解决方案的地方)
    • cmake_config.bat:执行CMake配置过程的脚本(双击直接运行)
    • CMakeLists.txt:CMake脚本
  2. 示例代码(CMakeLists.txt)

    cmake_minimum_required(VERSION 3.18)
    
    # 设置工程名称
    set(PROJECT_NAME KAIZEN) # 设置工程版本号
    set(PROJECT_VERSION "1.0.0.10" CACHE STRING "默认版本号") # 工程定义
    project(${PROJECT_NAME}
    LANGUAGES CXX C
    VERSION ${PROJECT_VERSION}
    ) # 打印开始日志
    message(STATUS "\n########## BEGIN_TEST_CACHE_VARIABLE") message("${PROJECT_NAME}_GLOBAL_VAR_1: ${${PROJECT_NAME}_GLOBAL_VAR}") # 修改缓存变量
    set(${PROJECT_NAME}_GLOBAL_VAR "I am a cache variable" CACHE STRING "定义一个STRING缓存变量" FORCE)
    message("${PROJECT_NAME}_GLOBAL_VAR_2: ${${PROJECT_NAME}_GLOBAL_VAR}") # 打印结束日志
    message(STATUS "########## END_TEST_CACHE_VARIABLE\n")
  3. 配置脚本(cmake_config.bat)

    @echo off
    set currentDir=%~dp0
    set buildDir=%currentDir%
    set cmakeOutputDir=%currentDir%\build
    cmake -S %buildDir% -B %cmakeOutputDir% -G"Visual Studio 16 2019" -T v140 -A x64 -D KAIZEN_GLOBAL_VAR="abcdef"
    pause

    注意:第5行,在终端增加-D KAIZEN_GLOBAL_VAR="abcdef",代码中可直接使用此缓存变量KAIZEN_GLOBAL_VAR

6.2 运行结果

  1. 本地环境

    同上文示例中的环境,不做赘述。

  2. 运行结果

    -- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.17763.
    -- The CXX compiler identification is MSVC 19.0.24245.0
    -- The C compiler identification is MSVC 19.0.24245.0
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting C compile features
    -- Detecting C compile features - done
    --
    ########## BEGIN_TEST_CACHE_VARIABLE
    KAIZEN_GLOBAL_VAR_1: abcdef
    KAIZEN_GLOBAL_VAR_2: I am a cache variable
    -- ########## END_TEST_CACHE_VARIABLE -- Configuring done
    -- Generating done
    -- Build files have been written to: F:/learn_cmake/build
    请按任意键继续. . .

6.3 小结

  1. 在终端配置CMake时,通过 -D 变量名=变量值 方式可以设定默认存在的Cache变量 或 修改缓存变量值。

    当然,有人可能会问:为什么-D就一定是缓存变量呢?很好,能这样提问说明思路很严谨,从示例代码中的确没法有力证明此预设变量一定就是缓存变量。

    逆向反推一下,我们把示例代码21行注释掉,然后删除build目录,重新双击bat脚本,然后查看build目录中的CMakeCache.txt文件,可以看到如下内容:

    如果不是缓存变量,按CMake的语法规则,不会记录到CMakeCache.txt文件中。

    综上所述:如果CMakeLists.txt脚本中默认已存在此缓存变量,那么此时只是赋值;

    如果默认不存在,那么此时就会默认创建一个全局Cache变量,并且赋值。

7 缓存变量应用注意事项

7.1 缓存变量,本质是全局变量

可以把缓存变量当做C、C++中的全局变量理解即可。类比法理解与体会,更易于学习与应用。

7.2 缓存变量,都会存储在CMakeCache.txt文件中

当你确认某个变量是缓存变量时,理论上你一定可以在CMakeCache.txt中找到此变量的记录项。

CMakeCache.txt文件中,还有很多默认的缓存变量,可自行查看与分析研究。

7.3 缓存变量发生问题,一定记得先删除build目录下的CMakeCache.txt文件,然后重新配置项目

CMake语法—缓存变量(Cache Variable)的更多相关文章

  1. CMake语法—普通变量与函数(Normal Variable And Function)

    目录 CMake语法-普通变量与函数(Normal Variable And Function) 1 CMake普通变量与函数示例 1.1 CMakeLists.txt 1.2 执行CMake配置脚本 ...

  2. CMake语法—普通变量与子目录(Normal Variable And Subdirectory)

    目录 CMake语法-普通变量与子目录(Normal Variable And Subdirectory) 1 CMake普通变量与子目录示例 1.1 代码目录结构 1.2 父目录CMakeLists ...

  3. CMake语法—普通变量与包含、宏(Normal Variable And Include、Macro)

    目录 CMake语法-普通变量与包含.宏(Normal Variable And Include.Macro) 1 CMake普通变量与包含.宏示例 1.1 代码目录结构 1.2 根目录CMakeLi ...

  4. CMake语法—环境变量(Environment Variable)

    目录 CMake语法-环境变量(Environment Variable) 1 定义环境变量 2 应用环境变量 2.1 代码结构 2.2 示例代码 2.3 运行结果 3 小结 CMake语法-环境变量 ...

  5. CMake语法—内置变量

    目录 CMake语法-内置变量 1 CMake变量分类 1.1 普通变量 1.2 缓存变量 1.3 环境变量 1.4 内置变量 2 CMake内置变量分类 2.1 提供信息的变量 2.2 改变行为的变 ...

  6. 【转载】CMake 两种变量原理

    原文地址:https://cslam.cn/archives/c9f565b5.html 摘要: 本文记录一下 CMake 变量的定义.原理及其使用.CMake 变量包含 Normal Variabl ...

  7. CMake 两种变量原理

    目录 [TOC] 1.两种变量的定义参考 2.两种变量的作用域原理及使用 1.Normal Variables (1).包含 add_subdirectory().function().(本质是值拷贝 ...

  8. yii中缓存(cache)详解

    缓存是用于提升网站性能的一种即简单又有效的途径.通过存储相对静态的数据至缓存以备所需,我们可以省去生成这些数据的时间.在 Yii 中使用缓存主要包括配置和访问缓存组件 . 内部方法 一.缓存配置: 1 ...

  9. yii中缓存(cache)详解 - 彼岸あ年華ツ

    缓存是用于提升网站性能的一种即简单又有效的途径.通过存储相对静态的数据至缓存以备所需,我们可以省去生成 这些数据的时间.在 Yii 中使用缓存主要包括配置和访问缓存组件 . 内部方法 一.缓存配置: ...

随机推荐

  1. 什么是协程?与线程和进程对比优劣在哪?gevent协程示例代码

      协程 协程,又称微线程,纤程.英文名Coroutine..一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在 ...

  2. js中的jQuery Validate增加手机号码验证

    $.validator.addMethod("isPhone", function(value,element) { var length = value.length; var ...

  3. JAVA获取访问者IP地址

    pom <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomc ...

  4. 再谈多线程模型之生产者消费者(多生产者和单一消费者 )(c++11实现)

    0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...

  5. 【剑指Offer】调整数组顺序使奇数位于偶数前面 解题报告(Python)

    [牛客网]调整数组顺序使奇数位于偶数前面 解题报告 标签(空格分隔): 牛客网 题目地址:https://www.nowcoder.com/questionTerminal/beb5aa231adc4 ...

  6. 【LeetCode】388. Longest Absolute File Path 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述: 题目大意 解题方法 日期 题目地址:https://leetcode. ...

  7. YAPTCHA(hdu2973)

    YAPTCHA Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  8. ROS机器人导航一 : 从英雄联盟到ROS导航

    写在前面: 这是这个系列的第一篇 本系列主要从零开始深入探索ROS(机器人操作系统)的导航和规划. 这个系列的目标,是让大家了解: 1.ROS的导航是怎么实现的 2.认识ROS里各种已有的导航算法,清 ...

  9. Java程序设计基础笔记 • 【第4章 条件结构】

    全部章节   >>>> 本章目录 4.1 条件结构 4.1.1 程序流程控制 ​4.1.2 单分支if结构 4.1.3 双分支if结构 4.1.4 实践练习 4.2 多重条件结 ...

  10. x86-2-保护模式

    x86-2-保护模式 操作系统负责计算机上的所有软件和硬件的管理,它可以百分百操作计算机的所有内容.但是,操作系统上编写的用户程序却应当有所限制,只允许用户程序访问属于自己程序的内容,不然整个生态就很 ...