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 定義示例代碼
-
代碼結構
- learn_cmake:為根目錄
- build:為CMake配置輸出目錄(在此例中即生成sln解決方案的地方)
- cmake_config.bat:執行CMake配置過程的腳本(雙擊直接運行)
- CMakeLists.txt:CMake腳本
-
示例代碼(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")
-
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 運行結果
-
本地環境
本地安裝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).
-
輸出結果
-- 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 小結
-
定義緩存變量時,可以不加FORCE選項:
示例程序中第21行未添加FORCE選項,第25行添加FORCE選項,結果暫時沒有發現兩者差異。后來其它類型都沒有加FORCE選項。
-
修改緩存變量時,一定要加FORCE選項,否則修改無效:
示例程序中第51行,試圖修改緩存變量的值,沒有添加FORCE選項,結合修改運行結果第23行(仍舊是原始值)可知,修改無效。
同理:示例程序中第59行,試圖修改緩存變量的值,沒有添加FORCE選項,結合修改運行結果第25行(仍舊是原始值)可知,修改無效。
相反,程序中第55行,企圖修改值時添加FORCE選項,結合修改運行結果第24行(修改后的值)可知,修改生效。
類比:程序中第63行,企圖修改值時添加FORCE選項,結合修改運行結果第26行(修改后的值)可知,修改生效。
-
Cache變量都會保存在CMakeCache.txt文件中。
-
不論定義或修改緩存變量時,建議都加上FORCE選項。結合1、2項所得。
因為試想一下,如果示例代碼21行的緩存變量MY_GLOBAL_VAR_STRING_NOFORCE在此行之前已經定義過,於此期望修改變量的值,結果如同51行。事與願違。
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 外部緩存條目
另外,可能還有人會問:為什么運行結果打印輸出共11行(16~26),查找結果中只有7行?
因為:示例程序中有四行(51、55、59、63)是修改緩存變量,所以修改后緩存變量值只是變化,變量個數和變量名稱都不變化。
4 緩存變量在函數(function)、宏(macro)、子目錄(subdirectory)、包含模塊(include)中應用
4.1 應用示例
-
代碼結構
- learn_cmake:為根目錄
- build:為CMake配置輸出目錄(在此例中即生成sln解決方案的地方)
- cmake_config.bat:執行CMake配置過程的腳本(雙擊直接運行)
- CMakeLists.txt:父目錄的CMake腳本
- src:子目錄,包含測試子目錄的CMakeLists.txt文件
- cmake:子目錄,包含測試include模塊的test_include_cache.cmake文件
-
父目錄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")
-
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}")
-
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}")
-
cmake_config.bat
與上文示例一樣,不做贅述。
4.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 關系示例
-
代碼結構
-
示例程序(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 運行結果
-
本地環境
同上文示例中的環境,不做贅述。
-
輸出結果
-- 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 小結
-
普通變量若與緩存變量同名(PS:注意順序,先有緩存變量,后定義普通變量),緩存變量會變為普通變量。
聽不懂?說人話?那就是:CMake 語法規定,當有一個與 Cache 變量同名的 Normal 變量出現時,后面使用這個變量都被當做 Normal 變量;如果一直沒有同名的 Normal 變量,CMake 會自動認定這個變量一直為 Cache 變量。
如示例中第19行,定義了一個Cache變量;第49行,又定義了一個同名的普通變量,從這里此變量“淪為”為了普通變量。
為了驗證,我們使用測試普通變量的函數進行了校核(第52行),的確與普通變量在函數作用域中應用表現一致。
另外,注意示例程序第56行,當我們用測試緩存變量的函數進行校核時(示例程序第39、57行的運行結果在第30、33行),發現在函數作用域中將其修改為緩存變量后,只在函數作用域中有效。
當退出函數作用域后,此變量依然是原舊值,即仍舊是普通變量。但是,與之形成鮮明對比的,在函數作用域中新定義的緩存變量第42行,從示例程序中第43、58行的運行結果31、34行分析,的確是全局有效的,因為值完全相同。
重要推論:父作用域的普通變量,在子作用域中即使修改為緩存變量,僅僅在子作用域中有效,退出子作用域后失效,即仍舊是普通變量。
-
相反:緩存變量若與普通變量同名,普通變量會"晉升"為緩存變量。
如示例中第65行,再定義了一個同名的緩存變量,從這里,此變量又“晉升”為了緩存變量。
為了驗證,我們使用測試緩存變量的函數進行了校核時(第68行),的確與緩存變量在函數作用域中應用表現一致。
另外,注意示例程序第72行,當我們用測試普通變量的函數進行校核時(示例程序第27、73行的運行結果在第50、53行),發現在函數作用域中將其修改為普通變量后,當退出函數作用域后,此變量的值才改變為修改后的值。
在函數內部之所以沒有改變(如運行結果第50行),因為進入函數的瞬時,變量的值為運行結果第49行打印的值,而示例程序第26行修改時添加PARENT_SCOPE選項,明確修改的是父作用域的值,所以對當前函數作用域”無關痛癢“。
最后,再明確一點:當72行調用函數test_normal_var結束后,該變量已變為普通變量,不再是緩存變量。
重要推論:父作用域的緩存變量,在子作用域中修改為普通變量(加PARENT_SCOPE選項),退出子作用域后仍舊是普通變量。
-
建議盡量避免兩者同名,容易混淆。
學習這個目的:一方面,滿足好奇心,探究一下;二方面,為了假如遇到類似問題,排查分析時有個基本規則認知。但強烈建議不要這樣使用,個人拙見,僅供參考。
6 通過終端預設緩存變量值
6.1 應用示例
-
代碼結構
- learn_cmake:為根目錄
- build:為CMake配置輸出目錄(在此例中即生成sln解決方案的地方)
- cmake_config.bat:執行CMake配置過程的腳本(雙擊直接運行)
- CMakeLists.txt:CMake腳本
-
示例代碼(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")
-
配置腳本(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 運行結果
-
本地環境
同上文示例中的環境,不做贅述。
-
運行結果
-- 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 小結
-
在終端配置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文件中,還有很多默認的緩存變量,可自行查看與分析研究。