GNU Make 中文手冊v3.8 學(xué)習(xí)_第1頁
GNU Make 中文手冊v3.8 學(xué)習(xí)_第2頁
GNU Make 中文手冊v3.8 學(xué)習(xí)_第3頁
GNU Make 中文手冊v3.8 學(xué)習(xí)_第4頁
GNU Make 中文手冊v3.8 學(xué)習(xí)_第5頁
已閱讀5頁,還剩17頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、 這個手冊翻譯整理: 徐海兵, 先表示一些尊敬. “本人在工作之余,花了18個多月時間完成對“info make”的翻譯整理,完成這個中文版手冊”, 工夫不負(fù)有心人. 鏈接器將.o文件中使用的函數(shù)和其它.o或者庫文件中的相關(guān)符號進(jìn)行合并,對所有文件中的符號進(jìn)行重新安排(重定位),并鏈接系統(tǒng)相關(guān)文件(程序啟動文件等)最終生成可執(zhí)行程序。 Linux中,共享庫文件格式通常為“ELF”格式。共享庫已經(jīng)具備了可執(zhí)行條件。 模塊中各個成員的地址(變量引用和函數(shù)調(diào)用)都是相對地址。使用此共享庫的程序在運(yùn)行時,共享庫被動態(tài)加載到內(nèi)存并和主程序在內(nèi)存中進(jìn)行連接。多個可執(zhí)行程序可共享庫文件的代碼段(多個程序可以

2、共享的使用庫中的某一個模塊,共享代碼,不共享數(shù)據(jù))。另外共享庫的成員對象可被執(zhí)行(由libdl.so提供支持)。 在make讀取Makefile以后會建立一個編譯過程的描述數(shù)據(jù)庫。此數(shù)據(jù)庫中記錄了所有各個文件之間的相互關(guān)系,以及它們的關(guān)系描述(要是我能看到這個數(shù)據(jù)庫,就能使用程序trace,你知道一個大型的project的makefile有多復(fù)雜) 當(dāng)使用make工具進(jìn)行編譯時,工程中以下幾種文件在執(zhí)行make時將會被編譯(重新編譯): 1. 所有的源文件沒有被編譯過,則對各個C源文件進(jìn)行編譯并進(jìn)行鏈接,生成最后的可執(zhí)行程序; 2. 每一個在上次執(zhí)行make之后修改過的C源代碼文件在本次執(zhí)行m

3、ake時將會被重新編譯; 3. 頭文件在上一次執(zhí)行make之后被修改。則所有包含此頭文件的C源文件在本次執(zhí)行make時將會被重新編譯。(這就是為什么頭文件修改后,很多文件都會被重新compile的原因) 首先書寫時,可以將一個較長行使用反斜線()來分解為多行,這樣可以使我們的Makefile書寫清晰、容易閱讀理解。但需要注意:反斜線之后不能有空格(這也是大家最容易犯的錯誤,錯誤比較隱蔽) 編譯.c源文件規(guī)則的命令可以不用明確給出。這是因?yàn)閙ake本身存在一個默認(rèn)的規(guī)則,能夠自動完成對.c文件的編譯并生成對應(yīng)的.o文件。它執(zhí)行命令“cc -c”來編譯.c源文件。 對一個目標(biāo)文件是“N.o”,倚賴

4、文件是“N.c”的規(guī)則,完全可以省略其規(guī)則的命令行,而由make自身決定使用默認(rèn)命令。此默認(rèn)規(guī)則稱為make的隱含規(guī)則。 書寫規(guī)則建議的方式是:單目標(biāo),多依賴。就是說盡量要做到一個規(guī)則中只存在一個目標(biāo)文件,可有多個依賴文件。盡量避免多目標(biāo),單依賴的方式。這樣后期維護(hù)也會非常方便,而且Makefile會更清晰、明了。 在這個Makefile中,根據(jù)依賴而不是目標(biāo)對規(guī)則進(jìn)行分組(Wrong!)。上例的Makefile就可以這樣來實(shí)現(xiàn): #sample Makefileobjects = main.o kbd.o command.o display.o insert.o search.o files

5、.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例子中頭文件“defs.h”作為所有.o文件的依賴文件。其它兩個頭文件作為其對應(yīng)規(guī)則的目標(biāo)中所列舉的所有.o文件的依賴文件。 但是這種風(fēng)格的Makefile并不值得我們借鑒。問題在于:同時把多個目標(biāo)文件的依賴放在同一個規(guī)則中進(jìn)行描述(一個規(guī)則中含有多個目標(biāo)文件),這樣導(dǎo)致規(guī)則定義不明了,比較混

6、亂。建議大家不要在Makefile中采用這種方式了書寫。否則后期維護(hù)將會是一件非常痛苦的事情。 “include”指示符告訴make暫停讀取當(dāng)前的Makefile,而轉(zhuǎn)去讀取“include”指定的一個或者多個文件,完成以后再繼續(xù)當(dāng)前Makefile的讀取。通常指示符“include”用在以下場合: 1. 有多個不同的程序,由不同目錄下的幾個獨(dú)立的Makefile來描述其重建規(guī)則。它們需要使用一組通用的變量定義或者模式規(guī)則。 2. 當(dāng)根據(jù)源文件自動產(chǎn)生依賴文件時;我們可以將自動產(chǎn)生的依賴關(guān)系保存在另外一個文件中,主Makefile使用指示符“include”包含這些文件。這 樣的做法比直接在主

7、Makefile中追加依賴文件的方法要明智的多。其它版本的make已經(jīng)使用這種方式來處理。(我想看看這方面的例子)當(dāng) 在這些目錄下都沒有找到“include”指定的文件時,make將會提示一個包含文件未找到的告警提示,但是不會立 刻退出。而是繼續(xù)處理Makefile的后續(xù)內(nèi)容。當(dāng)完成讀取整個Makefile后,make將試圖使用規(guī)則來創(chuàng)建通過指示符“include”指定的 但未找到的文件,當(dāng)不能創(chuàng)建它時(沒有創(chuàng)建這個文件的規(guī)則),make將提示致命錯誤并退出。 通常我們在Makefile中可使用“-include”來代替“include”,來忽略由于包含文件不存在或者無法創(chuàng)建時的錯誤提示(“-

8、”的意思是告訴make,忽略此操作的錯誤。make繼續(xù)執(zhí)行)。 Makefile中符號“$”有特殊的含義(表示變量或者函數(shù)的引用),在規(guī)則中需要使用符號“$”的地方,需要書寫兩個連續(xù)的(“$”)。 Maekfile中表示文件名時可使用通配符??墒褂玫耐ㄅ浞校骸?”、“?”和“”。在Makefile中這些統(tǒng)配符并不是可以用在任何地方,Makefile中統(tǒng)配符可以出現(xiàn)在以下兩種場合: 1. 可以用在規(guī)則的目標(biāo)、依賴中,make在讀取Makefile時會自動對其進(jìn)行匹配處理(通配符展開); 2. 可出現(xiàn)在規(guī)則的命令中,通配符的通配處理是在shell在執(zhí)行此命令時完成的。 除這兩種情況之外的其它上下

9、文中,不能直接使用通配符。而是需要通過函數(shù)“wildcard”來實(shí)現(xiàn)。 比如變量定義中使用的通配符不會被統(tǒng)配處理(因此在變量定義中不能使用通配符,否則在某些情況下會出現(xiàn)非預(yù)期的結(jié)果,下一小節(jié)將會詳細(xì)討論)。在 Makefile有這樣一個變量定義:“objects = *.o”。它表示變量“objects”的值是字符串“*.o”(并不是期望的空格分開的.o文件列表)。當(dāng)需要變量“objects”代表所有.o文件 列表示,需要使用函數(shù)“wildcard”(objects = $(wildcar *.o))。在規(guī)則中,通配符會被自動展開。但在變量的定義和函數(shù)引用時,通配符將失效。這種情況下如果需要通

10、配符有效,就需要使用函數(shù)“wildcard”,它的用法是:$(wildcard PATTERN.) 。 在Makefile中,它被展開為已經(jīng)存在的、使用空格分開的、匹配此模式的所有文件列表。如果不存在任何符合此模式的文件,函數(shù)會忽略模式字符并返回空。 一 般我們可以使用“$(wildcard *.c)”來獲取工作目錄下的所有的.c文件列表。復(fù)雜一些用法;可以使用“$(patsubst %.c,%.o,$(wildcard *.c)”,首先使用“wildcard”函數(shù)獲取工作目錄下的.c文件列表;之后將列表中所有文件名的后綴.c替換為.o。這樣我們就可以得到在當(dāng)前 目錄可生成的.o文件列表。因此

11、在一個目錄下可以使用如下內(nèi)容的Makefile來將工作目錄下的所有的.c文件進(jìn)行編譯并最后連接成為一個可執(zhí)行文件: #sample Makefile objects := $(patsubst %.c,%.o,$(wildcard *.c) foo : $(objects) cc -o foo $(objects)使用偽目標(biāo)有兩點(diǎn)原因:1. 避免在我們的Makefile中定義的只執(zhí)行命令的目標(biāo)(此目標(biāo)的目的為了執(zhí)行執(zhí)行一些列命令,而不需要創(chuàng)建這個目標(biāo))和工作目錄下的實(shí)際文件出現(xiàn)名字沖突。2. 提高執(zhí)行make時的效率,特別是對于一個大型的工程來說,編譯的效率也許你同樣關(guān)心。如:在make的并行

12、和遞歸執(zhí)行過程中。此情況下一般會存在一個變量,定義為所有需要make的子目錄。對多個目錄進(jìn)行make的實(shí)現(xiàn)方式可以是:在一個規(guī)則的命令行中使用shell循環(huán)來完成。如下:SUBDIRS = foo bar baz subdirs: for dir in $(SUBDIRS); do $(MAKE) -C $dir; done 但這種實(shí)現(xiàn)方法存在以下幾個問題:1. 當(dāng)子目錄執(zhí)行make出現(xiàn)錯誤時,make不會退出。就是說,在對某一個目錄執(zhí)行make失敗以后,會繼續(xù)對其他的目錄進(jìn)行make。在最終執(zhí)行失敗的情 況下,我們很難根據(jù)錯誤提示定位出具體是在那個目錄下執(zhí)行make時發(fā)生錯誤。這樣給問題定位

13、造成了很大的困難。為了解決這個問題,可以在命令行部分加入 錯誤監(jiān)測,在命令執(zhí)行錯誤后主動退出。不幸的是,如果在執(zhí)行make時使用了“-k”選項(xiàng),此方式將失效。2. 另外一個問題就是使用這種shell的循環(huán)方式時,沒有用到make對目錄的并行處理功能,由于規(guī)則的命令是一條完整的shell命令,不能被并行處理。 有了偽目標(biāo)之后,我們可以用它來克服以上實(shí)現(xiàn)方式所存在的兩個問題。SUBDIRS = foo bar baz .PHONY: subdirs $(SUBDIRS) subdirs: $(SUBDIRS) $(SUBDIRS):$(MAKE) -C $foo: baz 上邊的實(shí)現(xiàn)中有一個沒有命

14、令行的規(guī)則“foo: baz”,此規(guī)則用來限制子目錄的make順序。它的作用是限制同步目錄“foo”和“baz”的make過程(在處理“foo”目錄之前,需要等待 “baz”目錄處理完成)。提醒大家:在書寫一個并行執(zhí)行make的Makefile時,目錄的處理順序是需要特別注意的。(在broadcom的project中,clean的工作就是用了SUBDIRS類似的用法:SUBDIRS_APP = $(SUBDIRS_BROADCOM) $(SUBDIRS_OPENSOURCE) $(SUBDIRS_ALPHASOURCE)SUBDIRS = $(foreach dir, $(SUBDIRS_AP

15、P), $(shell if -d $(dir) ; then echo $(dir); fi)而后面subdirs: $(patsubset %, _dir_%, $(SUBDIRS)$(patsubset %, _dir_%, $(SUBDIRS): $(MAKE) -C $(patsubset _dir_%, %, $) $(TGT)) make存在一個內(nèi)嵌隱含變量“RM”,它被定義為:“RM = rm f”。 靜態(tài)模式規(guī)則是這樣一個規(guī)則:規(guī)則存在多個目標(biāo),并且不同的目標(biāo)可以根據(jù)目標(biāo)文件的名字來自動構(gòu)造出依賴文件。靜態(tài)模式規(guī)則比多目標(biāo)規(guī)則更通用,它不需要多個目標(biāo)具有相同的依賴。但是靜態(tài)模

16、式規(guī)則中的依賴文件必須是相類似的而不是完全相同的。首先,我們來看一下靜態(tài)模式規(guī)則的基本語法: TARGETS .: TARGET-PATTERN: PREREQ-PATTERNS . COMMANDS首 先在目標(biāo)模式和依賴模式中,一般需要包含模式字符“%”。在目標(biāo)模式(TAGET-PATTERN)中“%”可以匹配目標(biāo) 文件的任何部分,模式字符“%”匹配的部分就是“莖”。目標(biāo)文件和目標(biāo)模式的其余部分必須精確的匹配??匆粋€例子:目標(biāo)“foo.o”符合模式 “%.o”,其“莖”為“foo”。而目標(biāo)“foo.c”和“foo.out”就不符合此目標(biāo)模式。 每一個目標(biāo)的依賴文件是使用此目標(biāo)的“莖”代替依賴

17、模式(PREREQ-PATTERNS)中的模式字符“%”而得到。例如:上邊的例子中依賴模式 (PREREQ-PATTERNS)為“%.c”,那么使用“莖”“foo”替代依賴模式中的“%”得到的依賴文件就是“foo.c”。需要明確的一點(diǎn) 是:在模式規(guī)則的依賴列表中使用不包含模式字符“%”也是合法的。代表這個文件是所有目標(biāo)的依賴文件。 我們來看一個例子,它根據(jù)相應(yīng)的.c文件來編譯生成“foo.o”和“bar.o”文件: objects = foo.o bar.o all: $(objects) $(objects): %.o: %.c $(CC) -c $(CFLAGS) $ -o $如果存在一個

18、文件列表,其中一部分符合某一種模式而另外一部分符合另外一種模式,這種情況下我們可以使用“filter”函數(shù)來對這個文件列表進(jìn)行分類,在分類之后對確定的某一類使用模式規(guī)則。例如:files = foo.elc bar.o lose.o$(filter %.o,$(files): %.o: %.c$(CC) -c $(CFLAGS) $ -o $(filter %.elc,$(files): %.elc: %.elemacs -f batch-byte-compile $ $當(dāng)執(zhí)行此規(guī)則的命令時,自動環(huán)變量“$*”被展開為“莖”。在這里就是“big”和“l(fā)ittle”。 典型的用法是在使用“ech

19、o”命令輸出一些信息時。如:echo 開始編譯XXX模塊.執(zhí)行時,將會得到“開始編譯XXX模塊.”這條輸出信息。如果在命令行之前沒有字符“”,那么,make的輸出將是:echo編譯XXX模塊.編譯XXX模塊. 另 外,如果使用make的命令行參數(shù)“-n”或“-just-print”,那么make執(zhí)行時只顯示所要執(zhí)行的命令,但不會真正的去執(zhí)行這些命令。只有 在這種情況下make才會打印出所有make需要執(zhí)行的命令,其中也包括了使用“”字符開始的命令。這個選項(xiàng)對于我們調(diào)試Makefile非常有用,使 用這個選項(xiàng)我們可以按執(zhí)行順序打印出Makefile中所有需要執(zhí)行的所有命令。 命令的執(zhí)行規(guī)則中,當(dāng)

20、目標(biāo)需要被重建時。此規(guī)則所定義的命令將會被執(zhí)行,如果是多行命令,那么每一行命令將在一個獨(dú)立的子shell進(jìn)程中被執(zhí)行(就是說,每一行命 令的執(zhí)行是在一個獨(dú)立的shell進(jìn)城中完成)。因此,多行命令之間的執(zhí)行是相互獨(dú)立的,相互之間不存在依賴(多條命令行的執(zhí)行為多個相互獨(dú)立的進(jìn)程)。在Makefile中書寫在同一行中的多個命令屬于一個完整的shell命令行,書寫在獨(dú)立行的一條命令是一個獨(dú)立的shell命令行。因此:在一個規(guī)則的命令中,命令行“cd”改變目錄不會對其后的命令的執(zhí)行產(chǎn)生影響。就是說其后的命令執(zhí)行的工作目錄不會是之前使用“cd”進(jìn)入的那個目錄。如果要實(shí)現(xiàn)這個目的,就不能把“cd”和其后的

21、命令放在兩行來書寫。而應(yīng)該把這兩條命令寫在一行上,用分號分隔。這樣它們才是一個完整的shell命令行。如:foo : bar/losecd bar; gobble lose ./foo 中斷make的執(zhí)行make在執(zhí)行命令時如果收到一個致命信號(終止make),那么make將會刪除此過程中已經(jīng)重建的那些規(guī)則的目標(biāo)文件。其依據(jù)是此目標(biāo)文件的當(dāng)前時間戳和make開始執(zhí)行時此文件的時間戳是否相同。刪除這個目標(biāo)文件的目的是為了確保下一次make時目標(biāo)文件能夠被正確重建。其原因我們上一節(jié)已經(jīng)有所討論。假設(shè)正在編譯時鍵入“Ctrl-c”,此時編 譯器已經(jīng)開始寫文件“foo.o”,但是“Ctrl-c”產(chǎn)生的

22、信號關(guān)閉了編譯器。這種情況下文件“foo.o”可能是不完整的,但這個內(nèi)容不完整的 “foo.o”文件的時間戳比源程序foo.c的時間戳新。如果在make收到終止信號后不刪除文件“foo.o”而直接退出,那么下次執(zhí)行make 時此文件被認(rèn)為已是最新的而不會去重建它。最后在鏈接生成終極目標(biāo)時由于某一個.o文件的不完整,可能出現(xiàn)一堆令人難以理解的錯誤信息,或者產(chǎn)生了一個不 正確的終極目標(biāo)。所以在重新make之前,一定要make clean一下. 上層make過程要將所執(zhí)行的Makefile中的變量傳遞給子make過程,需要明確地指出。在GNU make中,實(shí)現(xiàn)此功能的指示符是“export”。當(dāng)一個

23、變量使用“export”進(jìn)行聲明后,變量和它的值將被加入到當(dāng)前工作的環(huán)境變量中,以后在 make執(zhí)行的所有規(guī)則的命令都可以使用這個變量。而當(dāng)沒有使用指示符“export”對任何變量進(jìn)行聲明的情況下,上層make只將那些已經(jīng)初始化的環(huán) 境變量(在執(zhí)行make之前已經(jīng)存在的環(huán)境變量)和使用命令行指定的變量(如命令“make CFLAGS +=-g”或者“make e CFLAGS +=-g”)傳遞給子make程序, 存在兩個特殊的變量“SHELL”和“MAKEFLAGS”,對于這兩個變量除非使用指示符“unexport”對它們進(jìn)行聲明,它們在整個make的執(zhí)行過程中始終被自動的傳遞給所有的子mak

24、e。 當(dāng)不希望將一個變量傳遞給子make時,可以使用指示符“unexport”來聲明這個變量。格式如下:unexport VARIABLE . 一個不帶任何參數(shù)的指示符“export”指示符:export含義是將此Makefile中定義的所有變量傳遞給子make過程。需要說明的是:單獨(dú)使用“export”來導(dǎo)出所有變量的行為是老版本GNU make所默認(rèn)的。但是在新版本的GNU make中取消了這一默認(rèn)的行為。因此在編寫和老版本GNU make兼容的Makefile時,需要使用特殊目標(biāo)“.EXPORT_ALL_VARIABLES”來代替“export”, -w選項(xiàng)在多級make的遞歸調(diào)用過程中

25、,選項(xiàng)“-w”或者“-print-directory”可以讓make在開始編譯一個目錄之前和完成此目錄的編譯之后 給出相應(yīng)的提示信息,方便開發(fā)人員跟蹤make的執(zhí)行過程。例如,在目錄“/u/gnu/make”目錄下執(zhí)行“make -w”,將會看到如下的一些信息:在開始執(zhí)行之前我們將看到:make: Entering directory /u/gnu/make.而在完成之后我們同樣將會看到:make: Leaving directory /u/gnu/make.通常,選項(xiàng)“-w”會被自動打開。在主控Makefile中當(dāng)如果使用“-C”參數(shù)來為make指定一個目錄或者使用“cd”進(jìn)入一個目錄時,“

26、-w”選項(xiàng)會被自動打開。主控make可以使用選項(xiàng)“-s”(“-slient”)來禁止此選項(xiàng)。另外,make的命令行選項(xiàng)“-no-print-directory”,將禁止所有關(guān)于目錄信息的打印。 變 量名是大小寫敏感的。變量 “foo”、“Foo”和“FOO”指的是三個不同的變量。Makefile傳統(tǒng)做法是變量名是全采用大寫的方式。推薦的做法是在對于內(nèi)部定義定義的一般 變量(例如:目標(biāo)文件列表objects)使用小寫方式,而對于一些參數(shù)列表(例如:編譯選項(xiàng)CFLAGS)采用大寫方式 當(dāng)我們定義了一個變量之后,就可以在Makefile的很多地方使用這個變量。變量的引用方式是:“$(VARIABLE

27、_NAME)”或者“$ VARIABLE_NAME ”來引用一個變量的定義。 注意:Makefile中在對一些簡單變量的引用,我們也可以不使用“()”和“”來標(biāo)記變量名,而直 接使用“$x”的格式 來實(shí)現(xiàn),此種用法僅限于變量名為單字符的情況。另外自動化變量也使用這種格式。對于一般多字符變量的引用必須使用括號了標(biāo)記,否則make將把變量名的首 字母作為作為變量而不是整個字符串(“$PATH”在Makefile中實(shí)際上是“$(P)ATH”)。這一點(diǎn)和shell中變量的引用方式不同。 shell中變量的引用可以是“$xx”或者“$xx”格式。但在Makefile中多字符變量名的引用只能是“$(xx)

28、”或者“$xx”格 式。 一般在我們書寫Makefile時,各部分變量引用的格式我們建議如下:1. make變量(Makefile中定義的或者是make的環(huán)境變量)的引用使用“$(VAR)”格式,無論“VAR”是單字符變量名還是多字符變量名。2. 出現(xiàn)在規(guī)則命令行中shell變量(一般為執(zhí)行命令過程中的臨時變量,它不屬于Makefile變量,而是一個shell變量)引用使用shell的“$tmp”格式。3. 對出現(xiàn)在命令行中的make變量我們同樣使用“$(CMDVAR)” 格式來引用。例如:# sample MakefileSUBDIRS := src foo.PHONY : subdirSu

29、bdir :for dir in $(SUBDIRS); do $(MAKE) C $dir | exit 1; done 下邊我們來看一個復(fù)雜一點(diǎn)的例子。分析一下直接展開式變量定義(:=)的用法,這里也用到了make的shell函數(shù)和變量“MAKELEVEL”(此變量在make的遞歸調(diào)用時代表make的調(diào)用深度)。其中包括了對函數(shù)、條件表達(dá)式和系統(tǒng)變量“MAKELEVEL”的使用:ifeq (0,$MAKELEVEL) cur-dir := $(shell pwd) whoami := $(shell whoami) host-type := $(shell arch)MAKE := $MA

30、KE host-type=$host-type whoami=$whoamiendif第一行是一個條件判斷,如果是頂層Makefile,就定義下列變量。否則不定義任何變量。第二、三、四、五行分別定義了一個變量,在進(jìn)行變量定義時對引用到的其它變量和函數(shù)展開。最后結(jié)束定義。利用直接展開式的特點(diǎn)我們可以書寫這樣一個規(guī)則:$subdirs:$MAKE cur-dir=$cur-dir/$ -C $ all它實(shí)現(xiàn)了在不同子目錄下變量“cur_dir”使用不同的值(為當(dāng)前工作目錄)。 在 復(fù)雜的Makefile中,推薦使用直接展開式變量。因?yàn)檫@種風(fēng)格變量的使用方式和大多數(shù)編程語言中的變量使用方式基本上相同

31、。它可以使一個比較復(fù)雜的 Makefile在一定程度上具有可預(yù)測性。而且這種變量允許我們利用之前所定義的值來重新定義它(比如使用某一個函數(shù)來對它以前的值進(jìn)行處理并重新賦 值),此方式在Makefile中經(jīng)常用到。盡量避免和減少遞歸式變量的使用。 “自動化變量”,諸如“$”等。規(guī)則命令行中的自動化變量“$”代表所有通過目錄搜索得到的依賴文件的完整路徑名(目錄 + 一般文件名)列表。“$”代表規(guī)則的目標(biāo)。所以對于一個規(guī)則我們可以進(jìn)行如下的描述: foo.o : foo.c cc -c $(CFLAGS) $ -o $ 第六章:Makefile中的變量需要全部閱讀! 通常我們會使用它來判斷一個變量的

32、值是否為空(不是任何字符)。參數(shù)值可能是通過引用變量或者函數(shù)得到的,因而在展開過程中可能造成參數(shù)值中包含空字符(空格等)。一般在這種情況時我們使用make的“strip”函數(shù)來對它變量的值進(jìn)行處理,去掉其中的空字符。格式為:ifeq ($(strip $(foo),)TEXT-IF-EMPTYendif 條件判斷 o ifeq, ifneqo ifdef,ifndef對于“ifdef”需要說明的是:ifdef 只是測試一個變量是否有值,不會對變量進(jìn)行替換展開來判斷變量的值是否為空。對于變量“VARIABLE-NAME”,除了“VARIABLE- NAME=”這種情況和從未定義該變量以外,使用其

33、它方式對它的定義都會使“ifdef”返回真。就是說,即使我們通過其它方式(比如,定義它的值引用了 其它的變量)給它賦了一個空值,“ifdef”也會返回真。 因此當(dāng)我們需要判斷一個變量的值是否為空的情況時,需要使用“ifeq”(或者“ifneq”)而不是“ifdef”。 make的內(nèi)嵌函數(shù)GNU make函數(shù)的調(diào)用格式類似于變量的引用,以“$”開始表示一個引用。語法格式如下:$(FUNCTION ARGUMENTS)或者:$FUNCTION ARGUMENTS對于函數(shù)調(diào)用的格式有以下幾點(diǎn)說明:調(diào)用語法格式中“FUNCTION”是需要調(diào)用的函數(shù)名,它應(yīng)該是make內(nèi)嵌的函數(shù)名。對于用戶自己的函數(shù)需

34、要通過make的“call”函數(shù)來間接調(diào)用。函數(shù)處理參數(shù)時,參數(shù)中如果存在對其它變量或者函數(shù)的引用,首先對這些引用進(jìn)行展開得到參數(shù)的實(shí)際內(nèi)容,而后才對它們進(jìn)行處理。 以下是GNU make內(nèi)嵌的文本(字符串)處理函數(shù) o $(subst FROM,TO,TEXT)o $(patsubst PATTERN,REPLACEMENT,TEXT)o $(strip STRINT)“strip”函數(shù)經(jīng)常用在條件判斷語句的表達(dá)式中,確保表達(dá)式比較的可靠和健壯!o $(findstring FIND,IN)o $(filter PATTERN,TEXT)o $(filter-out PATTERN.,TEX

35、T)o $(word N,TEXT)o $(wordlist S,E,TEXT)o $(words TEXT)o $(firstword NAMES) 文件名處理函數(shù) o $(suffix NAMES)o $(basename NAMES)o $(addsuffix SUFFIX,NAMES)o $(addprefix PREFIX,NAMES)o $(join LIST1,LIST2)o $(wildcard PATTERN)o $(foreach VAR,LIST,TEXT) dirs := a b c dfiles := $(foreach dir,$(dirs),$(wildcard

36、$(dir)/*)= files := $(wildcard a/* b/* c/* d/*)o $(if CONDITION,THEN-PART,ELSE-PART) SUBDIR += $(if $(SRC_DIR) $(SRC_DIR),/home/src)o eval函數(shù)o shell函數(shù) 函數(shù)“shell”所實(shí)現(xiàn)的功能和shell中的引用()相同 函數(shù)的返回結(jié)果是此命令在shell中的執(zhí)行結(jié)果。make僅僅對它的回返結(jié)果進(jìn)行處理;make將函數(shù)返回結(jié)果中的所有換行符(“n”)或者一對“nr”替換為單空格;并去掉末尾的回車符號(“n”)或者“nr”。 當(dāng)引用“shell”函數(shù)的變量定義

37、使用直接展開式定義時可以保證函數(shù)的展開是在make讀入Makefile時完成。后續(xù)對此變量的引用就不會有展開過程。這樣就可以避免規(guī)則命令行中的變量引用在命令行執(zhí)行時展開的情況發(fā)生 內(nèi)嵌變量“CFLAGS”代表了gcc編譯器編譯源文件的編譯選項(xiàng),我們就可以在Makefile中重新定義它,來改變編譯源文件所要使用的參數(shù)。 # sample MakefileCUR_DIR = $(shell pwd)INCS := $(CUR_DIR)/includeCFLAGS := -Wall I$(INCS)EXEF := foo bar.PHONY : all cleanall : $(EXEF)foo :

38、 CFLAGS+=-O2bar : CFLAGS+=-gclean :$(RM) *.o *.d $(EXES)例子中沒有出現(xiàn)任何關(guān)于源文件的描述。所有剩余工作全部交給了make去處理,它會自動尋找到相應(yīng)規(guī)則并執(zhí)行、最終完成目標(biāo)文件的重建。隱含規(guī)則為我們提供了一個編譯整個工程非常高效的手段,一個大的工程中毫無例外的會用到隱含規(guī)則。實(shí)際工作中,靈活運(yùn)用GNU make所提供的隱含規(guī)則功能,可以大大提供效率。 模式規(guī)則類似于普通規(guī)則。只是在模式規(guī)則中,目標(biāo)名中需要包含有模式字符“%”(一個),包含有模式字符“%”的目標(biāo)被用來 匹配一個文件 名,“%”可以匹配任何非空字符串。規(guī)則的依賴文件中同樣可以

39、使用“%”,依賴文件中模式字符“%”的取值情況由目標(biāo)中的“%”來決定。例如:對于模式規(guī) 則“%.o : %.c”,它表示的含義是:所有的.o文件依賴于對應(yīng)的.c文件。我們可以使用模式規(guī)則來定義隱含規(guī)則。因此,一個模式規(guī)則的格式為:%.o : %.c ; COMMAND.這個模式規(guī)則指定了如何由文件“N.c”來創(chuàng)建文件“N.o”,文件“N.c”應(yīng)該是已存在的或者可被創(chuàng)建的。 下面對所有的自動化變量進(jìn)行說明: $表示規(guī)則的目標(biāo)文件名。如果目標(biāo)是一個文檔文件(Linux中,一般稱.a文件為文檔文件,也稱為靜態(tài)庫文件),那么它代表這個文檔的文件名。在多目標(biāo)模式規(guī)則中,它代表的是哪個觸發(fā)規(guī)則被執(zhí)行的目標(biāo)

40、文件名。$%當(dāng)規(guī)則的目標(biāo)文件是一個靜態(tài)庫文件時,代表靜態(tài)庫的一個成員名。例如,規(guī)則的目標(biāo)是“foo.a(bar.o)”,那么,“$%”的值就為“bar.o”,“$”的值為“foo.a”。如果目標(biāo)不是靜態(tài)庫文件,其值為空。$規(guī)則的第一個依賴文件名。如果是一個目標(biāo)文件使用隱含規(guī)則來重建,則它代表由隱含規(guī)則加入的第一個依賴文件。$?所有比目標(biāo)文件更新的依賴文件列表,空格分割。如果目標(biāo)是靜態(tài)庫文件名,代表的是庫成員(.o文件)。$規(guī)則的所有依賴文件列表,使用空格分隔。如果目標(biāo)是靜態(tài)庫文件,它所代表的只能是所有庫成員(.o文件)名。一個文件可重復(fù)的出現(xiàn)在目標(biāo)的依賴中,變量“$”只記錄它的一次引用情況。就

41、是說變量“$”會去掉重復(fù)的依賴文件。$+類似“$”,但是它保留了依賴文件中重復(fù)出現(xiàn)的文件。主要用在程序鏈接時庫的交叉引用場合。$*在 模式規(guī)則和靜態(tài)模式規(guī)則中,代表“莖”。“莖”是目標(biāo)模式中“%”所代表的部分(當(dāng)文件名中存在目錄時,“莖”也包含目錄(斜杠之前)部分)。例如:文件 “dir/a.foo.b”,當(dāng)目標(biāo)的模式為“a.%.b”時,“$*”的值為“dir/a.foo”。“莖”對于構(gòu)造相關(guān)文件名非常有用。自動化變量“$*”需要兩點(diǎn)說明:對于一個明確指定的規(guī)則來說不存在“莖”,這種情況下“$*”的含義發(fā)生改變。此時,如果目標(biāo)文件名帶有一個可識別的后綴,那么“$*”表示文件中除后綴 以外的部分

42、。例如:“foo.c”則“$*”的值為:“foo”,因?yàn)?c是一個可識別的文件后綴名。GUN make對明確規(guī)則的這種奇怪的處理行為是為了和其它版本的make兼容。通常,在除靜態(tài)規(guī)則和模式規(guī)則以外,明確指定目標(biāo)文件的規(guī)則中應(yīng)該避免使用這個 變量。當(dāng)明確指定文件名的規(guī)則中目標(biāo)文件名包含不可識別的后綴時,此變量為空。自動化變量“$?”在顯式規(guī)則中也是非常有用的,使用它規(guī)則可以指定只對更新以后的依賴文件進(jìn)行操作。例如,靜態(tài)庫文件“l(fā)ibN.a”,它由一些.o文件組成。這個規(guī)則實(shí)現(xiàn)了只將更新后的.o文件加入到庫中:lib: foo.o bar.o lose.o win.oar r lib $?以 上羅

43、列的自動量變量中。其中有四個在規(guī)則中代表文件名($、$、$%、$*)。而其它三個的在規(guī)則中代表一個文件名列表。GUN make中,還可以通過這七個自動化變量來獲取一個完整文件名中的目錄部分和具體文件名部分。在這些變量中加入“D”或者“F”字符就形成了一系列變種的 自動環(huán)變量。這些變量會出現(xiàn)在以前版本的make中,在當(dāng)前版本的make中,可以使用“dir”或者“notdir”函數(shù)來實(shí)現(xiàn)同樣的功能。$(D)表示目標(biāo)文件的目錄部分(不包括斜杠)。如果“$”是“dir/foo.o”,那么“$(D)”的值為“dir”。如果“$”不存在斜杠,其值就是“.”(當(dāng)前目錄)。注意它和函數(shù)“dir”的區(qū)別!$(F

44、)目標(biāo)文件的完整文件名中除目錄以外的部分(實(shí)際文件名)。如果“$”為“dir/foo.o”,那么“$(F)”只就是“foo.o”?!?(F)”等價于函數(shù)“$(notdir $)”。$(*D)$(*F)分別代表目標(biāo)“莖”中的目錄部分和文件名部分。$(%D)$(%F)當(dāng)以如“archive(member)”形式靜態(tài)庫為目標(biāo)時,分別表示庫文件成員“member”名中的目錄部分和文件名部分。它僅對這種形式的規(guī)則目標(biāo)有效。$(D)$(F)分別表示規(guī)則中第一個依賴文件的目錄部分和文件名部分。$(D)$(F)分別表示所有依賴文件的目錄部分和文件部分(不存在同一文件)。$(+D)$(+F)分別表示所有依賴文件

45、的目錄部分和文件部分(可存在重復(fù)文件)。$(?D)$(?F)分別表示被更新的依賴文件的目錄部分和文件名部分。 后綴規(guī)則是一種古老定義隱含規(guī)則的方式,在新版本的make中使用模式規(guī)則作為對它的替代,模式規(guī)則相比后綴規(guī)則更加清晰明了。在現(xiàn)在版本中保留它的原因是為了能夠兼容舊的makefile文件。 判斷一個后綴規(guī)則是單后綴還是雙后綴的過程:判斷后綴規(guī)則的目標(biāo),如果其中只存在一個可被make識別的后綴,則規(guī)則是一個“單后綴”規(guī)則;當(dāng)規(guī)則目標(biāo)中存在兩個可被make識別的后綴時,這個規(guī)則就是一個“雙后綴”規(guī)則。(盡管單后綴很少出現(xiàn)) 例如:“.c”和“.o”都是make可識別的后綴。因此當(dāng)定義了一個目標(biāo)

46、是“.c.o”的規(guī)則時。make會將它作為一個雙后綴規(guī)則來處理,它的含義是所有“.o”文件的依賴文件是對應(yīng)的“.c”文件。下邊是使用后追規(guī)則定義的編譯.c源文件的規(guī)則: .c.o: $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $ $注意:一個后綴規(guī)則中不存在任何依賴文件。否則,此規(guī)則將被作為一個普通規(guī)則對待。因此規(guī)則:.c.o: foo.h $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $ $就不是一個后綴規(guī)則。它是一個目標(biāo)文件為“.c.o”、依賴文件是“foo.h”的普通規(guī)則。它也不等價于規(guī)則:%.o: %.c foo.h $(CC) -c $(

47、CFLAGS) $(CPPFLAGS) -o $ $ 可識別的后綴指的是特殊目標(biāo)“.SUFFIXES”所有依賴的名字。通過給特殊目標(biāo)“SUFFIXES”添加依賴來增加一個可被識別的后綴。像下邊這樣:.SUFFIXES: .hack .win它所實(shí)現(xiàn)的功能是把后綴“.hack”和“.win”加入到可識別后綴列表的末尾。如果需要重設(shè)默認(rèn)的可識別后綴,因該這樣來實(shí)現(xiàn):.SUFFIXES: #刪除所有已定義的可識別后綴.SUFFIXES: .c .o .h #重新定義 靜態(tài)庫文件需要使用“ar”來創(chuàng)建和維護(hù)。當(dāng)給靜態(tài)庫增建一個成員時(加入一個.o文件到靜態(tài)庫中),“ar”可直接將需要 增加的.o文件簡

48、單的 追加到靜態(tài)庫的末尾。之后當(dāng)我們使用這個庫進(jìn)行連接生成可執(zhí)行文件時,鏈接程序“l(fā)d”卻提示錯誤,這可能是:主程序使用了之前加入到庫中的.o文件中定 義的一個函數(shù)或者全局變量,但連接程序無法找到這個函數(shù)或者變量。 這個問題的原因是:之前我們將編譯完成的.o文件直接加入到了庫的末尾,卻并沒有更新庫的有效符號表。連接程序進(jìn)行連接時,在靜態(tài)庫的 符號索引 表中無法定位剛才加入的.o文件中定義的函數(shù)或者變量。這就需要在完成庫成員追加以后讓加入的所有.o文件中定義的函數(shù)(變量)有效,完成這個工作需要使 用另外一個工具“ranlib”來對靜態(tài)庫的符號索引表進(jìn)行更新。我們所使用到的靜態(tài)庫(文檔文件)中,存

49、 在這樣一個特殊的成員,它的名字是“_.SYMDEF”。它包含了靜態(tài)庫中所有成員所定義的有效符號(函數(shù)名、變量名)。因此,當(dāng)為庫增加了一個成員 時,相應(yīng)的就需要更新成員“_.SYMDEF”,否則所增加的成員中定義的所有的符號將無法被連接程序定位。完成更新的命令是:ranlib ARCHIVEFILE如果我們使用GNU ar工具來維護(hù)、管理靜態(tài)庫,我們就不需要考慮這一步。GNU ar本身已經(jīng)提供了在更新庫的同時更新符號索引表的功能(這是默認(rèn)行為,也可以通過命令行選項(xiàng)控制ar的具體行為。可參考 GNU ar工具的man手冊)。 靜態(tài)庫的后綴規(guī)則屬于后綴規(guī)則的特殊應(yīng)用,后綴規(guī)則在目前版本的GNU m

50、ake中已經(jīng)被模式規(guī)則替代。但目前版本make同樣支持舊風(fēng)格的后綴規(guī)則,主要是為了兼容老版本的make。對于靜態(tài)庫的重建也可以使用后綴規(guī)則。目 標(biāo)后綴需要是“.a”(在Linux(Unix)中、靜態(tài)庫的后綴為.a)。例如這樣一個后綴規(guī)則:.c.a:$(CC) $(CFLAGS) $(CPPFLAGS) -c $ -o $*.o$(AR) r $ $*.o$(RM) $*.o它相當(dāng)于模式規(guī)則:%.o: %.c$(CC) $(CFLAGS) $(CPPFLAGS) -c $ -o $*.o$(AR) r $ $*.o$(RM) $*.o 基本的約定 本節(jié)討論書寫Makefile時應(yīng)遵循的一些基本約

51、定,由于不同版本make之間的差異??赡茉贕NU make環(huán)境中正常工作的Makefile,換成其它版本的make執(zhí)行時會發(fā)生錯誤。為了最大可能的兼容不同版本的make,這里給出了一些基本的約定。1. 所有的Makefile中應(yīng)該包含這樣一行:SHELL = /bin/sh其目的是為了避免變量“SHELL”在有些系統(tǒng)上可能繼承同名的系統(tǒng)環(huán)境變量而導(dǎo)致錯誤。雖然在GNU版本的make中不會出現(xiàn)這種問題(GNU make中變量“SHELL”的默認(rèn)值是“/bin/sh”,它不同于系統(tǒng)環(huán)境變量“SHELL”)。2. 小心處理后綴和隱含規(guī)則。不同make可識別后綴和隱含規(guī)則可能不同,它可能會導(dǎo)致混亂或者

52、錯誤。因此在特定Makefile中明確限定可識別的后綴是一個不錯的主意。在Makefile中應(yīng)該這樣做:.SUFFIXES:.SUFFIXES: .c .o第一行首先取消掉make默認(rèn)的可識別后綴列表,第二行重新指定可識別的后綴列表。當(dāng)規(guī)則只有一個依賴文件時。應(yīng)該使用自動化變量“$”和“$”代替出現(xiàn)在命令的依賴文件和目標(biāo)文件(其它版本的make,只在隱含規(guī)則中設(shè)置自動化變量“$”)。對于一個這樣的規(guī)則:foo.o : bar.c $(CC) I. I$(srcdir) $(CFLAGS) c bar.o o foo.o我們在Makefile中應(yīng)該以這種方式來書寫:foo.o : bar.c $

53、(CC) I. I$(srcdir) $(CFLAGS) c $ $重建或者安裝目標(biāo)(一般是偽目標(biāo))的命令行可使用編譯器或者相關(guān)工具程序,這些命令使用一個變量來表示。這樣做的好處是:當(dāng)修改一個命令時,只需要更改代表命令的變量的值就可以了。對于以下的這些命令程序:ar bison cc flex install ld ldconfig lexmake makeinfo ranlib texi2dvi yacc在規(guī)則中的命令中,使用以下這些變量來代表它們:$(AR) $(BISON) $(CC) $(FLEX) $(INSTALL) $(LD) $(LDCONFIG)$(LEX)$(MAKE) $

54、(MAKEINFO) $(RANLIB) $(TEXI2DVI) $(YACC)如果規(guī)則的命令行需要使用“ranlib”或者“l(fā)dconfig”等這些工具時,需要考慮當(dāng)前的系統(tǒng)是否支持這些工具。當(dāng)在不支持它的系統(tǒng)中執(zhí)行包含此命令的規(guī)則時,要能夠給出提示信息(提示原因是告訴用戶系統(tǒng)不支持此命令,但不應(yīng)該出現(xiàn)錯誤而退出執(zhí)行)。在我們書寫的Makefile中應(yīng)該講所有的命令、選項(xiàng)作為變量定義,方便后期對命令的修改和對選項(xiàng)的修改。就是說用戶可以通過修改一個變量值來重新指定所要執(zhí)行的命令,或者來控制命令執(zhí)行的選項(xiàng)、參數(shù)等。當(dāng)使用變量來表示命令時,如果規(guī)則中需要使用此命令時,可通過引用代表此命令的變量來實(shí)現(xiàn)。例如:定義變量“CC = gcc”,規(guī)則中就可使用“$(CC)”來引用“gcc”。對于一些件管理器工具如“l(fā)n”,“rm”“mv”等,可以不為它們定義變量,而直接使用。所 有命令執(zhí)行的參數(shù)選項(xiàng)也應(yīng)該定義一個變量(可稱為命令的選項(xiàng)變量)。在命令變量(代表一個命令的變量)后添加“FLAGS”來命名這個選項(xiàng)變量。例如:變 量“CFLAGS”是c編譯

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論