Makefile-3-書寫規則
阿新 • • 發佈:2020-10-14
[toc]
---
## 前言
* 本筆記主要記錄Makefile一些概念要點。
* 本筆記為提取性筆記,章節與《跟我一起寫Makefile》同步,所以會看到有些小標題會跳過。
## 概念
## Chapter 3:書寫規則
### 3.3 在規則中使用萬用字元
* make 支援三個萬用字元:
* **\***
* 任意長度的任意字元
* **?**
* 長度為一的任意字元
* **~** (*以例子說明*)
* **~/test**
* 表示當前使用者的 $HOME 目錄下的 test 目錄
* **~lzm/test**
* 表示使用者為 lzm 的宿主目錄下的 test 目錄
* 若使用者沒有宿主目錄,則根據環境變數 **HOME** 而定。
**舉例**:
* 例子1
* 變數 objects 表示當前目錄下所有的 .o 檔案。
```makefile
objects := $(wildcard *.o)
```
* 例子2 *
* **注**:這裡的變數 objects 表示的就是 *.o,因為**就像 C語言 的巨集。**
```makefile
objects = *.o
```
* 例子3 *
* 列出當前所有 .c 檔案對應的 .o 檔案。
* 下面的 \*,是make的 \* ,% 是makefile的 % ,具體看函式定義。
```makefile
$(patsubst %.c,%.o,$(wildcard *.c))
```
### 3.4 檔案搜尋
* **VPATH** 變數
* Makefile 檔案會在當前目錄下尋找依賴檔案和目標檔案,在找不到的情況下就在 **VPATH** 變數中指定的路徑去找。
```makefile
VPATH = src:../headers
```
* 上面例子中就是 **VPATH** 的格式,用 **:** 隔開多個路徑。
* **vpath** 關鍵字
* **vpath** 關鍵字比 **VPATH** 變數更加靈活, **vpath** 可以指定不同的檔案在不同的搜尋目錄中。
* 使用方法有三種:
1. **vpath **在 。為符合模式 的檔案指定搜尋目錄 (多個目錄可以用**空格**或者 **:** 分開)。
2. **vpath ** 清除符合模式 的檔案的搜尋目錄。
3. **vpath** 清除所有已被設定好了的檔案搜尋目錄。
```makefile
vpath %.h include //指定.h型別檔案的搜尋路徑是include
```
### 3.8 自動生成依賴性
在 Makefile 中,我們的依賴關係可能會需要包含一系列的標頭檔案,比如,如果我們的 main.c 中有一 句 #include "defs.h" ,那麼我們的依賴關係應該是:
```makefile
main.o : main.c defs.h
```
大多數的 C/C++ 編譯器都支援一個“-M”的 選項,即自動找尋原始檔中包含的標頭檔案,並生成一個依賴關係。例如,如果我們執行下面的命令:
```makefile
cc -M main.c
```
其輸出是:
```makefile
main.o : main.c defs.h
```
** 注:如果使用 GNU 的 C/C++ 編譯器,得用 -MM 引數,不然,-M 引數會把一些標準庫的標頭檔案也包含進來。 **
如:
gcc -M main.c 的輸出是:
```makefile
main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/pthreadtypes.h \
/usr/include/bits/sched.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/include/bits/wchar.h /usr/include/gconv.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \
/usr/include/bits/stdio_lim.h
```
gcc -MM main.c 的輸出則是:
```makefile
main.o : main.c defs.h
```
#### 原理 *
* 隱晦規則(其中之一)
* 會自動生成 **gcc -c \*.c** 等語句。
* **如果使用了該 隱晦規則 規則,在不改變 原始檔 的情況下,改變其它(如標頭檔案),再去執行 make 命令,是不會幹活的。**
如果想使用 **隱晦規則** + **依賴自動包含標頭檔案**,可以往下看。
* gcc -MM main.c
* 會在 makefile 的依賴上自動新增 main.c 包含的標頭檔案。
**基於上面這個原理,我們可以把 main.c 包含的標頭檔案 資訊 自動識別出來並儲存到 main.d 檔案中。**
這時候,**.d** 檔案就出來了。
##### 直接解析例子 **
* **標頭檔案改變,make也會幹活。**
* 該指令碼實現了,**.c** 檔案編譯過程中,產生 **.d** 檔案。
```makefile
%.d : %.c
@set -e;rm -f $@; \
$(CC) -MM $(CPPFLAGS) $< > $@.; \
sed 's,$∗\.o[ :]*,\1.o $@ : ,g' < $@. > $@; \
rm -f $@.
```
[參考](https://blog.csdn.net/mingwanlau/article/details/12872829?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param)
* 原始碼解析
* **set -e;**
* 表示 直接在命令列模式上進行 sed 動作編輯,*其實此為預設選項*。
* **rm -f $@;**
* 表示 刪除所有目標檔案,即是刪除所有 **.d** 檔案。
* **$(CC) -MM $(CPPFLAGS) \$< > \$@.;**
* 表示 編譯產生的一些標註庫的標頭檔案資訊儲存到 **.d.xxx** 隨機檔案中。
* **$@.**
* 表示字尾為隨機的意思。
* 假設 CC= gcc ,CPPFLAGS=空,即 **gcc -MM main.c > main.d.xxxx**
* **>** 表示重定向的意思。
* 即是把 main.c 所依賴的標頭檔案資訊寫入 main.d.xxxx 檔案。如
```makefile
main.o: main.c defs.h
```
* **sed 's,$∗\.o[ :]*,\1.o $@ : ,g' < $@. > $@;**
* 表示執行linux 命令 sed
* **< $@.**
* 該檔案內容交給前面,讓 sed 語句處理
* **$∗\.o[ :]\***
* 為匹配欄位
* 表示 main 後面 .o 接著的字串段
* **$∗\.o[ :]\***
* 為替換欄位
* **$@. > $@**
* 輸入給 .d 檔案,即是 main.d,內容如下:
```makefile
main.o main.d : main.c defs.h
```
* **rm -f $@.**
* 刪除 main.d.xxxx 的隨機檔案。
* **生成 .d 檔案後,Makefile 檔案可以 include 該檔案進入 Makefile 中,這樣, .o .d 都是目標檔案,後面有很多依賴的標頭檔案,一旦標頭檔案更新,目標檔案便會更新**。
###### sed 命令
* **要點格式,具體百度**
* sed 為linux命令,用於替換。
```makefile
sed‘s:/usr/local:/usr:g’
```
* **s:** 就是於把 **:** 當作分隔符 **/**。
```makefile
sed‘s;/usr/local;/usr;g’
```
* **s;** 就是於把 **;** 當作分隔符 **/**。
## 參考
### 書籍
* 《GUN Makefile》
* 《跟我一起寫Makefile