03 最小CMake專案

2022-09-08 12:05:17

03 最小CMake專案

所有CMake專案都從一個CMakeLists.txt檔案開始,此檔案應該放在原始碼樹的最頂層目錄下。可以將CMakeLists.txt想象成CMake專案檔案,定義了從源和目標的構建到測試、打包和其他自定義任務的一切事物。他可以僅有簡單的幾行,也可以相當的複雜並且從其他目錄中匯入更多的檔案。CMakeLists.txt只是一個普通的文字檔案,通常像專案中其他原始檔一樣直接編輯。

和其他原始檔的語言規範類似,CMake也定義了自己的語言規範,其中有許多程式設計師熟悉的東西,例如變數、函數、宏、條件邏輯、迴圈、註釋等。這些概念和功能將在後續章節進行介紹。但目前,我們從一個簡單的構建開始。如下是一個極小的,格式良好的CMakeLists.txt檔案,用於生成一個簡單可執行檔案:

cmake_minimum_required(VERSION 3.2)
project(MyApp)
add_executable(myExe main.cpp)

範例中的每一行都執行了一個CMake內建命令。在CMake中,命令和其他語言的函數呼叫類似,只是雖然它們支援引數,但是不能直接返回值(後續章節顯示如何以其他方式將值返回給呼叫者),引數之間用空格分隔,可以跨多行分隔:

add_executable(myExe
    main.cpp
    src1.cpp
    src2.cpp)

命令的名字是大小寫不敏感的,但如今更常見的是命令名稱全使用小寫(CMake官方檔案也是如此)。

3.1 管理CMake版本

CMake一直在更新並新增對其他工具、平臺和特性的支援。CMake開發人員保證了每個新版本的向後相容性,因此當用戶更新到較新版本的CMake時,專案可以繼續像以前一樣構建。有時,CMake在新的版本中會更改自身的某一行為,或者引入更嚴格的檢查和警告,CMake提供了策略(policy)機制,允許專案不立即處理此類問題,而是宣告其「像CMake版本X.Y.Z一樣執行」。這允許CMake在內部修復錯誤並引入新功能,但仍可以保持與任何過去的版本的行為一致。

專案指定其預期的CMake版本行為的主要方式是使用cmake_minimum_required()命令。這個命令應該是CMakeLists.txt檔案的第一行,以便在進行任何其他行為之前進行檢查和設定,這個命令執行兩件事:

  • 它指定專案所需的CMake的最低版本。如果CMakeLists.txt檔案使用比指定版本更老的CMake來處理,則它會立即報錯並停止執行。這可確保在CMake執行之前,所需的最小功能集可用。
  • 它強制使CMake的行為與指定版本一致。

此命令十分重要,如果CMakeLists.txt的第一條命令不是cmake_minimum_required()命令,則CMake會發出警告,因為CMake需要知道如何為後續的命令設定策略行為(policy behavior)。對於大多數專案,將cmake_minimum_required()簡單地視為指定所需的最低CMake版本就足夠了,而另一個作用,將CMake行為設定為與特定版本相同,可以被認為是附帶的,在12 策略TODO中更詳細的討論了策略的設定並解釋瞭如何根據需要客製化此行為。

cmake_minimum_required()命令的格式很直觀:

cmake_minimum_required(VERSIOn major.minor[.patch[.tweak]])

VERSION關鍵字必須始終存在,並且提供的版本詳細資訊中有major.minor部分。在大多數專案中,不需要patchtweak部分,這些通常只出現在CMake的次要更新版本中。只有在需要修復CMake的特定的錯誤時,專案才應該指定patch部分,而由於CMake 3.x系列中沒有使用tweak,專案也不應該使用此部分。

開發人員應該仔細考慮它們的專案所需的CMake最低版本,3.2版本可能是現在專案所考慮的最低的版本,因為它為現代CMake提供了相當完整的功能集。2.8.12版本缺乏許多有用的功能,但它可能適用於較老的專案。如果需要在iOS等一些fast-moving平臺中開發,可能需要較新版本的CMake才能支援新的作業系統版本。

作為一般經驗法則,使用最新的CMake版本,不會給專案的構建帶來重大的問題。通常專案中最大的困難是需要支援較老的平臺,而這些平臺提供的CMake版本可能相當舊,對於這種情況,開發人員應該儘可能考慮安裝一個更新的版本,而不是將自己限制在非常舊的CMake版本上。另一方面,如果專案本身是其他專案的依賴項,那麼使用較新版本的CMake可能會給適配帶來障礙,在這種情況下,使用能滿足所需的CMake功能的較低的版本,如何可以則使用高版本的功能可能更好(12 策略TODO中介紹了實現此目的的方法)。這種方式防止了其他專案被迫使用比目標環境所提供的版本更高的CMake。使用舊版本的主要缺點是它可能會導致更多的告警,CMake鼓勵使用較新的版本。

3.2 project()命令

每個CMake專案都應該包含一個project()命令,並且應該緊隨在cmake_minimum_required()命令之後。這個命令的常用呼叫形式如下:

project(projectName
        [VERSION major[.minor[.patch[.tweak]]]]
        [LANGUAGES languageName ...])

其中projectName是必填項,只能包含字母、數位、下劃線(_)和連字元(-),在實踐中通常只使用字母和下劃線。由於不允許空格,所以專案名稱不必用引號包圍。此名稱可以用於具有某些專案生成器(例如Xcode和Visual Studio)的頂層專案中,也可用於專案的其他部分,例如作為打包和檔案後設資料的預設值,用來提供專案特定變數等。專案名稱是project()命令的唯一必須引數。

可選項VERSION僅在CMake 3.0及之後支援,與projectName一樣,版本詳細資訊被CMake用來填充一些變數或者作為預設的包後設資料,除此之外沒有其他意義。儘管如此,指定專案的版本是一個好的習慣,以便於專案的其他部分可以參照它,19 指定版本細節TODO中深入介紹了這一點,並說明了如何在CMakeLists.txt檔案中參照此版本資訊。

可選項LANGUAGES定義專案啟用的程式語言,支援的值包括C、CXX、Fortran、ASM、Java等,如果指定多種語言,使用空格分隔它們。在某些特殊情況下,專案可能宣告無需任何語言,此時可以使用LANGUAGES NONE來實現,後面章節介紹的技術利用了這種特殊的形式。如果沒有提供LANGUAGES選項,則CMake預設使用C和CXX作為LANGUAGES的值。CMake 3.0版本之前不支援LANGUAGES關鍵字,但仍可以指定語言,只需要按照舊版本的形式,在專案名稱後面追加:

project(myProj C CXX)

新的專案鼓勵指定3.0及以上的CMake版本,並使用帶有LANGUAGES關鍵字的形式。

project()命令不僅填充幾個變數,它的重要職責之一是檢查每種啟用的語言的編譯器,以確保它們能成功編譯和連結,可以儘早發現編譯器和連結器的問題。當檢查通過後,CMake將設定一些變數和屬性,這些變數和屬性控制著啟用的語言的構建過程。如果CMakeLists.txt檔案沒有呼叫project()或沒有儘早呼叫,那麼CMake將在內部隱式地使用預設語言C和CXX來呼叫它,確保正確設定編譯器和連結器,保證依賴於它們的命令的正確性。後續章節將詳細介紹如何設定工具鏈,並演示如何查詢和修改編譯器標誌、編譯器位置等內容。

當CMake檢查編譯器和連結器成功時,檢查結果將快取下載,以便在後續CMake執行時不重複檢查,這些快取的詳細資訊儲存在構建目錄的CMakeCache.txt檔案中,有關檢查的詳細資訊可以在構建區域的子目錄中找到,但開發人員通常不關注那裡,除非在使用不熟悉的編譯器或設定交叉編譯工具鏈檔案時。

3.3 構建基本可執行檔案

為了完成我們的最小范例,使用add_executable()命令告訴CMake使用一組原始檔來建立可執行檔案,此命令的基本形式為:

add_executable(targetName source1 [source2 ...])

此命令將建立一個可執行檔案,該可執行檔案在CMake中使用targetName來參照。此名稱可以包含字母、數位、下劃線和連字元。專案構建時,將在構建目錄生成一個可執行檔案,檔案的名字基於目標名稱且與平臺有關。考慮如下範例:

add_executable(myApp main.cpp)

預設情況下,Windows平臺中可執行檔名稱為myApp.exe,而在基於Unix的平臺上則為myApp。可以通過目標屬性來自定義可執行檔案的名稱,將在09_屬性TODO中介紹。可以使用多個不同目標名稱的add_executable()命令來建立多個可執行檔案,如果多個add_executable()命令使用了相同的目標名稱,CMake將報錯並結束執行。

3.4 註釋

在本章結束之前,需要學習一下CMakeLists.txt檔案的註釋。CMake的註釋和Unix shell指令碼格式類似,任何以#開頭的行都被視為註釋,除了在以引號包圍的字串中,文字行中#之後的任何內容也被視為註釋。如下顯示了一些註釋範例,並彙集了本章中介紹的概念:

cmake_minimum_required(VERSION3.2)

# 我們不使用C++編譯器,所以project()命令無需測試
# 平臺C++編譯器是否可用
project(MyApp VERSION 4.7.2 LANGUAGES C)

add_executable(mainToo
    main.c
    debug.c)

add_executable(testTool testTool.c)

3.5 建議

確保每個CMake專案在頂級CMakeLists.txt檔案的第一行中包含cmake_minimum_required()命令。在決定所需的最小版本號時,要記住版本越高,專案能夠使用的CMake功能就越多,這意味著專案能適配新的平臺或作業系統,但是也不可避免的引入了新的需要構建系統處理的問題。相反,如果建立一個為作業系統本身的一部分進行構建和分發的專案(常見於Linux),則最低CMake版本可能由系統本身的CMake版本決定。

如果專案需要CMake 3.0或更高版本,應該儘早將專案版本號寫入project()命令中。如果在專案生命週期的後期,要克服現有流程的慣性來改變專案的版本號,可能會很困難。

github地址:https://github.com/theArgs/CMake-Guide