Makefile 7——自动生成依赖关系 三颗星
后面會介紹gcc獲得源文件依賴的方法,gcc這個功能就是為make而存在的。我們采用gcc的-MM選項結(jié)合sed命令。使用sed進(jìn)行替換的目的是為了在目標(biāo)名前加上“objs/”前綴。gcc的-E選項,預(yù)處理。在生成依賴關(guān)系時,其實并不需要gcc編譯源文件,只要預(yù)處理就可以獲得依賴關(guān)系了。通過-E選項,可以避免生成依賴關(guān)系時gcc發(fā)出警告,以及提高依賴關(guān)系的生成效率。
現(xiàn)在,已經(jīng)找到自動生成依賴關(guān)系的方法了,那么如何將其整合到我們complicated項目的Makefile中呢?自動生成的依賴信息不能直接出現(xiàn)在Makefile中,因為不能動態(tài)地改變Makefile中的內(nèi)容,此時我們需要通過創(chuàng)建依賴關(guān)系文件的方式。假設(shè)依賴關(guān)系的文件以“.dep”結(jié)尾,因此我們新創(chuàng)建一個deps文件,用來存放依賴關(guān)系文件信息。
Makefile如下:
1 .PHONY: all clean 2 3 MKDIR = mkdir 4 RM = rm 5 RMFLAGS = -rf 6 7 CC=gcc 8 9 DIR_OBJS=objs 10 DIR_EXES=exes 11 DIR_DEPS=deps 12 13 DIRS =$(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS) 14 EXE=complicated 15 EXE:=$(addprefix $(DIR_EXES)/,$(EXE)) 16 SRCS=$(wildcard *.c) 17 OBJS=$(SRCS:.c=.o) 18 OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS)) 19 DEPS=$(SRCS:.c=.dep) 20 DEPS:=$(addprefix $(DIR_DEPS)/,$(DEPS)) 21 22 all:$(DIRS) $(DEPS) $(EXE) 23 $(DIRS): 24 $(MKDIR) $@ 25 $(EXE):$(OBJS) 26 $(CC) -o $@ $^ 27 $(DIR_OBJS)/%.o:%.c 28 $(CC) -o $@ -c $^ 29 $(DIR_DEPS)/%.dep:%.c 30 @echo "Creating $@ ..." 31 @set -e;\ 32 $(RM) $(RMFLAGS) $@.tmp;\ 33 $(CC) -E -MM $^ >$@.tmp;\ 34 sed 's,\(.*\)\.o[:]*,objs/\1.o:,g' <$@.tmp >$@;\ 35 $(RM) $(RMFLAGS) $@.tmp 36 clean: 37 $(RM) $(RMFLAGS) $(DIRS)(這個Makefile廢了不少力氣才想明白。。。)
和之前的complicated項目的Makefile相比:
1,增加了deps文件夾
2,刪除了目標(biāo)文件創(chuàng)建規(guī)則中的foo.h依賴,并將規(guī)則中的$<變回了$^
3,增加了了DEPS變量用于存放文件
4,為all目標(biāo)增加了$(DEPS)
5,增加了一個用于創(chuàng)建依賴關(guān)系問價你的規(guī)則。在這個規(guī)則中,使用了gcc的-E和-MM選項來獲取依賴關(guān)系。在生成最終的依賴關(guān)系文件之前,使用了一個由$@.tmp表示的臨時文件,且在依賴文件生成以后將其刪除。set -e的作用是告訴shell,在生成依賴關(guān)系文件的過程中如果出現(xiàn)任何錯誤就直接退出。shell異常退出的最終表現(xiàn)就是make會告訴我們出錯了,從而停止后續(xù)的make工作。如果不設(shè)置這一行,當(dāng)構(gòu)建依賴出錯時,make還會繼續(xù)后面的工作并最終出錯,這并不是我們希望看到的。讀者可以測試故意在源文件或者頭文件中植入錯誤并去掉set -e選項觀察make的行為和加上set -e有上面不同。
這里還有幾個知識點需要補(bǔ)充。
1.對于規(guī)則中的每一條命令,make都是在一個新的shell上運行它的。
2.如果希望多個命令在同一個shell中運行,可以用“;”將這些命令連起來。
3.當(dāng)命令很長時,可以用“\”將一個命令書寫成多行。
為了更好的理解第一點,我們做一個實驗。現(xiàn)假設(shè)需要創(chuàng)建一個test目錄,然后在這個test目錄下再創(chuàng)建一個subtest子目錄。編寫Makefile如下:
?
1 .PHONY:all 2 all: 3 @mkdir test 4 @cd test 5 @mkdir subtest?
可以看到test和subtest是同級目錄并非父子目錄,然后用上面提到的知識點更改Makefile:
1 .PHONY:all 2 all: 3 @mkdir test;\ 4 cd test;\ 5 mkdir subtest?
這樣就可以達(dá)到目的了。不過你可能會想,為什么這里后面的cd和最后一個mkdir不需要在前面加上@呢?那么我們加上試試呢?
?
如果使用了分號“ ;”,表示命令在同一個shell中運行,而且使用“ \”鏈接一條命令,既然是一條命令,自然不能夠識別后面的@cd或者@mkdir,因為最開始的mkdir使用@,讓終端不顯示執(zhí)行的指令,后面的cd和mkdir是在前面操作的情況下進(jìn)行的?,此時,直接使用命令即可。
還有一個需要注意的地方:
如同
EXE=complicated EXE:=$(addprefix $(DIR_EXES)/,$(EXE)) 這樣的Makefile,為什么第二個賦值我們是用:=而不是直接=呢?這也是需要注意的小細(xì)節(jié),這個在之前的隨筆中已經(jīng)說過,要是用=,會導(dǎo)致無限遞歸,為什么呢?因為EXE在復(fù)制號左邊,而右邊又有$(EXE)(EXE的引用),這樣會無限調(diào)用,make報錯。不信你可以試試。 最后,來到最難的一個東西: sed 's,\(.*\)\.o[:]*,objs/\1.o:,g' <$@.tmp >$@; 這個語句才是最難的,也是最費力的。 首先要簡單說明一下linux中sed的用法: 1.簡介 sed是非交互式的編輯器。它不會修改文件,除非使用shell重定向來保存結(jié)果(這里這個復(fù)雜命令就用了重定向來更改)。默認(rèn)情況下,所有的輸出行都被打印到屏幕上。 sed編輯器逐行處理文件(或輸入),并將結(jié)果發(fā)送到屏幕。具體過程如下:首先sed把當(dāng)前正在處理的行保存在一個臨時緩存區(qū)中(也稱為模式空間),然后處理臨時緩沖區(qū)中的行,完成后把該行發(fā)送到屏幕上。sed每處理完一行就將其從臨時緩沖區(qū)刪除,然后將下一行讀入,進(jìn)行處理和顯示。處理完輸入文件的最后一行后,sed便結(jié)束運行。sed把每一行都存在臨時緩沖區(qū)中,對這個副本進(jìn)行編輯,所以不會修改原文件。 2.定址 定址用于決定對哪些行進(jìn)行編輯。地址的形式可以是數(shù)字、正則表達(dá)式、或二者的結(jié)合。如果沒有指定地址,sed將處理輸入文件的所有行3.命令與選項
sed命令告訴sed如何處理由地址指定的各輸入行,如果沒有指定地址則處理所有的輸入行。
此處sed引用此博客, 參考鏈接:http://www.cnblogs.com/edwardlost/archive/2010/09/17/1829145.html
3.1 sed命令
| ?命令 | ?功能 |
| ?a\ | ?在當(dāng)前行后添加一行或多行。多行時除最后一行外,每行末尾需用“\”續(xù)行 |
| ?c\ | ?用此符號后的新文本替換當(dāng)前行中的文本。多行時除最后一行外,每行末尾需用"\"續(xù)行 |
| ?i\ | ?在當(dāng)前行之前插入文本。多行時除最后一行外,每行末尾需用"\"續(xù)行 |
| ?d | ?刪除行 |
| ?h | ?把模式空間里的內(nèi)容復(fù)制到暫存緩沖區(qū) |
| ?H | ?把模式空間里的內(nèi)容追加到暫存緩沖區(qū) |
| ?g | ?把暫存緩沖區(qū)里的內(nèi)容復(fù)制到模式空間,覆蓋原有的內(nèi)容 |
| ?G | ?把暫存緩沖區(qū)的內(nèi)容追加到模式空間里,追加在原有內(nèi)容的后面 |
| ?l | ?列出非打印字符 |
| ?p | ?打印行 |
| ?n | ?讀入下一輸入行,并從下一條命令而不是第一條命令開始對其的處理 |
| ?q | ?結(jié)束或退出sed |
| ?r | ?從文件中讀取輸入行 |
| ?! | ?對所選行以外的所有行應(yīng)用命令 |
| ?s | ?用一個字符串替換另一個 |
| ?g | ?在行內(nèi)進(jìn)行全局替換 |
| ? | ? |
| ?w | ?將所選的行寫入文件 |
| ?x | ?交換暫存緩沖區(qū)與模式空間的內(nèi)容 |
| ?y | ?將字符替換為另一字符(不能對正則表達(dá)式使用y命令) |
?
3.2 sed選項
| ?選項 | ?功能 |
| ?-e | ?進(jìn)行多項編輯,即對輸入行應(yīng)用多條sed命令時使用 |
| ?-n | ?取消默認(rèn)的輸出 |
| ?-f | ?指定sed腳本的文件名 |
?
| ?元字符 | ?功能 | ?示例 |
| ?^ | ?行首定位符 | ?/^my/? 匹配所有以my開頭的行 |
| ?$ | ?行尾定位符 | ?/my$/? 匹配所有以my結(jié)尾的行 |
| ?. | ?匹配除換行符以外的單個字符 | ?/m..y/? 匹配包含字母m,后跟兩個任意字符,再跟字母y的行 |
| ?* | ?匹配零個或多個前導(dǎo)字符 | ?/my*/? 匹配包含字母m,后跟零個或多個y字母的行 |
| ?[] | ?匹配指定字符組內(nèi)的任一字符 | ?/[Mm]y/? 匹配包含My或my的行 |
| ?[^] | ?匹配不在指定字符組內(nèi)的任一字符 | ?/[^Mm]y/? 匹配包含y,但y之前的那個字符不是M或m的行 |
| ?\(..\) | ?保存已匹配的字符 | ?1,20s/\(you\)self/\1r/? 標(biāo)記元字符之間的模式,并將其保存為標(biāo)簽1,之后可以使用\1來引用它。最多可以定義9個標(biāo)簽,從左邊開始編號,最左邊的是第一個。此例中,對第1到第20行進(jìn)行處理,you被保存為標(biāo)簽1,如果發(fā)現(xiàn)youself,則替換為your。 |
| ?& | ?保存查找串以便在替換串中引用 | ?s/my/**&**/??符號&代表查找串。my將被替換為**my** |
| ?\< | ?詞首定位符 | ?/\<my/? 匹配包含以my開頭的單詞的行 |
| ?\> | ?詞尾定位符 | ?/my\>/? 匹配包含以my結(jié)尾的單詞的行 |
| ?x\{m\} | ?連續(xù)m個x | ?/9\{5\}/ 匹配包含連續(xù)5個9的行 |
| ?x\{m,\} | ?至少m個x | ?/9\{5,\}/? 匹配包含至少連續(xù)5個9的行 |
| ?x\{m,n\} | ?至少m個,但不超過n個x | ?/9\{5,7\}/? 匹配包含連續(xù)5到7個9的行 |
現(xiàn)在來分解這個復(fù)雜表達(dá)式,首先,sed s表示我們想用一個字符串替換另一個字符串,這也是我們使用sed的原因,它的s命令就
可以達(dá)到這個效果。
's,\(.*\)\.o[:]*,objs/\1.o:,g'第一次分解,此時需要知道,單引號是一對的,即s前面的'和g后面的'是一個整體單引號,
這也是sed命令的基礎(chǔ),至于單引號和雙引號有什么區(qū)別,可百度谷歌或者必應(yīng)。(但是我之前測試的單引號和雙引號并不是我搜索所顯示的那樣,后面再試試吧)
繼續(xù)分解,s,中s是替換字符串的意思,這個在上面的表格中可以查詢到,逗號,表示模式分隔符,在這種有/出現(xiàn)的字符串中,我們選擇了逗號,作為分隔符號。
所以下一次分解應(yīng)該倒下一個逗號處, \(.*\)\.o[:]*,
這里首先看 .* 它表示匹配任意字符,\( \)是一個整體,也是通過上面的表格得到的,然后轉(zhuǎn)義字符\和.o在一起,把.的作用(匹配除換行符的單個字符)變成普通的.(就是一個字符.),那么這一句話就是
操作字符串所有有.o的且在.0后面(可以有空格)匹配:的零個或多個字符串。
objs/\1.o:,g
這里要解釋的是\1.o 這里用了轉(zhuǎn)義字符\加上1,這表示什么呢?尤其是這個1,表示的就是前面\( \)內(nèi)的字符串,這是組
的概念,如何知道是第幾組呢?前面的第一個\(\)的就是第一組,用轉(zhuǎn)義字符\1表示,依次類推。g在sed中表示行內(nèi)全局替換
這樣,我們做一個假設(shè)例子來說明。
abc.o : 用這個代表\(.*\)\.o[:]*
然后objs/\1.o:,g之后呢,abc.o :變成了 objs/abc.o: 這里相當(dāng)于給前面的通用匹配加上了objs/前綴,并且把:和.o之前的空格去掉了
最后這個<$@.tmp >$@;這不屬于sed的內(nèi)容了,屬于linux和Makefile的東西,$@.tmp重定向輸入給前面的sed替換操作,
$@代表目標(biāo)在Makefile中,$@.tmp是前面的Makefile生成的,<重定向,看方向是輸入,
就是把$@.tmp重定向輸入給sed,經(jīng)過sed替換之后,再輸出重定向 > 到$@,這個是目標(biāo)。
這樣再回過頭去看之前那個Makefile就可以看懂了。 ?
轉(zhuǎn)載于:https://www.cnblogs.com/yangguang-it/p/6818664.html
總結(jié)
以上是生活随笔為你收集整理的Makefile 7——自动生成依赖关系 三颗星的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 四则运算 结对编程
- 下一篇: 作业四-结队编程项目-四则运算