久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Makefile 规则

發布時間:2023/12/13 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Makefile 规则 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉自陳皓 (CSDN)

概述
——

什么是makefile?或許很多Winodws的程序員都不知道這個東西,因為那些Windows的IDE都
為你做了這個工作,但我覺得要作一個好的和 professional的程序員,makefile還是要懂
。這就好像現在有這么多的HTML的編輯器,但如果你想成為一個專業人士,你還是要了解
HTML的標識的含義。特別在Unix下的軟件編譯,你就不能不自己寫makefile了,會不會寫
makefile,從一個側面說明了一個人是否具備完成大型工程的能力。

因為,makefile關系到了整個工程的編譯規則。一個工程中的源文件不計數,其按類型、
功能、模塊分別放在若干個目錄中,makefile定義了一系列的規則來指定,哪些文件需要
先編譯,哪些文件需要后編譯,哪些文件需要重新編譯,甚至于進行更復雜的功能操作,
因為 makefile就像一個Shell腳本一樣,其中也可以執行操作系統的命令。

makefile帶來的好處就是——“自動化編譯”,一旦寫好,只需要一個make命令,整個工
程完全自動編譯,極大的提高了軟件開發的效率。make是一個命令工具,是一個解釋make
file中指令的命令工具,一般來說,大多數的IDE都有這個命令,比如:Delphi的make,V
isual C++的nmake,Linux下GNU的make。可見,makefile都成為了一種在工程方面的編譯
方法。

現在講述如何寫 makefile的文章比較少,這是我想寫這篇文章的原因。當然,不同廠商的
make各不相同,也有不同的語法,但其本質都是在“文件依賴性”上做文章,這里,我僅
對GNU的make進行講述,我的環境是RedHat Linux 8.0,make的版本是3.80。必竟,這個m
ake是應用最為廣泛的,也是用得最多的。而且其還是最遵循于IEEE 1003.2-1992 標準的
(POSIX.2)。

在這篇文檔中,將以C/C++的源碼作為我們基礎,所以必然涉及一些關于C/C++的編譯的知
識,相關于這方面的內容,還請各位查看相關的編譯器的文檔。這里所默認的編譯器是UN
IX下的GCC和CC。



關于程序的編譯和鏈接
——————————

在此,我想多說關于程序編譯的一些規范和方法,一般來說,無論是C、C++、還是pas,首
先要把源文件編譯成中間代碼文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,
即 Object File,這個動作叫做編譯(compile)。然后再把大量的Object File合成執行
文件,這個動作叫作鏈接(link)。

編譯時,編譯器需要的是語法的正確,函數與變量的聲明的正確。對于后者,通常是你需
要告訴編譯器頭文件的所在位置(頭文件中應該只是聲明,而定義應該放在C/C++文件中)
,只要所有的語法正確,編譯器就可以編譯出中間目標文件。一般來說,每個源文件都應
該對應于一個中間目標文件(O文件或是OBJ文件)。

鏈接時,主要是鏈接函數和全局變量,所以,我們可以使用這些中間目標文件(O文件或是
OBJ文件)來鏈接我們的應用程序。鏈接器并不管函數所在的源文件,只管函數的中間目標
文件(Object File),在大多數時候,由于源文件太多,編譯生成的中間目標文件太多,
而在鏈接時需要明顯地指出中間目標文件名,這對于編譯很不方便,所以,我們要給中間
目標文件打個包,在Windows下這種包叫“庫文件”(Library File),也就是 .lib 文件
,在UNIX下,是Archive File,也就是 .a 文件。

總結一下,源文件首先會生成中間目標文件,再由中間目標文件生成執行文件。在編譯時
,編譯器只檢測程序語法,和函數、變量是否被聲明。如果函數未被聲明,編譯器會給出
一個警告,但可以生成Object File。而在鏈接程序時,鏈接器會在所有的Object File中
找尋函數的實現,如果找不到,那到就會報鏈接錯誤碼(Linker Error),在VC下,這種
錯誤一般是:Link 2001錯誤,意思說是說,鏈接器未能找到函數的實現。你需要指定函數
的Object File.

好,言歸正傳,GNU的make有許多的內容,閑言少敘,還是讓我們開始吧。



Makefile 介紹
———————

make命令執行時,需要一個 Makefile 文件,以告訴make命令需要怎么樣的去編譯和鏈接
程序。

首先,我們用一個示例來說明Makefile的書寫規則。以便給大家一個感興認識。這個示例
來源于GNU的make使用手冊,在這個示例中,我們的工程有8個C文件,和3個頭文件,我們
要寫一個Makefile來告訴make命令如何編譯和鏈接這幾個文件。我們的規則是:
1)如果這個工程沒有編譯過,那么我們的所有C文件都要編譯并被鏈接。
2)如果這個工程的某幾個C文件被修改,那么我們只編譯被修改的C文件,并鏈接目標程序

3)如果這個工程的頭文件被改變了,那么我們需要編譯引用了這個頭文件的所有C文件,并
鏈接目標程序。

只要我們的Makefile寫得夠好,所有的這一切,我們只用一個make命令就可以完成,make
命令會自動智能地根據當前的文件修改的情況來確定哪些文件需要重編譯,從而自己編譯
所需要的文件和鏈接目標程序。


一、Makefile的規則

在講述這個Makefile之前,還是讓我們先來粗略地看一看Makefile的規則。

target ... : prerequisites ...
command
...
...

target也就是一個目標文件,可以是Object File,也可以是執行文件。還可以是一個標簽
(Label),對于標簽這種特性,在后續的“偽目標”章節中會有敘述。

prerequisites就是,要生成那個target所需要的文件或是目標。

command也就是make需要執行的命令。(任意的Shell命令)

這是一個文件的依賴關系,也就是說,target這一個或多個的目標文件依賴于prerequisi
tes中的文件,其生成規則定義在command中。說白一點就是說,prerequisites中如果有一
個以上的文件比target文件要新的話,command所定義的命令就會被執行。這就是 Makefi
le的規則。也就是Makefile中最核心的內容。

說到底,Makefile的東西就是這樣一點,好像我的這篇文檔也該結束了。呵呵。還不盡然
,這是Makefile的主線和核心,但要寫好一個Makefile還不夠,我會以后面一點一點地結
合我的工作經驗給你慢慢到來。內容還多著呢。:)


二、一個示例

正如前面所說的,如果一個工程有3個頭文件,和8個C文件,我們為了完成前面所述的那三
個規則,我們的Makefile應該是下面的這個樣子的。

edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

反斜杠(\)是換行符的意思。這樣比較便于Makefile的易讀。我們可以把這個內容保存在
文件為“Makefile”或“makefile”的文件中,然后在該目錄下直接輸入命令“make”就
可以生成執行文件edit。如果要刪除執行文件和所有的中間目標文件,那么,只要簡單地
執行一下“make clean”就可以了。

在這個makefile中,目標文件(target)包含:執行文件edit和中間目標文件(*.o),依
賴文件(prerequisites)就是冒號后面的那些 .c 文件和 .h文件。每一個 .o 文件都有
一組依賴文件,而這些 .o 文件又是執行文件 edit 的依賴文件。依賴關系的實質上就是
說明了目標文件是由哪些文件生成的,換言之,目標文件是哪些文件更新的。

在定義好依賴關系后,后續的那一行定義了如何生成目標文件的操作系統命令,一定要以
一個Tab鍵作為開頭。記住,make并不管命令是怎么工作的,他只管執行所定義的命令。m
ake會比較targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期
要比targets文件的日期要新,或者target不存在的話,那么,make就會執行后續定義的命
令。

這里要說明一點的是,clean不是一個文件,它只不過是一個動作名字,有點像C語言中的
lable一樣,其冒號后什么也沒有,那么,make就不會自動去找文件的依賴性,也就不會自
動執行其后所定義的命令。要執行其后的命令,就要在make命令后明顯得指出這個lable的
名字。這樣的方法非常有用,我們可以在一個makefile中定義不用的編譯或是和編譯無關
的命令,比如程序的打包,程序的備份,等等。



三、make是如何工作的

在默認的方式下,也就是我們只輸入make命令。那么,

1、make會在當前目錄下找名字叫“Makefile”或“makefile”的文件。
2、如果找到,它會找文件中的第一個目標文件(target),在上面的例子中,他會找到“
edit”這個文件,并把這個文件作為最終的目標文件。
3、如果edit文件不存在,或是edit所依賴的后面的 .o 文件的文件修改時間要比edit這個
文件新,那么,他就會執行后面所定義的命令來生成edit這個文件。
4、如果edit所依賴的.o文件也不存在,那么make會在當前文件中找目標為.o文件的依賴性
,如果找到則再根據那一個規則生成.o文件。(這有點像一個堆棧的過程)
5、當然,你的C文件和H文件是存在的啦,于是make會生成 .o 文件,然后再用 .o 文件生
命make的終極任務,也就是執行文件edit了。

這就是整個make的依賴性,make會一層又一層地去找文件的依賴關系,直到最終編譯出第
一個目標文件。在找尋的過程中,如果出現錯誤,比如最后被依賴的文件找不到,那么ma
ke就會直接退出,并報錯,而對于所定義的命令的錯誤,或是編譯不成功,make根本不理
。make只管文件的依賴性,即,如果在我找了依賴關系之后,冒號后面的文件還是不在,
那么對不起,我就不工作啦。

通過上述分析,我們知道,像clean這種,沒有被第一個目標文件直接或間接關聯,那么它
后面所定義的命令將不會被自動執行,不過,我們可以顯示要make執行。即命令——“ma
ke clean”,以此來清除所有的目標文件,以便重編譯。

于是在我們編程中,如果這個工程已被編譯過了,當我們修改了其中一個源文件,比如fi
le.c,那么根據我們的依賴性,我們的目標file.o會被重編譯(也就是在這個依性關系后
面所定義的命令),于是file.o的文件也是最新的啦,于是file.o的文件修改時間要比ed
it要新,所以edit也會被重新鏈接了(詳見edit目標文件后定義的命令)。

而如果我們改變了“command.h”,那么,kdb.o、command.o和files.o都會被重編譯,并
且,edit會被重鏈接。


四、makefile中使用變量

在上面的例子中,先讓我們看看edit的規則:

edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

我們可以看到[.o]文件的字符串被重復了兩次,如果我們的工程需要加入一個新的[.o]文
件,那么我們需要在兩個地方加(應該是三個地方,還有一個地方在 clean中)。當然,
我們的makefile并不復雜,所以在兩個地方加也不累,但如果makefile變得復雜,那么我
們就有可能會忘掉一個需要加入的地方,而導致編譯失敗。所以,為了makefile的易維護
,在makefile中我們可以使用變量。makefile的變量也就是一個字符串,理解成 C語言中
的宏可能會更好。

比如,我們聲明一個變量,叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJ,反正不管
什么啦,只要能夠表示obj文件就行了。我們在makefile一開始就這樣定義:

objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

于是,我們就可以很方便地在我們的makefile中以“$(objects)”的方式來使用這個變量
了,于是我們的改良版makefile就變成下面這個樣子:

objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

edit : $(objects)
cc -o edit $(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit $(objects)


于是如果有新的 .o 文件加入,我們只需簡單地修改一下 objects 變量就可以了。

關于變量更多的話題,我會在后續給你一一道來。


五、讓make自動推導

GNU的make很強大,它可以自動推導文件以及文件依賴關系后面的命令,于是我們就沒必要
去在每一個[.o]文件后都寫上類似的命令,因為,我們的make會自動識別,并自己推導命
令。

只要make看到一個[.o]文件,它就會自動的把[.c]文件加在依賴關系中,如果make找到一
個whatever.o,那么whatever.c,就會是whatever.o的依賴文件。并且 cc -c whatever.
c 也會被推導出來,于是,我們的makefile再也不用寫得這么復雜。我們的是新的makefi
le又出爐了。


objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

edit : $(objects)
cc -o edit $(objects)

main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
rm edit $(objects)

這種方法,也就是make的“隱晦規則”。上面文件內容中,“.PHONY”表示,clean是個偽
目標文件。

關于更為詳細的“隱晦規則”和“偽目標文件”,我會在后續給你一一道來。


六、另類風格的makefile

即然我們的make可以自動推導命令,那么我看到那堆[.o]和[.h]的依賴就有點不爽,那么
多的重復的[.h],能不能把其收攏起來,好吧,沒有問題,這個對于make來說很容易,誰
叫它提供了自動推導命令和文件的功能呢?來看看最新風格的makefile吧。

objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

edit : $(objects)
cc -o edit $(objects)

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

.PHONY : clean
clean :
rm edit $(objects)

這種風格,讓我們的makefile變得很簡單,但我們的文件依賴關系就顯得有點凌亂了。魚
和熊掌不可兼得。還看你的喜好了。我是不喜歡這種風格的,一是文件的依賴關系看不清
楚,二是如果文件一多,要加入幾個新的.o文件,那就理不清楚了。

七、清空目標文件的規則

每個Makefile中都應該寫一個清空目標文件(.o和執行文件)的規則,這不僅便于重編譯
,也很利于保持文件的清潔。這是一個“修養”(呵呵,還記得我的《編程修養》嗎)。
一般的風格都是:

clean:
rm edit $(objects)

更為穩健的做法是:

.PHONY : clean
clean :
-rm edit $(objects)

前面說過,.PHONY意思表示clean是一個“偽目標”,。而在rm命令前面加了一個小減號的
意思就是,也許某些文件出現問題,但不要管,繼續做后面的事。當然,clean的規則不要
放在文件的開頭,不然,這就會變成make的默認目標,相信誰也不愿意這樣。不成文的規
矩是——“clean從來都是放在文件的最后”。


上面就是一個makefile的概貌,也是makefile的基礎,下面還有很多makefile的相關細節
,準備好了嗎?準備好了就來。

Makefile 總述
———————

一、Makefile里有什么?

Makefile里主要包含了五個東西:顯式規則、隱晦規則、變量定義、文件指示和注釋。


1、顯式規則。顯式規則說明了,如何生成一個或多的的目標文件。這是由Makefile的書寫
者明顯指出,要生成的文件,文件的依賴文件,生成的命令。

2、隱晦規則。由于我們的make有自動推導的功能,所以隱晦的規則可以讓我們比較粗糙地
簡略地書寫Makefile,這是由make所支持的。

3、變量的定義。在Makefile中我們要定義一系列的變量,變量一般都是字符串,這個有點
你C語言中的宏,當Makefile被執行時,其中的變量都會被擴展到相應的引用位置上。

4、文件指示。其包括了三個部分,一個是在一個Makefile中引用另一個Makefile,就像C
語言中的include一樣;另一個是指根據某些情況指定Makefile中的有效部分,就像C語言
中的預編譯#if一樣;還有就是定義一個多行的命令。有關這一部分的內容,我會在后續的
部分中講述。

5、注釋。Makefile中只有行注釋,和UNIX的Shell腳本一樣,其注釋是用“#”字符,這個
就像C/C++中的“//”一樣。如果你要在你的Makefile中使用“#”字符,可以用反斜框進
行轉義,如:“\#”。

最后,還值得一提的是,在Makefile中的命令,必須要以[Tab]鍵開始。


二、Makefile的文件名

默認的情況下,make命令會在當前目錄下按順序找尋文件名為“GNUmakefile”、“makef
ile”、“Makefile”的文件,找到了解釋這個文件。在這三個文件名中,最好使用“Mak
efile”這個文件名,因為,這個文件名第一個字符為大寫,這樣有一種顯目的感覺。最好
不要用 “GNUmakefile”,這個文件是GNU的make識別的。有另外一些make只對全小寫的“
makefile”文件名敏感,但是基本上來說,大多數的make都支持“makefile”和“Makefi
le”這兩種默認文件名。

當然,你可以使用別的文件名來書寫Makefile,比如:“Make.Linux”,“Make.Solaris
”,“Make.AIX”等,如果要指定特定的Makefile,你可以使用make的“- f”和“--fil
e”參數,如:make -f Make.Linux或make --file Make.AIX。


三、引用其它的Makefile

在Makefile使用include關鍵字可以把別的Makefile包含進來,這很像C語言的#include,
被包含的文件會原模原樣的放在當前文件的包含位置。include的語法是:

include <filename>

filename可以是當前操作系統Shell的文件模式(可以保含路徑和通配符)

在 include前面可以有一些空字符,但是絕不能是[Tab]鍵開始。include和<filename>可
以用一個或多個空格隔開。舉個例子,你有這樣幾個Makefile:a.mk、b.mk、c.mk,還有
一個文件叫foo.make,以及一個變量$(bar),其包含了e.mk和 f.mk,那么,下面的語句:


include foo.make *.mk $(bar)

等價于:

include foo.make a.mk b.mk c.mk e.mk f.mk

make 命令開始時,會把找尋include所指出的其它Makefile,并把其內容安置在當前的位
置。就好像C/C++的#include指令一樣。如果文件都沒有指定絕對路徑或是相對路徑的話,
make會在當前目錄下首先尋找,如果當前目錄下沒有找到,那么,make還會在下面的幾個
目錄下找:

1、如果make執行時,有“-I”或“--include-dir”參數,那么make就會在這個參數所指
定的目錄下去尋找。
2、如果目錄<prefix>/include(一般是:/usr/local/bin或/usr/include)存在的話,m
ake也會去找。

如果有文件沒有找到的話,make會生成一條警告信息,但不會馬上出現致命錯誤。它會繼
續載入其它的文件,一旦完成makefile的讀取,make會再重試這些沒有找到,或是不能讀
取的文件,如果還是不行,make才會出現一條致命信息。如果你想讓make不理那些無法讀
取的文件,而繼續執行,你可以在 include前加一個減號“-”。如:

-include <filename>
其表示,無論include過程中出現什么錯誤,都不要報錯繼續執行。和其它版本make兼容的
相關命令是sinclude,其作用和這一個是一樣的。


四、環境變量 MAKEFILES

如果你的當前環境中定義了環境變量MAKEFILES,那么,make會把這個變量中的值做一個類
似于include的動作。這個變量中的值是其它的 Makefile,用空格分隔。只是,它和incl
ude不同的是,從這個環境變中引入的Makefile的“目標”不會起作用,如果環境變量中定
義的文件發現錯誤,make也會不理。

但是在這里我還是建議不要使用這個環境變量,因為只要這個變量一被定義,那么當你使
用make時,所有的 Makefile都會受到它的影響,這絕不是你想看到的。在這里提這個事,
只是為了告訴大家,也許有時候你的Makefile出現了怪事,那么你可以看看當前環境中有
沒有定義這個變量。


五、make的工作方式

GNU的make工作時的執行步驟入下:(想來其它的make也是類似)

1、讀入所有的Makefile。
2、讀入被include的其它Makefile。
3、初始化文件中的變量。
4、推導隱晦規則,并分析所有規則。
5、為所有的目標文件創建依賴關系鏈。
6、根據依賴關系,決定哪些目標要重新生成。
7、執行生成命令。

1-5 步為第一個階段,6-7為第二個階段。第一個階段中,如果定義的變量被使用了,那么
,make會把其展開在使用的位置。但make并不會完全馬上展開,make使用的是拖延戰術,
如果變量出現在依賴關系的規則中,那么僅當這條依賴被決定要使用了,變量才會在其內
部展開。

當然,這個工作方式你不一定要清楚,但是知道這個方式你也會對make更為熟悉。有了這
個基礎,后續部分也就容易看懂了。



書寫規則
————

規則包含兩個部分,一個是依賴關系,一個是生成目標的方法。

在 Makefile中,規則的順序是很重要的,因為,Makefile中只應該有一個最終目標,其它
的目標都是被這個目標所連帶出來的,所以一定要讓 make知道你的最終目標是什么。一般
來說,定義在Makefile中的目標可能會有很多,但是第一條規則中的目標將被確立為最終
的目標。如果第一條規則中的目標有很多個,那么,第一個目標會成為最終的目標。make
所完成的也就是這個目標。

好了,還是讓我們來看一看如何書寫規則。


一、規則舉例

foo.o : foo.c defs.h # foo模塊
cc -c -g foo.c

看到這個例子,各位應該不是很陌生了,前面也已說過,foo.o是我們的目標,foo.c和de
fs.h是目標所依賴的源文件,而只有一個命令“cc -c -g foo.c”(以Tab鍵開頭)。這個
規則告訴我們兩件事:

1、文件的依賴關系,foo.o依賴于foo.c和defs.h的文件,如果foo.c和defs.h的文件日期
要比foo.o文件日期要新,或是foo.o不存在,那么依賴關系發生。
2、如果生成(或更新)foo.o文件。也就是那個cc命令,其說明了,如何生成foo.o這個文
件。(當然foo.c文件include了defs.h文件)


二、規則的語法

targets : prerequisites
command
...

或是這樣:

targets : prerequisites ; command
command
...

targets是文件名,以空格分開,可以使用通配符。一般來說,我們的目標基本上是一個文
件,但也有可能是多個文件。

command是命令行,如果其不與“target吐舌rerequisites”在一行,那么,必須以[Tab鍵
]開頭,如果和prerequisites在一行,那么可以用分號做為分隔。(見上)

prerequisites也就是目標所依賴的文件(或依賴目標)。如果其中的某個文件要比目標文
件要新,那么,目標就被認為是“過時的”,被認為是需要重生成的。這個在前面已經講
過了。

如果命令太長,你可以使用反斜框(‘\’)作為換行符。make對一行上有多少個字符沒有
限制。規則告訴make兩件事,文件的依賴關系和如何成成目標文件。

一般來說,make會以UNIX的標準Shell,也就是/bin/sh來執行命令。


三、在規則中使用通配符

如果我們想定義一系列比較類似的文件,我們很自然地就想起使用通配符。make支持三各
通配符:“*”,“?”和“[...]”。這是和Unix的B-Shell是相同的。

波浪號(“~”)字符在文件名中也有比較特殊的用途。如果是“~/test”,這就表示當前
用戶的$HOME目錄下的test目錄。而“~hchen /test”則表示用戶hchen的宿主目錄下的te
st目錄。(這些都是Unix下的小知識了,make也支持)而在Windows或是MS-DOS 下,用戶
沒有宿主目錄,那么波浪號所指的目錄則根據環境變量“HOME”而定。

通配符代替了你一系列的文件,如“*.c”表示所以后綴為c的文件。一個需要我們注意的
是,如果我們的文件名中有通配符,如:“*”,那么可以用轉義字符“\”,如“\*”來
表示真實的“*”字符,而不是任意長度的字符串。

好吧,還是先來看幾個例子吧:

clean:
rm -f *.o

上面這個例子我不不多說了,這是操作系統Shell所支持的通配符。這是在命令中的通配符


print: *.c
lpr -p $?
touch print

上面這個例子說明了通配符也可以在我們的規則中,目標print依賴于所有的[.c]文件。其
中的“$?”是一個自動化變量,我會在后面給你講述。

objects = *.o

上面這個例子,表示了,通符同樣可以用在變量中。并不是說[*.o]會展開,不!objects
的值就是“*.o”。Makefile中的變量其實就是 C/C++中的宏。如果你要讓通配符在變量中
展開,也就是讓objects的值是所有[.o]的文件名的集合,那么,你可以這樣:

objects := $(wildcard *.o)

這種用法由關鍵字“wildcard”指出,關于Makefile的關鍵字,我們將在后面討論。


四、文件搜尋

在一些大的工程中,有大量的源文件,我們通常的做法是把這許多的源文件分類,并存放
在不同的目錄中。所以,當make需要去找尋文件的依賴關系時,你可以在文件前加上路徑
,但最好的方法是把一個路徑告訴make,讓make在自動去找。

Makefile文件中的特殊變量“VPATH”就是完成這個功能的,如果沒有指明這個變量,mak
e只會在當前的目錄中去找尋依賴文件和目標文件。如果定義了這個變量,那么,make就會
在當當前目錄找不到的情況下,到所指定的目錄中去找尋文件了。

VPATH = src:../headers

上面的的定義指定兩個目錄,“src”和“../headers”,make會按照這個順序進行搜索。
目錄由“冒號”分隔。(當然,當前目錄永遠是最高優先搜索的地方)

另一個設置文件搜索路徑的方法是使用make的“vpath”關鍵字(注意,它是全小寫的),
這不是變量,這是一個make的關鍵字,這和上面提到的那個 VPATH變量很類似,但是它更
為靈活。它可以指定不同的文件在不同的搜索目錄中。這是一個很靈活的功能。它的使用
方法有三種:

1、vpath <pattern> <directories>

為符合模式<pattern>的文件指定搜索目錄<directories>。

2、vpath <pattern>

清除符合模式<pattern>的文件的搜索目錄。

3、vpath

清除所有已被設置好了的文件搜索目錄。

vapth 使用方法中的<pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例
如,“%.h”表示所有以“.h”結尾的文件。<pattern>指定了要搜索的文件集,而<direc
tories>則指定了<pattern>的文件集的搜索的目錄。例如:

vpath %.h ../headers

該語句表示,要求make在“../headers”目錄下搜索所有以“.h”結尾的文件。(如果某
文件在當前目錄沒有找到的話)

我們可以連續地使用vpath語句,以指定不同搜索策略。如果連續的vpath語句中出現了相
同的<pattern>,或是被重復了的<pattern>,那么,make會按照vpath語句的先后順序來執
行搜索。如:

vpath %.c foo
vpath % blish
vpath %.c bar

其表示“.c”結尾的文件,先在“foo”目錄,然后是“blish”,最后是“bar”目錄。


vpath %.c foo:bar
vpath % blish

而上面的語句則表示“.c”結尾的文件,先在“foo”目錄,然后是“bar”目錄,最后才
是“blish”目錄。


五、偽目標

最早先的一個例子中,我們提到過一個“clean”的目標,這是一個“偽目標”,

clean:
rm *.o temp

正像我們前面例子中的“clean”一樣,即然我們生成了許多文件編譯文件,我們也應該提
供一個清除它們的“目標”以備完整地重編譯而用。 (以“make clean”來使用該目標)


因為,我們并不生成“clean”這個文件。“偽目標”并不是一個文件,只是一個標簽,由
于“偽目標”不是文件,所以make無法生成它的依賴關系和決定它是否要執行。我們只有
通過顯示地指明這個“目標”才能讓其生效。當然,“偽目標”的取名不能和文件名重名
,不然其就失去了“偽目標”的意義了。

當然,為了避免和文件重名的這種情況,我們可以使用一個特殊的標記“.PHONY”來顯示
地指明一個目標是“偽目標”,向make說明,不管是否有這個文件,這個目標就是“偽目
標”。

.PHONY : clean

只要有這個聲明,不管是否有“clean”文件,要運行“clean”這個目標,只有“make c
lean”這樣。于是整個過程可以這樣寫:

.PHONY: clean
clean:
rm *.o temp

偽目標一般沒有依賴的文件。但是,我們也可以為偽目標指定所依賴的文件。偽目標同樣
可以作為“默認目標”,只要將其放在第一個。一個示例就是,如果你的 Makefile需要一
口氣生成若干個可執行文件,但你只想簡單地敲一個make完事,并且,所有的目標文件都
寫在一個Makefile中,那么你可以使用“偽目標”這個特性:

all : prog1 prog2 prog3
.PHONY : all

prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o

prog2 : prog2.o
cc -o prog2 prog2.o

prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o

我們知道,Makefile中的第一個目標會被作為其默認目標。我們聲明了一個“all”的偽目
標,其依賴于其它三個目標。由于偽目標的特性是,總是被執行的,所以其依賴的那三個
目標就總是不如“all”這個目標新。所以,其它三個目標的規則總是會被決議。也就達到
了我們一口氣生成多個目標的目的。 “.PHONY : all”聲明了“all”這個目標為“偽目
標”。

隨便提一句,從上面的例子我們可以看出,目標也可以成為依賴。所以,偽目標同樣也可
成為依賴。看下面的例子:

.PHONY: cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff
rm program

cleanobj :
rm *.o

cleandiff :
rm *.diff

“make clean”將清除所有要被清除的文件。“cleanobj”和“cleandiff”這兩個偽目標
有點像“子程序”的意思。我們可以輸入“make cleanall”和“make cleanobj”和“ma
ke cleandiff”命令來達到清除不同種類文件的目的。

六、多目標

Makefile 的規則中的目標可以不止一個,其支持多目標,有可能我們的多個目標同時依賴
于一個文件,并且其生成的命令大體類似。于是我們就能把其合并起來。當然,多個目標
的生成規則的執行命令是同一個,這可能會可我們帶來麻煩,不過好在我們的可以使用一
個自動化變量“$@”(關于自動化變量,將在后面講述),這個變量表示著目前規則中所
有的目標的集合,這樣說可能很抽象,還是看一個例子吧。

bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@

上述規則等價于:

bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput

其中,-$(subst output,,$@)中的“$”表示執行一個Makefile的函數,函數名為subst,
后面的為參數。關于函數,將在后面講述。這里的這個函數是截取字符串的意思,“$@”
表示目標的集合,就像一個數組,“$@”依次取出目標,并執于命令。


七、靜態模式

靜態模式可以更加容易地定義多目標的規則,可以讓我們的規則變得更加的有彈性和靈活
。我們還是先來看一下語法:

<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
...


targets定義了一系列的目標文件,可以有通配符。是目標的一個集合。

target-parrtern是指明了targets的模式,也就是的目標集模式。

prereq-parrterns是目標的依賴模式,它對target-parrtern形成的模式再進行一次依賴目
標的定義。


這樣描述這三個東西,可能還是沒有說清楚,還是舉個例子來說明一下吧。如果我們的<t
arget-parrtern>定義成“%.o”,意思是我們的<target>集合中都是以“.o”結尾的,而
如果我們的<prereq-parrterns>定義成“%.c”,意思是對<target-parrtern>所形成的目
標集進行二次定義,其計算方法是,取<target-parrtern>模式中的“%”(也就是去掉了
[.o]這個結尾),并為其加上[.c]這個結尾,形成的新集合。

所以,我們的“目標模式”或是“依賴模式”中都應該有“%”這個字符,如果你的文件名
中有“%”那么你可以使用反斜杠“\”進行轉義,來標明真實的“%”字符。

看一個例子:

objects = foo.o bar.o

all: $(objects)

$(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

試想,如果我們的“%.o”有幾百個,那種我們只要用這種很簡單的“靜態模式規則”就可
以寫完一堆規則,實在是太有效率了。“靜態模式規則”的用法很靈活,如果用得好,那
會一個很強大的功能。再看一個例子:


files = foo.elc bar.o lose.o

$(filter %.o,$(files)): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
emacs -f batch-byte-compile $<


$(filter %.o,$(files))表示調用Makefile的filter函數,過濾“$filter”集,只要其中
模式為“%.o”的內容。其的它內容,我就不用多說了吧。這個例字展示了Makefile中更大
的彈性。


八、自動生成依賴性

在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聯系在一起呢。因為這樣一來,我們的Ma
kefile也要根據這些源文件重新生成,讓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命令的用法請參看相關的使用文檔。第四行就是
刪除臨時文件。

總而言之,這個模式要做的事就是在編譯器生成的依賴關系中加入[.d]文件的依賴,即把
依賴關系:

main.o : main.c defs.h

轉成:

main.o main.d : main.c defs.h

于是,我們的[.d]文件也會自動更新了,并會自動生成了,當然,你還可以在這個[.d]文
件中加入的不只是依賴關系,包括生成的命令也可一并加入,讓每個 [.d]文件都包含一個
完賴的規則。一旦我們完成這個工作,接下來,我們就要把這些自動生成的規則放進我們
的主Makefile中。我們可以使用 Makefile的“include”命令,來引入別的Makefile文件
(前面講過),例如:

sources = foo.c bar.c

include $(sources:.c=.d)

上述語句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一個替換,把變量$(sourc
es)所有[.c]的字串都替換成 [.d],關于這個“替換”的內容,在后面我會有更為詳細的
講述。當然,你得注意次序,因為include是按次來載入文件,最先載入的[.d]文件中的目
標會成為默認目標。



書寫命令
————

每條規則中的命令和操作系統Shell的命令行是一致的。make會一按順序一條一條的執行命
令,每條命令的開頭必須以[Tab]鍵開頭,除非,命令是緊跟在依賴規則后面的分號后的。
在命令行之間中的空格或是空行會被忽略,但是如果該空格或空行是以Tab鍵開頭的,那么
make會認為其是一個空命令。

我們在UNIX下可能會使用不同的Shell,但是make的命令默認是被“/bin/sh”——UNIX的
標準Shell解釋執行的。除非你特別指定一個其它的Shell。Makefile中,“#”是注釋符,
很像C/C++中的“//”,其后的本行字符都被注釋。

一、顯示命令

通常,make會把其要執行的命令行在命令執行前輸出到屏幕上。當我們用“@”字符在命令
行前,那么,這個命令將不被make顯示出來,最具代表性的例子是,我們用這個功能來像
屏幕顯示一些信息。如:

@echo 正在編譯XXX模塊......

當make執行時,會輸出“正在編譯XXX模塊......”字串,但不會輸出命令,如果沒有“@
”,那么,make將輸出:

echo 正在編譯XXX模塊......
正在編譯XXX模塊......

如果make執行時,帶入make參數“-n”或“--just-print”,那么其只是顯示命令,但不
會執行命令,這個功能很有利于我們調試我們的Makefile,看看我們書寫的命令是執行起
來是什么樣子的或是什么順序的。

而make參數“-s”或“--slient”則是全面禁止命令的顯示。



二、命令執行

當依賴目標新于目標時,也就是當規則的目標需要被更新時,make會一條一條的執行其后
的命令。需要注意的是,如果你要讓上一條命令的結果應用在下一條命令時,你應該使用
分號分隔這兩條命令。比如你的第一條命令是cd命令,你希望第二條命令得在cd之后的基
礎上運行,那么你就不能把這兩條命令寫在兩行上,而應該把這兩條命令寫在一行上,用
分號分隔。如:

示例一:
exec:
cd /home/hchen
pwd

示例二:
exec:
cd /home/hchen; pwd

當我們執行“make exec”時,第一個例子中的cd沒有作用,pwd會打印出當前的Makefile
目錄,而第二個例子中,cd就起作用了,pwd會打印出“/home/hchen”。

make 一般是使用環境變量SHELL中所定義的系統Shell來執行命令,默認情況下使用UNIX的
標準Shell——/bin/sh來執行命令。但在MS- DOS下有點特殊,因為MS-DOS下沒有SHELL環
境變量,當然你也可以指定。如果你指定了UNIX風格的目錄形式,首先,make會在SHELL所
指定的路徑中找尋命令解釋器,如果找不到,其會在當前盤符中的當前目錄中尋找,如果
再找不到,其會在PATH環境變量中所定義的所有路徑中尋找。MS- DOS中,如果你定義的命
令解釋器沒有找到,其會給你的命令解釋器加上諸如“.exe”、“.com”、“.bat”、“
.sh”等后綴。



三、命令出錯

每當命令運行完后,make會檢測每個命令的返回碼,如果命令返回成功,那么make會執行
下一條命令,當規則中所有的命令成功返回后,這個規則就算是成功完成了。如果一個規
則中的某個命令出錯了(命令退出碼非零),那么make就會終止執行當前規則,這將有可
能終止所有規則的執行。

有些時候,命令的出錯并不表示就是錯誤的。例如mkdir命令,我們一定需要建立一個目錄
,如果目錄不存在,那么mkdir就成功執行,萬事大吉,如果目錄存在,那么就出錯了。我
們之所以使用mkdir的意思就是一定要有這樣的一個目錄,于是我們就不希望mkdir出錯而
終止規則的運行。

為了做到這一點,忽略命令的出錯,我們可以在Makefile的命令行前加一個減號“-”(在
Tab鍵之后),標記為不管命令出不出錯都認為是成功的。如:

clean:
-rm -f *.o

還有一個全局的辦法是,給make加上“-i”或是“--ignore-errors”參數,那么,Makef
ile中所有命令都會忽略錯誤。而如果一個規則是以“.IGNORE”作為目標的,那么這個規
則中的所有命令將會忽略錯誤。這些是不同級別的防止命令出錯的方法,你可以根據你的
不同喜歡設置。

還有一個要提一下的make的參數的是“-k”或是“--keep-going”,這個參數的意思是,
如果某規則中的命令出錯了,那么就終目該規則的執行,但繼續執行其它規則。



四、嵌套執行make

在一些大的工程中,我們會把我們不同模塊或是不同功能的源文件放在不同的目錄中,我
們可以在每個目錄中都書寫一個該目錄的Makefile,這有利于讓我們的Makefile變得更加
地簡潔,而不至于把所有的東西全部寫在一個Makefile中,這樣會很難維護我們的Makefi
le,這個技術對于我們模塊編譯和分段編譯有著非常大的好處。

例如,我們有一個子目錄叫subdir,這個目錄下有個Makefile文件,來指明了這個目錄下
文件的編譯規則。那么我們總控的Makefile可以這樣書寫:

subsystem:
cd subdir && $(MAKE)

其等價于:

subsystem:
$(MAKE) -C subdir

定義$(MAKE)宏變量的意思是,也許我們的make需要一些參數,所以定義成一個變量比較利
于維護。這兩個例子的意思都是先進入“subdir”目錄,然后執行make命令。

我們把這個Makefile叫做“總控Makefile”,總控Makefile的變量可以傳遞到下級的Make
file中(如果你顯示的聲明),但是不會覆蓋下層的Makefile中所定義的變量,除非指定
了“-e”參數。

如果你要傳遞變量到下級Makefile中,那么你可以使用這樣的聲明:

export <variable ...>

如果你不想讓某些變量傳遞到下級Makefile中,那么你可以這樣聲明:

unexport <variable ...>

如:

示例一:

export variable = value

其等價于:

variable = value
export variable

其等價于:

export variable := value

其等價于:

variable := value
export variable

示例二:

export variable += value

其等價于:

variable += value
export variable

如果你要傳遞所有的變量,那么,只要一個export就行了。后面什么也不用跟,表示傳遞
所有的變量。

需要注意的是,有兩個變量,一個是SHELL,一個是MAKEFLAGS,這兩個變量不管你是否ex
port,其總是要傳遞到下層Makefile中,特別是MAKEFILES變量,其中包含了make的參數信
息,如果我們執行“總控Makefile”時有make參數或是在上層Makefile中定義了這個變量
,那么MAKEFILES變量將會是這些參數,并會傳遞到下層Makefile中,這是一個系統級的環
境變量。

但是make命令中的有幾個參數并不往下傳遞,它們是“-C”,“-f”,“-h”“-o”和“-W
”(有關Makefile參數的細節將在后面說明),如果你不想往下層傳遞參數,那么,你可
以這樣來:

subsystem:
cd subdir && $(MAKE) MAKEFLAGS=

如果你定義了環境變量MAKEFLAGS,那么你得確信其中的選項是大家都會用到的,如果其中
有“-t”,“-n”,和“-q”參數,那么將會有讓你意想不到的結果,或許會讓你異常地恐
慌。

還有一個在“嵌套執行”中比較有用的參數,“-w”或是“--print-directory”會在mak
e的過程中輸出一些信息,讓你看到目前的工作目錄。比如,如果我們的下級make目錄是“
/home/hchen/gnu/make”,如果我們使用“make -w”來執行,那么當進入該目錄時,我們
會看到:

make: Entering directory `/home/hchen/gnu/make'.

而在完成下層make后離開目錄時,我們會看到:

make: Leaving directory `/home/hchen/gnu/make'

當你使用“-C”參數來指定make下層Makefile時,“-w”會被自動打開的。如果參數中有
“-s”(“--slient”)或是“--no-print-directory”,那么,“-w”總是失效的。




五、定義命令包

如果Makefile中出現一些相同命令序列,那么我們可以為這些相同的命令序列定義一個變
量。定義這種命令序列的語法以“define”開始,以“endef”結束,如:

define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef

這里,“run-yacc”是這個命令包的名字,其不要和Makefile中的變量重名。在“define
”和“endef”中的兩行就是命令序列。這個命令包中的第一個命令是運行Yacc程序,因為
Yacc程序總是生成“y.tab.c”的文件,所以第二行的命令就是把這個文件改改名字。還是
把這個命令包放到一個示例中來看看吧。

foo.c : foo.y
$(run-yacc)

我們可以看見,要使用這個命令包,我們就好像使用變量一樣。在這個命令包的使用中,
命令包“run-yacc”中的“$^”就是“foo.y”,“$@”就是“foo.c”(有關這種以 “$
”開頭的特殊變量,我們會在后面介紹),make在執行命令包時,命令包中的每個命令會
被依次獨立執行。

使用變量
————

在 Makefile中的定義的變量,就像是C/C++語言中的宏一樣,他代表了一個文本字串,在
Makefile中執行的時候其會自動原模原樣地展開在所使用的地方。其與C/C++所不同的是,
你可以在Makefile中改變其值。在Makefile中,變量可以使用在“目標”,“依賴目標”
,“命令”或是 Makefile的其它部分中。

變量的命名字可以包含字符、數字,下劃線(可以是數字開頭),但不應該含有“:”、“
#”、“=”或是空字符(空格、回車等)。變量是大小寫敏感的,“foo”、“Foo”和“
FOO”是三個不同的變量名。傳統的Makefile的變量名是全大寫的命名方式,但我推薦使用
大小寫搭配的變量名,如:MakeFlags。這樣可以避免和系統的變量沖突,而發生意外的事
情。

有一些變量是很奇怪字串,如“$<”、“$@”等,這些是自動化變量,我會在后面介紹。


一、變量的基礎

變量在聲明時需要給予初值,而在使用時,需要給在變量名前加上“$”符號,但最好用小
括號“()”或是大括號“{}”把變量給包括起來。如果你要使用真實的“$”字符,那么
你需要用“$$”來表示。

變量可以使用在許多地方,如規則中的“目標”、“依賴”、“命令”以及新的變量中。
先看一個例子:

objects = program.o foo.o utils.o
program : $(objects)
cc -o program $(objects)

$(objects) : defs.h

變量會在使用它的地方精確地展開,就像C/C++中的宏一樣,例如:

foo = c
prog.o : prog.$(foo)
$(foo)$(foo) -$(foo) prog.$(foo)

展開后得到:

prog.o : prog.c
cc -c prog.c

當然,千萬不要在你的Makefile中這樣干,這里只是舉個例子來表明Makefile中的變量在
使用處展開的真實樣子。可見其就是一個“替代”的原理。

另外,給變量加上括號完全是為了更加安全地使用這個變量,在上面的例子中,如果你不
想給變量加上括號,那也可以,但我還是強烈建議你給變量加上括號。


二、變量中的變量

在定義變量的值時,我們可以使用其它變量來構造變量的值,在Makefile中有兩種方式來
在用變量定義變量的值。

先看第一種方式,也就是簡單的使用“=”號,在“=”左側是變量,右側是變量的值,右
側變量的值可以定義在文件的任何一處,也就是說,右側中的變量不一定非要是已定義好
的值,其也可以使用后面定義的值。如:

foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:
echo $(foo)

我們執行“make all”將會打出變量$(foo)的值是“Huh?”( $(foo)的值是$(bar),$(b
ar)的值是$(ugh),$(ugh)的值是“Huh?”)可見,變量是可以使用后面的變量來定義的。


這個功能有好的地方,也有不好的地方,好的地方是,我們可以把變量的真實值推到后面
來定義,如:

CFLAGS = $(include_dirs) -O
include_dirs = -Ifoo -Ibar

當“CFLAGS”在命令中被展開時,會是“-Ifoo -Ibar -O”。但這種形式也有不好的地方
,那就是遞歸定義,如:

CFLAGS = $(CFLAGS) -O

或:

A = $(B)
B = $(A)

這會讓make陷入無限的變量展開過程中去,當然,我們的make是有能力檢測這樣的定義,
并會報錯。還有就是如果在變量中使用函數,那么,這種方式會讓我們的make運行時非常
慢,更糟糕的是,他會使用得兩個make的函數“wildcard”和“shell”發生不可預知的錯
誤。因為你不會知道這兩個函數會被調用多少次。

為了避免上面的這種方法,我們可以使用make中的另一種用變量來定義變量的方法。這種
方法使用的是“:=”操作符,如:

x := foo
y := $(x) bar
x := later

其等價于:

y := foo bar
x := later

值得一提的是,這種方法,前面的變量不能使用后面的變量,只能使用前面已定義好了的
變量。如果是這樣:

y := $(x) bar
x := foo

那么,y的值是“bar”,而不是“foo bar”。

上面都是一些比較簡單的變量使用了,讓我們來看一個復雜的例子,其中包括了make的函
數、條件表達式和一個系統變量“MAKELEVEL”的使用:

ifeq (0,${MAKELEVEL})
cur-dir := $(shell pwd)
whoami := $(shell whoami)
host-type := $(shell arch)
MAKE := ${MAKE} host-type=${host-type} whoami=${whoami}
endif

關于條件表達式和函數,我們在后面再說,對于系統變量“MAKELEVEL”,其意思是,如果
我們的make有一個嵌套執行的動作(參見前面的“嵌套使用make”),那么,這個變量會
記錄了我們的當前Makefile的調用層數。

下面再介紹兩個定義變量時我們需要知道的,請先看一個例子,如果我們要定義一個變量
,其值是一個空格,那么我們可以這樣來:

nullstring :=
space := $(nullstring) # end of the line

nullstring 是一個Empty變量,其中什么也沒有,而我們的space的值是一個空格。因為在
操作符的右邊是很難描述一個空格的,這里采用的技術很管用,先用一個 Empty變量來標
明變量的值開始了,而后面采用“#”注釋符來表示變量定義的終止,這樣,我們可以定義
出其值是一個空格的變量。請注意這里關于“#”的使用,注釋符“#”的這種特性值得我
們注意,如果我們這樣定義一個變量:

dir := /foo/bar # directory to put the frobs in

dir這個變量的值是“/foo/bar”,后面還跟了4個空格,如果我們這樣使用這樣變量來指
定別的目錄——“$(dir)/file”那么就完蛋了。

還有一個比較有用的操作符是“?=”,先看示例:

FOO ?= bar

其含義是,如果FOO沒有被定義過,那么變量FOO的值就是“bar”,如果FOO先前被定義過
,那么這條語將什么也不做,其等價于:

ifeq ($(origin FOO), undefined)
FOO = bar
endif


三、變量高級用法

這里介紹兩種變量的高級使用方法,第一種是變量值的替換。

我們可以替換變量中的共有的部分,其格式是“$(var:a=b)”或是“${var:a=b}”,其意
思是,把變量“var”中所有以“a”字串“結尾”的“a”替換成“b”字串。這里的“結
尾”意思是“空格”或是“結束符”。

還是看一個示例吧:

foo := a.o b.o c.o
bar := $(foo:.o=.c)

這個示例中,我們先定義了一個“$(foo)”變量,而第二行的意思是把“$(foo)”中所有
以“.o”字串“結尾”全部替換成“.c”,所以我們的“$(bar)”的值就是“a.c b.c c.
c”。

另外一種變量替換的技術是以“靜態模式”(參見前面章節)定義的,如:

foo := a.o b.o c.o
bar := $(foo:%.o=%.c)

這依賴于被替換字串中的有相同的模式,模式中必須包含一個“%”字符,這個例子同樣讓
$(bar)變量的值為“a.c b.c c.c”。

第二種高級用法是——“把變量的值再當成變量”。先看一個例子:

x = y
y = z
a := $($(x))

在這個例子中,$(x)的值是“y”,所以$($(x))就是$(y),于是$(a)的值就是“z”。(注
意,是“x=y”,而不是“x=$(y)”)

我們還可以使用更多的層次:

x = y
y = z
z = u
a := $($($(x)))

這里的$(a)的值是“u”,相關的推導留給讀者自己去做吧。

讓我們再復雜一點,使用上“在變量定義中使用變量”的第一個方式,來看一個例子:


x = $(y)
y = z
z = Hello
a := $($(x))

這里的$($(x))被替換成了$($(y)),因為$(y)值是“z”,所以,最終結果是:a:=$(z),
也就是“Hello”。

再復雜一點,我們再加上函數:

x = variable1
variable2 := Hello
y = $(subst 1,2,$(x))
z = y
a := $($($(z)))

這個例子中,“$($($(z)))”擴展為“$($(y))”,而其再次被擴展為“$($(subst 1,2,$
(x)))”。$(x)的值是“variable1”,subst函數把“variable1”中的所有“1”字串替換
成“2”字串,于是,“variable1”變成“variable2”,再取其值,所以,最終,$(a)的
值就是$(variable2)的值—— “Hello”。(喔,好不容易)

在這種方式中,或要可以使用多個變量來組成一個變量的名字,然后再取其值:

first_second = Hello
a = first
b = second
all = $($a_$b)

這里的“$a_$b”組成了“first_second”,于是,$(all)的值就是“Hello”。

再來看看結合第一種技術的例子:

a_objects := a.o b.o c.o
1_objects := 1.o 2.o 3.o

sources := $($(a1)_objects:.o=.c)

這個例子中,如果$(a1)的值是“a”的話,那么,$(sources)的值就是“a.c b.c c.c”;
如果$(a1)的值是“1”,那么$(sources)的值是“1.c 2.c 3.c”。

再來看一個這種技術和“函數”與“條件語句”一同使用的例子:

ifdef do_sort
func := sort
else
func := strip
endif

bar := a d b g q c

foo := $($(func) $(bar))

這個示例中,如果定義了“do_sort”,那么:foo := $(sort a d b g q c),于是$(foo
)的值就是“a b c d g q”,而如果沒有定義“do_sort”,那么:foo := $(sort a d b
g q c),調用的就是strip函數。

當然,“把變量的值再當成變量”這種技術,同樣可以用在操作符的左邊:

dir = foo
$(dir)_sources := $(wildcard $(dir)/*.c)
define $(dir)_print
lpr $($(dir)_sources)
endef

這個例子中定義了三個變量:“dir”,“foo_sources”和“foo_print”。


四、追加變量值

我們可以使用“+=”操作符給變量追加值,如:

objects = main.o foo.o bar.o utils.o
objects += another.o

于是,我們的$(objects)值變成:“main.o foo.o bar.o utils.o another.o”(anothe
r.o被追加進去了)

使用“+=”操作符,可以模擬為下面的這種例子:

objects = main.o foo.o bar.o utils.o
objects := $(objects) another.o

所不同的是,用“+=”更為簡潔。

如果變量之前沒有定義過,那么,“+=”會自動變成“=”,如果前面有變量定義,那么“
+=”會繼承于前次操作的賦值符。如果前一次的是“:=”,那么“+=”會以“:=”作為其
賦值符,如:

variable := value
variable += more

等價于:

variable := value
variable := $(variable) more

但如果是這種情況:

variable = value
variable += more

由于前次的賦值符是“=”,所以“+=”也會以“=”來做為賦值,那么豈不會發生變量的
遞補歸定義,這是很不好的,所以make會自動為我們解決這個問題,我們不必擔心這個問
題。


五、override 指示符

如果有變量是通常make的命令行參數設置的,那么Makefile中對這個變量的賦值會被忽略
。如果你想在Makefile中設置這類參數的值,那么,你可以使用“override”指示符。其
語法是:

override <variable> = <value>

override <variable> := <value>

當然,你還可以追加:

override <variable> += <more text>

對于多行的變量定義,我們用define指示符,在define指示符前,也同樣可以使用ovveri
de指示符,如:

override define foo
bar
endef

六、多行變量

還有一種設置變量值的方法是使用define關鍵字。使用define關鍵字設置變量的值可以有
換行,這有利于定義一系列的命令(前面我們講過“命令包”的技術就是利用這個關鍵字
)。

define 指示符后面跟的是變量的名字,而重起一行定義變量的值,定義是以endef關鍵字
結束。其工作方式和“=”操作符一樣。變量的值可以包含函數、命令、文字,或是其它變
量。因為命令需要以[Tab]鍵開頭,所以如果你用define定義的命令變量中沒有以[Tab]鍵
開頭,那么make就不會把其認為是命令。

下面的這個示例展示了define的用法:

define two-lines
echo foo
echo $(bar)
endef


七、環境變量

make 運行時的系統環境變量可以在make開始運行時被載入到Makefile文件中,但是如果M
akefile中已定義了這個變量,或是這個變量由make命令行帶入,那么系統的環境變量的值
將被覆蓋。(如果make指定了“-e”參數,那么,系統環境變量將覆蓋Makefile中定義的
變量)

因此,如果我們在環境變量中設置了“CFLAGS”環境變量,那么我們就可以在所有的Make
file中使用這個變量了。這對于我們使用統一的編譯參數有比較大的好處。如果Makefile
中定義了CFLAGS,那么則會使用Makefile中的這個變量,如果沒有定義則使用系統環境變
量的值,一個共性和個性的統一,很像“全局變量”和“局部變量”的特性。

當make嵌套調用時(參見前面的“嵌套調用”章節),上層Makefile中定義的變量會以系
統環境變量的方式傳遞到下層的Makefile中。當然,默認情況下,只有通過命令行設置的
變量會被傳遞。而定義在文件中的變量,如果要向下層 Makefile傳遞,則需要使用expro
t關鍵字來聲明。(參見前面章節)

當然,我并不推薦把許多的變量都定義在系統環境中,這樣,在我們執行不用的Makefile
時,擁有的是同一套系統變量,這可能會帶來更多的麻煩。


八、目標變量

前面我們所講的在Makefile中定義的變量都是“全局變量”,在整個文件,我們都可以訪
問這些變量。當然,“自動化變量”除外,如“$<”等這種類量的自動化變量就屬于“規
則型變量”,這種變量的值依賴于規則的目標和依賴目標的定義。

當然,我樣同樣可以為某個目標設置局部變量,這種變量被稱為“Target-specific Vari
able”,它可以和“全局變量”同名,因為它的作用范圍只在這條規則以及連帶規則中,
所以其值也只在作用范圍內有效。而不會影響規則鏈以外的全局變量的值。

其語法是:

<target ...> : <variable-assignment>

<target ...> : overide <variable-assignment>

<variable-assignment>可以是前面講過的各種賦值表達式,如“=”、“:=”、“+=”或
是“?=”。第二個語法是針對于make命令行帶入的變量,或是系統環境變量。

這個特性非常的有用,當我們設置了這樣一個變量,這個變量會作用到由這個目標所引發
的所有的規則中去。如:

prog : CFLAGS = -g
prog : prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o

prog.o : prog.c
$(CC) $(CFLAGS) prog.c

foo.o : foo.c
$(CC) $(CFLAGS) foo.c

bar.o : bar.c
$(CC) $(CFLAGS) bar.c

在這個示例中,不管全局的$(CFLAGS)的值是什么,在prog目標,以及其所引發的所有規則
中(prog.o foo.o bar.o的規則),$(CFLAGS)的值都是“-g”


九、模式變量

在GNU的make中,還支持模式變量(Pattern-specific Variable),通過上面的目標變量
中,我們知道,變量可以定義在某個目標上。模式變量的好處就是,我們可以給定一種“
模式”,可以把變量定義在符合這種模式的所有目標上。

我們知道,make的“模式”一般是至少含有一個“%”的,所以,我們可以以如下方式給所
有以[.o]結尾的目標定義目標變量:

%.o : CFLAGS = -O

同樣,模式變量的語法和“目標變量”一樣:

<pattern ...> : <variable-assignment>

<pattern ...> : override <variable-assignment>

override同樣是針對于系統環境傳入的變量,或是make命令行指定的變量。

使用條件判斷
——————

使用條件判斷,可以讓make根據運行時的不同情況選擇不同的執行分支。條件表達式可以
是比較變量的值,或是比較變量和常量的值。

一、示例

下面的例子,判斷$(CC)變量是否“gcc”,如果是的話,則使用GNU函數編譯目標。

libs_for_gcc = -lgnu
normal_libs =

foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif

可見,在上面示例的這個規則中,目標“foo”可以根據變量“$(CC)”值來選取不同的函
數庫來編譯程序。

我們可以從上面的示例中看到三個關鍵字:ifeq、else和endif。ifeq的意思表示條件語句
的開始,并指定一個條件表達式,表達式包含兩個參數,以逗號分隔,表達式以圓括號括
起。else表示條件表達式為假的情況。endif表示一個條件語句的結束,任何一個條件表達
式都應該以endif結束。

當我們的變量$(CC)值是“gcc”時,目標foo的規則是:

foo: $(objects)
$(CC) -o foo $(objects) $(libs_for_gcc)

而當我們的變量$(CC)值不是“gcc”時(比如“cc”),目標foo的規則是:

foo: $(objects)
$(CC) -o foo $(objects) $(normal_libs)

當然,我們還可以把上面的那個例子寫得更簡潔一些:

libs_for_gcc = -lgnu
normal_libs =

ifeq ($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif

foo: $(objects)
$(CC) -o foo $(objects) $(libs)


二、語法

條件表達式的語法為:

<conditional-directive>
<text-if-true>
endif

以及:

<conditional-directive>
<text-if-true>
else
<text-if-false>
endif

其中<conditional-directive>表示條件關鍵字,如“ifeq”。這個關鍵字有四個。

第一個是我們前面所見過的“ifeq”

ifeq (<arg1>, <arg2> )
ifeq '<arg1>' '<arg2>'
ifeq "<arg1>" "<arg2>"
ifeq "<arg1>" '<arg2>'
ifeq '<arg1>' "<arg2>"

比較參數“arg1”和“arg2”的值是否相同。當然,參數中我們還可以使用make的函數。
如:

ifeq ($(strip $(foo)),)
<text-if-empty>
endif

這個示例中使用了“strip”函數,如果這個函數的返回值是空(Empty),那么<text-if
-empty>就生效。

第二個條件關鍵字是“ifneq”。語法是:

ifneq (<arg1>, <arg2> )
ifneq '<arg1>' '<arg2>'
ifneq "<arg1>" "<arg2>"
ifneq "<arg1>" '<arg2>'
ifneq '<arg1>' "<arg2>"

其比較參數“arg1”和“arg2”的值是否相同,如果不同,則為真。和“ifeq”類似。


第三個條件關鍵字是“ifdef”。語法是:

ifdef <variable-name>

如果變量<variable-name>的值非空,那到表達式為真。否則,表達式為假。當然,<vari
able-name>同樣可以是一個函數的返回值。注意,ifdef只是測試一個變量是否有值,其并
不會把變量擴展到當前位置。還是來看兩個例子:

示例一:
bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif

示例二:
foo =
ifdef foo
frobozz = yes
else
frobozz = no
endif

第一個例子中,“$(frobozz)”值是“yes”,第二個則是“no”。

第四個條件關鍵字是“ifndef”。其語法是:

ifndef <variable-name>

這個我就不多說了,和“ifdef”是相反的意思。

在<conditional-directive>這一行上,多余的空格是被允許的,但是不能以[Tab]鍵做為
開始(不然就被認為是命令)。而注釋符“#”同樣也是安全的。“else”和“endif”也
一樣,只要不是以[Tab]鍵開始就行了。

特別注意的是,make是在讀取Makefile時就計算條件表達式的值,并根據條件表達式的值
來選擇語句,所以,你最好不要把自動化變量(如“$@”等)放入條件表達式中,因為自
動化變量是在運行時才有的。

而且,為了避免混亂,make不允許把整個條件語句分成兩部分放在不同的文件中。



使用函數
————

在Makefile中可以使用函數來處理變量,從而讓我們的命令或是規則更為的靈活和具有智
能。make所支持的函數也不算很多,不過已經足夠我們的操作了。函數調用后,函數的返
回值可以當做變量來使用。


一、函數的調用語法

函數調用,很像變量的使用,也是以“$”來標識的,其語法如下:

$(<function> <arguments> )

或是

${<function> <arguments>}

這里,<function>就是函數名,make支持的函數不多。<arguments>是函數的參數,參數間
以逗號“,”分隔,而函數名和參數之間以“空格”分隔。函數調用以“$”開頭,以圓括
號或花括號把函數名和參數括起。感覺很像一個變量,是不是?函數中的參數可以使用變
量,為了風格的統一,函數和變量的括號最好一樣,如使用“$(subst a,b,$(x))”這樣的
形式,而不是“$(subst a,b,${x})”的形式。因為統一會更清楚,也會減少一些不必要的
麻煩。

還是來看一個示例:

comma:= ,
empty:=
space:= $(empty) $(empty)
foo:= a b c
bar:= $(subst $(space),$(comma),$(foo))

在這個示例中,$(comma)的值是一個逗號。$(space)使用了$(empty)定義了一個空格,$(
foo)的值是“a b c”,$(bar)的定義用,調用了函數“subst”,這是一個替換函數,這
個函數有三個參數,第一個參數是被替換字串,第二個參數是替換字串,第三個參數是替
換操作作用的字串。這個函數也就是把$(foo)中的空格替換成逗號,所以$(bar)的值是“
a,b,c”。


二、字符串處理函數

$(subst <from>,<to>,<text> )

名稱:字符串替換函數——subst。
功能:把字串<text>中的<from>字符串替換成<to>。
返回:函數返回被替換過后的字符串。

示例:

$(subst ee,EE,feet on the street),

把“feet on the street”中的“ee”替換成“EE”,返回結果是“fEEt on the strEEt
”。


$(patsubst <pattern>,<replacement>,<text> )

名稱:模式字符串替換函數——patsubst。
功能:查找<text>中的單詞(單詞以“空格”、“Tab”或“回車”“換行”分隔)是否符
合模式<pattern>,如果匹配的話,則以<replacement>替換。這里,<pattern>可以包括通
配符“%”,表示任意長度的字串。如果<replacement>中也包含“%”,那么,<replacem
ent>中的這個“%”將是<pattern>中的那個“%”所代表的字串。(可以用“\”來轉義,
以“\%”來表示真實含義的“%”字符)
返回:函數返回被替換過后的字符串。

示例:

$(patsubst %.c,%.o,x.c.c bar.c)

把字串“x.c.c bar.c”符合模式[%.c]的單詞替換成[%.o],返回結果是“x.c.o bar.o”


備注:

這和我們前面“變量章節”說過的相關知識有點相似。如:

“$(var:<pattern>=<replacement> )”
相當于
“$(patsubst <pattern>,<replacement>,$(var))”,

而“$(var: <suffix>=<replacement> )”
則相當于
“$(patsubst %<suffix>,%<replacement>,$(var))”。

例如有:objects = foo.o bar.o baz.o,
那么,“$(objects:.o=.c)”和“$(patsubst %.o,%.c,$(objects))”是一樣的。

$(strip <string> )

名稱:去空格函數——strip。
功能:去掉<string>字串中開頭和結尾的空字符。
返回:返回被去掉空格的字符串值。
示例:

$(strip a b c )

把字串“a b c ”去到開頭和結尾的空格,結果是“a b c”。

$(findstring <find>,<in> )

名稱:查找字符串函數——findstring。
功能:在字串<in>中查找<find>字串。
返回:如果找到,那么返回<find>,否則返回空字符串。
示例:

$(findstring a,a b c)
$(findstring a,b c)

第一個函數返回“a”字符串,第二個返回“”字符串(空字符串)

$(filter <pattern...>,<text> )

名稱:過濾函數——filter。
功能:以<pattern>模式過濾<text>字符串中的單詞,保留符合模式<pattern>的單詞。可
以有多個模式。
返回:返回符合模式<pattern>的字串。
示例:

sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo

$(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。

$(filter-out <pattern...>,<text> )

名稱:反過濾函數——filter-out。
功能:以<pattern>模式過濾<text>字符串中的單詞,去除符合模式<pattern>的單詞。可
以有多個模式。
返回:返回不符合模式<pattern>的字串。
示例:

objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o

$(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。

$(sort <list> )

名稱:排序函數——sort。
功能:給字符串<list>中的單詞排序(升序)。
返回:返回排序后的字符串。
示例:$(sort foo bar lose)返回“bar foo lose” 。
備注:sort函數會去掉<list>中相同的單詞。

$(word <n>,<text> )

名稱:取單詞函數——word。
功能:取字符串<text>中第<n>個單詞。(從一開始)
返回:返回字符串<text>中第<n>個單詞。如果<n>比<text>中的單詞數要大,那么返回空
字符串。
示例:$(word 2, foo bar baz)返回值是“bar”。

$(wordlist <s>,<e>,<text> )

名稱:取單詞串函數——wordlist。
功能:從字符串<text>中取從<s>開始到<e>的單詞串。<s>和<e>是一個數字。
返回:返回字符串<text>中從<s>到<e>的單詞字串。如果<s>比<text>中的單詞數要大,那
么返回空字符串。如果<e>大于<text>的單詞數,那么返回從<s>開始,到<text>結束的單
詞串。
示例: $(wordlist 2, 3, foo bar baz)返回值是“bar baz”。

$(words <text> )

名稱:單詞個數統計函數——words。
功能:統計<text>中字符串中的單詞個數。
返回:返回<text>中的單詞數。
示例:$(words, foo bar baz)返回值是“3”。
備注:如果我們要取<text>中最后的一個單詞,我們可以這樣:$(word $(words <text>
),<text> )。

$(firstword <text> )

名稱:首單詞函數——firstword。
功能:取字符串<text>中的第一個單詞。
返回:返回字符串<text>的第一個單詞。
示例:$(firstword foo bar)返回值是“foo”。
備注:這個函數可以用word函數來實現:$(word 1,<text> )。

以上,是所有的字符串操作函數,如果搭配混合使用,可以完成比較復雜的功能。這里,
舉一個現實中應用的例子。我們知道,make使用“VPATH”變量來指定“依賴文件”的搜索
路徑。于是,我們可以利用這個搜索路徑來指定編譯器對頭文件的搜索路徑參數CFLAGS,
如:

override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))

如果我們的“$(VPATH)”值是“src:../headers”,那么“$(patsubst %,-I%,$(subst :
, ,$(VPATH)))”將返回“-Isrc -I../headers”,這正是cc或gcc搜索頭文件路徑的參數



三、文件名操作函數

下面我們要介紹的函數主要是處理文件名的。每個函數的參數字符串都會被當做一個或是
一系列的文件名來對待。

$(dir <names...> )

名稱:取目錄函數——dir。
功能:從文件名序列<names>中取出目錄部分。目錄部分是指最后一個反斜杠(“/”)之
前的部分。如果沒有反斜杠,那么返回“./”。
返回:返回文件名序列<names>的目錄部分。
示例: $(dir src/foo.c hacks)返回值是“src/ ./”。

$(notdir <names...> )

名稱:取文件函數——notdir。
功能:從文件名序列<names>中取出非目錄部分。非目錄部分是指最后一個反斜杠(“/”
)之后的部分。
返回:返回文件名序列<names>的非目錄部分。
示例: $(notdir src/foo.c hacks)返回值是“foo.c hacks”。

$(suffix <names...> )

名稱:取后綴函數——suffix。
功能:從文件名序列<names>中取出各個文件名的后綴。
返回:返回文件名序列<names>的后綴序列,如果文件沒有后綴,則返回空字串。
示例:$(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。

$(basename <names...> )

名稱:取前綴函數——basename。
功能:從文件名序列<names>中取出各個文件名的前綴部分。
返回:返回文件名序列<names>的前綴序列,如果文件沒有前綴,則返回空字串。
示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar h
acks”。

$(addsuffix <suffix>,<names...> )

名稱:加后綴函數——addsuffix。
功能:把后綴<suffix>加到<names>中的每個單詞后面。
返回:返回加過后綴的文件名序列。
示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。

$(addprefix <prefix>,<names...> )

名稱:加前綴函數——addprefix。
功能:把前綴<prefix>加到<names>中的每個單詞后面。
返回:返回加過前綴的文件名序列。
示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”。

$(join <list1>,<list2> )

名稱:連接函數——join。
功能:把<list2>中的單詞對應地加到<list1>的單詞后面。如果<list1>的單詞個數要比<
list2>的多,那么,<list1>中的多出來的單詞將保持原樣。如果<list2>的單詞個數要比
<list1>多,那么,<list2>多出來的單詞將被復制到<list2>中。
返回:返回連接過后的字符串。
示例:$(join aaa bbb , 111 222 333)返回值是“aaa111 bbb222 333”。



四、foreach 函數


foreach 函數和別的函數非常的不一樣。因為這個函數是用來做循環用的,Makefile中的
foreach函數幾乎是仿照于Unix標準Shell(/bin /sh)中的for語句,或是C-Shell(/bin
/csh)中的foreach語句而構建的。它的語法是:



$(foreach <var>,<list>,<text> )



這個函數的意思是,把參數<list>中的單詞逐一取出放到參數<var>所指定的變量中,然后
再執行<text>所包含的表達式。每一次<text>會返回一個字符串,循環過程中,<text>的
所返回的每個字符串會以空格分隔,最后當整個循環結束時,<text>所返回的每個字符串
所組成的整個字符串(以空格分隔)將會是foreach函數的返回值。



所以,<var>最好是一個變量名,<list>可以是一個表達式,而<text>中一般會使用<var>
這個參數來依次枚舉<list>中的單詞。舉個例子:



names := a b c d

files := $(foreach n,$(names),$(n).o)



上面的例子中,$(name)中的單詞會被挨個取出,并存到變量“n”中,“$(n).o”每次根
據“$(n)”計算出一個值,這些值以空格分隔,最后作為foreach函數的返回,所以,$(f
iles)的值是“a.o b.o c.o d.o”。



注意,foreach中的<var>參數是一個臨時的局部變量,foreach函數執行完后,參數<var>
的變量將不在作用,其作用域只在foreach函數當中。





五、if 函數


if函數很像GNU的make所支持的條件語句——ifeq(參見前面所述的章節),if函數的語法
是:



$(if <condition>,<then-part> )



或是



$(if <condition>,<then-part>,<else-part> )



可見,if函數可以包含“else”部分,或是不含。即if函數的參數可以是兩個,也可以是
三個。<condition>參數是if的表達式,如果其返回的為非空字符串,那么這個表達式就相
當于返回真,于是,<then-part>會被計算,否則<else-part> 會被計算。



而if函數的返回值是,如果<condition>為真(非空字符串),那個<then- part>會是整個
函數的返回值,如果<condition>為假(空字符串),那么<else-part>會是整個函數的返
回值,此時如果<else-part>沒有被定義,那么,整個函數返回空字串。



所以,<then-part>和<else-part>只會有一個被計算。





六、call函數


call函數是唯一一個可以用來創建新的參數化的函數。你可以寫一個非常復雜的表達式,
這個表達式中,你可以定義許多參數,然后你可以用call函數來向這個表達式傳遞參數。
其語法是:



$(call <expression>,<parm1>,<parm2>,<parm3>...)



當 make執行這個函數時,<expression>參數中的變量,如$(1),$(2),$(3)等,會被參數
<parm1>,<parm2>,<parm3>依次取代。而<expression>的返回值就是 call函數的返回值
。例如:

reverse = $(1) $(2)

foo = $(call reverse,a,b)



那么,foo的值就是“a b”。當然,參數的次序是可以自定義的,不一定是順序的,如:




reverse = $(2) $(1)

foo = $(call reverse,a,b)



此時的foo的值就是“b a”。





七、origin函數
origin函數不像其它的函數,他并不操作變量的值,他只是告訴你你的這個變量是哪里來
的?其語法是:



$(origin <variable> )



注意,<variable>是變量的名字,不應該是引用。所以你最好不要在<variable>中使用“
$”字符。Origin函數會以其返回值來告訴你這個變量的“出生情況”,下面,是origin函
數的返回值:



“undefined”

如果<variable>從來沒有定義過,origin函數返回這個值“undefined”。



“default”

如果<variable>是一個默認的定義,比如“CC”這個變量,這種變量我們將在后面講述。




“environment”

如果<variable>是一個環境變量,并且當Makefile被執行時,“-e”參數沒有被打開。




“file”

如果<variable>這個變量被定義在Makefile中。



“command line”

如果<variable>這個變量是被命令行定義的。



“override”

如果<variable>是被override指示符重新定義的。



“automatic”

如果<variable>是一個命令運行中的自動化變量。關于自動化變量將在后面講述。



這些信息對于我們編寫Makefile是非常有用的,例如,假設我們有一個Makefile其包了一
個定義文件Make.def,在Make.def中定義了一個變量“bletch”,而我們的環境中也有一
個環境變量“bletch”,此時,我們想判斷一下,如果變量來源于環境,那么我們就把之
重定義了,如果來源于Make.def或是命令行等非環境的,那么我們就不重新定義它。于是
,在我們的Makefile中,我們可以這樣寫:



ifdef bletch

ifeq "$(origin bletch)" "environment"

bletch = barf, gag, etc.

endif

endif



當然,你也許會說,使用override關鍵字不就可以重新定義環境中的變量了嗎?為什么需
要使用這樣的步驟?是的,我們用override是可以達到這樣的效果,可是override過于粗
暴,它同時會把從命令行定義的變量也覆蓋了,而我們只想重新定義環境傳來的,而不想
重新定義命令行傳來的。





八、shell函數


shell 函數也不像其它的函數。顧名思義,它的參數應該就是操作系統Shell的命令。它和
反引號“`”是相同的功能。這就是說,shell函數把執行操作系統命令后的輸出作為函數
返回。于是,我們可以用操作系統命令以及字符串處理命令awk,sed等等命令來生成一個
變量,如:



contents := $(shell cat foo)



files := $(shell echo *.c)



注意,這個函數會新生成一個Shell程序來執行命令,所以你要注意其運行性能,如果你的
Makefile中有一些比較復雜的規則,并大量使用了這個函數,那么對于你的系統性能是有
害的。特別是Makefile的隱晦的規則可能會讓你的shell函數執行的次數比你想像的多得多






九、控制make的函數


make提供了一些函數來控制make的運行。通常,你需要檢測一些運行Makefile時的運行時
信息,并且根據這些信息來決定,你是讓make繼續執行,還是停止。



$(error <text ...> )



產生一個致命的錯誤,<text ...>是錯誤信息。注意,error函數不會在一被使用就會產生
錯誤信息,所以如果你把其定義在某個變量中,并在后續的腳本中使用這個變量,那么也
是可以的。例如:



示例一:

ifdef ERROR_001

$(error error is $(ERROR_001))

endif



示例二:

ERR = $(error found an error!)

.PHONY: err

err: ; $(ERR)



示例一會在變量ERROR_001定義了后執行時產生error調用,而示例二則在目錄err被執行時
才發生error調用。



$(warning <text ...> )



這個函數很像error函數,只是它并不會讓make退出,只是輸出一段警告信息,而make繼續
執行。

make 的運行
——————

一般來說,最簡單的就是直接在命令行下輸入make命令,make命令會找當前目錄的makefi
le來執行,一切都是自動的。但也有時你也許只想讓 make重編譯某些文件,而不是整個工
程,而又有的時候你有幾套編譯規則,你想在不同的時候使用不同的編譯規則,等等。本
章節就是講述如何使用make命令的。

一、make的退出碼

make命令執行后有三個退出碼:

0 —— 表示成功執行。
1 —— 如果make運行時出現任何錯誤,其返回1。
2 —— 如果你使用了make的“-q”選項,并且make使得一些目標不需要更新,那么返回2


Make的相關參數我們會在后續章節中講述。


二、指定Makefile

前面我們說過,GNU make找尋默認的Makefile的規則是在當前目錄下依次找三個文件——
“GNUmakefile”、“makefile”和“Makefile”。其按順序找這三個文件,一旦找到,就
開始讀取這個文件并執行。

當前,我們也可以給make命令指定一個特殊名字的Makefile。要達到這個功能,我們要使
用make的“-f”或是“--file”參數(“-- makefile”參數也行)。例如,我們有個mak
efile的名字是“hchen.mk”,那么,我們可以這樣來讓make來執行這個文件:

make –f hchen.mk

如果在make的命令行是,你不只一次地使用了“-f”參數,那么,所有指定的makefile將
會被連在一起傳遞給make執行。


三、指定目標

一般來說,make的最終目標是makefile中的第一個目標,而其它目標一般是由這個目標連
帶出來的。這是make的默認行為。當然,一般來說,你的 makefile中的第一個目標是由許
多個目標組成,你可以指示make,讓其完成你所指定的目標。要達到這一目的很簡單,需
在make命令后直接跟目標的名字就可以完成(如前面提到的“make clean”形式)

任何在makefile中的目標都可以被指定成終極目標,但是除了以“- ”打頭,或是包含了
“=”的目標,因為有這些字符的目標,會被解析成命令行參數或是變量。甚至沒有被我們
明確寫出來的目標也可以成為make的終極目標,也就是說,只要make可以找到其隱含規則
推導規則,那么這個隱含目標同樣可以被指定成終極目標。

有一個make的環境變量叫“MAKECMDGOALS”,這個變量中會存放你所指定的終極目標的列
表,如果在命令行上,你沒有指定目標,那么,這個變量是空值。這個變量可以讓你使用
在一些比較特殊的情形下。比如下面的例子:

sources = foo.c bar.c
ifneq ( $(MAKECMDGOALS),clean)
include $(sources:.c=.d)
endif

基于上面的這個例子,只要我們輸入的命令不是“make clean”,那么makefile會自動包
含“foo.d”和“bar.d”這兩個makefile。

使用指定終極目標的方法可以很方便地讓我們編譯我們的程序,例如下面這個例子:

.PHONY: all
all: prog1 prog2 prog3 prog4

從這個例子中,我們可以看到,這個makefile中有四個需要編譯的程序——“prog1”,
“prog2”, “prog3”和 “prog4”,我們可以使用“make all”命令來編譯所有的目標
(如果把all置成第一個目標,那么只需執行“make”),我們也可以使用“make prog2”
來單獨編譯目標“prog2”。

即然make可以指定所有makefile中的目標,那么也包括“偽目標”,于是我們可以根據這
種性質來讓我們的makefile根據指定的不同的目標來完成不同的事。在Unix世界中,軟件
發布時,特別是GNU這種開源軟件的發布時,其 makefile都包含了編譯、安裝、打包等功
能。我們可以參照這種規則來書寫我們的makefile中的目標。

“all”
這個偽目標是所有目標的目標,其功能一般是編譯所有的目標。
“clean”
這個偽目標功能是刪除所有被make創建的文件。
“install”
這個偽目標功能是安裝已編譯好的程序,其實就是把目標執行文件拷貝到指定的目標中去

“print”
這個偽目標的功能是例出改變過的源文件。
“tar”
這個偽目標功能是把源程序打包備份。也就是一個tar文件。
“dist”
這個偽目標功能是創建一個壓縮文件,一般是把tar文件壓成Z文件。或是gz文件。
“TAGS”
這個偽目標功能是更新所有的目標,以備完整地重編譯使用。
“check”和“test”
這兩個偽目標一般用來測試makefile的流程。

當然一個項目的makefile中也不一定要書寫這樣的目標,這些東西都是GNU的東西,但是我
想,GNU搞出這些東西一定有其可取之處(等你的UNIX下的程序文件一多時你就會發現這些
功能很有用了),這里只不過是說明了,如果你要書寫這種功能,最好使用這種名字命名
你的目標,這樣規范一些,規范的好處就是——不用解釋,大家都明白。而且如果你的ma
kefile中有這些功能,一是很實用,二是可以顯得你的makefile很專業(不是那種初學者
的作品)。


四、檢查規則

有時候,我們不想讓我們的makefile中的規則執行起來,我們只想檢查一下我們的命令,
或是執行的序列。于是我們可以使用make命令的下述參數:

“-n”
“--just-print”
“--dry-run”
“--recon”
不執行參數,這些參數只是打印命令,不管目標是否更新,把規則和連帶規則下的命令打
印出來,但不執行,這些參數對于我們調試makefile很有用處。

“-t”
“--touch”
這個參數的意思就是把目標文件的時間更新,但不更改目標文件。也就是說,make假裝編
譯目標,但不是真正的編譯目標,只是把目標變成已編譯過的狀態。

“-q”
“--question”
這個參數的行為是找目標的意思,也就是說,如果目標存在,那么其什么也不會輸出,當
然也不會執行編譯,如果目標不存在,其會打印出一條出錯信息。

“-W <file>”
“--what-if=<file>”
“--assume-new=<file>”
“--new-file=<file>”
這個參數需要指定一個文件。一般是是源文件(或依賴文件),Make會根據規則推導來運
行依賴于這個文件的命令,一般來說,可以和“-n”參數一同使用,來查看這個依賴文件
所發生的規則命令。

另外一個很有意思的用法是結合“-p”和“-v”來輸出makefile被執行時的信息(這個將
在后面講述)。


五、make的參數

下面列舉了所有GNU make 3.80版的參數定義。其它版本和產商的make大同小異,不過其它
產商的make的具體參數還是請參考各自的產品文檔。

“-b”
“-m”
這兩個參數的作用是忽略和其它版本make的兼容性。

“-B”
“--always-make”
認為所有的目標都需要更新(重編譯)。

“-C <dir>”
“--directory=<dir>”
指定讀取makefile的目錄。如果有多個“-C”參數,make的解釋是后面的路徑以前面的作
為相對路徑,并以最后的目錄作為被指定目錄。如:“make –C ~hchen/test –C prog”
等價于“make –C ~hchen/test/prog”。

“—debug[=<options>]”
輸出make的調試信息。它有幾種不同的級別可供選擇,如果沒有參數,那就是輸出最簡單
的調試信息。下面是<options>的取值:
a —— 也就是all,輸出所有的調試信息。(會非常的多)
b —— 也就是basic,只輸出簡單的調試信息。即輸出不需要重編譯的目標。
v —— 也就是verbose,在b選項的級別之上。輸出的信息包括哪個makefile被解析,不需
要被重編譯的依賴文件(或是依賴目標)等。
i —— 也就是implicit,輸出所以的隱含規則。
j —— 也就是jobs,輸出執行規則中命令的詳細信息,如命令的PID、返回碼等。
m —— 也就是makefile,輸出make讀取makefile,更新makefile,執行makefile的信息。


“-d”
相當于“--debug=a”。

“-e”
“--environment-overrides”
指明環境變量的值覆蓋makefile中定義的變量的值。

“-f=<file>”
“--file=<file>”
“--makefile=<file>”
指定需要執行的makefile。

“-h”
“--help”
顯示幫助信息。

“-i”
“--ignore-errors”
在執行時忽略所有的錯誤。

“-I <dir>”
“--include-dir=<dir>”
指定一個被包含makefile的搜索目標。可以使用多個“-I”參數來指定多個目錄。

“-j [<jobsnum>]”
“--jobs[=<jobsnum>]”
指同時運行命令的個數。如果沒有這個參數,make運行命令時能運行多少就運行多少。如
果有一個以上的“-j”參數,那么僅最后一個“-j”才是有效的。(注意這個參數在MS-D
OS中是無用的)

“-k”
“--keep-going”
出錯也不停止運行。如果生成一個目標失敗了,那么依賴于其上的目標就不會被執行了。


“-l <load>”
“--load-average[=<load]”
“—max-load[=<load>]”
指定make運行命令的負載。

“-n”
“--just-print”
“--dry-run”
“--recon”
僅輸出執行過程中的命令序列,但并不執行。

“-o <file>”
“--old-file=<file>”
“--assume-old=<file>”
不重新生成的指定的<file>,即使這個目標的依賴文件新于它。

“-p”
“--print-data-base”
輸出makefile中的所有數據,包括所有的規則和變量。這個參數會讓一個簡單的makefile
都會輸出一堆信息。如果你只是想輸出信息而不想執行 makefile,你可以使用“make -q
p”命令。如果你想查看執行makefile前的預設變量和規則,你可以使用“make –p –f
/dev/null”。這個參數輸出的信息會包含著你的makefile文件的文件名和行號,所以,用
這個參數來調試你的makefile會是很有用的,特別是當你的環境變量很復雜的時候。

“-q”
“--question”
不運行命令,也不輸出。僅僅是檢查所指定的目標是否需要更新。如果是0則說明要更新,
如果是2則說明有錯誤發生。

“-r”
“--no-builtin-rules”
禁止make使用任何隱含規則。

“-R”
“--no-builtin-variabes”
禁止make使用任何作用于變量上的隱含規則。

“-s”
“--silent”
“--quiet”
在命令運行時不輸出命令的輸出。

“-S”
“--no-keep-going”
“--stop”
取消“-k”選項的作用。因為有些時候,make的選項是從環境變量“MAKEFLAGS”中繼承下
來的。所以你可以在命令行中使用這個參數來讓環境變量中的“-k”選項失效。

“-t”
“--touch”
相當于UNIX的touch命令,只是把目標的修改日期變成最新的,也就是阻止生成目標的命令
運行。

“-v”
“--version”
輸出make程序的版本、版權等關于make的信息。

“-w”
“--print-directory”
輸出運行makefile之前和之后的信息。這個參數對于跟蹤嵌套式調用make時很有用。

“--no-print-directory”
禁止“-w”選項。

“-W <file>”
“--what-if=<file>”
“--new-file=<file>”
“--assume-file=<file>”
假定目標<file>需要更新,如果和“-n”選項使用,那么這個參數會輸出該目標更新時的
運行動作。如果沒有“-n”那么就像運行UNIX的“touch”命令一樣,使得<file>的修改時
間為當前時間。

“--warn-undefined-variables”
只要make發現有未定義的變量,那么就輸出警告信息。

隱含規則
————

在我們使用Makefile時,有一些我們會經常使用,而且使用頻率非常高的東西,比如,我
們編譯C/C++的源程序為中間目標文件(Unix下是[.o] 文件,Windows下是[.obj]文件)。
本章講述的就是一些在Makefile中的“隱含的”,早先約定了的,不需要我們再寫出來的
規則。

“隱含規則”也就是一種慣例,make會按照這種“慣例”心照不喧地來運行,那怕我們的
Makefile中沒有書寫這樣的規則。例如,把[.c]文件編譯成[.o]文件這一規則,你根本就
不用寫出來,make會自動推導出這種規則,并生成我們需要的[.o]文件。

“隱含規則”會使用一些我們系統變量,我們可以改變這些系統變量的值來定制隱含規則
的運行時的參數。如系統變量“CFLAGS”可以控制編譯時的編譯器參數。

我們還可以通過“模式規則”的方式寫下自己的隱含規則。用“后綴規則”來定義隱含規
則會有許多的限制。使用“模式規則”會更回得智能和清楚,但“后綴規則”可以用來保
證我們Makefile的兼容性。
我們了解了“隱含規則”,可以讓其為我們更好的服務,也會讓我們知道一些“約定俗成
”了的東西,而不至于使得我們在運行Makefile時出現一些我們覺得莫名其妙的東西。當
然,任何事物都是矛盾的,水能載舟,亦可覆舟,所以,有時候“隱含規則”也會給我們
造成不小的麻煩。只有了解了它,我們才能更好地使用它。


一、使用隱含規則

如果要使用隱含規則生成你需要的目標,你所需要做的就是不要寫出這個目標的規則。那
么,make會試圖去自動推導產生這個目標的規則和命令,如果make可以自動推導生成這個
目標的規則和命令,那么這個行為就是隱含規則的自動推導。當然,隱含規則是make事先
約定好的一些東西。例如,我們有下面的一個Makefile:

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

我們可以注意到,這個Makefile中并沒有寫下如何生成foo.o和bar.o這兩目標的規則和命
令。因為make的“隱含規則”功能會自動為我們自動去推導這兩個目標的依賴目標和生成
命令。

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)

因為,這已經是“約定”好了的事了,make和我們約定好了用C編譯器“cc”生成[.o]文件
的規則,這就是隱含規則。

當然,如果我們為[.o]文件書寫了自己的規則,那么make就不會自動推導并調用隱含規則
,它會按照我們寫好的規則忠實地執行。

還有,在make的“隱含規則庫”中,每一條隱含規則都在庫中有其順序,越靠前的則是越
被經常使用的,所以,這會導致我們有些時候即使我們顯示地指定了目標依賴,make也不
會管。如下面這條規則(沒有命令):

foo.o : foo.p

依賴文件“foo.p”(Pascal程序的源文件)有可能變得沒有意義。如果目錄下存在了“f
oo.c”文件,那么我們的隱含規則一樣會生效,并會通過 “foo.c”調用C的編譯器生成f
oo.o文件。因為,在隱含規則中,Pascal的規則出現在C的規則之后,所以,make找到可以
生成foo.o的 C的規則就不再尋找下一條規則了。如果你確實不希望任何隱含規則推導,那
么,你就不要只寫出“依賴規則”,而不寫命令。


二、隱含規則一覽

這里我們將講述所有預先設置(也就是make內建)的隱含規則,如果我們不明確地寫下規
則,那么,make就會在這些規則中尋找所需要規則和命令。當然,我們也可以使用make的
參數“-r”或“--no-builtin-rules”選項來取消所有的預設置的隱含規則。

當然,即使是我們指定了“-r”參數,某些隱含規則還是會生效,因為有許多的隱含規則
都是使用了“后綴規則”來定義的,所以,只要隱含規則中有“后綴列表 ”(也就一系統
定義在目標.SUFFIXES的依賴目標),那么隱含規則就會生效。默認的后綴列表是:.out,
.a, .ln, .o, .c, .cc, .C, .p, .f, .F, .r, .y, .l, .s, .S, .mod, .sym, .def, .
h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el。
具體的細節,我們會在后面講述。

還是先來看一看常用的隱含規則吧。

1、編譯C程序的隱含規則。
“<n>.o”的目標的依賴目標會自動推導為“<n>.c”,并且其生成命令是“$(CC) –c $(
CPPFLAGS) $(CFLAGS)”

2、編譯C++程序的隱含規則。
“<n>.o” 的目標的依賴目標會自動推導為“<n>.cc”或是“<n>.C”,并且其生成命令是
“$(CXX) –c $(CPPFLAGS) $(CFLAGS)”。(建議使用“.cc”作為C++源文件的后綴,而
不是“.C”)

3、編譯Pascal程序的隱含規則。
“<n>.o”的目標的依賴目標會自動推導為“<n>.p”,并且其生成命令是“$(PC) –c $(
PFLAGS)”。

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

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

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

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

8、鏈接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)相關聯,那么,
你最好寫出自己的生成規則,不然,隱含規則會報錯的。

9、Yacc C程序時的隱含規則。
“<n>.c”的依賴文件被自動推導為“n.y”(Yacc生成的文件),其生成命令是:“$(YA
CC) $(YFALGS)”。(“Yacc”是一個語法分析器,關于其細節請查看相關資料)

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

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

12、從C程序、Yacc文件或Lex文件創建Lint庫的隱含規則。
“<n>.ln” (lint生成的文件)的依賴文件被自動推導為“n.c”,其生成命令是:“$(
LINT) $(LINTFALGS) $(CPPFLAGS) -i”。對于“<n>.y”和“<n>.l”也是同樣的規則。



三、隱含規則使用的變量

在隱含規則中的命令中,基本上都是使用了一些預先設置的變量。你可以在你的makefile
中改變這些變量的值,或是在make的命令行中傳入這些值,或是在你的環境變量中設置這
些值,無論怎么樣,只要設置了這些特定的變量,那么其就會對隱含規則起作用。當然,
你也可以利用make的“-R”或“--no– builtin-variables”參數來取消你所定義的變量
對隱含規則的作用。

例如,第一條隱含規則——編譯C程序的隱含規則的命令是“$(CC) –c $(CFLAGS) $(CPP
FLAGS)”。Make默認的編譯命令是“cc”,如果你把變量“$(CC)”重定義成“gcc”,把
變量“$(CFLAGS)”重定義成 “-g”,那么,隱含規則中的命令全部會以“gcc –c -g $
(CPPFLAGS)”的樣子來執行了。

我們可以把隱含規則中使用的變量分成兩種:一種是命令相關的,如“CC”;一種是參數
相的關,如“CFLAGS”。下面是所有隱含規則中會用到的變量:

1、關于命令的變量。

AR
函數庫打包程序。默認命令是“ar”。
AS
匯編語言編譯程序。默認命令是“as”。
CC
C語言編譯程序。默認命令是“cc”。
CXX
C++語言編譯程序。默認命令是“g++”。
CO
從 RCS文件中擴展文件程序。默認命令是“co”。
CPP
C程序的預處理器(輸出是標準輸出設備)。默認命令是“$(CC) –E”。
FC
Fortran 和 Ratfor 的編譯器和預處理程序。默認命令是“f77”。
GET
從SCCS文件中擴展文件的程序。默認命令是“get”。
LEX
Lex方法分析器程序(針對于C或Ratfor)。默認命令是“lex”。
PC
Pascal語言編譯程序。默認命令是“pc”。
YACC
Yacc文法分析器(針對于C程序)。默認命令是“yacc”。
YACCR
Yacc文法分析器(針對于Ratfor程序)。默認命令是“yacc –r”。
MAKEINFO
轉換Texinfo源文件(.texi)到Info文件程序。默認命令是“makeinfo”。
TEX
從TeX源文件創建TeX DVI文件的程序。默認命令是“tex”。
TEXI2DVI
從Texinfo源文件創建軍TeX DVI 文件的程序。默認命令是“texi2dvi”。
WEAVE
轉換Web到TeX的程序。默認命令是“weave”。
CWEAVE
轉換C Web 到 TeX的程序。默認命令是“cweave”。
TANGLE
轉換Web到Pascal語言的程序。默認命令是“tangle”。
CTANGLE
轉換C Web 到 C。默認命令是“ctangle”。
RM
刪除文件命令。默認命令是“rm –f”。

2、關于命令參數的變量

下面的這些變量都是相關上面的命令的參數。如果沒有指明其默認值,那么其默認值都是
空。

ARFLAGS
函數庫打包程序AR命令的參數。默認值是“rv”。
ASFLAGS
匯編語言編譯器參數。(當明顯地調用“.s”或“.S”文件時)。
CFLAGS
C語言編譯器參數。
CXXFLAGS
C++語言編譯器參數。
COFLAGS
RCS命令參數。
CPPFLAGS
C預處理器參數。( C 和 Fortran 編譯器也會用到)。
FFLAGS
Fortran語言編譯器參數。
GFLAGS
SCCS “get”程序參數。
LDFLAGS
鏈接器參數。(如:“ld”)
LFLAGS
Lex文法分析器參數。
PFLAGS
Pascal語言編譯器參數。
RFLAGS
Ratfor 程序的Fortran 編譯器參數。
YFLAGS
Yacc文法分析器參數。


四、隱含規則鏈

有些時候,一個目標可能被一系列的隱含規則所作用。例如,一個[.o]的文件生成,可能
會是先被Yacc的[.y]文件先成[.c],然后再被C的編譯器生成。我們把這一系列的隱含規則
叫做“隱含規則鏈”。

在上面的例子中,如果文件[.c]存在,那么就直接調用C的編譯器的隱含規則,如果沒有[
.c]文件,但有一個[.y]文件,那么Yacc的隱含規則會被調用,生成[.c]文件,然后,再調
用C編譯的隱含規則最終由[.c]生成[.o]文件,達到目標。

我們把這種[.c]的文件(或是目標),叫做中間目標。不管怎么樣,make會努力自動推導
生成目標的一切方法,不管中間目標有多少,其都會執著地把所有的隱含規則和你書寫的
規則全部合起來分析,努力達到目標,所以,有些時候,可能會讓你覺得奇怪,怎么我的
目標會這樣生成?怎么我的makefile發瘋了?

在默認情況下,對于中間目標,它和一般的目標有兩個地方所不同:第一個不同是除非中
間的目標不存在,才會引發中間規則。第二個不同的是,只要目標成功產生,那么,產生
最終目標過程中,所產生的中間目標文件會被以“rm -f”刪除。

通常,一個被makefile指定成目標或是依賴目標的文件不能被當作中介。然而,你可以明
顯地說明一個文件或是目標是中介目標,你可以使用偽目標“.INTERMEDIATE”來強制聲明
。(如:.INTERMEDIATE : mid )

你也可以阻止make自動刪除中間目標,要做到這一點,你可以使用偽目標“.SECONDARY”
來強制聲明(如:.SECONDARY : sec)。你還可以把你的目標,以模式的方式來指定(如
:%.o)成偽目標“.PRECIOUS”的依賴目標,以保存被隱含規則所生成的中間文件。

在“隱含規則鏈”中,禁止同一個目標出現兩次或兩次以上,這樣一來,就可防止在make
自動推導時出現無限遞歸的情況。

Make 會優化一些特殊的隱含規則,而不生成中間文件。如,從文件“foo.c”生成目標程
序“foo”,按道理,make會編譯生成中間文件“foo.o”,然后鏈接成“foo”,但在實際
情況下,這一動作可以被一條“cc”的命令完成(cc –o foo foo.c),于是優化過的規
則就不會生成中間文件。



五、定義模式規則

你可以使用模式規則來定義一個隱含規則。一個模式規則就好像一個一般的規則,只是在
規則中,目標的定義需要有"%"字符。"%"的意思是表示一個或多個任意字符。在依賴目標
中同樣可以使用"%",只是依賴目標中的"%"的取值,取決于其目標。

有一點需要注意的是,"%"的展開發生在變量和函數的展開之后,變量和函數的展開發生在
make載入Makefile時,而模式規則中的"%"則發生在運行時。


1、模式規則介紹

模式規則中,至少在規則的目標定義中要包含"%",否則,就是一般的規則。目標中的"%"
定義表示對文件名的匹配,"%"表示長度任意的非空字符串。例如:"%.c"表示以".c"結尾
的文件名(文件名的長度至少為3),而"s.%.c"則表示以"s."開頭,".c"結尾的文件名(
文件名的長度至少為 5)。

如果"%"定義在目標中,那么,目標中的"%"的值決定了依賴目標中的"%"的值,也就是說,
目標中的模式的"%"決定了依賴目標中"%"的樣子。例如有一個模式規則如下:

%.o : %.c ; <command ......>

其含義是,指出了怎么從所有的[.c]文件生成相應的[.o]文件的規則。如果要生成的目標
是"a.o b.o",那么"%c"就是"a.c b.c"。

一旦依賴目標中的"%"模式被確定,那么,make會被要求去匹配當前目錄下所有的文件名,
一旦找到,make就會規則下的命令,所以,在模式規則中,目標可能會是多個的,如果有
模式匹配出多個目標,make就會產生所有的模式目標,此時,make關心的是依賴的文件名
和生成目標的命令這兩件事。


2、模式規則示例

下面這個例子表示了,把所有的[.c]文件都編譯成[.o]文件.

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

其中,"$@"表示所有的目標的挨個值,"$<"表示了所有依賴目標的挨個值。這些奇怪的變
量我們叫"自動化變量",后面會詳細講述。

下面的這個例子中有兩個目標是模式的:

%.tab.c %.tab.h: %.y
bison -d $<

這條規則告訴make把所有的[.y]文件都以"bison -d <n>.y"執行,然后生成"<n>.tab.c"和
"<n>.tab.h"文件。(其中,"<n>" 表示一個任意字符串)。如果我們的執行程序"foo"依
賴于文件"parse.tab.o"和"scan.o",并且文件"scan.o"依賴于文件"parse.tab.h",如果
"parse.y"文件被更新了,那么根據上述的規則,"bison -d parse.y"就會被執行一次,于
是,"parse.tab.o"和"scan.o"的依賴文件就齊了。(假設,"parse.tab.o" 由"parse.ta
b.c"生成,和"scan.o"由"scan.c"生成,而"foo"由"parse.tab.o"和"scan.o"鏈接生成,
而且foo和其[.o]文件的依賴關系也寫好,那么,所有的目標都會得到滿足)


3、自動化變量

在上述的模式規則中,目標和依賴文件都是一系例的文件,那么我們如何書寫一個命令來
完成從不同的依賴文件生成相應的目標?因為在每一次的對模式規則的解析時,都會是不
同的目標和依賴文件。

自動化變量就是完成這個功能的。在前面,我們已經對自動化變量有所提涉,相信你看到
這里已對它有一個感性認識了。所謂自動化變量,就是這種變量會把模式中所定義的一系
列的文件自動地挨個取出,直至所有的符合模式的文件都取完了。這種自動化變量只應出
現在規則的命令中。

下面是所有的自動化變量及其說明:

$@
表示規則中的目標文件集。在模式規則中,如果有多個目標,那么,"$@"就是匹配于目標
中模式定義的集合。

$%
僅當目標是函數庫文件中,表示規則中的目標成員名。例如,如果一個目標是"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"。這個特性是GNU make的
,很有可能不兼容于其它版本的make,所以,你應該盡量避免使用"$*",除非是在隱含規
則或是靜態模式中。如果目標中的后綴是make所不能識別的,那么"$*"就是空值。

當你希望只對更新過的依賴文件進行操作時,"$?"在顯式規則中很有用,例如,假設有一
個函數庫文件叫"lib",其由其它幾個object文件更新。那么把object文件打包的比較有效
率的Makefile規則是:

lib : foo.o bar.o lose.o win.o
ar r lib $?

在上述所列出來的自動量變量中。四個變量($@、$<、$%、$*)在擴展時只會有一個文件
,而另三個的值是一個文件列表。這七個自動化變量還可以取得文件的目錄名或是在當前
目錄下的符合模式的文件名,只需要搭配上"D"或"F"字樣。這是GNU make中老版本的特性
,在新版本中,我們使用函數"dir"或"notdir"就可以做到了。"D"的含義就是Directory,
就是目錄,"F"的含義就是File,就是文件。

下面是對于上面的七個變量分別加上"D"或是"F"的含義:

$(@D)
表示"$@"的目錄部分(不以斜杠作為結尾),如果"$@"值是"dir/foo.o",那么"$(@D)"就
是"dir",而如果"$@"中沒有包含斜杠的話,其值就是"."(當前目錄)。

$(@F)
表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那么"$(@F)"就是"foo.o","$(@F)"相
當于函數"$(notdir $@)"。

"$(*D)"
"$(*F)"
和上面所述的同理,也是取文件的目錄部分和文件部分。對于上面的那個例子,"$(*D)"返
回"dir",而"$(*F)"返回"foo"

"$(%D)"
"$(%F)"
分別表示了函數包文件成員的目錄部分和文件部分。這對于形同"archive(member)"形式的
目標中的"member"中包含了不同的目錄很有用。

"$(<D)"
"$(<F)"
分別表示依賴文件的目錄部分和文件部分。

"$(^D)"
"$(^F)"
分別表示所有依賴文件的目錄部分和文件部分。(無相同的)

"$(+D)"
"$(+F)"
分別表示所有依賴文件的目錄部分和文件部分。(可以有相同的)

"$(?D)"
"$(?F)"

分別表示被更新的依賴文件的目錄部分和文件部分。

最后想提醒一下的是,對于"$<",為了避免產生不必要的麻煩,我們最好給$后面的那個特
定字符都加上圓括號,比如,"$(< )"就要比"$<"要好一些。

還得要注意的是,這些變量只使用在規則的命令中,而且一般都是"顯式規則"和"靜態模式
規則"(參見前面"書寫規則"一章)。其在隱含規則中并沒有意義。

4、模式的匹配

一般來說,一個目標的模式有一個有前綴或是后綴的"%",或是沒有前后綴,直接就是一個
"%"。因為"%"代表一個或多個字符,所以在定義好了的模式中,我們把"%"所匹配的內容叫
做"莖",例如"%.c"所匹配的文件"test.c"中"test"就是"莖"。因為在目標和依賴目標中同
時有"%"時,依賴目標的"莖"會傳給目標,當做目標中的"莖"。

當一個模式匹配包含有斜杠(實際也不經常包含)的文件時,那么在進行模式匹配時,目
錄部分會首先被移開,然后進行匹配,成功后,再把目錄加回去。在進行"莖"的傳遞時,
我們需要知道這個步驟。例如有一個模式"e%t",文件"src/eat" 匹配于該模式,于是"sr
c/a"就是其"莖",如果這個模式定義在依賴目標中,而被依賴于這個模式的目標中又有個
模式"c%r",那么,目標就是"src/car"。("莖"被傳遞)


5、重載內建隱含規則

你可以重載內建的隱含規則(或是定義一個全新的),例如你可以重新構造和內建隱含規
則不同的命令,如:

%.o : %.c
$(CC) -c $(CPPFLAGS) $(CFLAGS) -D$(date)

你可以取消內建的隱含規則,只要不在后面寫命令就行。如:

%.o : %.s

同樣,你也可以重新定義一個全新的隱含規則,其在隱含規則中的位置取決于你在哪里寫
下這個規則。朝前的位置就靠前。


六、老式風格的"后綴規則"

后綴規則是一個比較老式的定義隱含規則的方法。后綴規則會被模式規則逐步地取代。因
為模式規則更強更清晰。為了和老版本的Makefile兼容,GNU make同樣兼容于這些東西。
后綴規則有兩種方式:"雙后綴"和"單后綴"。

雙后綴規則定義了一對后綴:目標文件的后綴和依賴目標(源文件)的后綴。如".c.o"相
當于"%o : %c"。單后綴規則只定義一個后綴,也就是源文件的后綴。如".c"相當于"% :
%.c"。

后綴規則中所定義的后綴應該是make所認識的,如果一個后綴是make所認識的,那么這個
規則就是單后綴規則,而如果兩個連在一起的后綴都被make所認識,那就是雙后綴規則。
例如:".c"和".o"都是make所知道。因而,如果你定義了一個規則是".c.o"那么其就是雙
后綴規則,意義就是".c" 是源文件的后綴,".o"是目標文件的后綴。如下示例:

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

后綴規則不允許任何的依賴文件,如果有依賴文件的話,那就不是后綴規則,那些后綴統
統被認為是文件名,如:

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

這個例子,就是說,文件".c.o"依賴于文件"foo.h",而不是我們想要的這樣:

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

后綴規則中,如果沒有命令,那是毫無意義的。因為他也不會移去內建的隱含規則。

而要讓make知道一些特定的后綴,我們可以使用偽目標".SUFFIXES"來定義或是刪除,如:


.SUFFIXES: .hack .win

把后綴.hack和.win加入后綴列表中的末尾。

.SUFFIXES: # 刪除默認的后綴
.SUFFIXES: .c .o .h # 定義自己的后綴

先清楚默認后綴,后定義自己的后綴列表。

make的參數"-r"或"-no-builtin-rules"也會使用得默認的后綴列表為空。而變量"SUFFIX
E"被用來定義默認的后綴列表,你可以用".SUFFIXES"來改變后綴列表,但請不要改變變量
"SUFFIXE"的值。


七、隱含規則搜索算法

比如我們有一個目標叫 T。下面是搜索目標T的規則的算法。請注意,在下面,我們沒有提
到后綴規則,原因是,所有的后綴規則在Makefile被載入內存時,會被轉換成模式規則。
如果目標是"archive(member)"的函數庫文件模式,那么這個算法會被運行兩次,第一次是
找目標T,如果沒有找到的話,那么進入第二次,第二次會把"member"當作T來搜索。

1、把T的目錄部分分離出來。叫D,而剩余部分叫N。(如:如果T是"src/foo.o",那么,
D就是"src/",N就是"foo.o")

2、創建所有匹配于T或是N的模式規則列表。

3、如果在模式規則列表中有匹配所有文件的模式,如"%",那么從列表中移除其它的模式


4、移除列表中沒有命令的規則。

5、對于第一個在列表中的模式規則:
1)推導其"莖"S,S應該是T或是N匹配于模式中"%"非空的部分。
2)計算依賴文件。把依賴文件中的"%"都替換成"莖"S。如果目標模式中沒有包含斜框字符
,而把D加在第一個依賴文件的開頭。
3)測試是否所有的依賴文件都存在或是理當存在。(如果有一個文件被定義成另外一個規
則的目標文件,或者是一個顯式規則的依賴文件,那么這個文件就叫"理當存在")
4)如果所有的依賴文件存在或是理當存在,或是就沒有依賴文件。那么這條規則將被采用
,退出該算法。

6、如果經過第5步,沒有模式規則被找到,那么就做更進一步的搜索。對于存在于列表中
的第一個模式規則:
1)如果規則是終止規則,那就忽略它,繼續下一條模式規則。
2)計算依賴文件。(同第5步)
3)測試所有的依賴文件是否存在或是理當存在。
4)對于不存在的依賴文件,遞歸調用這個算法查找他是否可以被隱含規則找到。
5)如果所有的依賴文件存在或是理當存在,或是就根本沒有依賴文件。那么這條規則被采
用,退出該算法。

7、如果沒有隱含規則可以使用,查看".DEFAULT"規則,如果有,采用,把".DEFAULT"的命
令給T使用。

一旦規則被找到,就會執行其相當的命令,而此時,我們的自動化變量的值才會生成。



使用make更新函數庫文件
———————————

函數庫文件也就是對Object文件(程序編譯的中間文件)的打包文件。在Unix下,一般是
由命令"ar"來完成打包工作。

一、函數庫文件的成員

一個函數庫文件由多個文件組成。你可以以如下格式指定函數庫文件及其組成:

archive(member)

這個不是一個命令,而一個目標和依賴的定義。一般來說,這種用法基本上就是為了"ar"
命令來服務的。如:

foolib(hack.o) : hack.o
ar cr foolib hack.o

如果要指定多個member,那就以空格分開,如:

foolib(hack.o kludge.o)

其等價于:

foolib(hack.o) foolib(kludge.o)

你還可以使用Shell的文件通配符來定義,如:

foolib(*.o)


二、函數庫成員的隱含規則

當 make搜索一個目標的隱含規則時,一個特殊的特性是,如果這個目標是"a(m)"形式的,
其會把目標變成"(m)"。于是,如果我們的成員是"%.o" 的模式定義,并且如果我們使用"
make foo.a(bar.o)"的形式調用Makefile時,隱含規則會去找"bar.o"的規則,如果沒有定
義bar.o的規則,那么內建隱含規則生效,make會去找bar.c文件來生成bar.o,如果找得到
的話,make執行的命令大致如下:

cc -c bar.c -o bar.o
ar r foo.a bar.o
rm -f bar.o

還有一個變量要注意的是"$%",這是專屬函數庫文件的自動化變量,有關其說明請參見"自
動化變量"一節。


三、函數庫文件的后綴規則

你可以使用"后綴規則"和"隱含規則"來生成函數庫打包文件,如:

.c.a:
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
$(AR) r $@ $*.o
$(RM) $*.o

其等效于:

(%.o) : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
$(AR) r $@ $*.o
$(RM) $*.o


四、注意事項

在進行函數庫打包文件生成時,請小心使用make的并行機制("-j"參數)。如果多個ar命
令在同一時間運行在同一個函數庫打包文件上,就很有可以損壞這個函數庫文件。所以,
在make未來的版本中,應該提供一種機制來避免并行操作發生在函數打包文件上。

但就目前而言,你還是應該不要盡量不要使用"-j"參數。



后序
——

終于到寫結束語的時候了,以上基本上就是GNU make的Makefile的所有細節了。其它的產
商的make基本上也就是這樣的,無論什么樣的make,都是以文件的依賴性為基礎的,其基
本是都是遵循一個標準的。這篇文檔中80%的技術細節都適用于任何的make,我猜測"函數
"那一章的內容可能不是其它make所支持的,而隱含規則方面,我想不同的make會有不同的
實現,我沒有精力來查看GNU的make和VC的nmake、BCB的make,或是別的UNIX下的make有些
什么樣的差別,一是時間精力不夠,二是因為我基本上都是在Unix下使用make,以前在SC
O Unix和IBM的AIX,現在在Linux、Solaris、HP-UX、AIX和Alpha下使用,Linux和Solari
s下更多一點。不過,我可以肯定的是,在Unix下的make,無論是哪種平臺,幾乎都使用了
Richard Stallman開發的make和cc/gcc的編譯器,而且,基本上都是GNU的make(公司里所
有的UNIX機器上都被裝上了GNU的東西,所以,使用GNU的程序也就多了一些)。GNU的東西
還是很不錯的,特別是使用得深了以后,越來越覺得GNU的軟件的強大,也越來越覺得GNU
的在操作系統中(主要是Unix,甚至Windows)"殺傷力"。

對于上述所有的make的細節,我們不但可以利用make這個工具來編譯我們的程序,還可以
利用make來完成其它的工作,因為規則中的命令可以是任何Shell之下的命令,所以,在U
nix下,你不一定只是使用程序語言的編譯器,你還可以在Makefile中書寫其它的命令,如
:tar、awk、mail、sed、cvs、compress、ls、rm、yacc、rpm、 ftp……等等,等等,來
完成諸如"程序打包"、"程序備份"、"制作程序安裝包"、"提交代碼"、"使用程序模板"、
"合并文件"等等五花八門的功能,文件操作,文件管理,編程開發設計,或是其它一些異
想天開的東西。比如,以前在書寫銀行交易程序時,由于銀行的交易程序基本一樣,就見
到有人書寫了一些交易的通用程序模板,在該模板中把一些網絡通訊、數據庫操作的、業
務操作共性的東西寫在一個文件中,在這些文件中用些諸如"@@@N、###N"奇怪字串標注一
些位置,然后書寫交易時,只需按照一種特定的規則書寫特定的處理,最后在make時,使
用awk和sed,把模板中的"@@@N、###N"等字串替代成特定的程序,形成C文件,然后再編譯
。這個動作很像數據庫的"擴展C"語言(即在C語言中用"EXEC SQL"的樣子執行SQL語句,
在用 cc/gcc編譯之前,需要使用"擴展C"的翻譯程序,如cpre,把其翻譯成標準C)。如果
你在使用make時有一些更為絕妙的方法,請記得告訴我啊。

回頭看看整篇文檔,不覺記起幾年前剛剛開始在Unix下做開發的時候,有人問我會不會寫
Makefile時,我兩眼發直,根本不知道在說什么。一開始看到別人在vi中寫完程序后輸入
"!make"時,還以為是vi的功能,后來才知道有一個Makefile在作怪,于是上網查啊查,那
時又不愿意看英文,發現就根本沒有中文的文檔介紹Makefile,只得看別人寫的Makefile
,自己瞎碰瞎搞才積累了一點知識,但在很多地方完全是知其然不知所以然。后來開始從
事UNIX下產品軟件的開發,看到一個400人年,近200萬行代碼的大工程,發現要編譯這樣
一個龐然大物,如果沒有Makefile,那會是多么恐怖的一樣事啊。于是橫下心來,狠命地
讀了一堆英文文檔,才覺得對其掌握了。但發現目前網上對Makefile介紹的文章還是少得
那么的可憐,所以想寫這樣一篇文章,共享給大家,希望能對各位有所幫助。

現在我終于寫完了,看了看文件的創建時間,這篇技術文檔也寫了兩個多月了。發現,自
己知道是一回事,要寫下來,跟別人講述又是另外一回事,而且,現在越來越沒有時間專
研技術細節,所以在寫作時,發現在闡述一些細節問題時很難做到嚴謹和精練,而且對先
講什么后講什么不是很清楚,所以,還是參考了一些國外站點上的資料和題綱,以及一些
技術書籍的語言風格,才得以完成。整篇文檔的提綱是基于GNU的 Makefile技術手冊的提
綱來書寫的,并結合了自己的工作經驗,以及自己的學習歷程。因為從來沒有寫過這么長
,這么細的文檔,所以一定會有很多地方存在表達問題,語言歧義或是錯誤。因些,我迫
切地得等待各位給我指證和建議,以及任何的反饋。

最后,還是利用這個后序,介紹一下自己。我目前從事于所有Unix平臺下的軟件研發,主
要是做分布式計算/網格計算方面的系統產品軟件,并且我對于下一代的計算機革命——網
格計算非常地感興趣,對于分布式計算、P2P、Web Service、J2EE技術方向也很感興趣,
同時,對于項目實施、團隊管理、項目管理也小有心得,希望同樣和我戰斗在“技術和管
理并重”的陣線上的年輕一代,能夠和我多多地交流。我的MSN是:haoel@hotmail.com(
常用),QQ是:753640(不常用)。(注:請勿給我MSN的郵箱發信,由于hotmail的垃圾
郵件導致我拒收這個郵箱的所有來信)

我歡迎任何形式的交流,無論是討論技術還是管理,或是其它海闊天空的東西。除了政治
和娛樂新聞我不關心,其它只要積極向上的東西我都歡迎!

最最后,我還想介紹一下make程序的設計開發者。

首當其沖的是: Richard Stallman

開源軟件的領袖和先驅,從來沒有領過一天工資,從來沒有使用過Windows操作系統。對于
他的事跡和他的軟件以及他的思想,我無需說過多的話,相信大家對這個人并不比我陌生
,這是他的主頁:http://www.stallman.org/ 。



第二位是:Roland McGrath

個人主頁是:http://www.frob.com/~roland/ ,下面是他的一些事跡:

1) 合作編寫了并維護GNU make。

2) 和Thomas Bushnell一同編寫了GNU Hurd。

3) 編寫并維護著GNU C library。

4) 合作編寫并維護著部分的GNU Emacs。



在此,向這兩位開源項目的斗士致以最真切的敬意。

轉載于:https://www.cnblogs.com/kernux/articles/makefile.html

總結

以上是生活随笔為你收集整理的Makefile 规则的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

日日碰狠狠躁久久躁蜜桃 | 久久久国产精品无码免费专区 | 亚洲国产精品美女久久久久 | 亚洲精品综合一区二区三区在线 | 在线观看国产一区二区三区 | 女人色极品影院 | av香港经典三级级 在线 | 国产人妻精品一区二区三区不卡 | 国产精品久久久久久无码 | 图片小说视频一区二区 | 国产综合色产在线精品 | 日本精品少妇一区二区三区 | 久久99精品国产麻豆蜜芽 | 成人精品视频一区二区三区尤物 | 国产精品美女久久久 | 色偷偷人人澡人人爽人人模 | 欧美老人巨大xxxx做受 | 日本一本二本三区免费 | 欧美成人免费全部网站 | 高潮喷水的毛片 | 亚洲国产欧美在线成人 | 捆绑白丝粉色jk震动捧喷白浆 | 久久国产精品萌白酱免费 | 青青青爽视频在线观看 | 成年美女黄网站色大免费视频 | 成人av无码一区二区三区 | 超碰97人人射妻 | 无码国产乱人伦偷精品视频 | 国产成人久久精品流白浆 | 99久久亚洲精品无码毛片 | 少妇高潮喷潮久久久影院 | 精品久久综合1区2区3区激情 | 日产精品99久久久久久 | 国产人妻大战黑人第1集 | 中文无码伦av中文字幕 | 久久精品成人欧美大片 | 国产在线无码精品电影网 | 波多野结衣乳巨码无在线观看 | 免费国产黄网站在线观看 | 国产熟女一区二区三区四区五区 | yw尤物av无码国产在线观看 | 精品欧洲av无码一区二区三区 | a在线观看免费网站大全 | 夜夜影院未满十八勿进 | 岛国片人妻三上悠亚 | 日本成熟视频免费视频 | 久久久久久a亚洲欧洲av冫 | 欧美日韩一区二区免费视频 | 丰满肥臀大屁股熟妇激情视频 | 俺去俺来也在线www色官网 | 国产精品亚洲lv粉色 | 久久久精品456亚洲影院 | 免费国产黄网站在线观看 | 免费人成在线观看网站 | 色狠狠av一区二区三区 | 久久婷婷五月综合色国产香蕉 | 亚洲国产精品久久久久久 | 高清国产亚洲精品自在久久 | а√资源新版在线天堂 | 久久久久久久女国产乱让韩 | 免费无码av一区二区 | 欧美熟妇另类久久久久久多毛 | 色一情一乱一伦一视频免费看 | 国产成人无码午夜视频在线观看 | 日本乱偷人妻中文字幕 | 天天拍夜夜添久久精品大 | 在线欧美精品一区二区三区 | 国产97人人超碰caoprom | 十八禁真人啪啪免费网站 | 天堂亚洲2017在线观看 | 中文无码精品a∨在线观看不卡 | 少妇人妻大乳在线视频 | 国产成人人人97超碰超爽8 | 人妻无码久久精品人妻 | 欧洲精品码一区二区三区免费看 | 亚洲理论电影在线观看 | 国产又粗又硬又大爽黄老大爷视 | 性开放的女人aaa片 | 亚洲国产精品无码一区二区三区 | 香蕉久久久久久av成人 | 亚洲欧美日韩国产精品一区二区 | 国产片av国语在线观看 | 少妇性俱乐部纵欲狂欢电影 | 亚洲国产av美女网站 | 高潮毛片无遮挡高清免费视频 | 大肉大捧一进一出好爽视频 | 久久亚洲国产成人精品性色 | 国产真实夫妇视频 | 真人与拘做受免费视频 | 亚洲 高清 成人 动漫 | 最新国产乱人伦偷精品免费网站 | 亚洲精品国偷拍自产在线观看蜜桃 | 在线观看国产一区二区三区 | 夜夜躁日日躁狠狠久久av | 99riav国产精品视频 | 亚洲爆乳精品无码一区二区三区 | 性啪啪chinese东北女人 | 国产成人综合美国十次 | 男女爱爱好爽视频免费看 | 中文字幕人妻丝袜二区 | 日韩亚洲欧美精品综合 | 日本一卡2卡3卡四卡精品网站 | 亚洲国产高清在线观看视频 | 无码av最新清无码专区吞精 | 中文字幕乱码人妻无码久久 | 国产精品国产三级国产专播 | 欧美亚洲日韩国产人成在线播放 | 中文字幕无码免费久久99 | 久久久久se色偷偷亚洲精品av | 一本色道婷婷久久欧美 | 成人综合网亚洲伊人 | 欧美精品免费观看二区 | 乱码午夜-极国产极内射 | 色一情一乱一伦 | 最新国产麻豆aⅴ精品无码 | 在线а√天堂中文官网 | 伊人久久大香线蕉午夜 | 乌克兰少妇性做爰 | 曰韩少妇内射免费播放 | 老司机亚洲精品影院 | 18黄暴禁片在线观看 | 妺妺窝人体色www婷婷 | 综合网日日天干夜夜久久 | 国产激情无码一区二区 | 午夜精品久久久久久久久 | 国产色精品久久人妻 | 精品国偷自产在线 | 色婷婷av一区二区三区之红樱桃 | 荫蒂被男人添的好舒服爽免费视频 | 国产手机在线αⅴ片无码观看 | 亚洲男人av天堂午夜在 | 欧美zoozzooz性欧美 | 国产色xx群视频射精 | 国内揄拍国内精品少妇国语 | 亚洲欧美中文字幕5发布 | 妺妺窝人体色www在线小说 | 亚洲精品久久久久avwww潮水 | 中文字幕av日韩精品一区二区 | 国产精品18久久久久久麻辣 | 狠狠色噜噜狠狠狠7777奇米 | 97久久超碰中文字幕 | 成人欧美一区二区三区黑人免费 | 欧美日本精品一区二区三区 | 51国偷自产一区二区三区 | 成人欧美一区二区三区 | 无码人妻精品一区二区三区不卡 | 岛国片人妻三上悠亚 | 97久久超碰中文字幕 | 国产猛烈高潮尖叫视频免费 | 成人女人看片免费视频放人 | 国产香蕉尹人视频在线 | 女人被爽到呻吟gif动态图视看 | 国产麻豆精品一区二区三区v视界 | 男女爱爱好爽视频免费看 | 亚洲另类伦春色综合小说 | 国产精品内射视频免费 | 狠狠色噜噜狠狠狠7777奇米 | 亚洲gv猛男gv无码男同 | 午夜嘿嘿嘿影院 | 天堂在线观看www | 兔费看少妇性l交大片免费 | 丰满岳乱妇在线观看中字无码 | 夜先锋av资源网站 | 粉嫩少妇内射浓精videos | 精品国产乱码久久久久乱码 | 亚洲午夜无码久久 | 欧美性猛交内射兽交老熟妇 | 噜噜噜亚洲色成人网站 | 亚洲精品久久久久中文第一幕 | 亚洲精品一区国产 | 人妻中文无码久热丝袜 | 性生交片免费无码看人 | 国产特级毛片aaaaaa高潮流水 | 欧美熟妇另类久久久久久不卡 | 亚洲中文字幕va福利 | 国产亚洲精品久久久闺蜜 | 久久无码专区国产精品s | 精品无码一区二区三区的天堂 | 亚洲乱码国产乱码精品精 | 天下第一社区视频www日本 | 免费人成在线观看网站 | 中文字幕久久久久人妻 | 婷婷丁香五月天综合东京热 | 精品少妇爆乳无码av无码专区 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 久久久国产一区二区三区 | 大乳丰满人妻中文字幕日本 | 九月婷婷人人澡人人添人人爽 | 亚洲精品中文字幕 | 亚洲日韩中文字幕在线播放 | 亚洲精品一区国产 | 日本大香伊一区二区三区 | 日本爽爽爽爽爽爽在线观看免 | 性欧美大战久久久久久久 | 中文字幕av日韩精品一区二区 | 精品国产一区二区三区av 性色 | 国产无遮挡又黄又爽又色 | 97精品国产97久久久久久免费 | 国产亚洲精品久久久ai换 | 无码精品国产va在线观看dvd | 成年女人永久免费看片 | 国产人妖乱国产精品人妖 | 少妇无套内谢久久久久 | 国产成人一区二区三区别 | 国产精品.xx视频.xxtv | 国产成人精品视频ⅴa片软件竹菊 | 日韩成人一区二区三区在线观看 | 欧美黑人性暴力猛交喷水 | 久在线观看福利视频 | 日本xxxx色视频在线观看免费 | 欧美亚洲国产一区二区三区 | 成人免费视频视频在线观看 免费 | 国产偷自视频区视频 | 精品一二三区久久aaa片 | 久久精品女人天堂av免费观看 | 男女下面进入的视频免费午夜 | 国产sm调教视频在线观看 | 最近免费中文字幕中文高清百度 | aⅴ在线视频男人的天堂 | 77777熟女视频在线观看 а天堂中文在线官网 | 国产两女互慰高潮视频在线观看 | 人妻少妇被猛烈进入中文字幕 | 国产精品亚洲专区无码不卡 | 亚洲精品一区二区三区四区五区 | 国产精品内射视频免费 | 午夜熟女插插xx免费视频 | 亚洲人成影院在线无码按摩店 | 一本精品99久久精品77 | 永久免费观看美女裸体的网站 | 图片区 小说区 区 亚洲五月 | 国产综合久久久久鬼色 | 伊人久久大香线蕉亚洲 | 无码国产乱人伦偷精品视频 | 老熟妇仑乱视频一区二区 | 国产欧美精品一区二区三区 | 少女韩国电视剧在线观看完整 | 国产亚洲精品久久久ai换 | 亚洲精品国产品国语在线观看 | 野外少妇愉情中文字幕 | 亚洲一区二区三区偷拍女厕 | 丁香花在线影院观看在线播放 | 人妻有码中文字幕在线 | 亚洲中文字幕成人无码 | 天天摸天天碰天天添 | 国产综合在线观看 | 色五月丁香五月综合五月 | 欧美性生交xxxxx久久久 | 粗大的内捧猛烈进出视频 | 亚洲毛片av日韩av无码 | 2020久久超碰国产精品最新 | 国产亚洲人成在线播放 | 成人三级无码视频在线观看 | 国产人妻人伦精品 | 久久久www成人免费毛片 | 中文字幕乱码人妻无码久久 | 国产成人无码午夜视频在线观看 | 亚洲中文字幕久久无码 | 国産精品久久久久久久 | 日本爽爽爽爽爽爽在线观看免 | 未满小14洗澡无码视频网站 | 国产亚洲精品久久久闺蜜 | 欧美黑人性暴力猛交喷水 | 欧美真人作爱免费视频 | 亚洲人成人无码网www国产 | 国产极品美女高潮无套在线观看 | 亚洲一区二区三区含羞草 | 娇妻被黑人粗大高潮白浆 | 一本无码人妻在中文字幕免费 | 国产亚洲精品久久久久久国模美 | 亚洲伊人久久精品影院 | 久久成人a毛片免费观看网站 | 双乳奶水饱满少妇呻吟 | 国产精品无码一区二区桃花视频 | 日韩欧美中文字幕在线三区 | 国产亚洲人成a在线v网站 | 精品偷自拍另类在线观看 | 国产精品美女久久久网av | 日韩欧美中文字幕在线三区 | 青青青爽视频在线观看 | 亚洲а∨天堂久久精品2021 | aⅴ在线视频男人的天堂 | 日本又色又爽又黄的a片18禁 | 内射后入在线观看一区 | 亚洲国产综合无码一区 | 永久免费观看国产裸体美女 | 国产欧美熟妇另类久久久 | 国产精品久久久一区二区三区 | 久久久久亚洲精品中文字幕 | 国产人妻精品一区二区三区不卡 | 亚洲精品国产第一综合99久久 | 欧美激情内射喷水高潮 | 无码av岛国片在线播放 | 国产极品美女高潮无套在线观看 | 国产免费观看黄av片 | 欧美色就是色 | 精品国产一区av天美传媒 | 国产精品亚洲一区二区三区喷水 | 色五月五月丁香亚洲综合网 | 国产无av码在线观看 | 97色伦图片97综合影院 | 国产精品福利视频导航 | 青青青爽视频在线观看 | 蜜臀av在线播放 久久综合激激的五月天 | 国产精品久久久久久无码 | 精品国产av色一区二区深夜久久 | 欧美35页视频在线观看 | 爱做久久久久久 | 精品无人区无码乱码毛片国产 | 免费无码肉片在线观看 | 国产精品久久久久久久9999 | 欧美熟妇另类久久久久久多毛 | 精品无人区无码乱码毛片国产 | 久久亚洲日韩精品一区二区三区 | 中国大陆精品视频xxxx | 国产成人亚洲综合无码 | 久久精品国产一区二区三区肥胖 | 婷婷综合久久中文字幕蜜桃三电影 | 好爽又高潮了毛片免费下载 | 国产精品人人爽人人做我的可爱 | 精品亚洲成av人在线观看 | 奇米影视7777久久精品人人爽 | 久久精品国产一区二区三区 | 色婷婷久久一区二区三区麻豆 | 又大又紧又粉嫩18p少妇 | 无码人妻精品一区二区三区下载 | 人人妻人人澡人人爽人人精品浪潮 | 性做久久久久久久久 | 国产精品人人爽人人做我的可爱 | 亚洲中文字幕无码中字 | a片免费视频在线观看 | 国产精品久久久久久无码 | 人妻少妇精品无码专区二区 | 免费无码肉片在线观看 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 麻花豆传媒剧国产免费mv在线 | 狂野欧美性猛xxxx乱大交 | 欧美刺激性大交 | 亚洲国产精华液网站w | 日本成熟视频免费视频 | 日日噜噜噜噜夜夜爽亚洲精品 | 精品一区二区三区无码免费视频 | 成人aaa片一区国产精品 | 亚洲性无码av中文字幕 | 亚洲中文字幕在线无码一区二区 | 久久久精品成人免费观看 | 国产精品亚洲а∨无码播放麻豆 | 在线视频网站www色 | 在线成人www免费观看视频 | 国产猛烈高潮尖叫视频免费 | 欧美第一黄网免费网站 | 四虎4hu永久免费 | 日本一区二区三区免费播放 | 国产在线aaa片一区二区99 | 无码人妻出轨黑人中文字幕 | 日韩精品久久久肉伦网站 | 高清国产亚洲精品自在久久 | 扒开双腿疯狂进出爽爽爽视频 | 蜜臀av无码人妻精品 | 亚洲成a人片在线观看无码 | 国产无遮挡又黄又爽免费视频 | 成人一在线视频日韩国产 | 精品国产一区二区三区四区在线看 | 女人色极品影院 | 蜜桃av抽搐高潮一区二区 | 亚洲 欧美 激情 小说 另类 | 中文字幕无码免费久久99 | 亚洲国产精华液网站w | 亚洲国产精品无码久久久久高潮 | 3d动漫精品啪啪一区二区中 | 日本精品人妻无码77777 天堂一区人妻无码 | 国内精品九九久久久精品 | 久久久久av无码免费网 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 久久午夜无码鲁丝片秋霞 | 又黄又爽又色的视频 | 荡女精品导航 | 亚洲精品午夜国产va久久成人 | 丁香啪啪综合成人亚洲 | 最近的中文字幕在线看视频 | 动漫av一区二区在线观看 | 无码av中文字幕免费放 | 国产成人综合色在线观看网站 | 99久久婷婷国产综合精品青草免费 | 亚洲精品久久久久avwww潮水 | 国产偷国产偷精品高清尤物 | 国内揄拍国内精品人妻 | 日本www一道久久久免费榴莲 | 中文字幕人妻无码一区二区三区 | 在线天堂新版最新版在线8 | 亚洲爆乳无码专区 | 亚洲国产午夜精品理论片 | 久久久久人妻一区精品色欧美 | 亚洲国产欧美国产综合一区 | 一本色道久久综合亚洲精品不卡 | 好男人社区资源 | 亚洲精品美女久久久久久久 | 国产乱子伦视频在线播放 | 无码人妻黑人中文字幕 | 亚洲 日韩 欧美 成人 在线观看 | 精品国产麻豆免费人成网站 | 日本熟妇人妻xxxxx人hd | 亚洲区欧美区综合区自拍区 | 天天躁夜夜躁狠狠是什么心态 | 激情综合激情五月俺也去 | 久久久中文字幕日本无吗 | 国产激情一区二区三区 | 久在线观看福利视频 | 久久久婷婷五月亚洲97号色 | 老司机亚洲精品影院无码 | www成人国产高清内射 | av在线亚洲欧洲日产一区二区 | 欧美日本免费一区二区三区 | 综合网日日天干夜夜久久 | 免费无码一区二区三区蜜桃大 | 国产精品99久久精品爆乳 | 久久精品国产日本波多野结衣 | 99久久久无码国产精品免费 | 国产莉萝无码av在线播放 | 日韩欧美成人免费观看 | 国产精品a成v人在线播放 | 亚洲国产午夜精品理论片 | 亚洲国产av美女网站 | 久久久久se色偷偷亚洲精品av | 鲁大师影院在线观看 | aa片在线观看视频在线播放 | 成年美女黄网站色大免费全看 | 熟妇激情内射com | 成人无码精品一区二区三区 | 亚洲国产午夜精品理论片 | 三上悠亚人妻中文字幕在线 | 日韩精品无码一区二区中文字幕 | 日本乱人伦片中文三区 | 亚洲熟妇色xxxxx亚洲 | 中文字幕av伊人av无码av | 欧美熟妇另类久久久久久多毛 | 亚洲男人av天堂午夜在 | 99视频精品全部免费免费观看 | 午夜福利试看120秒体验区 | 老头边吃奶边弄进去呻吟 | 天天躁夜夜躁狠狠是什么心态 | 中文字幕人成乱码熟女app | 国产精华av午夜在线观看 | 久久99精品久久久久婷婷 | 男女爱爱好爽视频免费看 | 欧美变态另类xxxx | 久久久久成人精品免费播放动漫 | 亚洲日本va午夜在线电影 | 亚洲熟妇自偷自拍另类 | 精品国产麻豆免费人成网站 | 娇妻被黑人粗大高潮白浆 | 亚洲乱码国产乱码精品精 | 日本一区二区三区免费高清 | 国产高清av在线播放 | 久久久久久久久888 | 四虎影视成人永久免费观看视频 | 国产69精品久久久久app下载 | 亚洲人成影院在线观看 | 四虎永久在线精品免费网址 | 中文字幕人成乱码熟女app | 无码中文字幕色专区 | 丰满妇女强制高潮18xxxx | 精品久久久久久亚洲精品 | 国产无套内射久久久国产 | 乱人伦人妻中文字幕无码久久网 | 无码帝国www无码专区色综合 | 国产亚洲精品久久久久久国模美 | 国产在线一区二区三区四区五区 | 国产精品久久久久久亚洲影视内衣 | 性欧美熟妇videofreesex | 乱中年女人伦av三区 | 免费乱码人妻系列无码专区 | 香蕉久久久久久av成人 | 久久精品女人的天堂av | 日韩 欧美 动漫 国产 制服 | 久久精品国产99精品亚洲 | 亚洲一区二区三区偷拍女厕 | 亚洲热妇无码av在线播放 | 国色天香社区在线视频 | 成人女人看片免费视频放人 | 久久久久久久人妻无码中文字幕爆 | 最新国产麻豆aⅴ精品无码 | 粗大的内捧猛烈进出视频 | 日韩视频 中文字幕 视频一区 | 国产欧美亚洲精品a | 日日麻批免费40分钟无码 | 久久精品视频在线看15 | 国产精品无码永久免费888 | 中文字幕乱妇无码av在线 | 九九在线中文字幕无码 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 丰满岳乱妇在线观看中字无码 | 国产在线aaa片一区二区99 | 国产在线aaa片一区二区99 | 亚洲熟妇色xxxxx亚洲 | 中文字幕人成乱码熟女app | 亚洲精品中文字幕乱码 | 18无码粉嫩小泬无套在线观看 | 给我免费的视频在线观看 | 97久久国产亚洲精品超碰热 | 67194成是人免费无码 | 国产成人精品三级麻豆 | 人妻少妇精品无码专区二区 | 亚洲欧美综合区丁香五月小说 | 欧美 日韩 亚洲 在线 | 欧美freesex黑人又粗又大 | 欧洲欧美人成视频在线 | 久久视频在线观看精品 | 国产绳艺sm调教室论坛 | 国产午夜福利亚洲第一 | 在线观看免费人成视频 | 色情久久久av熟女人妻网站 | 亚洲欧美日韩成人高清在线一区 | 国产亚洲精品久久久久久久久动漫 | 乱码av麻豆丝袜熟女系列 | 色综合久久久无码网中文 | 在线成人www免费观看视频 | 波多野结衣乳巨码无在线观看 | 十八禁视频网站在线观看 | 大色综合色综合网站 | 精品国偷自产在线 | 亚洲国产精品美女久久久久 | 亚洲自偷自拍另类第1页 | 人妻与老人中文字幕 | 国产免费久久久久久无码 | 久久久国产精品无码免费专区 | 2020最新国产自产精品 | 亚洲成av人片在线观看无码不卡 | 日日碰狠狠躁久久躁蜜桃 | 无码人妻精品一区二区三区下载 | 国产亚洲美女精品久久久2020 | 丰满岳乱妇在线观看中字无码 | 日本丰满护士爆乳xxxx | 国产亚洲精品精品国产亚洲综合 | 丝袜 中出 制服 人妻 美腿 | 人人妻人人澡人人爽欧美一区九九 | 国产精品亚洲а∨无码播放麻豆 | 亚洲精品国产a久久久久久 | 国产极品美女高潮无套在线观看 | 高潮喷水的毛片 | 久久久精品456亚洲影院 | 内射欧美老妇wbb | 在线播放免费人成毛片乱码 | 国产精品国产自线拍免费软件 | 性生交大片免费看l | 日韩视频 中文字幕 视频一区 | 少妇性俱乐部纵欲狂欢电影 | 97色伦图片97综合影院 | 亚洲 日韩 欧美 成人 在线观看 | 国产精品18久久久久久麻辣 | 天下第一社区视频www日本 | 久久亚洲精品成人无码 | 国产精品第一区揄拍无码 | 强伦人妻一区二区三区视频18 | 国产又粗又硬又大爽黄老大爷视 | 两性色午夜视频免费播放 | 乌克兰少妇xxxx做受 | 午夜无码区在线观看 | 日本大乳高潮视频在线观看 | 精品欧美一区二区三区久久久 | 麻豆果冻传媒2021精品传媒一区下载 | 狠狠色丁香久久婷婷综合五月 | 性欧美熟妇videofreesex | 久久亚洲国产成人精品性色 | 成在人线av无码免费 | 国产69精品久久久久app下载 | 日韩精品成人一区二区三区 | 久久人人爽人人人人片 | 国产激情无码一区二区 | 精品无码国产自产拍在线观看蜜 | 综合网日日天干夜夜久久 | 久久久av男人的天堂 | 国产精品对白交换视频 | 国产精华av午夜在线观看 | 色五月丁香五月综合五月 | 日韩成人一区二区三区在线观看 | 国产人妻人伦精品 | 99久久精品国产一区二区蜜芽 | 日产国产精品亚洲系列 | 久久99精品久久久久久动态图 | 久久亚洲a片com人成 | 麻花豆传媒剧国产免费mv在线 | 婷婷五月综合激情中文字幕 | 国产精品丝袜黑色高跟鞋 | 国产一区二区三区精品视频 | 色五月丁香五月综合五月 | 青青久在线视频免费观看 | 少妇的肉体aa片免费 | 久久久久久a亚洲欧洲av冫 | 99久久久国产精品无码免费 | 国产精品香蕉在线观看 | 国产精品久久久久久久影院 | 激情亚洲一区国产精品 | 丝袜足控一区二区三区 | 亚洲啪av永久无码精品放毛片 | 玩弄少妇高潮ⅹxxxyw | 欧美日韩一区二区综合 | 精品国产精品久久一区免费式 | 乱人伦人妻中文字幕无码 | 国产精品亚洲五月天高清 | 天天躁日日躁狠狠躁免费麻豆 | 18禁黄网站男男禁片免费观看 | 国产黑色丝袜在线播放 | 无码国产色欲xxxxx视频 | 免费人成在线观看网站 | 人妻互换免费中文字幕 | 免费无码午夜福利片69 | 妺妺窝人体色www在线小说 | 婷婷五月综合激情中文字幕 | 5858s亚洲色大成网站www | 桃花色综合影院 | 国产极品美女高潮无套在线观看 | www一区二区www免费 | 国产高清av在线播放 | 日日摸夜夜摸狠狠摸婷婷 | 国产成人精品一区二区在线小狼 | 成人无码精品一区二区三区 | 日日噜噜噜噜夜夜爽亚洲精品 | 色五月五月丁香亚洲综合网 | 亚洲精品成人av在线 | 色综合久久久久综合一本到桃花网 | 在线视频网站www色 | 亚洲精品综合五月久久小说 | 亚洲精品午夜国产va久久成人 | 欧美成人高清在线播放 | 日本熟妇人妻xxxxx人hd | 色综合久久久无码中文字幕 | 无码帝国www无码专区色综合 | 东北女人啪啪对白 | 野狼第一精品社区 | 5858s亚洲色大成网站www | 精品亚洲成av人在线观看 | 亚洲精品成人av在线 | 精品亚洲韩国一区二区三区 | 亚洲码国产精品高潮在线 | 日日鲁鲁鲁夜夜爽爽狠狠 | 无码毛片视频一区二区本码 | 丰满肥臀大屁股熟妇激情视频 | 激情综合激情五月俺也去 | 精品欧美一区二区三区久久久 | 97精品国产97久久久久久免费 | 日韩欧美中文字幕公布 | 天堂久久天堂av色综合 | 亚洲日韩av一区二区三区四区 | 日日鲁鲁鲁夜夜爽爽狠狠 | 国产人妻精品一区二区三区不卡 | 国内老熟妇对白xxxxhd | 亚洲国产av精品一区二区蜜芽 | 亚洲人成无码网www | 国产亚洲精品久久久久久大师 | 中文字幕av无码一区二区三区电影 | 久久久av男人的天堂 | 四虎国产精品一区二区 | 狠狠色欧美亚洲狠狠色www | 麻花豆传媒剧国产免费mv在线 | 欧美变态另类xxxx | 99久久精品午夜一区二区 | 99久久久国产精品无码免费 | 少妇无码av无码专区在线观看 | 99久久无码一区人妻 | 久激情内射婷内射蜜桃人妖 | 国产三级久久久精品麻豆三级 | 99视频精品全部免费免费观看 | 国产亚洲精品久久久久久大师 | 国产精品无码一区二区桃花视频 | 性史性农村dvd毛片 | 夜夜高潮次次欢爽av女 | 成在人线av无码免费 | 国产在线精品一区二区三区直播 | 久久精品丝袜高跟鞋 | 色婷婷久久一区二区三区麻豆 | 久久久久久久女国产乱让韩 | 精品久久综合1区2区3区激情 | 国产午夜福利亚洲第一 | 天堂一区人妻无码 | 三上悠亚人妻中文字幕在线 | 波多野42部无码喷潮在线 | 精品少妇爆乳无码av无码专区 | aa片在线观看视频在线播放 | av在线亚洲欧洲日产一区二区 | 午夜精品久久久久久久 | 熟妇人妻中文av无码 | 国产精品久久久一区二区三区 | 欧美 丝袜 自拍 制服 另类 | 无码毛片视频一区二区本码 | 一本久道久久综合婷婷五月 | 7777奇米四色成人眼影 | 啦啦啦www在线观看免费视频 | 国产一区二区三区影院 | 人人爽人人爽人人片av亚洲 | 欧美性生交活xxxxxdddd | 亚洲另类伦春色综合小说 | 99久久人妻精品免费二区 | 日本高清一区免费中文视频 | 免费观看激色视频网站 | 亚洲中文字幕无码一久久区 | 成人亚洲精品久久久久 | 欧美熟妇另类久久久久久不卡 | 自拍偷自拍亚洲精品被多人伦好爽 | 亚洲人成人无码网www国产 | 国产无遮挡又黄又爽又色 | 欧美野外疯狂做受xxxx高潮 | 久久 国产 尿 小便 嘘嘘 | 免费看男女做好爽好硬视频 | 亚洲国产av精品一区二区蜜芽 | av在线亚洲欧洲日产一区二区 | 精品久久久中文字幕人妻 | 一本色道婷婷久久欧美 | 最近中文2019字幕第二页 | 2019午夜福利不卡片在线 | 亚洲精品中文字幕久久久久 | 精品亚洲韩国一区二区三区 | 中国女人内谢69xxxxxa片 | 亚洲区小说区激情区图片区 | √天堂资源地址中文在线 | 红桃av一区二区三区在线无码av | 一本色道久久综合狠狠躁 | 好男人社区资源 | 色一情一乱一伦一视频免费看 | 国产精品久久久久久久9999 | 夜先锋av资源网站 | 日日躁夜夜躁狠狠躁 | ass日本丰满熟妇pics | 无码乱肉视频免费大全合集 | 精品国产青草久久久久福利 | 在线а√天堂中文官网 | 色欲av亚洲一区无码少妇 | 精品国产一区二区三区四区在线看 | 少妇性荡欲午夜性开放视频剧场 | 妺妺窝人体色www在线小说 | av无码电影一区二区三区 | 少妇人妻大乳在线视频 | 欧美人与牲动交xxxx | 妺妺窝人体色www婷婷 | 国产精品久免费的黄网站 | 98国产精品综合一区二区三区 | 成人aaa片一区国产精品 | 国产一区二区三区日韩精品 | 亚洲欧美中文字幕5发布 | 亚洲日韩av片在线观看 | 麻豆果冻传媒2021精品传媒一区下载 | 国产性生交xxxxx无码 | 国产午夜亚洲精品不卡下载 | 少妇无套内谢久久久久 | 国产成人综合色在线观看网站 | 亚洲国产欧美日韩精品一区二区三区 | yw尤物av无码国产在线观看 | 国产香蕉尹人综合在线观看 | 无码人妻av免费一区二区三区 | 亚洲 欧美 激情 小说 另类 | 人人妻人人澡人人爽人人精品浪潮 | 国产成人综合在线女婷五月99播放 | 国产精品怡红院永久免费 | 久久久久se色偷偷亚洲精品av | 娇妻被黑人粗大高潮白浆 | 亚洲中文字幕乱码av波多ji | 亚洲高清偷拍一区二区三区 | 久久久精品国产sm最大网站 | 黑人巨大精品欧美一区二区 | 亚洲男女内射在线播放 | 99久久人妻精品免费一区 | 欧美高清在线精品一区 | 强辱丰满人妻hd中文字幕 | 东京一本一道一二三区 | 亚洲精品成a人在线观看 | 十八禁视频网站在线观看 | 性做久久久久久久久 | 国内综合精品午夜久久资源 | 欧美亚洲日韩国产人成在线播放 | 日韩欧美中文字幕在线三区 | 久久精品国产亚洲精品 | 欧美性生交xxxxx久久久 | 青青青手机频在线观看 | 2020久久超碰国产精品最新 | 成熟女人特级毛片www免费 | 国产激情综合五月久久 | 精品国产青草久久久久福利 | 国产在线一区二区三区四区五区 | 久久亚洲国产成人精品性色 | 欧美兽交xxxx×视频 | 国产人妻久久精品二区三区老狼 | 四虎影视成人永久免费观看视频 | 国产精品对白交换视频 | 无码国模国产在线观看 | 亚洲欧美国产精品久久 | 精品欧美一区二区三区久久久 | 丰满少妇女裸体bbw | 国产一区二区三区影院 | 人人妻人人澡人人爽欧美一区九九 | 精品偷自拍另类在线观看 | 人妻人人添人妻人人爱 | 精品厕所偷拍各类美女tp嘘嘘 | 久久精品人妻少妇一区二区三区 | 少妇邻居内射在线 | 中文字幕 人妻熟女 | 欧美性猛交内射兽交老熟妇 | 精品国产一区二区三区四区 | 国产亚洲美女精品久久久2020 | 亚洲 另类 在线 欧美 制服 | 亚洲阿v天堂在线 | 色欲人妻aaaaaaa无码 | 漂亮人妻洗澡被公强 日日躁 | 日本熟妇乱子伦xxxx | 国产av一区二区三区最新精品 | 乌克兰少妇性做爰 | 午夜嘿嘿嘿影院 | 欧美人与善在线com | 久久人人爽人人爽人人片av高清 | 日本又色又爽又黄的a片18禁 | 激情内射亚州一区二区三区爱妻 | 久久久久免费精品国产 | 国产 浪潮av性色四虎 | 日韩视频 中文字幕 视频一区 | а√天堂www在线天堂小说 | 亚洲成a人片在线观看日本 | 亚洲 另类 在线 欧美 制服 | 日本va欧美va欧美va精品 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 亚洲 a v无 码免 费 成 人 a v | 亚洲日韩精品欧美一区二区 | 东京热一精品无码av | 成人精品视频一区二区三区尤物 | 午夜福利一区二区三区在线观看 | 99在线 | 亚洲 | 欧美日韩人成综合在线播放 | 国产国语老龄妇女a片 | 国产精品久久国产三级国 | 亚洲娇小与黑人巨大交 | 又大又紧又粉嫩18p少妇 | 精品亚洲成av人在线观看 | 中文字幕无码免费久久99 | 暴力强奷在线播放无码 | 久久久久久久人妻无码中文字幕爆 | 免费看少妇作爱视频 | 日本熟妇大屁股人妻 | 我要看www免费看插插视频 | 亚洲人成网站免费播放 | 中文字幕av伊人av无码av | 欧美肥老太牲交大战 | 成人无码精品一区二区三区 | 少妇性俱乐部纵欲狂欢电影 | 国产午夜视频在线观看 | 熟妇人妻无码xxx视频 | 国产卡一卡二卡三 | 无人区乱码一区二区三区 | 国产精品无码永久免费888 | а√天堂www在线天堂小说 | 免费中文字幕日韩欧美 | 亚洲最大成人网站 | 亚洲色在线无码国产精品不卡 | 狂野欧美激情性xxxx | 免费人成网站视频在线观看 | 无码av中文字幕免费放 | 久久精品一区二区三区四区 | 少妇久久久久久人妻无码 | 未满成年国产在线观看 | 国产高清不卡无码视频 | 国产精品久久福利网站 | 色狠狠av一区二区三区 | 在线 国产 欧美 亚洲 天堂 | 最新国产麻豆aⅴ精品无码 | 亚洲无人区午夜福利码高清完整版 | 在线看片无码永久免费视频 | 日本乱偷人妻中文字幕 | 免费播放一区二区三区 | 天天燥日日燥 | 一本精品99久久精品77 | 久在线观看福利视频 | 人妻体内射精一区二区三四 | 少妇无套内谢久久久久 | 99久久精品午夜一区二区 | 亚洲成a人片在线观看无码 | 沈阳熟女露脸对白视频 | 久久99精品国产.久久久久 | 日韩人妻系列无码专区 | 伊人久久大香线焦av综合影院 | √天堂中文官网8在线 | 99久久精品无码一区二区毛片 | 亚洲熟妇自偷自拍另类 | 久久无码专区国产精品s | 天天做天天爱天天爽综合网 | 九九久久精品国产免费看小说 | 熟妇人妻激情偷爽文 | 全球成人中文在线 | 一本久道久久综合狠狠爱 | 黑人粗大猛烈进出高潮视频 | 无码帝国www无码专区色综合 | 日韩精品a片一区二区三区妖精 | 国产在线一区二区三区四区五区 | 日本丰满熟妇videos | 欧美日韩色另类综合 | 18无码粉嫩小泬无套在线观看 | 国产亚洲精品精品国产亚洲综合 | 欧美国产日韩亚洲中文 | 精品国产一区二区三区四区 | 欧美激情综合亚洲一二区 | 午夜精品一区二区三区在线观看 | 亚欧洲精品在线视频免费观看 | 精品无码成人片一区二区98 | 亚洲一区av无码专区在线观看 | 国产艳妇av在线观看果冻传媒 | 久久精品99久久香蕉国产色戒 | 国产精品无码mv在线观看 | 人人妻人人澡人人爽精品欧美 | 色综合久久久无码中文字幕 | 永久黄网站色视频免费直播 | 帮老师解开蕾丝奶罩吸乳网站 | 日本精品少妇一区二区三区 | 99精品国产综合久久久久五月天 | 国产偷自视频区视频 | 日日天日日夜日日摸 | 少妇愉情理伦片bd | 欧美精品免费观看二区 | 亚洲欧洲无卡二区视頻 | 在线天堂新版最新版在线8 | 中文字幕无码乱人伦 | 中文字幕乱妇无码av在线 | 奇米影视7777久久精品 | 性生交片免费无码看人 | 男女性色大片免费网站 | 国产精品人人爽人人做我的可爱 | 亚洲国产欧美在线成人 | 日本大香伊一区二区三区 | 天天躁日日躁狠狠躁免费麻豆 | aⅴ亚洲 日韩 色 图网站 播放 | 日本xxxx色视频在线观看免费 | 日本大乳高潮视频在线观看 | 久久精品99久久香蕉国产色戒 | 俺去俺来也www色官网 | 亚洲精品久久久久久久久久久 | 日日摸日日碰夜夜爽av | 久久精品人人做人人综合 | 国产精品高潮呻吟av久久4虎 | 国产成人综合美国十次 | 色婷婷香蕉在线一区二区 | 成在人线av无码免观看麻豆 | 澳门永久av免费网站 | 日韩av激情在线观看 | 国产卡一卡二卡三 | 又色又爽又黄的美女裸体网站 | 特级做a爰片毛片免费69 | 久久国内精品自在自线 | 亚洲熟悉妇女xxx妇女av | 色综合久久久久综合一本到桃花网 | 久久无码中文字幕免费影院蜜桃 | 国产精品美女久久久久av爽李琼 | 狠狠色色综合网站 | 亚洲中文字幕无码一久久区 | 国产黑色丝袜在线播放 | 西西人体www44rt大胆高清 | 国产特级毛片aaaaaaa高清 | 国产精品18久久久久久麻辣 | 国产色精品久久人妻 | 亚洲国产精品成人久久蜜臀 | 亚洲人成影院在线无码按摩店 | 99在线 | 亚洲 | 欧美老人巨大xxxx做受 | 日韩人妻系列无码专区 | 亚洲欧美中文字幕5发布 | 国内精品一区二区三区不卡 | 亚洲精品一区二区三区在线 | 日韩在线不卡免费视频一区 | 久久精品中文闷骚内射 | 麻豆果冻传媒2021精品传媒一区下载 | 亚洲爆乳大丰满无码专区 | 伊人久久大香线蕉午夜 | 亚洲精品久久久久久一区二区 | 亚洲 另类 在线 欧美 制服 | 国产又爽又猛又粗的视频a片 | 国产乱人偷精品人妻a片 | 国模大胆一区二区三区 | 国产色视频一区二区三区 | 日日天干夜夜狠狠爱 | 黑人大群体交免费视频 | 国产农村乱对白刺激视频 | 国产精品丝袜黑色高跟鞋 | 欧美性生交活xxxxxdddd | √8天堂资源地址中文在线 | 国产成人精品优优av | 中文字幕av无码一区二区三区电影 | 亚洲精品一区二区三区大桥未久 | 日本乱人伦片中文三区 | 一本久久伊人热热精品中文字幕 | 久久国产36精品色熟妇 | 国产美女极度色诱视频www | 亚洲码国产精品高潮在线 | 国产猛烈高潮尖叫视频免费 | 国产精品久久久久久久影院 | 无码纯肉视频在线观看 | 亚洲国产精品无码一区二区三区 | 少妇无码吹潮 | 无码成人精品区在线观看 | 成人综合网亚洲伊人 | 欧美大屁股xxxxhd黑色 | 久久午夜无码鲁丝片秋霞 | 久久午夜夜伦鲁鲁片无码免费 | 久久久久国色av免费观看性色 | 国产在线无码精品电影网 | 欧美成人午夜精品久久久 | 性欧美牲交xxxxx视频 | 国语自产偷拍精品视频偷 | 99精品视频在线观看免费 | 理论片87福利理论电影 | 日韩欧美群交p片內射中文 | 亚洲熟悉妇女xxx妇女av | 亚洲熟女一区二区三区 | 高清国产亚洲精品自在久久 | 亚洲国产精品无码久久久久高潮 | 澳门永久av免费网站 | 鲁一鲁av2019在线 | 色五月丁香五月综合五月 | 国产精品久久久久久亚洲影视内衣 | 国产一区二区三区精品视频 | 美女极度色诱视频国产 | 乱码av麻豆丝袜熟女系列 | 成人影院yy111111在线观看 | 99久久亚洲精品无码毛片 | 无码av免费一区二区三区试看 | 啦啦啦www在线观看免费视频 | 欧美乱妇无乱码大黄a片 | a国产一区二区免费入口 | 成人免费视频视频在线观看 免费 | 丰满少妇熟乱xxxxx视频 | 国产农村妇女高潮大叫 | 国产精品人妻一区二区三区四 | 欧美日韩色另类综合 | 亚洲色大成网站www国产 | 在线欧美精品一区二区三区 | 无码人妻av免费一区二区三区 | 欧美精品一区二区精品久久 | 国产激情无码一区二区app | 乱码av麻豆丝袜熟女系列 | 亚洲日韩一区二区三区 | 国产精品永久免费视频 | 婷婷丁香六月激情综合啪 | 国内精品久久久久久中文字幕 | 日韩精品乱码av一区二区 | 国产麻豆精品精东影业av网站 | 激情内射亚州一区二区三区爱妻 | 亚洲欧洲日本无在线码 | 亚洲精品国产a久久久久久 | 玩弄少妇高潮ⅹxxxyw | √8天堂资源地址中文在线 | 男女爱爱好爽视频免费看 | 中文字幕人成乱码熟女app | 强辱丰满人妻hd中文字幕 | 亚洲国产欧美国产综合一区 | 国产免费久久久久久无码 | 激情亚洲一区国产精品 | 中文字幕无码视频专区 | 又色又爽又黄的美女裸体网站 | 亚洲色偷偷男人的天堂 | 午夜不卡av免费 一本久久a久久精品vr综合 | 无码人妻精品一区二区三区下载 | 亚洲一区二区三区偷拍女厕 | 国产成人午夜福利在线播放 | 国产黄在线观看免费观看不卡 | а√资源新版在线天堂 | 欧美肥老太牲交大战 | 奇米影视7777久久精品人人爽 | 狠狠色噜噜狠狠狠狠7777米奇 | 色婷婷综合激情综在线播放 | 无码人妻丰满熟妇区毛片18 | 亚洲成a人片在线观看无码 | 国产艳妇av在线观看果冻传媒 | 午夜无码人妻av大片色欲 | 伊人久久大香线蕉av一区二区 | 色欲人妻aaaaaaa无码 | 欧美喷潮久久久xxxxx | 亚洲gv猛男gv无码男同 | 久久aⅴ免费观看 | 蜜桃无码一区二区三区 | 色婷婷综合激情综在线播放 | 欧美野外疯狂做受xxxx高潮 | 精品人妻人人做人人爽夜夜爽 | 国产午夜视频在线观看 | 沈阳熟女露脸对白视频 | 亚洲精品国产品国语在线观看 | 中文字幕人妻无码一夲道 | 精品乱码久久久久久久 | 精品无人区无码乱码毛片国产 | 给我免费的视频在线观看 | 伊人久久婷婷五月综合97色 | 在线精品国产一区二区三区 | 国产口爆吞精在线视频 | 少妇邻居内射在线 | 一本久久伊人热热精品中文字幕 | 欧美熟妇另类久久久久久不卡 | 日本xxxx色视频在线观看免费 | 亚洲乱码国产乱码精品精 | 亚洲欧美日韩成人高清在线一区 | 日本欧美一区二区三区乱码 | 丁香花在线影院观看在线播放 | 欧美35页视频在线观看 | 日韩欧美群交p片內射中文 | 色综合久久久无码中文字幕 | 亚洲国产av美女网站 | 亚洲成av人综合在线观看 | 免费无码av一区二区 | 亚洲精品国产第一综合99久久 | 熟妇女人妻丰满少妇中文字幕 | 国产黑色丝袜在线播放 | 国产成人精品一区二区在线小狼 | 国产精品久久久久久亚洲毛片 | 1000部啪啪未满十八勿入下载 | 亚洲自偷精品视频自拍 | 撕开奶罩揉吮奶头视频 | 国产又爽又猛又粗的视频a片 | 国产精品-区区久久久狼 | 高潮毛片无遮挡高清免费视频 | 精品国偷自产在线视频 | 人妻夜夜爽天天爽三区 | 女人被男人躁得好爽免费视频 | 麻豆国产人妻欲求不满谁演的 | 少女韩国电视剧在线观看完整 | 少妇的肉体aa片免费 | 无遮挡啪啪摇乳动态图 | 99久久精品国产一区二区蜜芽 | 精品无码国产一区二区三区av | 久久午夜无码鲁丝片午夜精品 | 人妻少妇精品无码专区二区 | 国产精品久久久久无码av色戒 | 亚洲精品久久久久中文第一幕 | 无码中文字幕色专区 | 亚洲精品国产精品乱码视色 | 精品国精品国产自在久国产87 | 又色又爽又黄的美女裸体网站 | 超碰97人人射妻 | 永久黄网站色视频免费直播 | 亚洲精品一区二区三区四区五区 | 日本一卡2卡3卡四卡精品网站 | 内射老妇bbwx0c0ck | 久久五月精品中文字幕 | 鲁一鲁av2019在线 | 国内精品人妻无码久久久影院蜜桃 | 国产乱人伦偷精品视频 | 欧美野外疯狂做受xxxx高潮 | 成人性做爰aaa片免费看 | 人妻无码αv中文字幕久久琪琪布 | 精品亚洲韩国一区二区三区 | 欧洲欧美人成视频在线 | 国产精品久久久久久亚洲影视内衣 | 精品国产一区二区三区四区 | 久久久久se色偷偷亚洲精品av | 久久午夜无码鲁丝片秋霞 | 亚洲а∨天堂久久精品2021 | 成 人 网 站国产免费观看 | 乱人伦人妻中文字幕无码久久网 | 久久久精品欧美一区二区免费 | 娇妻被黑人粗大高潮白浆 | 久久熟妇人妻午夜寂寞影院 | 少妇人妻大乳在线视频 | 亚洲国产欧美日韩精品一区二区三区 | 99久久人妻精品免费一区 | 精品无人国产偷自产在线 | 99久久精品国产一区二区蜜芽 | 麻豆国产97在线 | 欧洲 | 300部国产真实乱 | 免费观看又污又黄的网站 | 无码人中文字幕 | 欧美日韩亚洲国产精品 | 性欧美牲交在线视频 | 国产精品毛片一区二区 | 最新国产麻豆aⅴ精品无码 | 99麻豆久久久国产精品免费 | 欧美性生交xxxxx久久久 | 精品久久久无码中文字幕 | 在线播放免费人成毛片乱码 | 乱人伦人妻中文字幕无码久久网 | 夜精品a片一区二区三区无码白浆 | 久久伊人色av天堂九九小黄鸭 | 在线a亚洲视频播放在线观看 | 国产精品办公室沙发 | 无码人妻少妇伦在线电影 | 丰满少妇人妻久久久久久 | 欧美野外疯狂做受xxxx高潮 | 欧美xxxx黑人又粗又长 | 我要看www免费看插插视频 | 又大又硬又爽免费视频 | 5858s亚洲色大成网站www | 人妻无码αv中文字幕久久琪琪布 | 中文字幕无线码免费人妻 | 国产成人久久精品流白浆 | 国产午夜亚洲精品不卡 | 国产精品多人p群无码 | 无码人妻精品一区二区三区不卡 | 成人综合网亚洲伊人 | 亚洲国产欧美国产综合一区 | 亚洲日韩中文字幕在线播放 | 色欲av亚洲一区无码少妇 | 4hu四虎永久在线观看 | 日日躁夜夜躁狠狠躁 | 狂野欧美性猛交免费视频 | 少妇人妻av毛片在线看 | 国产色视频一区二区三区 | 亚洲国产欧美国产综合一区 | 国产真实乱对白精彩久久 | 99久久精品无码一区二区毛片 | 亚洲自偷精品视频自拍 | 对白脏话肉麻粗话av | 丁香啪啪综合成人亚洲 | 久久久中文字幕日本无吗 | 人人妻人人澡人人爽人人精品浪潮 | 日本熟妇乱子伦xxxx | 最新国产乱人伦偷精品免费网站 | 国语精品一区二区三区 | 日韩精品无码一本二本三本色 | 国精品人妻无码一区二区三区蜜柚 | 日韩人妻无码中文字幕视频 | 亚洲综合久久一区二区 | 久久人妻内射无码一区三区 | 激情人妻另类人妻伦 | 色婷婷香蕉在线一区二区 | 少妇人妻偷人精品无码视频 | 给我免费的视频在线观看 | 强伦人妻一区二区三区视频18 | 久久久婷婷五月亚洲97号色 | 黑人巨大精品欧美黑寡妇 | 国产人妻人伦精品1国产丝袜 | 亚洲国产精品成人久久蜜臀 | 又大又黄又粗又爽的免费视频 | 中文字幕色婷婷在线视频 | 国产一区二区三区四区五区加勒比 | 樱花草在线播放免费中文 | 国产亚洲精品久久久久久久 | 日本一卡2卡3卡四卡精品网站 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 性欧美大战久久久久久久 | 国产另类ts人妖一区二区 | 国内少妇偷人精品视频 | 国产亚洲精品久久久久久国模美 | 又湿又紧又大又爽a视频国产 | 中文无码伦av中文字幕 | 蜜桃av抽搐高潮一区二区 | 国产乱子伦视频在线播放 | 国产香蕉尹人视频在线 | 男人的天堂2018无码 | 精品无人国产偷自产在线 | 福利一区二区三区视频在线观看 | 九九综合va免费看 | 人人妻人人澡人人爽人人精品浪潮 | 日本精品人妻无码免费大全 | 国产网红无码精品视频 | 国产精品国产自线拍免费软件 | 日本大香伊一区二区三区 | 国产成人精品视频ⅴa片软件竹菊 | 一二三四在线观看免费视频 | 高清不卡一区二区三区 | 亚洲欧美中文字幕5发布 | 成人欧美一区二区三区黑人免费 | 无套内谢的新婚少妇国语播放 | 国内揄拍国内精品少妇国语 | 国产网红无码精品视频 | 无码人妻精品一区二区三区下载 | 亚洲第一无码av无码专区 | 国产亚av手机在线观看 | 97精品国产97久久久久久免费 | 蜜桃无码一区二区三区 | 精品无码一区二区三区爱欲 | 300部国产真实乱 | 天天躁日日躁狠狠躁免费麻豆 | 色一情一乱一伦 | 久久亚洲国产成人精品性色 | 亚洲精品鲁一鲁一区二区三区 | 女人高潮内射99精品 | 国产内射老熟女aaaa | 欧美人与善在线com | 国产精品高潮呻吟av久久4虎 | 又紧又大又爽精品一区二区 | 夜先锋av资源网站 | 任你躁在线精品免费 | 国产内射老熟女aaaa | 精品熟女少妇av免费观看 | 久激情内射婷内射蜜桃人妖 | 澳门永久av免费网站 | 精品国产av色一区二区深夜久久 | 中文精品久久久久人妻不卡 | 全黄性性激高免费视频 | 久久www免费人成人片 | 西西人体www44rt大胆高清 | 国产另类ts人妖一区二区 | 国产激情一区二区三区 | 久久久无码中文字幕久... | 久久久久亚洲精品中文字幕 | 无码吃奶揉捏奶头高潮视频 | 国产亚洲精品久久久ai换 | 国产极品美女高潮无套在线观看 | 老熟妇仑乱视频一区二区 | 夜先锋av资源网站 | 成人片黄网站色大片免费观看 | 亚洲gv猛男gv无码男同 | 樱花草在线社区www | 国产成人一区二区三区在线观看 | 一本久久a久久精品vr综合 | 色婷婷久久一区二区三区麻豆 | 亚洲大尺度无码无码专区 | 亚洲欧美国产精品专区久久 | 久久99精品久久久久久动态图 | 日韩av无码中文无码电影 | 国产激情艳情在线看视频 | 久久久久亚洲精品中文字幕 | 久久精品女人天堂av免费观看 | 国产精品久久久久久久9999 | 亚洲精品中文字幕乱码 | 久久综合狠狠综合久久综合88 | 亚洲精品欧美二区三区中文字幕 | 国产麻豆精品一区二区三区v视界 | 亚洲国产欧美国产综合一区 | 国产无av码在线观看 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 国产精品无码mv在线观看 | 亚洲人成影院在线无码按摩店 | 亚洲一区二区三区香蕉 | 色综合久久网 | 欧美国产日产一区二区 | 欧美国产日韩亚洲中文 | 亚洲成a人片在线观看无码3d | 国产精品高潮呻吟av久久4虎 | 欧美人妻一区二区三区 | 婷婷综合久久中文字幕蜜桃三电影 | 国产口爆吞精在线视频 | 日韩无套无码精品 | 国产午夜福利100集发布 | 亚洲精品成人av在线 | 精品久久久无码中文字幕 | 人妻人人添人妻人人爱 | 成熟女人特级毛片www免费 | 一本色道久久综合亚洲精品不卡 | 久久久精品欧美一区二区免费 | 天天躁日日躁狠狠躁免费麻豆 | 日韩亚洲欧美精品综合 | 成人三级无码视频在线观看 | 国产高清不卡无码视频 | 日韩欧美群交p片內射中文 | 国产高清av在线播放 | а√天堂www在线天堂小说 | 亚洲中文字幕av在天堂 | 久久国产精品_国产精品 | 好男人社区资源 | 女人被男人躁得好爽免费视频 | 中文字幕无码日韩专区 | 荫蒂被男人添的好舒服爽免费视频 | 乱中年女人伦av三区 | 乱人伦人妻中文字幕无码久久网 | 一本久道久久综合狠狠爱 | 少妇一晚三次一区二区三区 | 中文字幕乱码亚洲无线三区 | 亚洲综合在线一区二区三区 | 欧美激情内射喷水高潮 | 色一情一乱一伦一区二区三欧美 | 亚洲精品一区二区三区婷婷月 | 亚洲欧洲日本综合aⅴ在线 | 天堂无码人妻精品一区二区三区 | 国产成人无码av片在线观看不卡 | 日本丰满熟妇videos | 亚洲日本va中文字幕 | 亚洲成av人影院在线观看 | 樱花草在线播放免费中文 | 精品无码一区二区三区爱欲 | 西西人体www44rt大胆高清 | 日韩精品无码一区二区中文字幕 | 无码播放一区二区三区 | 国产人成高清在线视频99最全资源 | 国产又粗又硬又大爽黄老大爷视 | 国产真人无遮挡作爱免费视频 | 乱人伦人妻中文字幕无码久久网 | 国产女主播喷水视频在线观看 | 欧美日韩视频无码一区二区三 | 无遮挡国产高潮视频免费观看 | 国产特级毛片aaaaaaa高清 | 国产人妻精品一区二区三区 | 亚洲精品中文字幕久久久久 | 无码人妻久久一区二区三区不卡 | 老子影院午夜精品无码 | 国产成人久久精品流白浆 | 日本xxxx色视频在线观看免费 | 好男人社区资源 | 伊人久久大香线蕉午夜 | 最近免费中文字幕中文高清百度 | 亚洲国产综合无码一区 | 亚洲成av人片在线观看无码不卡 | 大屁股大乳丰满人妻 | 老子影院午夜伦不卡 | 精品久久8x国产免费观看 | 人妻体内射精一区二区三四 | 国产精品二区一区二区aⅴ污介绍 | 狂野欧美激情性xxxx | 香蕉久久久久久av成人 | 巨爆乳无码视频在线观看 | 精品水蜜桃久久久久久久 | 国产福利视频一区二区 | 亚洲va中文字幕无码久久不卡 | 亚洲成av人在线观看网址 | 久精品国产欧美亚洲色aⅴ大片 | 狠狠色色综合网站 | 亚洲а∨天堂久久精品2021 | 在线播放免费人成毛片乱码 | 久久综合九色综合欧美狠狠 | 国产人成高清在线视频99最全资源 | 日本乱人伦片中文三区 | 国产精品va在线观看无码 | 老熟女重囗味hdxx69 | 清纯唯美经典一区二区 | 成人精品视频一区二区三区尤物 | 伦伦影院午夜理论片 | 成熟妇人a片免费看网站 | √天堂资源地址中文在线 | 免费国产成人高清在线观看网站 | 狠狠噜狠狠狠狠丁香五月 | 男女作爱免费网站 | 久久久久亚洲精品男人的天堂 | 免费无码的av片在线观看 | 啦啦啦www在线观看免费视频 | 亚洲国产午夜精品理论片 | 国产成人av免费观看 | 亚洲日本在线电影 | 人人妻人人澡人人爽欧美精品 | 一区二区三区高清视频一 | 国产在热线精品视频 | 精品日本一区二区三区在线观看 | 久久综合给合久久狠狠狠97色 | 色婷婷欧美在线播放内射 | 伊人久久大香线蕉亚洲 | 中文字幕无码视频专区 | 曰本女人与公拘交酡免费视频 | 久9re热视频这里只有精品 | 国产精品沙发午睡系列 | 精品熟女少妇av免费观看 | 97久久精品无码一区二区 | 少妇无码av无码专区在线观看 | 亚洲中文字幕成人无码 | 两性色午夜视频免费播放 | 色婷婷久久一区二区三区麻豆 | 麻豆国产人妻欲求不满 | 伊人久久婷婷五月综合97色 | 麻豆精品国产精华精华液好用吗 | 日日天干夜夜狠狠爱 | 自拍偷自拍亚洲精品10p | 欧美日本免费一区二区三区 | 中文无码精品a∨在线观看不卡 | 亚洲中文字幕无码中文字在线 | 男女爱爱好爽视频免费看 | a在线亚洲男人的天堂 | 999久久久国产精品消防器材 | 欧美三级不卡在线观看 | 亚洲综合精品香蕉久久网 | 日本高清一区免费中文视频 | 亚洲国产欧美日韩精品一区二区三区 | 骚片av蜜桃精品一区 | 中文字幕无码av激情不卡 | 伊人久久大香线焦av综合影院 | 欧洲欧美人成视频在线 | 亚洲精品国产精品乱码不卡 | 丝袜美腿亚洲一区二区 | 99精品国产综合久久久久五月天 | 国产色xx群视频射精 | 国产内射老熟女aaaa | 久久精品丝袜高跟鞋 | 中文字幕无线码免费人妻 | 日韩精品无码一区二区中文字幕 | 亚洲精品国产第一综合99久久 | 久久精品视频在线看15 | 亚洲精品国产a久久久久久 | 97资源共享在线视频 | 欧美精品一区二区精品久久 | 天天综合网天天综合色 | 国产成人精品无码播放 | 色综合久久久无码网中文 | 久久久精品国产sm最大网站 | 久久99精品久久久久久 | av人摸人人人澡人人超碰下载 | 99国产精品白浆在线观看免费 | 成人亚洲精品久久久久 | 亚洲精品一区二区三区四区五区 | 欧美 丝袜 自拍 制服 另类 | 日韩视频 中文字幕 视频一区 | 任你躁国产自任一区二区三区 | 狠狠躁日日躁夜夜躁2020 | 老头边吃奶边弄进去呻吟 | √天堂资源地址中文在线 | 久久久精品456亚洲影院 | 黑人粗大猛烈进出高潮视频 | 帮老师解开蕾丝奶罩吸乳网站 | 国产艳妇av在线观看果冻传媒 | 日韩成人一区二区三区在线观看 | 在线播放免费人成毛片乱码 | 中文毛片无遮挡高清免费 | 亚洲国产精品无码一区二区三区 | 成熟妇人a片免费看网站 | 日韩精品乱码av一区二区 | 欧美怡红院免费全部视频 | 欧美一区二区三区 | 国产三级精品三级男人的天堂 | 亚洲国产av美女网站 | 台湾无码一区二区 | 在线观看国产午夜福利片 | 三级4级全黄60分钟 | 国产亚洲视频中文字幕97精品 | 蜜臀av在线播放 久久综合激激的五月天 | 黑人巨大精品欧美一区二区 | 久久久久se色偷偷亚洲精品av | 国模大胆一区二区三区 | 香蕉久久久久久av成人 | 天天综合网天天综合色 | 久久99久久99精品中文字幕 | 亚洲中文字幕无码中文字在线 | 少妇无码av无码专区在线观看 | 精品日本一区二区三区在线观看 | 久久精品无码一区二区三区 | 国产无遮挡又黄又爽免费视频 | 99精品国产综合久久久久五月天 | a在线观看免费网站大全 | 欧美黑人乱大交 | 亚洲精品久久久久久一区二区 | 国产精品久久久久7777 | 国产偷抇久久精品a片69 | 中文无码成人免费视频在线观看 | 中文字幕日产无线码一区 | 在线精品亚洲一区二区 | 最新版天堂资源中文官网 | 亚洲熟女一区二区三区 | 香蕉久久久久久av成人 | 亚洲日韩中文字幕在线播放 | 国产av久久久久精东av | 日日碰狠狠丁香久燥 | 少妇无套内谢久久久久 | 高潮毛片无遮挡高清免费 | 亚洲精品综合五月久久小说 | 熟妇女人妻丰满少妇中文字幕 | 亚洲国产欧美日韩精品一区二区三区 | 国产又爽又黄又刺激的视频 | 亚洲欧洲无卡二区视頻 | 无码人妻丰满熟妇区毛片18 | 欧美国产日韩久久mv | 夜夜高潮次次欢爽av女 | 久久亚洲a片com人成 | 国产黄在线观看免费观看不卡 | 一区二区三区乱码在线 | 欧洲 | 在线看片无码永久免费视频 | 少妇厨房愉情理9仑片视频 | 强奷人妻日本中文字幕 | 国产激情无码一区二区 | 粗大的内捧猛烈进出视频 | 天天av天天av天天透 | 久9re热视频这里只有精品 | 天堂亚洲2017在线观看 | 波多野42部无码喷潮在线 | 国产乱子伦视频在线播放 | аⅴ资源天堂资源库在线 | 久久国产自偷自偷免费一区调 | 无码av最新清无码专区吞精 | 午夜理论片yy44880影院 | 国内少妇偷人精品视频免费 | 欧美日韩在线亚洲综合国产人 | 天堂а√在线中文在线 | 亚洲综合久久一区二区 | 亚洲一区二区观看播放 | 国产精品无码成人午夜电影 | 久久99久久99精品中文字幕 | 水蜜桃亚洲一二三四在线 | 国产色xx群视频射精 | 久久精品成人欧美大片 | 亚洲日韩av一区二区三区四区 | 国产激情综合五月久久 | 老熟妇乱子伦牲交视频 | 亚洲精品成a人在线观看 | 东京热男人av天堂 | 2020久久香蕉国产线看观看 | 好男人社区资源 | 久久无码专区国产精品s | 成人影院yy111111在线观看 | 77777熟女视频在线观看 а天堂中文在线官网 | 国产亚洲精品久久久久久国模美 | 伊人久久大香线蕉午夜 | 欧美丰满少妇xxxx性 | 免费网站看v片在线18禁无码 | 自拍偷自拍亚洲精品被多人伦好爽 | 在教室伦流澡到高潮hnp视频 | 在线成人www免费观看视频 | 国产美女极度色诱视频www | 久久午夜无码鲁丝片午夜精品 | 成熟妇人a片免费看网站 | 午夜精品一区二区三区的区别 | 国产精品无码永久免费888 | 少妇厨房愉情理9仑片视频 | 国产精品久久久久久久影院 | 国产做国产爱免费视频 | 亚洲中文字幕无码中文字在线 | 国産精品久久久久久久 | 男女猛烈xx00免费视频试看 | 夜精品a片一区二区三区无码白浆 | 国产午夜福利100集发布 | 国产精品igao视频网 | 人人爽人人爽人人片av亚洲 | 国内少妇偷人精品视频 | 色综合视频一区二区三区 | 精品国产国产综合精品 | 亚洲精品久久久久久久久久久 | 国产免费无码一区二区视频 | 老熟女重囗味hdxx69 | 亚洲日本va午夜在线电影 | 一本色道久久综合亚洲精品不卡 | 成人性做爰aaa片免费看 | 亚洲精品综合一区二区三区在线 | 国产精品久久久久久久9999 | 两性色午夜视频免费播放 | 强伦人妻一区二区三区视频18 | 成在人线av无码免观看麻豆 | 狠狠综合久久久久综合网 | 狂野欧美激情性xxxx | 少妇性l交大片欧洲热妇乱xxx | 国产精品国产三级国产专播 | 激情国产av做激情国产爱 | 麻花豆传媒剧国产免费mv在线 | 日本www一道久久久免费榴莲 | 亚洲国产午夜精品理论片 | 骚片av蜜桃精品一区 | 免费视频欧美无人区码 | 国产口爆吞精在线视频 | 内射爽无广熟女亚洲 | 无码任你躁久久久久久久 | 福利一区二区三区视频在线观看 | 中文字幕乱码人妻二区三区 | 国产人妻精品午夜福利免费 | 内射爽无广熟女亚洲 | 国产真实夫妇视频 | 国产精品无码一区二区三区不卡 | 亚洲色无码一区二区三区 | 亚洲 另类 在线 欧美 制服 | 丰满少妇熟乱xxxxx视频 | 国产精品美女久久久网av | 国产精品内射视频免费 | 天堂а√在线中文在线 | 天堂一区人妻无码 | 国内精品人妻无码久久久影院 | 7777奇米四色成人眼影 | 中文字幕日韩精品一区二区三区 | 无码人妻出轨黑人中文字幕 | 日本xxxx色视频在线观看免费 | 国产特级毛片aaaaaa高潮流水 | 99精品国产综合久久久久五月天 | 无码午夜成人1000部免费视频 | 亚洲精品一区三区三区在线观看 | 麻豆国产人妻欲求不满谁演的 | 成人试看120秒体验区 | 久久午夜无码鲁丝片 | 久久久久久av无码免费看大片 | 国产精品理论片在线观看 | 久久亚洲中文字幕精品一区 | 亚洲中文字幕成人无码 | 成人无码精品1区2区3区免费看 | 亚洲日本va午夜在线电影 | 国产国产精品人在线视 | 日本在线高清不卡免费播放 | 国内精品一区二区三区不卡 | 天天爽夜夜爽夜夜爽 | 色五月五月丁香亚洲综合网 | 国产激情艳情在线看视频 | 国产成人无码a区在线观看视频app | 人人妻人人澡人人爽欧美一区九九 | 性欧美大战久久久久久久 | 国产情侣作爱视频免费观看 | 久久人人97超碰a片精品 | 亚洲爆乳精品无码一区二区三区 | 午夜性刺激在线视频免费 | 久久这里只有精品视频9 | 给我免费的视频在线观看 | 天干天干啦夜天干天2017 | 国产熟女一区二区三区四区五区 | 四虎4hu永久免费 | 久久综合狠狠综合久久综合88 | 少妇高潮喷潮久久久影院 | 免费人成在线视频无码 | 中文字幕乱码人妻无码久久 | 欧美精品国产综合久久 | 亚洲人成人无码网www国产 | 曰韩无码二三区中文字幕 |