CMake應用:核心語法篇

本文是深入CMakeLists.txt之前的前導文章,介紹CMake語言的核心概念,以及常用的CMake腳本命令,以期對CMake的語法能有比較好的認知和實踐基礎。

在前一篇文章中介紹了CMake的核心概念,使用的一般流程,並通過一個實例講解了CMake命令行工具之一的cmake命令的使用方法。
該系列往期文章:

  1. CMake應用:基礎篇

在開始深入如何編寫完備的CMakeLists.txt之前,先了解下CMake的語言和它的組織方式對後續內容的理解是很有幫助的。本文將會介紹以下內容:

  1. CMake語言的核心概念
  2. CMake常用腳本命令及示例

一 CMake語法核心概念

下面介紹的內容,可以只先有一些概念,不求甚解,在後續需要深入的時候查看文檔即可。

CMake的命令有不同類型,包括腳本命令、項目配置命令和測試命令,細節可以查看官網cmake-commands

CMake語言在項目配置中組織爲三種源文件類型:

  1. 目錄:CMakeLists.txt,針對的是一個目錄,描述如何針對目錄(Source tree)生成構建系統,會用到項目配置命令;
  2. 腳本:<script>.cmake,就是一個CMake語言的腳本文件,可使用cmake -P直接執行,只能包含腳本命令;
  3. 模塊:<module>.cmake,實現一些模塊化的功能,可以被前面兩者包含,比如include(CTest)啓用測試功能。

1 註釋

行註釋使用"#";塊註釋使用"#[[Some comments can be multi lines or in side the command]]"。比如:

# Multi line comments follow
#[[
Author: FarmerLi, 公衆號: 很酷的程序員/RealCoolEngineer
Date: 2021-04-27
]]

2 變量

CMake中使用setunset命令設置或者取消設置變量。CMake中有以下常用變量類型。

一般變量

設置的變量可以是字符串,數字或者列表(直接設置多個值,或者使用分號隔開的字符串格式爲"v1;v2;v3"),比如:

# Set variable
set(AUTHOR_NAME Farmer)
set(AUTHOR "Farmer Li")
set(AUTHOR Farmer\ Li)

# Set list
set(SLOGAN_ARR To be)   # Saved as "To;be"
set(SLOGAN_ARR To;be)
set(SLOGAN_ARR "To;be")

set(NUM 30)   # Saved as string, but can compare with other number string
set(FLAG ON)  # Bool value

主要有以下要點:

  1. 如果要設置的變量值包含空格,則需要使用雙引號或者使用"\"轉義,否則可以省略雙引號;
  2. 如果設置多個值或者字符串值的中間有";",則保存成list,同樣是以";"分割的字符串;
  3. 變量可以被list命令操作,單個值的變量相當於只有一個元素的列表;
  4. 引用變量:${<variable>},在if()條件判斷中可以簡化爲只用變量名<variable>

Cache變量

Cache變量(緩存條目,cache entries)的作用主要是爲了提供用戶配置選項,如果用戶沒有指定,則使用默認值,設置方法如下:

# set(<variable> <value>... CACHE <type> <docstring> [FORCE])
set(CACHE_VAR "Default cache value" CACHE STRING "A sample for cache variable")

要點:

  1. 主要爲了提供可配置變量,比如編譯開關;
  2. 引用CACHE變量:$CACHE{<varialbe>}

Cache變量會被保存在構建目錄下的CMakeCache.txt中,緩存起來之後是不變的,除非重新配置更新

環境變量

修改當前處理進程的環境變量,設置和引用格式爲:

# set(ENV{<variable>} [<value>])
set(ENV{ENV_VAR} "$ENV{PATH}")
message("Value of ENV_VAR: $ENV{ENV_VAR}")

和CACHE變量類似,要引用環境變量,格式爲:$ENV{<variable>}

3 條件語句

支持的語法有:

  1. 字符串比較,比如:STREQUAL、STRLESS、STRGREATER等;
  2. 數值比較,比如:EQUAL、LESS、GREATER等;
  3. 布爾運算,AND、OR、NOT
  4. 路徑判斷,比如:EXISTS、IS_DIRECTORY、IS_ABSOLUTE等;
  5. 版本號判斷;等等;
  6. 使用小括號可以組合多個條件語句,比如:(cond1) AND (cond2 OR (cond3))

對於常量

  1. ON、YES、TRUE、Y和非0值均被視爲True
  2. 0、OFF、NO、FALSE、N、IGNORE、空字符串、NOTFOUND、及以"-NOTFOUND"結尾的字符串均視爲False

對於變量,只要其值不是常量中爲False的情形,則均視爲True

二 常用的腳本命令

有了前面的總體概念,下面掌握一些常用的CMake命令,對於CMake腳本編寫就可以有不錯的基礎。

1 消息打印

前面已經有演示,即message命令,其實就是打印log,用來打印不同信息,常用命令格式爲:

message([<mode>] "message text" ...)

其中mode就相當於打印的等級,常用的有這幾個選項:

  1. 空或者NOTICE:比較重要的信息,如前面演示中的格式
  2. DEBUG:調試信息,主要針對開發者
  3. STATUS:項目使用者可能比較關心的信息,比如提示當前使用的編譯器
  4. WARNING:CMake警告,不會打斷進程
  5. SEND_ERROR:CMake錯誤,會繼續執行,但是會跳過生成構建系統
  6. FATAL_ERROR:CMake致命錯誤,會終止進程

2 條件分支

這裏以if()/elseif()/else()/endif()舉個例子,for/while循環也是類似的:

set(EMPTY_STR "")
if (NOT EMPTY_STR AND FLAG AND NUM LESS 50 AND NOT NOT_DEFINE_VAR)
    message("The first if branch...")
elseif (EMPTY_STR)
    message("EMPTY_STR is not empty")
else ()
    message("All other case")
endif()

3 列表操作

list是CMake中進行列表操作的一個命令,有很多有用的子命令,比較常用的有:

  1. APPEND,往列表中添加元素;
  2. LENGTH,獲取列表元素個數;
  3. JOIN,將列表元素用指定的分隔符連接起來;

示例如下:

set(SLOGAN_ARR To be)   # Saved as "To;be"
set(SLOGAN_ARR To;be)
set(SLOGAN_ARR "To;be")
set(WECHAT_ID_ARR Real Cool Eengineer)
list(APPEND SLOGAN_ARR a)                # APPEND sub command
list(APPEND SLOGAN_ARR ${WECHAT_ID_ARR}) # Can append another list
list(LENGTH SLOGAN_ARR SLOGAN_ARR_LEN)   # LENGTH sub command
# Convert list "To;be;a;Real;Cool;Engineer"
# To string "To be a Real Cool Engineer"
list(JOIN SLOGAN_ARR " " SLOGEN_STR)
message("Slogen list length: ${SLOGAN_ARR_LEN}")
message("Slogen list: ${SLOGAN_ARR}")
message("Slogen list to string: ${SLOGEN_STR}\n")

對於列表常用的操作,list命令都基本實現了,需要其他功能直接查閱官方文檔即可。

4 文件操作

CMake的file命令支持的操作比較多,可以讀寫、創建或複製文件和目錄、計算文件hash、下載文件、壓縮文件等等。
使用的語法都比較類似,以筆者常用的遞歸遍歷文件爲例,下面是獲取src目錄下兩個子目錄內所有c文件的列表的示例:

file(GLOB_RECURSE ALL_SRC
        src/module1/*.c
        src/module2/*.c
        )

GLOB_RECURSE表示執行遞歸查找,查找目錄下所有符合指定正則表達式的文件。

5 配置文件生成

使用configure_file命令可以將配置文件模板中的特定內容替換,生成目標文件。
輸入文件中的內容@VAR@或者${VAR}在輸出文件中將被對應的變量值替換。
使用方式爲:

set(VERSION 1.0.0)
configure_file(version.h.in "${PROJECT_SOURCE_DIR}/version.h")

假設version.in.h的內容爲:

#define VERSION "@VERSION@"

那麼生成的version.h的內容爲:

#define VERSION "1.0.0"

6 執行系統命令

使用execute_process命令可以執行一條或者順序執行多條系統命令,對於需要使用系統命令獲取一些變量值是有用的。比如獲取當前倉庫最新提交的commit的commit id:

execute_process(COMMAND bash "-c" "git rev-parse --short HEAD" OUTPUT_VARIABLE COMMIT_ID)

7 查找庫文件

通過find_library在指定的路徑和相關默認路徑下查找指定名字的庫,常用的格式如下:

find_library (<VAR> name1 [path1 path2 ...])

找到的庫就可以被其他target使用,表明依賴關係。

8 include其他模塊

include命令將cmake文件或者模塊加載並執行。比如:

include(CPack) # 開啓打包功能
include(CTest) # 開啓測試相關功能

CMake自帶有很多有用的模塊,可以看看官網的鏈接:cmake-modules,對支持的功能稍微有所瞭解,後續有需要再細看文檔。

當然,如果感興趣,也可以直接看CMake安裝路徑下的目錄CMake\share\cmake-3.20\Modules中的模塊源文件。

文中的示例代碼均共享在開源倉庫:https://gitee.com/RealCoolEngineer/cmake-template,當前commit id:f8f3948
關於CMake腳本源文件的示例位於路徑:cmake/script_demo.cmake,可以使用cmake -P cmake/script_demo.cmake執行查看結果。
關於配置文件生成的操作在項目根目錄的CMakeLists.txt中也有示例。

Ok,對於CMake的核心語法概念,以及常用的腳本命令掌握這麼多就可以開始下一步了。
下一篇文章將會詳細介紹CMakeLists.txt的書寫,將會涉及到編譯、鏈接、測試和打包幾個部分,值得期待(●'◡'●)。

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