CMake學習筆記(2)

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 目錄中的測試文件進行排序相關的測試
    現在相當于是要進行模塊化測試,對于calcsort目錄中的源文件來說,可以將它們先編譯成庫文件(可以是靜態庫也可以是動態庫)然后在提供給測試文件使用即可。庫文件的本質其實還是代碼,只不過是從文本格式變成了二進制格式。
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,但是開始和結束是必須要成對出現的,分別為:ifendif

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>)

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

2、AND

if(<cond1> AND <cond2>)

如果cond1cond2同時為True,返回True否則返回False

3、OR

if(<cond1> OR <cond2>)

如果cond1cond2兩個條件中至少有一個為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 中循環有兩種方式,分別是:foreachwhile

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:這是一個正整數,表示范圍的起始值,也就是說最小值為 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,通過setlist可以獲得
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循環也就退出了。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/65951.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/65951.shtml
英文地址,請注明出處:http://en.pswp.cn/web/65951.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

旅游網站設計與實現

文末附有完整項目代碼 在當今數字化時代&#xff0c;旅游網站成為人們獲取旅游信息的重要途徑。本文將詳細介紹旅游網站的設計與實現&#xff0c;讓你輕松了解其中的技術奧秘&#xff01; 一、項目背景 隨著社會經濟的發展&#xff0c;人們對精神消費愈發重視&#xff0c;旅游…

【C++】size_t究竟是什么?全面解析與深入拓展

博客主頁&#xff1a; [小????????] 本文專欄: C 文章目錄 &#x1f4af;前言&#x1f4af;一、什么是size_t&#xff1f;為什么需要size_t&#xff1f; &#x1f4af;二、size_t的特性與用途1. size_t是無符號類型示例&#xff1a; 2. size_t的跨平臺適應性示例對…

【物流管理系統 - IDEAJavaSwingMySQL】基于Java實現的物流管理系統導入IDEA教程

有問題請留言或私信 步驟 下載項目源碼&#xff1a;項目源碼 解壓項目源碼到本地 打開IDEA 左上角&#xff1a;文件 → 新建 → 來自現有源代碼的項目 找到解壓在本地的項目源代碼文件&#xff0c;點擊確定&#xff0c;根據圖示步驟繼續導入項目 查看項目目錄&#xff…

ssh2-sftp-client和ssh2配合使用js腳本快速部署項目到服務器

有時候因為服務器不能實現github或者gitlab的自動部署服務&#xff0c;所以就需要使用腳本來實現自動部署&#xff0c;可以省時省力&#xff0c;一勞永逸。這里就使用ssh2-sftp-client和ssh2來實現&#xff0c;即便是需要sudo權限&#xff0c;也是可以的。 1.先將本地打包后的…

深度解析Linux中的調試器gdb/cgdb的使用

Linux下我們編譯好的代碼&#xff0c;無法直接調試 gcc/g默認的工作模式是realse模式 程序要調試的話&#xff0c;必須是debug模式&#xff0c;也就是說編譯的時候要加-g選項 gdb攜帶調試信息的exe 我們現在在文件夾里面創建一個文件lesson11 里面創建一個累加的代碼&…

【Maui】動態菜單實現(綁定數據視圖)

前言 .NET 多平臺應用 UI (.NET MAUI) 是一個跨平臺框架&#xff0c;用于使用 C# 和 XAML 創建本機移動和桌面應用。 使用 .NET MAUI&#xff0c;可從單個共享代碼庫開發可在 Android、iOS、macOS 和 Windows 上運行的應用。 .NET MAUI 是一款開放源代碼應用&#xff0c;是 X…

Bash語言的語法糖

Bash語言的語法糖 引言 在現代編程語言中&#xff0c;“語法糖”是一個非常常見的術語&#xff0c;它指的是那些使代碼更加易讀、易寫的語法特性。盡管這些特性并不改變語言的功能&#xff0c;但它們能顯著提升開發者的編程體驗。在眾多編程語言中&#xff0c;Bash&#xff0…

linux---Nginx詳細教程(包含安裝,網站部署)

Nginx是一個高性能的HTTP和反向代理服務器&#xff0c;也可以用作郵件代理服務器&#xff0c;其以占有內存少、并發能力強、穩定性高、豐富的功能集、低系統資源消耗而聞名。以下是對Nginx的詳細教程&#xff1a; 一、Nginx簡介 Nginx由俄羅斯人開發&#xff0c;第一個公開版…

RNN之:LSTM 長短期記憶模型-結構-理論詳解-及實戰(Matlab向)

0.前言 遞歸&#xff01;循環神經網絡Recurrent Neural Network 循環神經網絡&#xff08;又稱遞歸神經網絡&#xff0c;Recurrent Neural Network&#xff0c;RNN&#xff09;。是一種用于處理序列數據的神經網絡結構&#xff0c;具有記憶功能&#xff0c;能夠捕捉序列中的時…

泛目錄和泛站有什么差別

啥是 SEO 泛目錄&#xff1f; 咱先來說說 SEO 泛目錄是啥。想象一下&#xff0c;你有一個巨大的圖書館&#xff0c;里面的書架上擺滿了各種各樣的書&#xff0c;每一本書都代表著一個網頁。而 SEO 泛目錄呢&#xff0c;就像是一個超級圖書管理員&#xff0c;它的任務就是把這些…

初識@ffmpeg/ffmpeg庫

前言 FFmpeg是一套可以用來記錄、轉換數字音頻、視頻,并且能夠利用它們來創建一個新的流媒體格式的自由軟件項目,它被廣泛應用在視頻處理、音頻處理以及直播領域。其中,@ffmpeg/ffmpeg 是一個將 FFmpeg 編譯為 WebAssembly(WASM)的庫,可支持幾乎所有的音視頻格式。 安裝…

【圖像去噪】論文復現:反向擴散中加入MAP將擴散模型從高斯去噪推廣到真實去噪!DiffusionVI的Pytorch源碼復現,跑通源碼,梳理理論,單卡可執行!

請先看【專欄介紹文章】:【圖像去噪(Image Denoising)】關于【圖像去噪】專欄的相關說明,包含適配人群、專欄簡介、專欄亮點、閱讀方法、定價理由、品質承諾、關于更新、去噪概述、文章目錄、資料匯總、問題匯總(更新中) 完整代碼和訓練好的模型權重文件下載鏈接見本文底…

Windows 藍牙驅動開發-簡介

藍牙(英語&#xff1a;Bluetooth)是一種無線通信技術標準&#xff0c;用來讓固定與移動設備&#xff0c;在短距離間交換資料&#xff0c;以形成個人局域網(PAN)。其使用短波特高頻(UHF)無線電波&#xff0c;經由2.4至2.485 GHz的ISM頻段來進行通信。1994年由電信商愛立信(Erics…

【Vue】全局/局部組件使用流程(Vue2為例)

全局組件和局部組件區別 如何使用 全局組件&#xff1a;全局注冊后&#xff0c;可以在任意頁面中直接使用。局部組件&#xff1a;在頁面中需要先導入子組件路徑&#xff0c;注冊組件才能使用。 適用場景 全局組件&#xff1a;適用于高頻使用的組件&#xff0c;如導航欄、業…

【Pytorch實用教程】PyTorch 中如何輸出模型參數:全面指南

文章目錄 PyTorch 中如何輸出模型參數:全面指南1. 為什么需要輸出模型參數?2. PyTorch 中輸出模型參數的方法2.1 使用 `model.parameters()` 輸出所有參數2.2 使用 `model.named_parameters()` 輸出參數名稱和值2.3 使用 `model.state_dict()` 輸出模型的參數字典2.4 輸出特定…

vscode vue 自動格式化

vscode vue 自動格式化 安裝Prettier和Vetur插件 選擇設置&#xff0c;并且轉到編輯文件。增加如下內容。 {"editor.formatOnSave": true,"editor.defaultFormatter": "esbenp.prettier-vscode","[vue]": {"editor.defaultFor…

1、docker概念和基本使用命令

docker概念 微服務&#xff1a;不再是以完整的物理機為基礎的服務軟件&#xff0c;而是借助于宿主機的性能。以小量的形式&#xff0c;單獨部署的應用。 docker&#xff1a;是一個開源的應用容器引擎&#xff0c;基于go語言開發的&#xff0c;使用時apache2.0的協議。docker是…

Genymotion配套VirtualBox所在地址

在 Genymotion打開虛擬機前需要先打開VirtualBox中的虛擬機 C:\Program Files\Oracle\VirtualBox\VirtualBox.exe 再開啟genymotion中的虛擬機開關

【Linux】深刻理解軟硬鏈接

一.軟硬鏈接操作 1.軟連接 touch 創建一個文件file.txt &#xff0c;對該文件創建對應的軟鏈接改怎么做呢&#xff1f; ln -s file.txt file-soft.link .給對應文件創建軟連接。 軟連接本質就是一個獨立的文件&#xff0c;因為我們對應的軟連接有獨立的inode&#xff0c;他…

linux下MySQL的數據存放

在 Linux 下安裝的 MySQL&#xff0c;數據表的數據默認存放在 My曉SQL 數據庫的**數據目錄**&#xff08;Data Directory&#xff09;中。具體來說&#xff0c;MySQL 會將所有數據庫的數據存儲在一個由 MySQL 配置文件中指定的目錄里。該目錄包含了所有數據庫的表、索引、日志等…