# CMake 專案建置 ###### tags: `NTOU CSE C++ Programming` `CMake` > 教學文件和作業說明文件: https://hackmd.io/@kogiokka/ntou-cse-cpp-nav > 範例程式和範例專案:[Google Drive]( https://drive.google.com/drive/folders/100YvcqccLgY_27mJnubRSuPALrMAghJQ?usp=sharing) CMake 是一個跨平台的建置自動化軟體,用於軟體的建置、測試和包裝。CMake 會依照你撰寫的腳本產生平台上原生建置系統的設定檔(例如:Makefile),再使用原生建置系統編譯你的專案。 CMake專案的建置流程: 1. 配置CMake快取(Configure Step) 2. 產生原生建置系統的設定檔(Generate Step) 3. 編譯並執行(Compile & Run) 參考資料:https://cgold.readthedocs.io/en/latest/tutorials/cmake-stages.html ## 環境配置 ### Windows > 如果你想在 Visual Studio 中使用 CMake 設定專案,請直接到「**搭配 Visual Studio**」的章節。 * CMake:https://cmake.org/download/ * TDM-GCC:https://jmeubank.github.io/tdm-gcc/ * Ninja: https://github.com/ninja-build/ninja/releases CMake 版本選擇 **Latest Release**,TDM-GCC 版本選擇 **MinGW-w64 based**(64位元)。Ninja 壓縮檔內為單一檔案的可執行檔,解壓縮後將`ninja.exe`放置在`C:/dev/bin`目錄下。並且將`"C:\dev\bin"`加入系統環境變數(**設定→關於→進階系統設定→環境變數**)。 1. 在終端機輸入指令測試是否正確安裝:  ### macOS 使用[Homebrew](https://brew.sh/)套件管理工具(必須先下載XCode)。開啟終端機後執行下列指令安裝Homebrew: ```zsh /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ``` 腳本跑完後請依照最後的提示訊息把 `brew` 指令加到 `PATH` 環境變數中。 安裝所需軟體: ```sh brew install cmake brew install gcc brew install ninja ``` ### Linux Debian/Ubuntu ```text apt install gcc g++ cmake ninja-build ``` CentOS/Fedora ```text dnf install gcc gcc-c++ cmake ninja-build ``` ## 範例〇、簡單的CMake專案 專案目錄結構 ```text Project0 ├── CMakeLists.txt └── main.cpp ``` CMakeLists.txt ```cmake= cmake_minimum_required(VERSION 3.16) # CMake_最低版本_需求 project("Hello World") # 建立一個CMake專案,並給予專案名稱 add_executable(hello-world) # 建立一個CMake目標,目標為可執行檔 target_sources(hello-world PRIVATE "main.cpp") # 指定建置該CMake目標時所使用的來源檔案 ``` main.cpp ```cxx= #include <iostream> int main() { std::cout << "Hello, world!" << std::endl; return 0; } ``` ## 範例一、多個原始碼檔案的CMake專案 專案目錄結構 ```text Project1 ├── CMakeLists.txt ├── f1.cpp ├── f1.h ├── f2.cpp ├── f2.h └── main.cpp ``` CMakeLists.txt ```cmake= cmake_minimum_required(VERSION 3.16) project("Project 01") # 新增一個 CMake 目標,目標型態為可執行檔。 add_executable(project-01) # 指定建置該 CMake 目標時所使用的來源檔案,不必包含標頭檔。 # PRIVATE之後列出來的檔案路徑是從 CMakeLists.txt 所在的目錄起始的相對 # 路徑。以空格區隔即可,換行加縮排是為了可讀性和方便編輯。 target_sources(project-01 PRIVATE "main.cpp" "f1.cpp" "f2.cpp" # "f3.cpp" # "f4.cpp" # ... ) ``` ## 範例二、建立和使用靜態函式庫 專案目錄結構 ```text Project2 ├── CMakeLists.txt ├── libstatic1 │ ├── CMakeLists.txt │ ├── f1.cpp │ ├── f1.h │ ├── f2.cpp │ └── f2.h └── main.cpp ``` CMakeLists.txt ```cmake= cmake_minimum_required(VERSION 3.16) project("Project 02") # 新增一個 CMake 目標,目標型態為可執行檔。 add_executable(project-02) target_sources(project-02 PRIVATE "main.cpp" ) # 新增目標 project-02 的 Include 目錄 target_include_directories(project-02 PRIVATE "libstatic1/" ) # 將指定資料夾的 CMake 專案(含有 CMakeLists.txt)一起加入建置。 add_subdirectory("libstatic1/") # 新增目標 project-02 所連結的函式庫。函式庫名稱為其他 CMake 專案的目標名稱。通常來自 # find_package 或 add_subdirectory。以這個範例來說,libstatic1 函式庫是來自 # add_subdirectory 指令所加入的 CMake 專案。 target_link_libraries(project-02 PRIVATE libstatic1 ) ``` libstatic1/CMakeLists.txt ```cmake= # 新增一個 CMake 目標,目標型態為靜態函式庫。 add_library(libstatic1 STATIC) target_sources(libstatic1 PRIVATE "f1.cpp" "f2.cpp" ) ``` ## 搭配 Visual Studio Code Visual Studio Code 的使用者介面([官方文件](https://code.visualstudio.com/docs/getstarted/userinterface)):  ### 安裝 Visual Studio Code下載頁面:https://code.visualstudio.com/download 使用到的延伸模組: * [C/C++](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) * [CMake Tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools) 在左側**活動列**點選**延伸模組**按鈕,在 Marketplace 中搜尋上面列出來的模組並安裝。 ### 設定檔 在 CMake 專案的根目錄建立`.vscode`資料夾,並在裡面新增[工作區設定檔](https://code.visualstudio.com/docs/getstarted/settings#_workspace-settings),`settings.json`。開啟設定檔中請將`cmake.generator` 設成 `Ninja`。如下列原始碼: `.vscode/settings.json` ```json= { // 設定CMake產生器 "cmake.generator": "Ninja", // 設定CMake變數 "cmake.configureSettings": { // 例: // CMAKE_C_COMPILER: "C:/TDM-GCC-64/bin/gcc.exe" // CMAKE_CXX_COMPILER: "C:/TDM-GCC-64/bin/g++.exe" // CMAKE_PREFIX_PATH: "<list-of-search-paths>" // CMAKE_INSTALL_PREFIX: "<prefix-of-installation-path>" // ...... }, } ``` `cmake.configureSettings` 是設定 CMake 的變數,可以指定C/C++編譯器路徑,可執行檔的安裝路徑等等。可以先留空。 ### 開啟 CMake 專案 **檔案→開啟資料夾**,開啟 CMake 專案的根目錄。 VSCode 會提示是否要執行 CMake 配置,選擇 Yes。  在上方出現的選單中選擇GCC。VSCode會自動開始CMake配置,配置完成後你會在輸出的方框內看到完成配置和完成產生的訊息。  完成配置後,按下方狀態列的播放鍵編譯並執行。 ## 搭配 Visual Studio 近年來微軟已經把 CMake 整合進 Visual Studio 的 C++ 工作負載中,現在在 Visual Studio 中建立和開啟 CMake 專案已經是再簡單不過的事。 ### 開啟 CMake 專案 在**開始視窗**點選**開啟本機資料夾**,或是在主視窗的選單點擊**檔案→開啟→資料夾**。在開啟的對話視窗選擇 CMake 專案的根目錄,也就是最上層的`CMakeLists.txt`資料夾。開啟後請稍微等一下,Visual Studio會自動開始配置CMake的快取。如果在配置期沒有出現問題,Visual Studio會在輸出面板回報「已完成 CMake 的產生」。  在選取啟動項目的下拉選單選取 CMake 目標就可以編譯並執行。  ### 建立 CMake 專案 在**開始視窗**點選**建立新的專案→CMake 專案**,或是在主視窗的選單點擊**檔案→新增→專案→CMake 專案**。一樣勾選「將解決方案與專案置於相同目錄中」簡化目錄結構。  > Visual Studio 2017 > > 檔案→新增→專案→已安裝→Visual C++→跨平台→CMake→CMake 專案 > 取消勾選「為方案建立目錄」。 ## 搭配 KDevelop  ### 安裝 Windows和macOS使用者請到此下載:https://www.kdevelop.org/download Linux上可以直接由發行版的套件管理工具安裝: ```bash # Debian/Ubuntu apt install kdevelop # Fedora dnf install kdevelop # openSUSE zypper install kdevelop5 ``` ### 建立專案 **專案 → 由樣本建立...** 開啟「建立新專案」的視窗。選擇專案模板:**Standard → Terminal**,右欄選單選擇`CMake C++`。 ### 開啟專案 **專案 → 開啟/匯入專案...**,選擇 CMake 專案的根目錄(`CMakeListst.txt`所在的位置)。開啟後你會看見 Project Manager 欄位顯示為 `CMakeListst.txt (CMake Project Manager)`,按完成。  接下來進到專案的初始設定。KDevelop把建置目錄(Build directory)預設在`<CMake專案根目錄>/build`,但我們希望編譯產生的檔案會依照編譯型態(Debug 或 Release)分成不同資料夾。所以請在建置目錄的`.../build`之後接一個依照編譯型態命名的資料夾。簡而言之,Debug組態的建置目錄變成`<CMake專案根目錄>/build/Debug`,而待會提到的Release組態的建置目錄會變成`<CMake專案根目錄>/build/Release`。  開啟專案後,等待 CMake 執行配置。如果看到下方輸出訊息顯示「Configuring done」和「Generating done」表示 CMake 成功產生快取。在側邊欄點擊專案分頁就可以開始瀏覽和撰寫程式碼囉!  ### 編譯 如果你的 CMake 專案依照上述的步驟有成功產生快取的話,就可以開始編譯了。按左上角工作列的「編譯」,或是按`F8`。 ### 執行 按下工作列的執行,或是 `Shift+F9`。 #### 設定啟動器 當編譯完成後你按下工作列上的執行,KDevelop 會跳出一個**啟動設定**的視窗,尋問你應該如何啟動你的專案。在左上角**新增**選單中選擇我們專案中的 **CMake 目標**。以 `Project02` 為例,在選單中你會看到透過 `add_executable` 加入的 `project-02`,還有 `add_library` 加入的 `libstatic1`。因為我們想要「啟動」的是 `project-02` 這個可執行檔,所以選擇它。  新增完成後,你會看到右側面板有一欄**專案目標**顯示「Project02/project-02」。這就是你預設要啟動的目標了,按 OK。 > 為何要多一個這樣的設定而不是直接執行?假設未來在開發大型應用程式時,一個專案可能包含好幾個部件,有主程式相依於其他可執行檔或函式庫。那這時你就會需要指定每個部件的執行順序。 #### 多個啟動器 上方「執行選單 → 設定啟動器...」可以再新增啟動方式。啟動器一次只能選擇一個,也就是當你按下`Shift+F9`時會代入的設定。到「執行選單 → 目前的啟動設定」做切換。 ### 新增編譯組態 在開啟專案時,我們設定了Debug組態。現在來新增Release組態。在主選單點擊**專案 → 開啟設定...**,進到「設定專案」的頁面後點擊右上角的「+」按鈕。  又是剛剛設定建置目錄的頁面。編譯型態選擇`Release`,建置目錄請記得設為`<CMake專案根目錄>/build/Release`。  上述設定完成後按 OK → Apply,你就可以在設定專案的頁面選擇 Debug 或 Release:  ## 使用 Command Line 將終端機的工作目錄切換到專案的根目錄,再用 `cmake` 產生 CMake 快取,最後執行建置。在命令列使用 `cmake` 指令產生CMake快取時,用到的參數種類主要有下列四種: ```text cmake -S <專案目錄> -B <建置目錄> -G <產生器> -D <變數>=<值> ``` 1. `-S`,指定原始碼目錄。通常是專案的根目錄。如果沒有此選項,預設是終端機當下的**工作目錄**。 2. `-B`,指定建置目錄。通常會在專案根目錄之下多一個 `build/` 或 `out/` 資料夾存放 CMake 快取和建置期產生的檔案(例如可執行檔)。 4. `-G`,指定用來生成原生建置系統檔案的 [CMake 產生器](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)。常用的是 `Makefile` 或 `Ninja`,有支援 CMake 專案的 IDE 通常也是搭配上述其中一個。 3. `-D`,定義 CMake 變數。例如 [`CMAKE_BUILD_TYPE`](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) 和 [`CMAKE_MAKE_PROGRAM`](https://cmake.org/cmake/help/latest/variable/CMAKE_MAKE_PROGRAM.html)。 將參數換行可以增加可讀性,但必須要用跳脫字元來接續。 ### 產生 CMake 快取 #### Linux ```zsh= # 類Unix系統的殼層(shell)中跳脫字元為反斜線(backslash):「\」。 # Debug cmake -B build/Debug -G Ninja \ -D CMAKE_BUILD_TYPE=Debug # Release cmake -B build/Release -G Ninja \ -D CMAKE_BUILD_TYPE=Release ``` #### macOS ```zsh= # 類Unix系統的殼層(shell)中跳脫字元為反斜線(backslash):「\」。 # Debug cmake -B build/Debug -G Ninja \ -D CMAKE_BUILD_TYPE=Debug # Release cmake -B build/Release -G Ninja \ -D CMAKE_BUILD_TYPE=Release ``` #### Windows 終端機建議到微軟商店下載 Windows Terminal 使用。 PowerShell ```powershell # 如果電腦上有安裝多個C/C++編譯器,可以透過設定CC/CXX環境變數指定編譯器。 $Env:CC = "C:/TDM-GCC-64/bin/gcc.exe"; $Env:CXX = "C:/TDM-GCC-64/bin/g++.exe"; # PowerShell的跳脫字元為反引號(backtick):「`」 # Debug cmake -B build/Debug -G Ninja ` -D CMAKE_MAKE_PROGRAM="C:/dev/bin/ninja.exe" ` -D CMAKE_BUILD_TYPE=Debug # Release cmake -B build/Release -G Ninja ` -D CMAKE_MAKE_PROGRAM="C:/dev/bin/ninja.exe" ` -D CMAKE_BUILD_TYPE=Release ``` CMD(命令提示字元) ```cmd :: 如果電腦上有安裝多個C/C++編譯器,可以透過設定CC/CXX環境變數指定編譯器。 set CC="C:/TDM-GCC-64/bin/gcc.exe"; set CXX="C:/TDM-GCC-64/bin/g++.exe"; :: CMD的跳脫字元為脫字符(caret):「^」。 :: Debug cmake -B build/Debug -G Ninja ^ -D CMAKE_MAKE_PROGRAM="C:/dev/bin/ninja.exe" ^ -D CMAKE_BUILD_TYPE=Debug :: Release cmake -B build/Release -G Ninja ^ -D CMAKE_MAKE_PROGRAM="C:/dev/bin/ninja.exe" ^ -D CMAKE_BUILD_TYPE=Release ``` ### 編譯 `cmake --build` 後面接剛才 CMake 產生快取的位置。 ```sh # Debug cmake --build build/Debug # Release cmake --build build/Release ``` ### 執行 產生出的可執行檔會放在 CMake 快取資料夾中,名稱會是 CMake 目標名稱。例如 `build/Debug/<target-name>.exe`。可以在終端機用相對路徑的方式執行它,如果終端機的目錄在專案根目錄,執行的方式就是`./build/Debug/<target-name>.exe`。 ## 安裝 CMake 提供 `install` 指令來指示一個專案應該怎麼安裝。簡單來說,是把專案編譯後產生的執行檔、函式庫和各式資源複製到每個作業系統上預設的安裝路徑,例如 Windows 上的 `C:\Program Files` 或 Linux 上的 `/usr`。開發者也能夠藉由設定 CMake 變數來指定安裝路徑。 ### 為何需要安裝專案 程式開啟時都有所謂的「**工作目錄(Working directory)**」,通常就是可執行檔所在的位置。當程式在讀取外部檔案時,找尋檔案的路徑是從工作目錄開始算起。所以如果程式碼中你要讀取一個路徑為`TestData/hello.txt`的測試檔案,當程式編譯完成要執行時,`hello.txt` 就必須放在可執行檔所在資料夾之下的`TestData/`資料夾。 工作目錄不一定都是可執行檔所在的資料夾,如果你是從終端機(例:命令提示字元)執行程式,則工作目錄會是你終端機所在的目錄。假設現在有一個可執行檔 `a.exe` 位於`<專案根目錄>/build/Debug/`資料夾中。如果你終端機進到`<專案根目錄>`並透過輸入`./build/Debug/a.exe` 執行程式,那麼依剛才的範例,`a.exe`找的檔案路徑就會是`<專案根目錄>/TestData/hello.txt`。 一般的整合開發環境(IDE)在執行你的程式時預設的工作目錄會是可執行檔的目錄。因此,若`hello.txt`放在專案的`TestData` 資料夾之下(即`<專案根目錄>/TestData/hello.txt`),那麼位於 `<專案根目錄>/build/Debug/` 的`a.exe`在執行時就會找不到它。CMake的`install`指令可把建置出的 CMake 目標(可執行檔)和程式的資源(例如範例的`hello.txt`)安裝到同一個資料夾下。但這個資料夾的預設路徑會是系統安裝應用程式的地方,例如`C:/Program Files/`。如果我們不希望讓自己測試的東西就這樣安裝在系統上,就可以透過定義 `CMAKE_INSTALL_PREFIX` 變數來改變安裝的路徑。 ### 開發工具設定 #### Visual Studio 不需額外設定變數,在**選取啟動項目**的選單中選擇「`<可執行檔名稱>.exe (安裝)`」的選項即可。 #### Visual Studio Code 在工作區設定檔(`.vscode/settings.json`)中的 `cmake.configureSettings` 加入`CMAKE_INSTALL_PREFIX` 變數。並加入`cmake.debugConfig`的設定,如下: `.vscode/settings.json` ```json= { "cmake.generator": "Ninja", "cmake.debugConfig": { "cwd": "${workspaceFolder}/installed" }, "cmake.configureSettings": { "CMAKE_INSTALL_PREFIX": "${workspaceFolder}/installed" } } ``` 請在 CMake Tools 下方的工具列把預設的建置目標從`[all]`改成`[install]`,再執行 `Build`,你就會看到專案根目錄多了一個 `install/` 資料夾。這時就可以按執行了。  #### KDevelop 在建立專案時的 `Extra arguments` 欄位輸入 `-DCMAKE_INSTALL_PREFIX=<專案根目錄>/install`。若是已經建立的專案,請到**專案→設定專案→顯示進階→Extra arguments**修改。 在設定啟動器的視窗中,請將上方執行檔的路徑指向`install/`資料夾中的可執行檔。還有在下方相依性的部分,把動作欄位改成編譯並安裝。 #### Command Line 產生 CMake 快取時,在 `cmake` 的參數的部分加上 `-D CMAKE_INSTALL_PREFIX=<專案根目錄>/install`。例如: ```sh cmake -B build/Debug -G Ninja \ -D CMAKE_BUILD_TYPE=Debug \ -D CMAKE_INSTALL_PREFIX=<專案根目錄>/install ``` 編譯並安裝 ```sh cmake --build build/Debug --target install ``` 進到 `install/` 資料夾,讓工作目錄位於 `<專案根目錄>/install/` 再執行。 ```sh cd install/ ./<target-name>.exe ``` ## VCPKG ### VSCode ```json { "cmake.buildDirectory": "${workspaceFolder}/build", "cmake.generator": "Ninja", "cmake.configureSettings": { "CMAKE_TOOLCHAIN_FILE": "<path-to-vcpkg.cmake>", "VCPKG_TARGET_TRIPLET": "x64-windows", "CMAKE_RC_COMPILER": "<path-to-windres.exe>" } } ```
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.