# 【軟體開發】CMake & Makefile ## 何謂 Makefile 在軟體開發中,make 是一個工具程式,藉由由讀取一個叫做 `makefile` 的檔案,自動化建構軟體。 `make` 是linux中的命令工具,它可以用來解析 `Makefile` 中的指令。 `Makefile` 則是一份用於管理和自動化編譯流程的配置文件,它明確指定計算機如何編譯、連結程式碼,並在大型專案中通過設計的規則顯著提升效率,減少不必要的編譯時間。 * `make` 可以解決相依性問題 * 透過撰寫規則,減少編譯時間,減少重複編譯過程,只需編譯有修改的 * 可以透過 `makefile` 來掌握程式碼架構 ![image](https://hackmd.io/_uploads/Sk8NSxBA1g.png =50%x) ### Makefile Workflow 執行`make`時,會執行以下內容 1. `make`後會去當前目錄尋找`makefile`或者`GNUmakefile`的檔案 2. 在`makefile`的文件中找到**第一個目標文件(target)**,作為最終得目標文件 ### Makefile Content 1. 顯示規則 表示如何生成**一個或多個目標文件** 2. 隱式規則 簡略地書寫`Makefile`的規則,規則中有`.o`的檔案會自動把`.c`也加入到依賴關係中 3. 變數定義 類似C語言的`define`,將變數置換到引用的位置上 4. 文件指示 5. 註釋與換行 `Makefile`中只有行註釋,用`#`符號,換行則是`/` ### Makefile Syntax ``` target: 目標檔1 目標檔2 <tab>gcc -o 目標執行檔案 目標檔1 目標檔2 ``` * **目標(Target)** 一個目標檔,可以是`object`檔案,也可以是`執行檔`,也可是一個標籤 * **依賴(Dependecy, Prerequisites)** 要產生的目標檔中,所需要的相依性檔案 * **命令(Command)** 建立專案時所需要的shell的指令 * target與相依檔案(目標檔案)之間需要以`:`隔開 * 行首必須要有`<tab>`,不能是空白 * `Makefile`是由很多相依性項目(dependencies)和法則(rules)所組成 * 相依性項目(dependencies):描述target,產生該檔案相關的原始碼檔案 * 法則(rules): 則是說明如何根據相依性檔案,來建立目標檔 * 相依性項目(dependency) ``` myapp: main.o 2.o 3.o main.o: main.c a.h 2.o: 2.c a.h b.h 3.o: 3.c b.h c.h ``` * `myapp`與`main.o`、`2.o`、`3.o`相關 * `main.o`與`main.c`、`a.h`相關,依此類推 * 變數定義 * `=`: 進行變數賦值動作,並以`$(name)`使用 * `:=`: 會覆蓋之前變數的值 * `?=`: 如果變數有值的話pass,沒有則進行賦值 * `+=`: 這個就是串接了 * 萬用字元 `%`為萬用字元的語法,代表所有可能的字串,例如`%.c`代表所以suffix為`.c`的子串 ``` CC = gcc OBJS = a.o b.o c.o all: test %.o: %.c $(CC) -c -o $@ $< test: $(OBJS) $(CC) -o $@ $^ ``` * 自動化變數 能夠化簡語法的一種方法,如果今天檔案有300多種,這樣寫太累了,就讓makefile自動推導 * `$@`:表示目前的target * `$^`:代表目前相依性的項目的所有東西 * `$<`:代表目前相依性的項目的第一個東西,gcc只能編譯`.c`,不能編譯`.h`喔 * `$?`: TODO: 代補充 ## CMake CMake 是一個跨平台的建構系統(Build System),用於軟體的建置、測試和包裝。CMake 會依照你撰寫的腳本產生平台上原生建置系統的設定檔(例如:Makefile),再使用原生建置系統編譯你的專案,CMake 的設計目標是簡化跨平台開發,讓開發者能夠以統一的方式生成不同作業系統上的建構檔案(如 Makefile、Visual Studio 專案檔案等)。 ### CMake Flow 建置流程 1. 配置 CMake 快取 (Configure Step) 2. 產生原生建置系統的設定檔 (Generate Step) 3. 編譯並執行 (Compile Step) ### CMake 語法 CMake由命令式腳本語言組成,以命令組成,每個命令以括號內包裹參數 ```cmake! 命令(參數1 參數2 ...) ``` #### 指定最低版本 ```cmake! cmake_minimum_required(VERSION 3.10) ``` * 定義運行 cmake 版本 #### 專案名稱 ```cmake! project(ProjectName VERSION 1.0) ``` * `project()` 命令設定專案名稱,並可選指定版本號 #### 設定語言標準 ```bash! set(EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) ``` #### 建立一個函數庫 ```bash! add_library(<name> [STATIC | SHARED | MODULE] source1.cpp source2.cpp ...) ``` #### 添加可執行文件 ```cmake! add_executable(MyMain main.cpp) ``` * `add_executable()` 用於生成可執行文件 #### 尋找外部套件 ```cmake! find_package(OpenGL REQUIRED) target_link_libraries(MyMain PRIVATE OpenGL::GL) ``` #### 加入子目錄 自動尋找子目錄 `CMakeLists.txt` 執行 ```bash! add_subdirectory(core) ``` #### 產生 Compile Commands 產生一個名為 `compile_commands.json` 的檔案,裡面詳細記錄了每個 `.cpp/.cu` 檔案的實際編譯指令(包含 include path、定義、flags 等) ```)_ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ``` --- ## Reference * [Makefile 語法和示範](https://hackmd.io/@sysprog/SySTMXPvl) * [簡單學 makefile:makefile 介紹與範例程式](https://www.mropengate.com/2018/01/makefile.html)