---
# System prepended metadata

title: Makefile 學習筆記
tags: [makefile]

---


# Makefile 學習筆記

###### tags: `makefile` 

這邊記錄我學習 makefile 的路線。網路上的 makefile 筆記我是覺得有點難發漏，所以這邊記錄一下，在看這篇筆記之前，你可以需要先熟悉 gcc 系列的操作，如果不知道怎麼使用 gcc 或是的 linux shell 指令的話，可以去找 jserv 或者是鳥哥會有更詳盡的說明。

預計的學習路線
```mermaid
flowchart LR

c語言--> gcc --> makefile --> cmake 
```

## 開發環境
```shell
$ gcc --version
gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0

$ lscpu
Architecture:            x86_64
  CPU op-mode(s):        32-bit, 64-bit
  Address sizes:         39 bits physical, 48 bits virtual
  Byte Order:            Little Endian
CPU(s):                  4
  On-line CPU(s) list:   0-3
Vendor ID:               GenuineIntel
  Model name:            Intel(R) Core(TM) i3-10110U CPU @ 2.10GHz

```


## Makefile 是什麼？

只要你設定正確，你只需要在 shell 打 `make` 他就會自動幫你把該編譯的做編譯，該更新的做更新。

```shell
make
```

## 最簡單的 makefile 怎麼寫？

### 簡單的 makefile 例題

創一個資料夾放三個檔案

```
.
├── main.c
├── myHello.c
└── myHello.h
```

三個檔案分別是`main.c`

```c
#include <stdio.h>
#include "myHello.h"

int main(int argc, char const *argv[])
{
    myPrintf();
    return 0;
}
```
`myHello.h`
```c
void myPrintf(void);
```
`myHello.c`
```c
#include <stdio.h>
#include "myHello.h"

void myPrintf(void){
    printf("Hello\n");
    return;
}
```

基本上這個可以直接用 `gcc` 編譯，他會編譯兩個 .c 檔案，然後把他們 link 在一起使用。這個 `-I` 會讓 gcc 去尋找現在的資料夾 `.` 找有相依的檔案一起拿來編譯。

```shell
$ gcc -o main  main.c myHello.c -I.
$ ./main
```

那我們要 make 幹嘛？當你有更多的 .c 檔案要寫的時候你就會覺得很煩躁了。那怎麼用 make 呢？

### makefile 格式

```makefile
fun: main.c myHello.c
	gcc -o yoyo main.c myHello.c -I.
```

- `fun` 是你的目標名字，也就是之後你可以在shell 打 `make fun`  這樣使用
- 用分號分開的後面是相依檔案，本例題的檔案是 `main.c` 跟 `myHello.c`
- 下一行用 `tab` 開頭，放你想要執行的指令。

### 如何使用make變數？

當我們編譯很多檔案的時候，編譯器應該都是用 `gcc`，他們相關的 flag 應該也是相似的，所以如果能使用變數的話應該就太好了。那我們會這樣用

```makefile
CC = gcc
CFLAGS = -I.

happy: main.o myHello.o
	$(CC) -o yoyo main.o myHello.o $(CFLAGS)
```

你的 macro 叫什麼都可以，但是 `CC` 代表 c compiler 、`CFLAGS` 代表編譯器的選項，像是 `-I -g -Wall ` 之類的。

### 改成 .o 有什麼差別？

makefile會覺得 happy 這個目標需要 main.o 跟 myHello.o 這兩個檔案，有了這兩個檔案之後會再用幫你做出 yoyo 這個檔案。所以他的輸出會變成
```shell
$ make 
gcc -I.   -c -o main.o main.c
gcc -I.   -c -o myHello.o myHello.c
gcc -o yoyo main.o myHello.o -I.
```

第 2 行跟第 3 行是 make 內建的模板幫你做出來的，相當於以下的寫法

```makefile
happy: main.o myHello.o
	$(CC) -o yoyo main.o myHello.o $(CFLAGS)

main.o: main.c myHello.h
	$(CC) -c -o main.o main.c $(CFLAGS)

myHello.o: myHello.c myHello.h
	$(CC) -c -o myHello.o  myHello.c $(CFLAGS)
```

有沒有發現有很多重複的地方？有重複的地方就該出現在一個健康的工程師，把這份 makefile 更精簡的寫出來

### 使用萬用字元 `%` 

```makefile
%.o : %.c $(DEPS)
	$(CC) -c -o %.o %.c $(CFLAGS)
```

`%` 代表萬用字元，應該是跟 regex 的 `.` 是類似的意思。 `%.o` 會對應到 `abc.o` 或是 `123.o` 這樣

### 使用自動化變數 `$@` 跟 `$<`

- `$@` 工作目標檔名
- `$<` 第一個必要條件的檔名
- `$^` 所有必要條件的檔名，並以空格隔開這些檔名 (這份清單已移除重複的檔名)

```makefile
%.o : %.c $(DEPS)
	$(CC) -c -o $@ $< $(CFLAGS)
    
happy: main.o myHello.o
	$(CC) -o $@ $^ $(CFLAGS)
```

happy 就是工作目標， `$@` 就會是 `happy` 。


## 參考連結

- [GNU Make 官方網站](https://www.gnu.org/software/make/manual/make.html)
- [用範例學 makefile](https://makefiletutorial.com/#make-clean)
- [Jserv 的 makefile 教學](https://hackmd.io/@sysprog/SySTMXPvl)