CMake語法—普通變量與包含、宏(Normal Variable And Include、Macro)
1 CMake普通變量與包含、宏示例
1.1 代碼目錄結構
- learn_cmake:為根目錄(即父目錄)
- build:為CMake配置輸出目錄(在此例中即生成sln解決方案的地方)
- cmake_config.bat:執行CMake配置過程的腳本(雙擊直接運行即可)
- CMakeLists.txt:根目錄CMake腳本
- cmake:子目錄,包含測試普通變量include與函數(function)的模塊test_include_with_func.cmake文件。主要用於對比與宏macro的區別
- test_include_with_macro.cmake: 測試普通變量include並與宏(macro)的模塊.cmake文件
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}
)
# 打印開始日志(include和宏)
message(STATUS "\n########## BEGIN_TEST_INCLUDE_MACRO")
# 普通變量1個值
set(MY_LOCAL_VAR "aaa")
message(STATUS "MY_LOCAL_VAR_1: ${MY_LOCAL_VAR}")
# 在相同作用域修改普通變量值
set(MY_LOCAL_VAR "bbb")
message(STATUS "MY_LOCAL_VAR_2: ${MY_LOCAL_VAR}")
# include 根目錄中的camke test_variable_macro.camke文件
message(STATUS "\n########## Begin include camke test_variable_macro")
# 默認CMAKE_MODULE_PATH這個值為空,使用時需要追增
message(STATUS "CMAKE_MODULE_PATH_1: ${CMAKE_MODULE_PATH}")
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
message(STATUS "CMAKE_MODULE_PATH_2: ${CMAKE_MODULE_PATH}")
# 真正include操作
include(test_include_with_macro)
message(STATUS "########## End include camke test_variable_macro\n")
# include后先驗證一下普通變量值的變化(即修改是否生效)
message(STATUS "MY_LOCAL_VAR_7: ${MY_LOCAL_VAR}")
# include后訪問一下被include模塊module中的普通變量值
message(STATUS "HIS_VAR_1: ${HIS_VAR}")
# 調用include模塊中定義的宏
test_macro("macro") ## 調用宏操作
# 調用宏之后訪問一下宏中定義的普通變量值
message(STATUS "macro_inner_val_2: ${macro_inner_val}")
# 調用宏之后驗證一下宏中修改普通變量值的變化(即修改是否生效)
message(STATUS "HIS_VAR_3: ${HIS_VAR}")
message(STATUS "MY_LOCAL_VAR_8: ${MY_LOCAL_VAR}")
# 注銷/取消/釋放變量MY_LOCAL_VAR
unset(MY_LOCAL_VAR)
# 打印結束日志(include和宏)
message(STATUS "\n########## END_TEST_INCLUDE_MACRO\n")
# 打印開始日志(include和函數)
message(STATUS "\n########## BEGIN_TEST_INCLUDE_FUNC")
# 普通變量1個值
set(MY_LOCAL_VAR "AAA")
message(STATUS "MY_LOCAL_VAR_1: ${MY_LOCAL_VAR}")
# 在相同作用域修改普通變量值
set(MY_LOCAL_VAR "BBB")
message(STATUS "MY_LOCAL_VAR_2: ${MY_LOCAL_VAR}")
# include 子目錄cmake中的camke test_variable_func.camke文件
message(STATUS "\n########## Begin include cmake test_variable_func")
# 同上
message(STATUS "CMAKE_MODULE_PATH_1: ${CMAKE_MODULE_PATH}")
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
message(STATUS "CMAKE_MODULE_PATH_2: ${CMAKE_MODULE_PATH}")
# 真正include操作
include(test_include_with_func)
message(STATUS "########## End include camke test_variable_func\n")
# include后先驗證一下普通變量值的變化(即修改是否生效)
message(STATUS "MY_LOCAL_VAR_7: ${MY_LOCAL_VAR}")
# include后訪問一下被include模塊module中的普通變量值
message(STATUS "HER_VAR_1: ${HER_VAR}")
# 調用include模塊中定義的函數
func("func_var") ## 調用函數操作
# 調用函數之后訪問一下函數中定義的普通變量值
message(STATUS "func_inner_val_2: ${func_inner_val}")
# 調用函數之后驗證一下函數中修改普通變量值的變化(即修改是否生效)
message(STATUS "HER_VAR_3: ${HER_VAR}")
message(STATUS "MY_LOCAL_VAR_8: ${MY_LOCAL_VAR}")
# 調用函數之后訪問一下函數中定義的普通變量值(注意與95行區別)
message(STATUS "MY_MODULE_FUN_VAR_2: ${MY_MODULE_FUN_VAR}")
# 打印結束日志(include和函數)
message(STATUS "\n########## END_TEST_INCLUDE_FUNC\n")
1.3 根目錄test_include_with_macro.cmake
# 進入標志
message(STATUS "##### Enter test_include_with_macro.cmake file")
# 驗證訪問include()主目錄CMakeList.txt中變量
message(STATUS "MY_LOCAL_VAR_3: ${MY_LOCAL_VAR}")
# 修改include()主目錄CMakeList.txt中變量
set(MY_LOCAL_VAR "ccc")
message(STATUS "MY_LOCAL_VAR_4: ${MY_LOCAL_VAR}")
# 修改include()主目錄CMakeList.txt中變量值加PARENT_SCOPE選項
# set(MY_LOCAL_VAR "ddd" PARENT_SCOPE) # CMake Warning 警告:主目錄不被視為父目錄,即相當於同作用域
# 在此cmake文件中定義變量
set(HIS_VAR "123")
# 定義宏
macro(test_macro M_VAR)
# 調用宏
message(STATUS "##### Start Call macro")
# 打印宏參數值
message(STATUS "M_VAR: ${M_VAR}")
# 宏內部定義變量
set(macro_inner_val "C++")
# 打印宏內部變量值
message(STATUS "macro_inner_val_1: ${macro_inner_val}")
# 修改此cmake文件中變量值
set(HIS_VAR "456")
message(STATUS "HIS_VAR_2: ${HIS_VAR}")
# 打印include()主目錄CMakeList.txt中變量
message(STATUS "MY_LOCAL_VAR_5: ${MY_LOCAL_VAR}")
# 修改include()主目錄CMakeList.txt中變量值
set(MY_LOCAL_VAR "ddd")
message(STATUS "MY_LOCAL_VAR_6: ${MY_LOCAL_VAR}")
# 修改include()主目錄CMakeList.txt中變量值加PARENT_SCOPE選項
# set(MY_LOCAL_VAR "eee" PARENT_SCOPE) # CMake Warning 警告:主目錄不被視為父目錄,即與宏也同作用域
message(STATUS "##### Stop Call macro")
endmacro(test_macro)
message(STATUS "##### Leave test_include_with_macro.cmake file")
1.4 cmake子目錄中的test_include_with_func.cmake
# 進入標志
message(STATUS "##### Enter test_include_with_func.cmake file")
# 驗證訪問include()主目錄CMakeList.txt中變量
message(STATUS "MY_LOCAL_VAR_3: ${MY_LOCAL_VAR}")
# 修改include()主目錄CMakeList.txt中變量
set(MY_LOCAL_VAR "CCC")
message(STATUS "MY_LOCAL_VAR_4: ${MY_LOCAL_VAR}")
# 修改include()主目錄CMakeList.txt中變量值加PARENT_SCOPE選項
# set(MY_LOCAL_VAR "ddd" PARENT_SCOPE) # CMake Warning 警告:主目錄不被視為父目錄,即相當於同作用域
# 在此cmake文件中定義變量
set(HER_VAR "red")
# 定義函數
function(func F_VAR)
# 調用函數
message(STATUS "##### Start Call Func")
# 打印函數參數值
message(STATUS "F_VAR: ${F_VAR}")
# 函數內部定義變量
set(func_inner_val "JAVA")
# 打印函數內部變量值
message(STATUS "func_inner_val_1: ${func_inner_val}")
# 修改此cmake文件中變量值
set(HER_VAR "black")
message(STATUS "HER_VAR_2: ${HER_VAR}")
# 打印include()主目錄CMakeList.txt中變量
message(STATUS "MY_LOCAL_VAR_5: ${MY_LOCAL_VAR}")
# 修改include()主目錄CMakeList.txt中變量值不加PARENT_SCOPE選項
set(MY_LOCAL_VAR "DDD")
message(STATUS "MY_LOCAL_VAR_6: ${MY_LOCAL_VAR}")
# 定義變量加PARENT_SCOPE選項
set(MY_MODULE_FUN_VAR "I am a module inner func variable" PARENT_SCOPE) # 正常無警告。根目錄被視為父目錄,即函數與其不屬於同一作用域
message(STATUS "MY_MODULE_FUN_VAR_1: ${MY_MODULE_FUN_VAR}")
message(STATUS "##### Stop Call Func")
endfunction()
message(STATUS "##### Leave test_include_with_func.cmake file")
1.5 執行CMake配置腳本
@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 運行結果
2.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.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_INCLUDE_MACRO
-- MY_LOCAL_VAR_1: aaa
-- MY_LOCAL_VAR_2: bbb
--
########## Begin include camke test_variable_macro
-- CMAKE_MODULE_PATH_1:
-- CMAKE_MODULE_PATH_2: F:/learn_cmake
-- ##### Enter test_include_with_macro.cmake file
-- MY_LOCAL_VAR_3: bbb
-- MY_LOCAL_VAR_4: ccc
-- ##### Leave test_include_with_macro.cmake file
-- ########## End include camke test_variable_macro
-- MY_LOCAL_VAR_7: ccc
-- HIS_VAR_1: 123
-- ##### Start Call macro
-- M_VAR: macro
-- macro_inner_val_1: C++
-- HIS_VAR_2: 456
-- MY_LOCAL_VAR_5: ccc
-- MY_LOCAL_VAR_6: ddd
-- ##### Stop Call macro
-- macro_inner_val_2: C++
-- HIS_VAR_3: 456
-- MY_LOCAL_VAR_8: ddd
--
########## END_TEST_INCLUDE_MACRO
--
########## BEGIN_TEST_INCLUDE_FUNC
-- MY_LOCAL_VAR_1: AAA
-- MY_LOCAL_VAR_2: BBB
--
########## Begin include cmake test_variable_func
-- CMAKE_MODULE_PATH_1: F:/learn_cmake
-- CMAKE_MODULE_PATH_2: F:/learn_cmake;F:/learn_cmake/cmake
-- ##### Enter test_include_with_func.cmake file
-- MY_LOCAL_VAR_3: BBB
-- MY_LOCAL_VAR_4: CCC
-- ##### Leave test_include_with_func.cmake file
-- ########## End include camke test_variable_func
-- MY_LOCAL_VAR_7: CCC
-- HER_VAR_1: red
-- ##### Start Call Func
-- F_VAR: func_var
-- func_inner_val_1: JAVA
-- HER_VAR_2: black
-- MY_LOCAL_VAR_5: CCC
-- MY_LOCAL_VAR_6: DDD
-- MY_MODULE_FUN_VAR_1:
-- ##### Stop Call Func
-- func_inner_val_2:
-- HER_VAR_3: red
-- MY_LOCAL_VAR_8: CCC
-- MY_MODULE_FUN_VAR_2: I am a module inner func variable
--
########## END_TEST_INCLUDE_FUNC
-- Configuring done
-- Generating done
-- Build files have been written to: F:/learn_cmake/build
請按任意鍵繼續. . .
2.3 結論
2.3.1 定義普通變量方式
set(<variable> <value>... [PARENT_SCOPE])
2.3.2 variable:表示普通變量名稱;
2.3.3 value:表示變量的值列表,value后面有三個點,表示可變參值,即變量的值可以為0或1或n個值
具體示例及說明可參見隨筆加深理解與體會。
2.3.4 PARENT_SCOPE:表示父作用域,若加此選項,特別明確定義或修改父作用域變量的值
2.3.5 普通變量,在include模塊中或macro宏中,與上層中應用完全相同,不存在作用域的差異。
換句話表述:如果學習過隨筆CMake語法—普通變量與函數和 CMake語法—普通變量與子目錄的讀者,經過清晰對比,這里可理解為與Function和Subdirectory的情況剛好相反。
本來計划把include和宏分開寫,結果發現兩者完全相同,整合作為一篇隨筆即可。
當然,這樣來看,函數Function和子目錄Subdirectory也可以作為一篇隨筆理解,因后來發現此兩者也完全相同。
下面從示例程序出發,再結合輸出結果分析(為了方便理解,上文示例程序分別用1.2、1.3、1.4代表;輸出結果用2.2代表):
-
普通變量,在include模塊中與Macro宏中應用,與上層中完全相同。
如示例1.2中第23行、1.3中第8、38行、1.4中的8行,結合輸出2.2中第17、24、35、53行結果可知,形散神不散,一氣呵成。
反倒是1.3中第12、42行因為添加PARENT_SCOPE選項,被CMake發出警告,提示“畫蛇添足”,有多此一舉之嫌。
-
在include模塊和Macro宏中定義的普通變量,在上層中也可以正常應用,不存在差異。
如示例1.3中第15、26行、1.4中15行,結合輸出2.2中第29、32、58行結果可知,渾然一體,熔於一爐。
與之形成鮮明對比如示例1.4中第26、31行,結果輸出2.2中第67、68行結果可知,函數中就存在作用域的區分和差異。
另外,示例1.4中第42行,在函數中定義父作用域變量,加PARENT_SCOPE選項,無任何警告,且輸出2.2中第70行的確有效果。
-
include和macro與.cmake模塊文件存放位置無關。
如示例1.3程序存放在根目錄,示例1.4存放在cmake文件夾中,include后結果是完全相同的效果。
-
普通變量,在include和macro中應用方式統一(無隔閡),在function和subdirectory中應用方式一致(分伯仲)。
可結合示例中函數func和宏test_macro仔細體會一下彼此的差異和個性。
比如1.3中第31行對變量的修改,1.4中第31行對變量的修改,從輸出2.2中第38、68行結果可知,兩者差異還是很清晰。
-
unset 注銷、取消、釋放變量
unset(<variable> PARENT_SCOPE)
variable:設置要清除的變量名
PARENT_SCOPE(可選):若清除的是父作用域中的變量,需要加此選項;否則,無效果。可參照set逆向理解與體會。
尤其注意示例1.2中第56行,為了前后兩種測試更有可比性,第46行對同一個變量進行”清零”處理。