makefile提升篇—自動推導和自動化變量

    在咱們看別人寫的有關項目的makefile文件時,常常會由於別人使用了makefile提供的自動推導規則或者自動變量,而致使只知道項目編譯的大體流程,對其中具體的實現和編譯流程不熟悉。有的時候帶着疑問上百度,發現有關的makefile的問題的回答都不是本身所要找的答案。下面本身給你們說說本身遇到的問題和一下關於自動推導的規則,以幫助你們更好的學習和解決問題。函數

問題1:%.o : %.c學習

%.o : %.c
     $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

 網上回答:把全部的[.c]文件都編譯成[.o]文件ui

疑惑:一、  %.o : %.c是怎麼把全部的[.c]文件都編譯成[.o]文件,具體流程是什麼?spa

          二、是一步一步編譯的,仍是一下所有將.c文件所有編譯成.o文件?命令行

問題2:all : $(BIN)code

BIN = hello
all : $(BIN)

%.o:%.c
	$(CC) $(CFLAGS) -c $< -o $@

疑惑:一、all : $(BIN) 中的all依賴目標是hello,hello是怎麼生成的呢?編譯器

    讓咱們帶着疑問去看看makefile的自動推導規則和自動化變量。自動化

自動化變量

    所謂自動化變量,就是這種變量會把模式中所定義的一系列的文件自動地挨個取出直至全部的符合模式的文件都取完了。這種自動化變量只應出如今規則的命令中編譯

$@
表示規則中的目標文件集。在模式規則中,若是有多個目標,那麼, 「$@」就是匹配於
目標中模式定義的集合。注意,其目標是一個一個取出來的class

$<
依賴目標中的第一個目標名字。若是依賴目標是以模式(即「%」 )定義的,那麼「$<」
將是符合模式的一系列的文件集。注意,其是一個一個取出來的。

$^
全部的依賴目標的集合。以空格分隔。若是在依賴目標中有多個重複的,那個這個變量
會去除重複的依賴目標,只保留一份。

$+
這個變量很像「$^」 ,也是全部依賴目標的集合。只是它不去除重複的依賴目標。

$%
僅當目標是函數庫文件中,表示規則中的目標成員名。例如,若是一個目標是
「foo.a(bar.o)」 ,那麼, 「$%」就是「bar.o」 , 「$@」就是「foo.a」 。若是目標不是函
數庫文件(Unix 下是[.a],Windows 下是[.lib]) ,那麼,其值爲空。

$?
全部比目標新的依賴目標的集合。以空格分隔。

$*
這個變量表示目標模式中"%"及其以前的部分。若是目標是"dir/a.foo.b",而且目標的模式是"a.%.b",那麼,"$*"的值就是"dir/a.foo"。這個變量對於構造有關聯的文件名是比較有較。若是目標中沒有模式的定義,那麼"$*"也就不能被推導出,可是,若是目標文件的後綴是make所識別的,那麼"$*"就是除了後綴的那一部分。例如:若是目標是"foo.c",由於".c"是make所能識別的後綴名,因此,"$*"的值就是"foo"。

自動推導規則

隱含規則的自動推導

    使用makefile的隱含規則來生成你所須要的目標,你所須要作的就是不要寫出這個目標的規則。而是讓make會去自動推導產生這個目標的規則和命令,若是make能夠自動推導生成這個目標的規則和命令,那麼這個行爲就是隱含規則的自動推導。

foo : foo.o bar.o
cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
#makfile會根根隱含規則自動將foo.c bar.c編譯生成foo.o bar.o。

make會在本身的「隱含規則」庫中尋找能夠用的規則,若是找到,那麼就會使用。若是找不到,那麼就會報錯。在上面的那個例子中,make調用的隱含規則是,把[.o]的目標的依賴
文件置成[.c],並使用C的編譯命令「cc –c $(CFLAGS) [.c]」來生成[.o]的目標。也就是
說,咱們徹底沒有必要寫下下面的兩條規則:

foo.o : foo.c
cc –c foo.c $(CFLAGS)
bar.o : bar.c
cc –c bar.c $(CFLAGS)

成用的隱含規則

一、編譯C程序的隱含規則。
「<n>;.o」的目標的依賴目標會自動推導爲「<n>;.c」,而且其生成命令是「$(CC) –c$(CPPFLAGS) $(CFLAGS)」

二、編譯C++程序的隱含規則。
「<n>;.o」的目標的依賴目標會自動推導爲「<n>;.cc」或是「<n>;.C」,而且其生成命令是「$(CXX) –c $(CPPFLAGS) $(CFLAGS)」。(建議使用「.cc」做爲C++源文件的後綴,而不是「.C」)

三、編譯Pascal程序的隱含規則。
「<n>;.o」的目標的依賴目標會自動推導爲「<n>;.p」,而且其生成命令是「$(PC) –c$(PFLAGS)」。

四、編譯Fortran/Ratfor程序的隱含規則。
「<n>;.o」的目標的依賴目標會自動推導爲「<n>;.r」或「<n>;.F」或「<n>;.f」,而且其生成命令是:
「.f」 「$(FC) –c $(FFLAGS)」
「.F」 「$(FC) –c $(FFLAGS) $(CPPFLAGS)」
「.f」 「$(FC) –c $(FFLAGS) $(RFLAGS)」

五、預處理Fortran/Ratfor程序的隱含規則。
「<n>;.f」的目標的依賴目標會自動推導爲「<n>;.r」或「<n>;.F」。這個規則只是轉換Ratfor或有預處理的Fortran程序到一個標準的Fortran程序。其使用的命令是:
「.F」 「$(FC) –F $(CPPFLAGS) $(FFLAGS)」
「.r」 「$(FC) –F $(FFLAGS) $(RFLAGS)」

六、編譯Modula-2程序的隱含規則。
「<n>;.sym」的目標的依賴目標會自動推導爲「<n>;.def」 ,而且其生成命令是: 「$(M2C) $(M2FLAGS) $(DEFFLAGS)」。「<n.o>;」 的目標的依賴目標會自動推導爲「<n>;.mod」,而且其生成命令是:「$(M2C) $(M2FLAGS) $(MODFLAGS)」。

七、彙編和彙編預處理的隱含規則。
「<n>;.o」 的目標的依賴目標會自動推導爲「<n>;.s」,默認使用編譯品「as」,而且其生成命令是: 「$(AS) $(ASFLAGS)」 。 「<n>;.s」的目標的依賴目標會自動推導爲 「<n>;.S」 ,默認使用C預編譯器「cpp」,而且其生成命令是:「$(AS) $(ASFLAGS)」。

八、連接Object文件的隱含規則。
「<n>;」目標依賴於「<n>;.o」 ,經過運行C的編譯器來運行連接程序生成(通常是 「ld」 ) ,其生成命令是:「$(CC) $(LDFLAGS) <n>;.o $(LOADLIBES) $(LDLIBS)」。這個規則對於只有一個源文件的工程有效,同時也對多個Object文件(由不一樣的源文件生成)的也有效。例如以下規則:
x : y.o z.o
而且「x.c」、「y.c」和「z.c」都存在時,隱含規則將執行以下命令:
cc -c x.c -o x.o
cc -c y.c -o y.o
cc -c z.c -o z.o
cc x.o y.o z.o -o x
rm -f x.o
rm -f y.o
rm -f z.o
若是沒有一個源文件(如上例中的x.c)和你的目標名字(如上例中的x)相關聯,那麼,你最好寫出本身的生成規則,否則,隱含規則會報錯的。

九、Yacc C程序時的隱含規則。
「<n>;.c」 的依賴文件被自動推導爲 「n.y」 (Yacc生成的文件) , 其生成命令是: 「$(YACC) $(YFALGS)」。(「Yacc」是一個語法分析器,關於其細節請查看相關資料)

十、Lex C程序時的隱含規則。
「<n>;.c」的依賴文件被自動推導爲「n.l」 (Lex生成的文件),其生成命令是: 「$(LEX) $(LFALGS)」。(關於「Lex」的細節請查看相關資料)

十一、Lex Ratfor程序時的隱含規則。
「<n>;.r」的依賴文件被自動推導爲「n.l」 (Lex生成的文件),其生成命令是: 「$(LEX)
$(LFALGS)」。

十二、從C程序、Yacc文件或Lex文件建立Lint庫的隱含規則。
「<n>;.ln」 (lint生成的文件) 的依賴文件被自動推導爲 「n.c」 , 其生成命令是: 「$(LINT) $(LINTFALGS) $(CPPFLAGS) -i」。對於「<n>;.y」和「<n>;.l」也是一樣的規則。

隱含規則使用的變量

    在隱含規則中的命令中,基本上都是使用了一些預先設置的變量。你能夠在你的makefile中改變這些變量的值,或是在make的命令行中傳入這些值,或是在你的環境變量中設置這些
值,不管怎麼樣,只要設置了這些特定的變量,那麼其就會對隱含規則起做用。固然,你也能夠利用make的「-R」或「--no–builtin-variables」參數來取消你所定義的變量對隱含規則的做用。

一、關於命令的變量。
AR
函數庫打包程序。默認命令是「ar」。
AS
彙編語言編譯程序。默認命令是「as」。
CC
C語言編譯程序。默認命令是「cc」。
CXX
C++語言編譯程序。默認命令是「g++」。
CO
從 RCS文件中擴展文件程序。默認命令是「co」。
CPP
C程序的預處理器(輸出是標準輸出設備)。默認命令是「$(CC) –E」。
RM
刪除文件命令。默認命令是「rm –f」。

二、關於命令參數的變量
若是沒有指明其默認值,那麼其默認值都是空。

ARFLAGS
函數庫打包程序AR命令的參數。默認值是「rv」。
ASFLAGS
彙編語言編譯器參數。(當明顯地調用「.s」或「.S」文件時)。
CFLAGS
C語言編譯器參數。
CXXFLAGS
C++語言編譯器參數。
COFLAGS
RCS命令參數。
CPPFLAGS
C預處理器參數。( C 和 Fortran 編譯器也會用到)。
LDFLAGS
連接器參數。(如:「ld」)

隱含規則鏈
例如,一個[.o]的文件生成,可能會是先被Yacc的[.y]文件先成[.c],而後再被C的編譯器生成。咱們把這一系列的隱含規則叫作「隱含規則鏈」。

中間目標:咱們把這種[.c]的文件(或是目標),叫作中間目標。無論怎麼樣,make會努力自動推導生成目標的一切方法,無論中間目標有多少,其都會執着地把全部的隱含規則和你書寫的規則所有合起來分析,努力達到目標

中間目標不一樣點:在默認狀況下,對於中間目標,它和通常的目標有兩個地方所不一樣:第一個不一樣是除非中間的目標不存在,纔會引起中間規則。第二個不一樣的是,只要目標成功產生,那麼,產生最終目標過程當中,所產生的中間目標文件會被以「rm -f」刪除

強制聲明中間目標:一般,一個被makefile指定成目標或是依賴目標的文件不能被看成中介。然而,你能夠明顯地說明一個文件或是目標是中介目標,你可使用僞目標「.INTERMEDIATE」來強制聲明。(如:.INTERMEDIATE : mid )

阻止make自動刪除中間目標:你可使用僞目標「.SECONDARY」來強制聲明(如:.SECONDARY : sec)。你還能夠把你的目標,以模式的方式來指定(如:%.o)成僞目標「.PRECIOUS」的依賴目標,以保存被隱含規則所生成的中間文件。

最後,相信你們應該都知道了上面問題的答案了吧。

問題1:%.o : %.c的生成流程?

    makefile會利用本身的隱含規則和自動化變量,依次取出.o的目標文件,而後將這個.o依賴的.c文件找出來,編譯成.o文件

問題2: all : $(BIN) 中的all依賴目標是hello,hello是怎麼生成的呢?

    首先,makefile會找到hello的依賴文件hello.o

    而後,makefile會找到hello..o的依賴文件hello.c

    最後,將hello.c編譯成hello..o,再編譯成hello文件

相關文章
相關標籤/搜索