1. 程式人生 > >makefile生成 *.d 依賴檔案及 gcc -M -MF -MP 等相關選項說明

makefile生成 *.d 依賴檔案及 gcc -M -MF -MP 等相關選項說明

1. 為什麼要使用字尾名為 .d 的依賴檔案?


在 Makefile 中, 我們的依賴關係可能需要包含一系列的標頭檔案。 
比如 
main.c 原始檔內容如下:

#include "stdio.h"
#include "defs.h"

int main(int argc, char *argv[])
{
    printf("Hello, %s!\n", NAME);
    return 0;
}       


defs.h 標頭檔案如下:

#ifndef _DEFS_H_
#define _DEFS_H_

#define NAME    "makefile"

#endif _DEFS_H_

那麼依賴關係應該如下: 
    main.o : main.c stdio.h defs.h ...   

但如果是一個比較大型的工程,你必需清楚每一個 C 原始檔包含了哪些標頭檔案,並且在加入或刪除標頭檔案時,也需要小心地修改 Makefile,這是一個很沒有維護性的工作。為了避免這種繁重而又容易出錯的事情,我們可以使用 C/C++ 編譯的一個功能。大多數的 C/C++ 編譯器都支援一個 “-M” 的選項,即自動找尋原始檔中包含的標頭檔案,並生成一個依賴關係。例如,執行下面的命令:

gcc -M main.c   
其輸出如下:  
    main.o : main.c defs.h

由編譯器自動生成依賴關係,這樣做的好處有以下幾點:

不必手動書寫若干檔案的依賴關係,由編譯器自動生成
不管是 .c 檔案還是 .h 檔案有更新,目標檔案都會重新編譯

 

2. 使用說明:


引數介紹:

-M 
生成檔案的依賴關係,同時也把一些標準庫的標頭檔案也包含了進來。本質是告訴前處理器輸出一個適合 make 的規則,用於描述各目標檔案的依賴關係。對於每個原始檔,前處理器輸出 一個 make 規則,該規則的目標項 (target) 是原始檔對應的目標檔名,依賴項 (dependency) 是原始檔中 ‘#include’ 引用的所有檔案,生成的規則可以是單行,但如果太長,就用’\’換行符續成多行。規則 顯示在標準輸出,不產生預處理過的C程式。 
注意:該選項預設打開了 -E 選項, -E 引數的用處是使得編譯器在預處理結束時就停止編譯
例如: gcc -M main.c 
則在終端上輸出如下: 
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/_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

-MM 
生成檔案的依賴關係,和 -M 類似,但不包含標準庫的標頭檔案
例如:gcc -MM main.c 
則在終端上輸出如下: 
main.o: main.c defs.h

-MG 
要求把缺失的標頭檔案按存在對待,並且假定他們和源程式檔案在同一目錄下.必須和 ‘-M’ 選項一起用.

-MF File 
當使用了 ‘-M’ 或者 ‘-MM’ 選項時,則把依賴關係寫入名為 ‘File’ 的檔案中。若同時也使用了 ‘-MD’ 或 ‘-MMD’,’-MF’ 將覆寫輸出的依賴檔案的名稱

例如:gcc -M -MF main.d main.c
    則 '—M' 輸出的內容就存在於 main.d 檔案中了
1
2
-MD 
等同於 ‘-M -MF File’,但是預設關閉了 ‘-E’ 選項. 其輸出的檔名是基於 ‘-o’ 選項,若給定了 ‘-o’ 選項,則輸出的檔名是 ‘-o’ 指定的檔名,並新增 .d 字尾,若沒有給定,則輸入的檔名作為輸出的檔名,並新增.d字尾,同時繼續指定的編譯工作 
注意:’-MD’ 不會像 ‘-M’ 那樣阻止正常的編譯任務. 因為它預設關閉了 ‘-E’ 選項, 比如命令中使用了 -c 選項,其結果要生成 .o 檔案,若使用了 ‘-M’ 選項,則不會生成 .o 檔案,若使用的是 ‘-MD’ 選項,則會生成 .o 檔案
例如1:gcc -E -MD main.c
本目錄下生成了以下檔案:
    main.d 
同時在終端上輸出了 main.c 檔案的預處理結果

經實測發現,不使用 '-o' 指定輸出檔名,以下情況有細微的差別:
gcc -E main.c //不使用 '-o',則把結果輸出在終端上
gcc -S main.c //不使用 '-o',則把結果預設輸出到以輸入檔名為名稱的 .s 檔案中,即 main.s
gcc -c main.c //同上
gcc main.o   //不使用 '-o',則把結果預設輸出到 a.out 可執行檔案中

例如2:gcc -E -o tmp.i -MD main.c
本目錄下生成了以下檔案:
    tmp.d tmp.i

例如3:gcc -c -MD main.c
本目錄下生成了以下檔案:
    main.d main.o

例如4:gcc -c -o tmp.o -MD main.c
本目錄下生成了以下檔案:
    tmp.d tmp.o

例如5: gcc -MD main.c
本目錄下生成了以下檔案:
    a.out main.d 

例如6: gcc -M -MD main.c
本目錄下生成了以下檔案:
    main.d  //並不會生成a.out可執行檔案,因為 '-M' 預設打開了 '-E' 選項,使得編譯器在預處理結束後就停止編譯


-MMD 
類似於 ‘-MD’,但是輸出的依賴檔案中,不包含標準標頭檔案

-MP 
生成的依賴檔案裡面,依賴規則中的所有.h依賴項都會在該檔案中生成一個偽目標,其不依賴任何其他依賴項。該偽規則將避免刪除了對應的標頭檔案而沒有更新 “Makefile” 去匹配新的依賴關係而導致make出錯的情況出現。 
(英文描述:This option instructs CPP to add a phony target for each dependency 
other than the main file, causing each to depend on nothing. These 
dummy rules work around errors ‘make’ gives if you remove header 
files without updating the ‘Makefile’ to match.)

例如1: gcc -c -MM -MD main.c
生成的 main.d 檔案內容如下:
main.o: main.c defs.h

例如1: gcc -c -MM -MD main.c -MP
生成的 main.d 檔案內容如下:
main.o: main.c defs.h
defs.h:  //該選項會生成該偽目標,其沒有任何依賴項,若不使用 '-MP' 選項,則不會生成該偽目標規則

-MT Target 
在生成的依賴檔案中,指定依賴規則中的目標
例如: gcc -MF main.d -MG -MM -MP -MT main.d -MT main.o main.c

$ cat main.d    #檢視生成的依賴檔案的內容
main.d main.o: main.c
注:依賴規則中main.d 和 main.o 目標都是通過'-MT'選項指定的
 


3. 使用參考:


以上簡單介紹了 gcc -M 相關的選項,旨在讓 make 自動推導並生成檔案的依賴關係. 
以下提供一個比較好的 gcc -M 選項的參考示例, 它將自動生成依賴檔案,並儲存在指定目錄下的 ‘.d’ 檔案中。

makefile如下所示:

SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o)
DEPS=$(SRCS:.c=.d)

.PHONY: all clean

all: main

-include $(DEPS)   #註釋:'-'號的作用:載入錯誤時,會繼續執行 make,主要是考慮到首次 make 時,目錄中若不存在 '*.d' 檔案時,載入便會產生錯誤而停止 make 的執行

%.o:%.c
    gcc -c -g -Wall $< -o [email protected] -MD -MF $*.d -MP 

main: $(OBJS)
    gcc $^ -o [email protected]   #註釋:$^:表示所有的依賴檔案 [email protected]:表示目標檔案

clean: 
        rm -f  *.d *.o main

仍舊以本篇文章開頭的原始檔進行 make,將生成如下檔案: 
main : 可執行檔案 
main.o : 編譯的二進位制目標檔案 
main.d:儲存了 main.o 依賴關係的檔案

註釋: $* 表示目標模式中 '%' 及其之前的部分.如果目標是 'dir/a.foo.b',
並且目標的模式為 'a.%.b',那麼 '$*' 的值就是 'dir/a.foo'. 
如果目標中沒有模式的定義,那麼 '$*' 就不能被推匯出.
但是,如果目標檔案是 make 所識別的,那麼 '$*' 就是除了字尾的那一部分,

例如:目標是 'foo.c',因為 '.c' 是 make 所能識別的字尾名,
所以 '$*' 的值就是 'foo'.這個特性是 GNU make 的.



4. 延伸說明:


Makefile 檔案中使用比較多的自動變數:

 - [email protected] : 表示一個規則中的目標.當規則中有多個目標時,[email protected] 所指的是其中任何造成規則的命令執行的目標
 - $^ : 表示規則中的所有依賴項
 - $< : 表示規則中的第一個依賴項

例如 Makefile 檔案內容如下:
target1 target2:dep1 dep2 dep3
    @echo "Tar:[email protected], First Dep:$<, All Dep:$^"

dep1 dep2 dep3:    #註釋:不寫該行目標規則,執行 make 會報錯,因為 makefile 目錄下不僅沒有 dep1 dep2 dep3 檔案,makefile 檔案中也沒有以這些依賴檔案做為目標的規則.

執行 make: 
1. make target1 或者 make (把檔案中第一個目標當作首要目標)
終端輸出如下:
    Tar:target1, First Dep:dep1, All Dep:dep1 dep2 dep3

2. make target2
終端輸出如下:
    Tar:target2, First Dep:dep1, All Dep:dep1 dep2 dep3

3. make target1 target2
終端輸出如下:
    Tar:target2, First Dep:dep1, All Dep:dep1 dep2 dep3
    Tar:target2, First Dep:dep1, All Dep:dep1 dep2 dep3


相信大家在看了以上例子,能夠更加理解這三個自動化變數的含義,尤其是 ‘[email protected]‘, 前面已經說明了其含義,這裡就不再贅述了.
轉自https://blog.csdn.net/qq1452008/article/details/50855810