CMake學習記錄——下

轉載來自:https://subingwen.cn/cmake/CMake-advanced/

1. 嵌套的 CMake
如果項目很大,或者項目中有很多的源碼目錄,在通過 CMake 管理項目的時候如果只使用一個 CMakeLists.txt,那麼這個文件相對會比較複雜,有一種化繁爲簡的方式就是給每個源碼目錄都添加一個 CMakeLists.txt 文件(頭文件目錄不需要),這樣每個文件都不會太複雜,而且更靈活,更容易維護。

先來看一下下面的這個的目錄結構:

$ tree
.
├── build
├── calc
│   ├── add.cpp
│   ├── CMakeLists.txt
│   ├── div.cpp
│   ├── mult.cpp
│   └── sub.cpp
├── CMakeLists.txt
├── include
│   ├── calc.h
│   └── sort.h
├── sort
│   ├── CMakeLists.txt
│   ├── insert.cpp
│   └── select.cpp
├── test1
│   ├── calc.cpp
│   └── CMakeLists.txt
└── test2
├── CMakeLists.txt
└── sort.cpp
6 directories, 15 files

include 目錄:頭文件目錄
calc 目錄:目錄中的四個源文件對應的加、減、乘、除算法
對應的頭文件是 include 中的 calc.h
sort 目錄 :目錄中的兩個源文件對應的是插入排序和選擇排序算法
對應的頭文件是 include 中的 sort.h
test1 目錄:測試目錄,對加、減、乘、除算法進行測試
test2 目錄:測試目錄,對排序算法進行測試
可以看到各個源文件目錄所需要的 CMakeLists.txt 文件現在已經添加完畢了。接下來庖丁解牛,我們依次分析一下各個文件中需要添加的內容。

1.1 準備工作
1.1.1 節點關係
衆所周知,Linux 的目錄是樹狀結構,所以嵌套的 CMake 也是一個樹狀結構,最頂層的 CMakeLists.txt 是根節點,其次都是子節點。因此,我們需要了解一些關於 CMakeLists.txt 文件變量作用域的一些信息:

根節點 CMakeLists.txt 中的變量全局有效
父節點 CMakeLists.txt 中的變量可以在子節點中使用
子節點 CMakeLists.txt 中的變量只能在當前節點中使用
1.1.2 添加子目錄
接下來我們還需要知道在 CMake 中父子節點之間的關係是如何建立的,這裏需要用到一個 CMake 命令:

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

source_dir:指定了 CMakeLists.txt 源文件和代碼文件的位置,其實就是指定子目錄
binary_dir:指定了輸出文件的路徑,一般不需要指定,忽略即可。
EXCLUDE_FROM_ALL:在子路徑下的目標默認不會被包含到父路徑的 ALL 目標裏,並且也會被排除在 IDE 工程文件之外。用戶必須顯式構建在子路徑下的目標。
通過這種方式 CMakeLists.txt 文件之間的父子關係就被構建出來了。

1.2 解決問題
在上面的目錄中我們要做如下事情:

通過 test1 目錄中的測試文件進行計算器相關的測試
通過 test2 目錄中的測試文件進行排序相關的測試
現在相當於是要進行模塊化測試,對於 calc 和 sort 目錄中的源文件來說,可以將它們先編譯成庫文件(可以是靜態庫也可以是動態庫)然後在提供給測試文件使用即可。庫文件的本質其實還是代碼,只不過是從文本格式變成了二進制格式。

1.2.1 根目錄
根目錄中的 CMakeLists.txt 文件內容如下:

cmake_minimum_required(VERSION 3.0)
project(test)
# 定義變量
# 靜態庫生成的路徑
set(LIB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
# 測試程序生成的路徑
set(EXEC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
# 頭文件目錄
set(HEAD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include)
# 靜態庫的名字
set(CALC_LIB calc)
set(SORT_LIB sort)
# 可執行程序的名字
set(APP_NAME_1 test1)
set(APP_NAME_2 test2)
# 添加子目錄
add_subdirectory(calc)
add_subdirectory(sort)
add_subdirectory(test1)
add_subdirectory(test2)

在根節點對應的文件中主要做了兩件事情:定義全局變量和添加子目錄。

定義的全局變量主要是給子節點使用,目的是爲了提高子節點中的 CMakeLists.txt 文件的可讀性和可維護性,避免冗餘並降低出差的概率。
一共添加了四個子目錄,每個子目錄中都有一個 CMakeLists.txt 文件,這樣它們的父子關係就被確定下來了。
1.2.2 calc 目錄
calc 目錄中的 CMakeLists.txt 文件內容如下:

cmake_minimum_required(VERSION 3.0)
project(CALCLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${CALC_LIB} STATIC ${SRC})

第 3 行 aux_source_directory:搜索當前目錄(calc 目錄)下的所有源文件
第 4 行 include_directories:包含頭文件路徑,HEAD_PATH 是在根節點文件中定義的
第 5 行 set:設置庫的生成的路徑,LIB_PATH 是在根節點文件中定義的
第 6 行 add_library:生成靜態庫,靜態庫名字 CALC_LIB 是在根節點文件中定義的
1.2.3 sort 目錄
sort 目錄中的 CMakeLists.txt 文件內容如下:

cmake_minimum_required(VERSION 3.0)
project(SORTLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${SORT_LIB} SHARED ${SRC})

第 6 行 add_library:生成動態庫,動態庫名字 SORT_LIB 是在根節點文件中定義的
這個文件中的內容和 calc 節點文件中的內容類似,只不過這次生成的是動態庫。

在生成庫文件的時候,這個庫可以是靜態庫也可以是動態庫,一般需要根據實際情況來確定。如果生成的庫比較大,建議將其製作成動態庫。

1.2.4 test1 目錄
test1 目錄中的 CMakeLists.txt 文件內容如下:

cmake_minimum_required(VERSION 3.0)
project(CALCTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
# include_directories(${HEAD_PATH})
link_libraries(${CALC_LIB})
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
add_executable(${APP_NAME_1} ${SRC})

第 4 行 include_directories:指定頭文件路徑,HEAD_PATH 變量是在根節點文件中定義的
第 6 行 link_libraries:指定可執行程序要鏈接的靜態庫,CALC_LIB 變量是在根節點文件中定義的
第 7 行 set:指定可執行程序生成的路徑,EXEC_PATH 變量是在根節點文件中定義的
第 8 行 add_executable:生成可執行程序,APP_NAME_1 變量是在根節點文件中定義的
此處的可執行程序鏈接的是靜態庫,最終靜態庫會被打包到可執行程序中,可執行程序啓動之後,靜態庫也就隨之被加載到內存中了。

1.2.5 test2 目錄
test2 目錄中的 CMakeLists.txt 文件內容如下:

cmake_minimum_required(VERSION 3.0)
project(SORTTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
# link_directories(${LIB_PATH})
add_executable(${APP_NAME_2} ${SRC})
target_link_libraries(${APP_NAME_2} ${SORT_LIB})

第四行 include_directories:包含頭文件路徑,HEAD_PATH 變量是在根節點文件中定義的
第五行 set:指定可執行程序生成的路徑,EXEC_PATH 變量是在根節點文件中定義的
第六行 link_directories:指定可執行程序要鏈接的動態庫的路徑,LIB_PATH 變量是在根節點文件中定義的
第七行 add_executable:生成可執行程序,APP_NAME_2 變量是在根節點文件中定義的
第八行 target_link_libraries:指定可執行程序要鏈接的動態庫的名字
在生成可執行程序的時候,動態庫不會被打包到可執行程序內部。當可執行程序啓動之後動態庫也不會被加載到內存,只有可執行程序調用了動態庫中的函數的時候,動態庫纔會被加載到內存中,且多個進程可以共用內存中的同一個動態庫,所以動態庫又叫共享庫。

1.2.6 構建項目
一切準備就緒之後,開始構建項目,進入到根節點目錄的 build 目錄中,執行 cmake 命令,如下:

$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/abc/cmake/calc/build

可以看到在 build 目錄中生成了一些文件和目錄,如下所示:

 

$ tree build -L 1
build
├── calc # 目錄
├── CMakeCache.txt # 文件
├── CMakeFiles # 目錄
├── cmake_install.cmake # 文件
├── Makefile # 文件
├── sort # 目錄
├── test1 # 目錄
└── test2 # 目錄

然後在 build 目錄下執行 make 命令:

 

通過上圖可以得到如下信息:

在項目根目錄的 lib 目錄中生成了靜態庫 libcalc.a
在項目根目錄的 lib 目錄中生成了動態庫 libsort.so
在項目根目錄的 bin 目錄中生成了可執行程序 test1
在項目根目錄的 bin 目錄中生成了可執行程序 test2
最後再來看一下上面提到的這些文件是否真的被生成到對應的目錄中了:

$ tree bin/ lib/
bin/
├── test1
└── test2
lib/
├── libcalc.a
└── libsort.so

由此可見,真實不虛,至此,項目構建完畢。

寫在最後:

在項目中,如果將程序中的某個模塊製作成了動態庫或者靜態庫並且在CMakeLists.txt 中指定了庫的輸出目錄,而後其它模塊又需要加載這個生成的庫文件,此時直接使用就可以了,如果沒有指定庫的輸出路徑或者需要直接加載外部提供的庫文件,此時就需要使用 link_directories 將庫文件路徑指定出來。

2. 流程控制
在 CMake 的 CMakeLists.txt 中也可以進行流程控制,也就是說可以像寫 shell 腳本那樣進行條件判斷和循環。

2.1 條件判斷
關於條件判斷其語法格式如下:

if(<condition>)
<commands>
elseif(<condition>) # 可選快, 可以重複
<commands>
else() # 可選快
<commands>
endif()

在進行條件判斷的時候,如果有多個條件,那麼可以寫多個 elseif,最後一個條件可以使用 else,但是開始和結束是必須要成對出現的,分別爲:if 和 endif。

2.1.1 基本表達式

if(<expression>)

如果是基本表達式,expression 有以下三種情況:常量、變量、字符串。

如果是 1, ON, YES, TRUE, Y, 非零值,非空字符串時,條件判斷返回 True
如果是 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND,空字符串時,條件判斷返回 False
2.1.2 邏輯判斷
NOT

if(NOT <condition>)

其實這就是一個取反操作,如果條件 condition 爲 True 將返回 False,如果條件 condition 爲 False 將返回 True。

AND

if(<cond1> AND <cond2>)

如果 cond1 和 cond2 同時爲 True,返回 True 否則返回 False。

OR

if(<cond1> OR <cond2>)

如果 cond1 和 cond2 兩個條件中至少有一個爲 True,返回 True,如果兩個條件都爲 False 則返回 False。

2.1.3 比較
基於數值的比較

if(<variable|string> LESS <variable|string>)
if(<variable|string> GREATER <variable|string>)
if(<variable|string> EQUAL <variable|string>)
if(<variable|string> LESS_EQUAL <variable|string>)
if(<variable|string> GREATER_EQUAL <variable|string>)

LESS:如果左側數值小於右側,返回 True
GREATER:如果左側數值大於右側,返回 True
EQUAL:如果左側數值等於右側,返回 True
LESS_EQUAL:如果左側數值小於等於右側,返回 True
GREATER_EQUAL:如果左側數值大於等於右側,返回 True
基於字符串的比較

if(<variable|string> STRLESS <variable|string>)
if(<variable|string> STRGREATER <variable|string>)
if(<variable|string> STREQUAL <variable|string>)
if(<variable|string> STRLESS_EQUAL <variable|string>)
if(<variable|string> STRGREATER_EQUAL <variable|string>)

STRLESS:如果左側字符串小於右側,返回 True
STRGREATER:如果左側字符串大於右側,返回 True
STREQUAL:如果左側字符串等於右側,返回 True
STRLESS_EQUAL:如果左側字符串小於等於右側,返回 True
STRGREATER_EQUAL:如果左側字符串大於等於右側,返回 True
2.1.4 文件操作
判斷文件或者目錄是否存在

if(EXISTS path-to-file-or-directory)

如果文件或者目錄存在返回 True,否則返回 False。

判斷是不是目錄

if(IS_DIRECTORY path)

此處目錄的 path 必須是絕對路徑
如果目錄存在返回 True,目錄不存在返回 False。
判斷是不是軟連接

if(IS_SYMLINK file-name)

此處的 file-name 對應的路徑必須是絕對路徑
如果軟鏈接存在返回 True,軟鏈接不存在返回 False。
軟鏈接相當於 Windows 裏的快捷方式
判斷是不是絕對路徑

if(IS_ABSOLUTE path)

關於絕對路徑:
如果是 Linux,該路徑需要從根目錄開始描述
如果是 Windows,該路徑需要從盤符開始描述
如果是絕對路徑返回 True,如果不是絕對路徑返回 False。
2.1.5 其它
判斷某個元素是否在列表中

if(<variable|string> IN_LIST <variable>)

CMake 版本要求:大於等於 3.3
如果這個元素在列表中返回 True,否則返回 False。
比較兩個路徑是否相等

if(<variable|string> PATH_EQUAL <variable|string>)

CMake 版本要求:大於等於 3.24
如果這個元素在列表中返回 True,否則返回 False。
關於路徑的比較其實就是另個字符串的比較,如果路徑格式書寫沒有問題也可以通過下面這種方式進行比較:

if(<variable|string> STREQUAL <variable|string>)

我們在書寫某個路徑的時候,可能由於誤操作會多寫幾個分隔符,比如把 /a/b/c 寫成 /a//b///c,此時通過 STREQUAL 對這兩個字符串進行比較肯定是不相等的,但是通過 PATH_EQUAL 去比較兩個路徑,得到的結果確實相等的,可以看下面的例子:

cmake_minimum_required(VERSION 3.26)
project(test)

if("/home//robin///Linux" PATH_EQUAL "/home/robin/Linux")
message("路徑相等")
else()
message("路徑不相等")
endif()

message(STATUS "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")

if("/home//robin///Linux" STREQUAL "/home/robin/Linux")
message("路徑相等")
else()
message("路徑不相等")
endif()

輸出的日誌信息如下:
路徑相等
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
路徑不相等

通過得到的結果我們可以得到一個結論:在進行路徑比較的時候,如果使用 PATH_EQUAL 可以自動剔除路徑中多餘的分割線然後再進行路徑的對比,使用 STREQUAL 則只能進行字符串比較。

關於 if 的更多條件判斷,請參考官方文檔
2.2 循環
在 CMake 中循環有兩種方式,分別是:foreach 和 while。

2.2.1 foreach
使用 foreach 進行循環,語法格式如下:

foreach(<loop_var> <items>)
<commands>
endforeach()

通過 foreach 我們就可以對 items 中的數據進行遍歷,然後通過 loop_var 將遍歷到的當前的值取出,在取值的時候有以下幾種用法:

foreach(<loop_var> RANGE <stop>)

RANGE:關鍵字,表示要遍歷範圍
stop:這是一個正整數,表示範圍的結束值,在遍歷的時候從 0 開始,最大值爲 stop。
loop_var:存儲每次循環取出的值
舉例說明:

cmake_minimum_required(VERSION 3.2)
project(test)
# 循環
foreach(item RANGE 10)
message(STATUS "當前遍歷的值爲: ${item}" )
endforeach()

輸出的日誌信息是這樣的:

$ cmake ..
-- 當前遍歷的值爲: 0
-- 當前遍歷的值爲: 1
-- 當前遍歷的值爲: 2
-- 當前遍歷的值爲: 3
-- 當前遍歷的值爲: 4
-- 當前遍歷的值爲: 5
-- 當前遍歷的值爲: 6
-- 當前遍歷的值爲: 7
-- 當前遍歷的值爲: 8
-- 當前遍歷的值爲: 9
-- 當前遍歷的值爲: 10
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/abc/a/build
再次強調:在對一個整數區間進行遍歷的時候,得到的範圍是這樣的 【0,stop】,右側是閉區間包含 stop 這個值。

方法 2

foreach(<loop_var> RANGE <start> <stop> [<step>])

這是上面方法1 的加強版,我們在遍歷一個整數區間的時候,除了可以指定起始範圍,還可以指定步長。

RANGE:關鍵字,表示要遍歷範圍
start:這是一個正整數,表示範圍的起始值,也就是說最小值爲 start
stop:這是一個正整數,表示範圍的結束值,也就是說最大值爲 stop
step:控制每次遍歷的時候以怎樣的步長增長,默認爲1,可以不設置
loop_var:存儲每次循環取出的值
舉例說明:

cmake_minimum_required(VERSION 3.2)
project(test)

foreach(item RANGE 10 30 2)
message(STATUS "當前遍歷的值爲: ${item}" )
endforeach()

輸出的結果如下:

$ cmake ..
-- 當前遍歷的值爲: 10
-- 當前遍歷的值爲: 12
-- 當前遍歷的值爲: 14
-- 當前遍歷的值爲: 16
-- 當前遍歷的值爲: 18
-- 當前遍歷的值爲: 20
-- 當前遍歷的值爲: 22
-- 當前遍歷的值爲: 24
-- 當前遍歷的值爲: 26
-- 當前遍歷的值爲: 28
-- 當前遍歷的值爲: 30
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/abc/a/build

再次強調:在使用上面的方式對一個整數區間進行遍歷的時候,得到的範圍是這樣的 【start,stop】,左右兩側都是閉區間,包含 start 和 stop 這兩個值,步長 step 默認爲 1,可以不設置。

方法 3

foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])

這是 foreach 的另一個變體,通過這種方式我們可以對更加複雜的數據進行遍歷,前兩種方式只適用於對某個正整數範圍內的遍歷。

IN:關鍵字,表示在 xxx 裏邊

LISTS:關鍵字,對應的是列表 list,通過 set、list 可以獲得

ITEMS:關鍵字,對應的也是列表

loop_var:存儲每次循環取出的值

cmake_minimum_required(VERSION 3.2)
project(test)
# 創建 list
set(WORD a b c d)
set(NAME ace sabo luffy)
# 遍歷 list
foreach(item IN LISTS WORD NAME)
message(STATUS "當前遍歷的值爲: ${item}" )
endforeach()

在上面的例子中,創建了兩個 list 列表,在遍歷的時候對它們兩個都進行了遍歷(可以根據實際需求選擇同時遍歷多個或者只遍歷一個)。輸出的日誌信息如下:

$ cd build/
$ cmake ..
-- 當前遍歷的值爲: a
-- 當前遍歷的值爲: b
-- 當前遍歷的值爲: c
-- 當前遍歷的值爲: d
-- 當前遍歷的值爲: ace
-- 當前遍歷的值爲: sabo
-- 當前遍歷的值爲: luffy
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/abc/a/build

一共輸出了 7 個字符串,說明遍歷是沒有問題的。接下來看另外一種方式:

cmake_minimum_required(VERSION 3.2)
project(test)

set(WORD a b c "d e f")
set(NAME ace sabo luffy)
foreach(item IN ITEMS ${WORD} ${NAME})
message(STATUS "當前遍歷的值爲: ${item}" )
endforeach()

 

在上面的例子中,遍歷過程中將關鍵字 LISTS 改成了 ITEMS,後邊跟的還是一個或者多個列表,只不過此時需要通過 ${} 將列表中的值取出。其輸出的信息和上一個例子是一樣的:

$ cd build/
$ cmake ..
-- 當前遍歷的值爲: a
-- 當前遍歷的值爲: b
-- 當前遍歷的值爲: c
-- 當前遍歷的值爲: d e f
-- 當前遍歷的值爲: ace
-- 當前遍歷的值爲: sabo
-- 當前遍歷的值爲: luffy
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/abc/a/build

 


小細節:在通過 set 組織列表的時候,如果某個字符串中有空格,可以通過雙引號將其包裹起來,具體的操作方法可以參考上面的例子。

方法 4
注意事項:這種循環方式要求 CMake 的版本大於等於 3.17。

foreach(<loop_var>... IN ZIP_LISTS <lists>)

通過這種方式,遍歷的還是一個或多個列表,可以理解爲是方式3 的加強版。因爲通過上面的方式遍歷多個列表,但是又想把指定列表中的元素取出來使用是做不到的,在這個加強版中就可以輕鬆實現。

loop_var:存儲每次循環取出的值,可以根據要遍歷的列表的數量指定多個變量,用於存儲對應的列表當前取出的那個值。
如果指定了多個變量名,它們的數量應該和列表的數量相等
如果只給出了一個 loop_var,那麼它將一系列的 loop_var_N 變量來存儲對應列表中的當前項,也就是說 loop_var_0 對應第一個列表,loop_var_1 對應第二個列表,以此類推......
如果遍歷的多個列表中一個列表較短,當它遍歷完成之後將不會再參與後續的遍歷(因爲其它列表還沒有遍歷完)。
IN:關鍵字,表示在 xxx 裏邊
ZIP_LISTS:關鍵字,對應的是列表 list,通過 set 、list 可以獲得

cmake_minimum_required(VERSION 3.17)
project(test)
# 通過list給列表添加數據
list(APPEND WORD hello world "hello world")
list(APPEND NAME ace sabo luffy zoro sanji)
# 遍歷列表
foreach(item1 item2 IN ZIP_LISTS WORD NAME)
message(STATUS "當前遍歷的值爲: item1 = ${item1}, item2=${item2}" )
endforeach()

message("=============================")
# 遍歷列表
foreach(item IN ZIP_LISTS WORD NAME)
message(STATUS "當前遍歷的值爲: item1 = ${item_0}, item2=${item_1}" )
endforeach()

 

在這個例子中關於列表數據的添加是通過 list 來實現的。在遍歷列表的時候一共使用了兩種方式,一種提供了多個變量來存儲當前列表中的值,另一種只有一個變量,但是實際取值的時候需要通過變量名_0、變量名_1、變量名_N 的方式來操作,注意事項:第一個列表對應的編號是 0,第一個列表對應的編號是 0,第一個列表對應的編號是 0。

上面的例子輸出的結果如下:

$ cd build/
$ cmake ..
-- 當前遍歷的值爲: item1 = hello, item2=ace
-- 當前遍歷的值爲: item1 = world, item2=sabo
-- 當前遍歷的值爲: item1 = hello world, item2=luffy
-- 當前遍歷的值爲: item1 = , item2=zoro
-- 當前遍歷的值爲: item1 = , item2=sanji
=============================
-- 當前遍歷的值爲: item1 = hello, item2=ace
-- 當前遍歷的值爲: item1 = world, item2=sabo
-- 當前遍歷的值爲: item1 = hello world, item2=luffy
-- 當前遍歷的值爲: item1 = , item2=zoro
-- 當前遍歷的值爲: item1 = , item2=sanji
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/robin/abc/a/build

2.2.2 while
除了使用 foreach 也可以使用 while 進行循環,關於循環結束對應的條件判斷的書寫格式和 if/elseif 是一樣的。while 的語法格式如下:

while(<condition>)
<commands>
endwhile()

while 循環比較簡單,只需要指定出循環結束的條件即可:

cmake_minimum_required(VERSION 3.5)
project(test)
# 創建一個列表 NAME
set(NAME luffy sanji zoro nami robin)
# 得到列表長度
list(LENGTH NAME LEN)
# 循環
while(${LEN} GREATER 0)
message(STATUS "names = ${NAME}")
# 彈出列表頭部元素
list(POP_FRONT NAME)
# 更新列表長度
list(LENGTH NAME LEN)
endwhile()

輸出的結果如下:

$ cd build/
$ cmake ..
-- names = luffy;sanji;zoro;nami;robin
-- names = sanji;zoro;nami;robin
-- names = zoro;nami;robin
-- names = nami;robin
-- names = robin
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/robin/abc/a/build

可以看到當列表中的元素全部被彈出之後,列表的長度變成了 0,此時 while 循環也就退出了。

文章作者: 蘇丙榲
文章鏈接: https://subingwen.cn/cmake/CMake-advanced/
版權聲明: 本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 愛編程的大丙!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章