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.cpp6 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})
link_directories(${LIB_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 邏輯判斷
1、NOT
if(NOT <condition>)
其實這就是一個取反操作,如果條件condition
為True
將返回False
,如果條件condition
為False
將返回True
。
2、AND
if(<cond1> AND <cond2>)
如果cond1
和cond2
同時為True
,返回True
否則返回False
。
3、OR
if(<cond1> OR <cond2>)
如果cond1
和cond2
兩個條件中至少有一個為True
,返回True
,如果兩個條件都為False
則返回False
。
2.1.3 比較
1、基于數值的比較
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
2、基于字符串的比較
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 文件操作
1、判斷文件或者目錄是否存在
if(EXISTS path-to-file-or-directory)
如果文件或者目錄存在返回True
,否則返回False
。
2、判斷是不是目錄
if(IS_DIRECTORY path)
- 此處目錄的 path 必須是絕對路徑
- 如果目錄存在返回True,目錄不存在返回False。
3、判斷是不是軟連接
if(IS_SYMLINK file-name)
- 此處的
file-name
對應的路徑必須是絕對路徑 - 如果軟鏈接存在返回
True
,軟鏈接不存在返回False
。 - 軟鏈接相當于 Windows 里的快捷方式
4、判斷是不是絕對路徑
if(IS_ABSOLUTE path)
- 關于絕對路徑:
- 如果是
Linux
,該路徑需要從根目錄開始描述 - 如果是
Windows
,該路徑需要從盤符開始描述
- 如果是
- 如果是絕對路徑返回
True
,如果不是絕對路徑返回False
。
2.1.5 其它
1、判斷某個元素是否在列表中
if(<variable|string> IN_LIST <variable>)
- CMake 版本要求:大于等于3.3
- 如果這個元素在列表中返回True,否則返回False。
2、比較兩個路徑是否相等
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
將遍歷到的當前的值取出,在取值的時候有以下幾種用法:
方法1
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
:這是一個正整數,表示范圍的起始值,也就是說最小值為 startstop
:這是一個正整數,表示范圍的結束值,也就是說最大值為 stopstep
:控制每次遍歷的時候以怎樣的步長增長,默認為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
循環也就退出了。