多目標linux
Makefile 的規則中的目標能夠不止一個,其支持多目標,有可能咱們的多個目標同時依賴於一個文件,而且其生成的命令大致相似。因而咱們就能把其合併起來。可是若是多個目標的生成規則的執行命令是同一個,這會給咱們帶來不少的工做量。在makefile中可使用$@。這個變量表示目前規則中的全部目標的集合。相似的變量還有$^,$<,$?spa
$@ 表示目標文件
$^ 表示全部的依賴文件
$< 表示第一個依賴文件
$? 表示比目標還要新的依賴文件列表文檔
來看下面的例子:get
假設目錄裏面有hello.c hi.c main.c makefile編譯器
按照makefile規則寫的話應該以下:it
main: main.o hello.o hi.o自動化
gcc -o main main.o hello.o hi.oio
main.o: main.c編譯
cc -c main.cclass
hello.o: hello.c
cc -c hello.c
hi.o: hi.c
cc -c hi.c
clean:
rm *.o
rm main
改用符號進行替代
main: main.o hello.o hi.o
gcc -o $@ $^
main.o: main.c
gcc -c $<
hello.o: hello.c
gcc -c $<
hi.o: hi.c
gcc -c $<
clean:
rm *.o
rm main
靜態規則
靜態模式能夠更加容易地定義多目標的規則,可讓咱們的規則變得更加的有彈性和靈活。語法以下:
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
targets 定義了一系列的目標文件, 能夠有通配符。是目標的一個集合。target-parrtern 是指明瞭 targets 的模式,也就是的目標集模式。prereq-parrterns 是目標的依賴模式,它對 target-parrtern 造成的模式再進行一次依賴目標的定義。
若是咱們的<target-parrtern>定義成 「%.o」,意思是咱們的<target>集合中都是以「.o」結尾的,而若是咱們的<prereq-parrterns>定義成「%.c」,意思是對<target-parrtern>所造成的目標集進行二次定義,其計算方法是,取<target-parrtern>模式中的「%」(也就是去掉了[.o]這個結尾) ,併爲其加上[.c]這個結尾,造成的新集合
來看一個例子:
objects = foo.o bar.o
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
上面的例子中,指明瞭咱們的目標從$object 中獲取,「%.o」代表要全部以「.o」結尾的目標,也就是「foo.o bar.o」,也就是變量$object 集合的模式,而依賴模式「%.c」則取模式「%.o」的「%」,也就是「foo bar」,併爲其加下「.c」的後綴,因而,咱們的依賴目標就是「foo.c bar.c」。而命令中的「$<」和「$@」則是自動化變量, 「$<」表示全部的依賴目標集(也
就是「foo.c bar.c」) ,「$@」表示目標集(也就是「foo.o bar.o」) 。
因而,上面的規則展開後等價於下面的規則:
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
自動生成依賴性
在 Makefile 中,咱們的依賴關係可能會須要包含一系列的頭文件,好比,若是咱們的 main.c 中有一句「#include "defs.h"」,那麼咱們的依賴關係應該是:
main.o : main.c defs.h
可是,若是是一個比較大型的工程,你必需清楚哪些 C 文件包含了哪些頭文件,而且,你在加入或刪除頭文件時,也須要當心地修改Makefile,這是一個很沒有維護性的工做。爲了不這種繁重而又容易出錯的事情,咱們可使用 C/C++編譯的一個功能。大多數的C/C++編譯器都支持一個「-M」的選項,即自動找尋源文件中包含的頭文件,並生成一個依賴關係。例如,若是咱們執行下面的命令:
cc -M main.c
其輸出是:
main.o : main.c defs.h
因而由編譯器自動生成的依賴關係,這樣一來,你就沒必要再手動書寫若干文件的依賴關係,而由編譯器自動生成了。須要提醒一句的是,若是你使用 GNU 的 C/C++編譯器,你得用「-MM」參數,否則,「-M」參數會把一些標準庫的頭文件也包含進來。
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/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 的輸出則是:
main.o: main.c defs.h
那麼,編譯器的這個功能如何與咱們的 Makefile 聯繫在一塊兒呢。由於這樣一來,咱們的 Makefile 也要根據這些源文件從新生成, 讓 Makefile自已依賴於源文件?這個功能並不現實, 不過咱們能夠有其它手段來迂迴地實現這一功能。GNU 組織建議把編譯器爲每個源文件的自動生成的依賴關係放到一個文件中,爲每個「name.c」的文件都生成一個「name.d」的 Makefile 文件,[.d]文件中就存放對應[.c]文件的依賴關係。
因而,咱們能夠寫出[.c]文件和[.d]文件的依賴關係,並讓 make 自動更新或自成[.d]文件,並把其包含在咱們的主 Makefile 中,這樣,咱們就能夠自動化地生成每一個文件的依賴關係了。這裏,咱們給出了一個模式規則來產生[.d]文件:
%.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
這個規則的意思是,全部的[.d]文件依賴於[.c]文件,「rm -f $@」的意思是刪除全部的目標,也就是[.d]文件,第二行的意思是,爲每一個依賴文件「$<」, 也就是[.c]文件生成依賴文件, 「$@」表示模式「%.d」文件,若是有一個 C 文件是 name.c,那麼「%」就是「name」,「$$$$」意爲一個隨機編號,第二行生成的文件有多是「name.d.12345」,第三行使用
sed 命令作了一個替換,關於 sed 命令的用法請參看相關的使用文檔。第四行就是刪除臨時文件。