CMake語法—緩存變量(Cache Variable)


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}")
    
    ### 修改緩存變量
    
    # 修改定義時不加FORCE的緩存變量(不加FORCE選項方式修改)
    set(MY_GLOBAL_VAR_STRING_NOFORCE "modify_abcdef_without_force" CACHE STRING "修改一個STRING緩存變量")
    message("MY_GLOBAL_VAR_STRING_NOFORCE: ${MY_GLOBAL_VAR_STRING_NOFORCE}")
    
    # 修改定義時不加FORCE的緩存變量(加FORCE選項方式修改)
    set(MY_GLOBAL_VAR_STRING_NOFORCE "modify_abcdef_with_force" CACHE STRING "修改一個STRING緩存變量" FORCE)
    message("MY_GLOBAL_VAR_STRING_NOFORCE: ${MY_GLOBAL_VAR_STRING_NOFORCE}")
    
    # 修改定義時加FORCE的STRING類型緩存變量(不加FORCE選項方式修改)
    set(MY_GLOBAL_VAR_STRING "modify_abc_without_force" CACHE STRING "修改STRING緩存變量")
    message("MY_GLOBAL_VAR_STRING: ${MY_GLOBAL_VAR_STRING}")
    
    # 修改定義時加FORCE的STRING類型緩存變量(加FORCE選項方式修改)
    set(MY_GLOBAL_VAR_STRING "modify_abc_with_force" CACHE STRING "修改STRING緩存變量" FORCE)
    message("MY_GLOBAL_VAR_STRING: ${MY_GLOBAL_VAR_STRING}")
    
    # 打印結束日志
    message(STATUS "\n########## 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.19044.
    -- 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_NOFORCE: abcdef
    MY_GLOBAL_VAR_STRING_NOFORCE: modify_abcdef_with_force
    MY_GLOBAL_VAR_STRING: abc
    MY_GLOBAL_VAR_STRING: modify_abc_with_force
    --
    ########## END_TEST_CACHE_VARIABLE
    
    -- Configuring done
    -- Generating done
    -- Build files have been written to: F:/learn_cmake/build
    請按任意鍵繼續. . .
    

2.4 小結

  1. 定義緩存變量時,可以不加FORCE選項:

    示例程序中第21行未添加FORCE選項,第25行添加FORCE選項,結果暫時沒有發現兩者差異。后來其它類型都沒有加FORCE選項。

  2. 修改緩存變量時,一定要加FORCE選項,否則修改無效:

    示例程序中第51行,試圖修改緩存變量的值,沒有添加FORCE選項,結合修改運行結果第23行(仍舊是原始值)可知,修改無效。

    同理:示例程序中第59行,試圖修改緩存變量的值,沒有添加FORCE選項,結合修改運行結果第25行(仍舊是原始值)可知,修改無效。

    相反,程序中第55行,企圖修改值時添加FORCE選項,結合修改運行結果第24行(修改后的值)可知,修改生效。

    類比:程序中第63行,企圖修改值時添加FORCE選項,結合修改運行結果第26行(修改后的值)可知,修改生效。

  3. Cache變量都會保存在CMakeCache.txt文件中。

  4. 不論定義或修改緩存變量時,建議都加上FORCE選項。結合1、2項所得。

    因為試想一下,如果示例代碼21行的緩存變量MY_GLOBAL_VAR_STRING_NOFORCE在此行之前已經定義過,於此期望修改變量的值,結果如同51行。事與願違。

3 CMakeCache.txt文件

CMake中的緩存變量都會保存在CMakeCache.txt文件中。

至於為何這樣干,可以逆向理解,如若不保存,想作為全局變量根本沒法實現。

限於篇幅,在此不展示CMakeCache.txt中的內容。因為其中包括很多默認的緩存變量,比較多。可在本地自行運行示例程序后查看即可。

如上示例中定義的緩存變量,可以從CMakeCache.txt中找到保存記錄,如下圖:

cache記錄

細心人估計已經從上圖查找結果中看出差異:為什么前6個緩存變量與第7個緩存變量行數竟然差距100多行?

因為:第7個變量被定義為INTERNAL類型,CMakeCache.txt文件中會把INTERNAL類型與EXTERNAL類型分開記錄。

CMakeCache.txt文件中把緩存變量分為兩種:

  • INTERNAL cache entries 內部緩存條目
  • EXTERNAL cache entries 外部緩存條目

另外,可能還有人會問:為什么運行結果打印輸出共11行(16~26),查找結果中只有7行?

因為:示例程序中有四行(51、55、59、63)是修改緩存變量,所以修改后緩存變量值只是變化,變量個數和變量名稱都不變化。

4 緩存變量在函數(function)、宏(macro)、子目錄(subdirectory)、包含模塊(include)中應用

4.1 應用示例

  1. 代碼結構

    目錄結構2

    • 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. 代碼結構

    目錄結構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文件,可以看到如下內容:

    DMode

    如果不是緩存變量,按CMake的語法規則,不會記錄到CMakeCache.txt文件中。

    綜上所述:如果CMakeLists.txt腳本中默認已存在此緩存變量,那么此時只是賦值;

    如果默認不存在,那么此時就會默認創建一個全局Cache變量,並且賦值。

7 緩存變量應用注意事項

7.1 緩存變量,本質是全局變量

可以把緩存變量當做C、C++中的全局變量理解即可。類比法理解與體會,更易於學習與應用。

7.2 緩存變量,都會存儲在CMakeCache.txt文件中

當你確認某個變量是緩存變量時,理論上你一定可以在CMakeCache.txt中找到此變量的記錄項。

CMakeCache.txt文件中,還有很多默認的緩存變量,可自行查看與分析研究。

7.3 緩存變量發生問題,一定記得先刪除build目錄下的CMakeCache.txt文件,然后重新配置項目


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM