拋棄 Autotools 向 CMake 邁進吧
在我以前的文章 Autotools 入門 一文中,我說明了如何使用 Autotools 來管理和打包代碼。這是一個強大且通用的平台,可輕鬆集成到許多打包系統中,包括 RPM、APT、pkgsrc 等等。它的語法和結構可能會令人困惑,但幸運的是,我們還有其他選擇,開源的 CMake 就是其中一個。
CMake 是一個用於構建、測試和打包軟體的跨平台套件。它使用簡單而清晰的語法,因此即使你以前從未使用過構建系統,也很容易開始使用。
安裝 CMake
CMake 可能已經安裝在你的 Linux 系統上。如果沒有,你可以使用發行版的程序包管理器進行安裝:
$ sudo dnf install cmake
在 Debian 或者其他相似的系統上:
$ sudo apt install cmake
在 Mac 上,你可以使用 MacPorts 或者 Homebrew 來安裝:
$ sudo port install cmake
在 Windows 上,你可以使用 Chocolatey 或者直接從 CMake 網站 下載二進位來安裝。
使用 CMake
對於想要從源代碼構建軟體的開發人員或用戶來說,CMake 是一種快速簡便的編譯和安裝方法。 CMake 分階段工作:
- 首先,在
cmake
步驟中,CMake 掃描計算機查看一些默認設置。默認設置包括庫的位置以及在系統上安裝軟體的位置。 - 接下來,使用系統上的
make
命令(在 Linux 上是 GUN Make,在 NetBSD 上是 NetBSD Make)來編譯程序。這個過程通常是將人類可讀的源代碼轉換成機器語言。 - 最後,在
make install
一步中,那些編譯過的文件將被拷貝到(在cmake
步驟中掃描出來的)計算機上合適的位置。
這看起來很簡單,當你使用 CMake 時就是這樣。
CMake 的可移植性
CMake 在設計時就考慮了可移植性。雖然它不能使你的項目在所有 POSIX 平台上都能正常工作(這取決於作為開發者的你),但它可以確保將標記為要安裝的文件安裝到已知平台上最合適的位置。而且由於有了 CMake 之類的工具,對於高級用戶而言,根據其系統需求自定義和覆蓋任何不合適的選項都很容易。
使用 CMake,你只需要知道將哪些文件安裝到哪個常規位置即可。它會照顧其他一切。不再需要自定義安裝腳本,它們有可能在任何未經測試的操作系統上失敗。
打包
像 Autotools 一樣,CMake 也得到了很好的打包支持。無論它們是打包成 RPM 還是 DEB 或 TGZ(或其他任何東西),將帶有 CMake 的項目交給打包者,他們的工作既簡單又直接。打包工具支持 CMake,因此可能不需要進行任何修補或者調整。在許多情況下,可以自動將 CMake 項目整合到工作流中。
如何使用 CMake
要在項目中使用 CMake,只需在項目目錄中創建 CMakeLists.txt
文件。首先,聲明最低要求的 CMake 版本以及項目名稱和版本。CMake 會努力在儘可能長時間內保持兼容性,但是隨著你使用的時間越長,並且關注它最新的開發動態,你就會知道哪些特性是你所依賴的。
cmake_minimum_required(VERSION 3.10)
project(Hello VERSION 1.0)
如你可能已經看到的那樣,CMake 的語法是一個帶有括弧和參數的命令。大寫的 VERSION
字元串不是任意的,也不只是格式。它們是 project
命令中的有效參數。
在繼續之前,先寫一個簡單的 C 或者 C++ 的 hello world
程序。為了簡單,我就寫了六行 C 代碼,並把它保存在 hello.c
中(為了匹配我在 CMakeLists.txt
中可執行文件的名字)。
#include <stdio.h>
int main() {
printf("Hello open sourcen");
return 0;
}
不過,不要搞錯了,CMake 不僅適用於 C 和 C++。它可以處理任意文件,並且有許多可用的命令,因此它可以幫助你維護許多不同形式的項目。
CMake 網站中記錄了所有有效的內置命令及其可用參數,因此無論你要做什麼,都可以輕鬆發現所需的功能。不過,這是一個簡單的示例,因此,你需要的下一個命令是必不可少的 —— 你必須為 CMake 定義要構建的代碼:
add_executable(Hello hello.c)
這個命令指定了你編譯後的二進位文件的名字為 Hello
。因此,它與你在終端中執行帶有 -o Hello
的 gcc
命令是一樣的。
在一些比較複雜的項目中,你可能還需要使用庫文件,你可以使用 add library
命令來鏈接庫文件。
在你設置了你想要構建和標記為安裝的文件之後,你必須要告訴 CMake 一旦用戶安裝了程序,最終的應用程序應該在哪個位置。
在這個簡單的例子里,你僅需要做的一件事就是在你的 CMakeLists.txt
文件里添加 install
命令。install
命令接受幾個參數。但是在這個例子中,你僅需要使用 TARGET
命令來指定你要安裝文件的名字。
install(TARGETS Hello)
向 CMake 工程添加一些文件
一個軟體項目向用戶交付的往往不僅僅只有代碼,還有一些其他的文件數據,例如手冊或者是信息頁、示例項目,或者是配置文件。你可以使用與包含編譯文件時類似的工作流程,將任意數據包含在 CMake 項目中:在 CMakelists.txt
文件中使用 file
命令,然後說明一下這些文件要安裝在哪裡。
例如,你可以在這個項目中包含一個 assets
目錄,你可以使用 file
命令,後面跟上 COPY
和 DESTINATION
參數來告訴 CMake 將這些額外的文件複製到你的分發包中。
file(COPY assets DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
這個 ${CMAKE_CURRENT_BINARY_DIR}
變數是一個特殊的 CMake 內置變數,表示 CMake 正在處理的目錄。換句話說,你的任何文件都會被複制到編譯目錄(在你運行 cmake
命令後,這個過程會更加清晰,到時候回過頭來看一下)。
因為這些額外的數據文件有些雜亂不堪(如果你不信的話,可以看一下 /usr/share
這個目錄)。對於你自己的項目創建一個子文件夾對誰都有好處。最好也帶上版本名字。你可以通過在 CMAKE_CURRENT_BINARY_DIR
中指定一個新的目錄,使用你選擇的項目名稱,後面跟一個為你的項目命名的特殊變數和你在項目聲明中為它設置的 VERSION
。
file(COPY assets DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/Hello-${Hello_VERSION}")
定義安裝位置
你已經定義你要編譯的文件,因此現在你要告訴 CMake 你的程序要安裝在哪個位置。比如你的主程序,這個要程使用 install
命令:
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Hello-${Hello_VERSION}" TYPE DATA)
這裡有一些新的參數。DIRECTORY
參數指定了數據文件是一個目錄,而不是一個文件(FILE
)或者腳本(SCRIPT
)。你使用的參數和複製一些額外文件到編譯目錄時是一樣。另外,在 install
命令中 TYPE
或者 DESTINATION
必須要指定其一。TYPE
參數指定了通用的文件類型,這些文件通常將會被放到合適的位置。在 Linux 系統上,TYPE DATA
一般是 /usr/local/share
或者 /usr/share
,除非用戶定義了其他的位置。
這是諸如 CMake 之類的良好構建系統的強大功能之一。你不必擔心文件的確切位置,因為你知道用戶可以更改 CMake 的首選默認設置,並且 CMake 將構建代碼以使其正常工作。
運行 CMake
CMake 有多種方式來讓你執行命令,你可以在終端或者在一個可交互的程序上執行命令,或者你也可以使用它的圖形界面(GUI)。我比較偏向於使用終端命令,但是我也喜歡使用一些其他的方式(相比與在 Makefile
中查找那些晦澀的變數然後去修改它們更勝一籌)。
對於編譯過開源 C++ 項目的任何人,都熟悉的第一步是創建一個 build
目錄,進入到該目錄,然後運行 cmake ..
命令。 我是一個懶惰的打字員,所以我將構建目錄命名為 b
,但是你可以使用最合適的方式:
$ mkdir b
$ cd b
$ cmake ..
-- The C compiler identification is GNU 11.1.1
-- The CXX compiler identification is GNU 11.1.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /var/home/seth/demo-hello/b
$
這或多或少相當於經典的 ./configure; make; make install
中的 ./configure
。看一下你的構建目錄,CMake 已經幫你生成了幾個新的文件,來讓你的項目更完整。這裡生成了 CMake 的數據文件、一個常規的 Makefile
文件(這是一個免費提供的 247 行的文件,但對於越複雜的項目,行數要多得多),還有一個包含這個示常式序的任意非編譯數據的 Hello-1.0
目錄。
$ ls
CMakeCache.txt
CMakeFiles
Makefile
Hello-1.0
cmake_install.cmake
接下來,你可以進行構建。你可以使用 CMake 的 --build
選項來做這件事,使用當前的構建目錄作為源目錄。
$ cmake --build .
Scanning dependencies of target Hello
[ 50%] Building C object CMakeFiles/Hello.dir/hello.c.o
[100%] Linking C executable Hello
[100%] Built target Hello
或者你可以運行 make
命令。這將讀取由 CMake 生成的 Makefile
文件。在這個例子中,make
默認的行為就是由源程序 hello.c
生成目標文件。
$ make
Scanning dependencies of target Hello
[ 50%] Building C object CMakeFiles/Hello.dir/hello.c.o
[100%] Linking C executable Hello
[100%] Built target Hello
$
如你所料,Hello
二進位可執行文件現在存在於當前的構建目錄中。因為它是一個簡單的自包含應用程序,所以你可以運行它進行測試:
$ ./Hello
Hello open source
$
最後,你可以用 --install
選項進行安裝。因為我不希望我的簡單的 「hello world」 應用程序真的被安裝到我的系統上,我設置了 --prefix
選項,將 CMake 的目標從根目錄(/
)重定向到 /tmp
的一個子目錄。
$ cmake --install . --prefix /tmp/hello/
-- Install configuration: ""
-- Installing: /tmp/dist-hello/usr/local/bin/Hello
-- Installing: /tmp/dist-hello/usr/local/share/Hello-1.0
-- Installing: /tmp/dist-hello/usr/local/share/Hello-1.0/assets/file0
-- Installing: /tmp/dist-hello/usr/local/share/Hello-1.0/assets/file1
另外,你也可以運行 make install
來調用 Makefile
的安裝動作。同樣,為了避免在我的系統上安裝一個演示程序,我在這個例子中設置了 DESTDIR
變數,將安裝目標重定向到 /tmp
的一個子目錄:
$ mkdir /tmp/dist-hello
$ make install DESTDIR=/tmp/dist-hello
[100%] Built target Hello
Install the project...
-- Install configuration: ""
-- Installing: /tmp/dist-hello/usr/local/bin/Hello
-- Installing: /tmp/dist-hello/usr/local/share/Hello-1.0
-- Installing: /tmp/dist-hello/usr/local/share/Hello-1.0/assets/file0
-- Installing: /tmp/dist-hello/usr/local/share/Hello-1.0/assets/file1
看一下輸出的內容,來確定它具體的安裝位置,這個程序已經安裝好了。
快速自定義
CMake 的安裝前綴(由 CMAKE_INSTALL_PREFIX
變數指定)默認是在 /usr/local
這個位置,但是所有的 CMake 變數都可以在你運行 cmake
命令的時候,加一個 -D
選項來改變它。
$ cmake -DCMAKE_INSTALL_PREFIX=/usr ..
$ make install DESTDIR=/tmp/dist-hello
$ make install DESTDIR=/tmp/dist-hello
[100%] Built target Hello
Install the project...
-- Install configuration: ""
-- Installing: /tmp/dist-hello/usr/bin/Hello
-- Installing: /tmp/dist-hello/usr/share/Hello-1.0
-- Installing: /tmp/dist-hello/usr/share/Hello-1.0/assets/file0
-- Installing: /tmp/dist-hello/usr/share/Hello-1.0/assets/file1
所有由 CMake 使用的變數都可以通過這種方式來修改。
互動式的 CMake
CMake 的交互模式是一種用於配置安裝環境的友好而有用的方法。要讓用戶知道該項目使用的所有可能的 CMake 變數是一件工作量很大的事,因此 CMake 互動式界面是他們無需查看 Makefile
和 CMakeLists
即可發現自定義選項的簡便方法。
為了調用這個互動式的 CMake,使用 ccmake
命令,在這個簡單的項目里沒有太多的東西。但是對於像 Rosegarden 這樣的大型項目,這將非常有用。
![Rosegarden](/data/attachment/album/202105/24/183540nflz4ftd1gflfu77.jpg "Rosegarden")
CMake 的更多知識
還有很多很多的 CMake 知識需要去了解。作為一個開發者,我非常喜歡它簡潔的語法、詳盡的文檔、可擴展性以及便捷性。作為一個用戶我非常喜歡 CMake 友好且實用的錯誤提示信息還有它的用戶界面,如果你的項目還未開始使用構建系統,請了解一下 CMake 吧。你以及以後嘗試打包你應用程序的任何人都不會後悔。
via: https://opensource.com/article/21/5/cmake
作者:Seth Kenlon 選題:lujun9972 譯者:amwps290 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive