本節(jié)實(shí)驗(yàn)所需的源文件和頭文件:
原文件:func.c
#include "stdio.h"
#include "func.h"
void foo()
{
printf("void foo() : %s\n", HELLO);
}
原文件:main.c
#include <stdio.h>
#include "func.h"
int main()
{
foo();
return 0;
}
頭文件func.c
#ifndef FUNC_H
#define FUNC_H
#define HELLO "Hello D.T."
void foo();
#endif
問題:
OBJS := func.o main.o
hello.out : $(OBJS)
@gcc -o $@ $^
@echo "Target File ==> $@"
$(OBJS) : %.o : %.c
@gcc -o $@ -c $<
此時看似可以編譯成功,但存在潛在隱患。
存在問題:目標(biāo)文件只依賴于.c文件,而沒有關(guān)注.h文件,這樣當(dāng).h文件的內(nèi)容更新時,不會重新編譯.c文件。
解決方案:
我們將.h文件也作為依賴寫到Makefile中。
OBJS := func.o main.o
hello.out : $(OBJS)
@gcc -o $@ $^
@echo "Target File ==> $@"
$(OBJS) : %.o : %.c func.h
@gcc -o $@ -c $<
上述解決方案問題:
頭文件作為依賴出現(xiàn)于每一個目標(biāo)文件對應(yīng)的規(guī)則中,當(dāng)頭文件改動,任何源文件都會被重新編譯(編譯低效),而且當(dāng)項(xiàng)目中頭文件數(shù)量巨大時,Makefile件很難維護(hù)。
通過命令自動生成對頭文件的依賴,將生成的依賴自動包含進(jìn)入Makefile中,當(dāng)頭文件改動后,自動確認(rèn)需要重新編譯的文件。
預(yù)備工作:
1.Linux命令sed,sed時一個流編輯器,用于流文本的修改(增、刪、查、改),文件替換,格式為:sed ‘s/abc/xyz/g’;
Sed可以支持正則表達(dá),sed ‘s/(.).o[ :]/objs/\1.o : /g’ 正則匹配目標(biāo)((.).o[ :]),替換值(objs/\1.o : )
2.編譯器選項(xiàng),生成依賴關(guān)系
gcc -MM 獲取目標(biāo)的完整依賴關(guān)系
gcc -M 獲取目標(biāo)的部分依賴關(guān)系
3.Makefile中目標(biāo)拆分技巧,將目標(biāo)的完整依賴拆分為多個部分依賴
.PHONY : test a b c
test : a b
test : b c
test :
@echo "$^"
輸出結(jié)果:a b c
思考:如果使用上面的預(yù)備工作實(shí)現(xiàn)頭文件的自動依賴?
Make中的include關(guān)鍵字,類似于C語言中的關(guān)鍵字,在處理是將所包含的文件的內(nèi)容原封不動的搬到當(dāng)前文件。
語法:include filename
Eg: include foo.make *.mk $(var)
Make對include關(guān)鍵字的處理方式,在當(dāng)前目錄搜索或者指定目錄搜索目標(biāo)文件,搜索成功:將文件內(nèi)容搬入當(dāng)前Makefile中;搜索失敗,以文件名作為目標(biāo)查找并執(zhí)行對應(yīng)規(guī)則。當(dāng)文件名對應(yīng)的規(guī)則不存在時,產(chǎn)生錯誤。
下面的代碼怎么執(zhí)行,為什么?
.PHONY : all
include test.txt
all :
@echo "this is all"
test.txt :
@echo "test.txt"
@touch test.txt
初次執(zhí)行文件,自然搜索不到test.txt文件,然后會test.txt文件名作為目標(biāo)查找并執(zhí)行對應(yīng)規(guī)則,輸出結(jié)果:
注意:在include關(guān)鍵字前面加上-,可以消除警告。
1.Makefile中的命令執(zhí)行時,每一條命令默認(rèn)都是一個新的進(jìn)程;(這樣當(dāng)我們希望使用上一個命令的執(zhí)行結(jié)果,繼續(xù)執(zhí)行命令時往往得不到結(jié)果,譬如下面的代碼);
.PHONY : all
all :
set -e;
mkdir test;
cd test;
mkdir subtest
輸出結(jié)果:
很顯然,沒有達(dá)到我們與其的目的(在test文件夾中創(chuàng)建subtest文件夾)
2.可以通過接續(xù)符(;)將多個命令組合成為一個命令,組合的命令一次在同一個進(jìn)程中被執(zhí)行;
3.可以使用set -e指定發(fā)生錯誤時立即退出。
.PHONY : all
all :
set -e; \
mkdir test; \
cd test; \
mkdir subtest
輸出結(jié)果:
1.通過gcc -MM 和sed命令得到.dep文件(目標(biāo)的部分依賴),并使用接續(xù)符使得命令可以連續(xù)執(zhí)行;
2.通過include指令包含所有的.dep依賴文件(當(dāng).dep文件不存在時,查找與.dep文件同名的規(guī)則并執(zhí)行)
.PHONY : all clean
MKDIR := mkdir
RM := rm -fr
CC := gcc
SRCS := $(wildcard *.c)
DEPS := $(SRCS:.c=.dep)
-include $(DEPS)
all :
@echo "all"
%.dep : %.c
@echo "Creating $@ ..."
@set -e; \
$(CC) -MM -E $^ | sed 's,\(.*\)\.o[ :]*,objs/\1.o : ,g' > $@
clean :
$(RM) $(DEPS)
輸出結(jié)果:
我們此時已經(jīng)成功的生成了依賴文件main.dep和func.dep并在文件中記錄了目標(biāo)和依賴的關(guān)系。
思考:如果組織依賴文件相關(guān)的規(guī)則與源碼編譯相關(guān)的規(guī)則,進(jìn)而形成功能完整的Makefile?
如何在makefile中組織.dep文件到指定目錄?
解決思路:
當(dāng)include 發(fā)現(xiàn).dep文件不存在時,通過規(guī)則和命令創(chuàng)建deps文件夾,將所有的.dep文件創(chuàng)建到deps文件夾,并在.dep文件中記錄目標(biāo)文件的依賴關(guān)系。
$(DIR_DEPS) :
$(MKDIR) $@
$(DIR_DEPS)/%.dep : $(DIR_DEPS) %.c
@echo "Creating $@ ..."
@set -e; \
$(CC) -MM -E $^ | sed 's,\(.*\)\.o[ :]*,objs/\1.o : ,g' > $@
這樣做確實(shí)解決了上述問題,生成了deps文件夾:
但同時我們看到兩個問題:
1.因?yàn)橐蕾囍邪琩eps文件夾,以deps文件夾作為 gcc -MM 的輸入時沒有意義的,會報(bào)告warning,所以使用下面的方法過濾掉deps文件夾
$(CC) -MM -E $(filter %.c, $^) | sed 's,\(.*\)\.o[ :]*,objs/\1.o : ,g' > $@
2.func.dep被重復(fù)創(chuàng)建了多次?
問題本質(zhì)分析:
deps文件夾的時間屬性會因?yàn)橐蕾囄募?chuàng)建而發(fā)生改變,make發(fā)現(xiàn)deps文件夾比對于的目標(biāo)更新時,會觸發(fā)相應(yīng)規(guī)則的重新解釋和命令的執(zhí)行。
解決方案:使用ifeq動態(tài)決定.dep目標(biāo)的依賴;
ifeq ("$(wildcard $(DIR_DEPS))", "")
$(DIR_DEPS)/%.dep : $(DIR_DEPS) %.c
else
$(DIR_DEPS)/%.dep : %.c
endif
1.使用- 不但關(guān)閉了include發(fā)出的警告,同時關(guān)閉了錯誤,當(dāng)發(fā)生錯誤時,make將忽略這些錯誤。
2.如果include 觸發(fā)規(guī)則創(chuàng)建了文件則會發(fā)生下面的事情:
// 使用include 時的暗黑操作
if(如果目標(biāo)文件不存在)
{
//以文件名為規(guī)則查找并執(zhí)行,
if(查找到的規(guī)則中創(chuàng)建了文件)
{
//將創(chuàng)建成功的目標(biāo)文件包含進(jìn)當(dāng)前makefile
}
}
else // 如果目標(biāo)文件存在
{
// 將目標(biāo)文件包含進(jìn)當(dāng)前makefile
if(以目標(biāo)文件名查找是否有相應(yīng)的規(guī)則)
{
if(比較規(guī)則的依賴關(guān)系,決定是否執(zhí)行規(guī)則的命令)
{
// (依賴文件更新,則執(zhí)行)
}
else
{
// 無操作
}
}
else
{
// 無操作
}
}
實(shí)驗(yàn)1:include包含的目標(biāo)文件不存在,并且以文件名為目標(biāo)的規(guī)則存在,并在規(guī)則中創(chuàng)建了文件
.PHONY : all
-include test.txt
all :
@echo "this is all"
test.txt :
@echo "creating $@ ..."
@echo "other : ; @echo "this is other" " > test.txt
我們期望了輸出結(jié)果因該是:this is all,因?yàn)閍ll是第一個(默認(rèn))目標(biāo)。
運(yùn)行結(jié)果:
原因在于當(dāng)出現(xiàn)上面的情況時:以文件名為規(guī)則查找并執(zhí)行,同時如果查找到的規(guī)則中創(chuàng)建了文件,將創(chuàng)建成功的目標(biāo)文件包含進(jìn)當(dāng)前makefile,此時在makefile中第一個目標(biāo)變成了other
實(shí)驗(yàn)2:
.PHONY : all
-include test.txt
all :
@echo "this is all"
test.txt : b.txt
@echo "creating $@ ..."
當(dāng)不存在b.txt時的運(yùn)行結(jié)果:
當(dāng)存在b.txt,但b.txt文件比test.txt文件舊時的運(yùn)行結(jié)果:
當(dāng)存在b.txt,但b.txt文件比test.txt文件新時的運(yùn)行結(jié)果:
結(jié)論:如果目標(biāo)文件存在:將目標(biāo)包含進(jìn)當(dāng)前makefile,以目標(biāo)文件名查找是否有相應(yīng)的規(guī)則
如果有則比較規(guī)則的依賴關(guān)系,決定是否執(zhí)行規(guī)則的命令(依賴文件更新,則執(zhí)行),如果規(guī)則中的命令更新了目標(biāo)文件,替換之前包含了的內(nèi)容。未更新,則無操作。
以目標(biāo)文件名查找是否有相應(yīng)的規(guī)則,不能找到,則無操作
實(shí)驗(yàn)3:
.PHONY : all
-include test.txt
all :
@echo "$@ : $^"
test.txt : b.txt
@echo "creating $@ ..."
@echo "all : c.txt" > test.txt
a.txt內(nèi)容:
all : a.txt
當(dāng)該文件中所需的所有文件都存在,并且test.txt的內(nèi)容為最新時,make all輸出結(jié)果:
當(dāng)b.txt文件最新時,make all輸出結(jié)果:
經(jīng)過前面的技巧學(xué)習(xí),我們現(xiàn)可以去完成這個自動生成依賴關(guān)系的想法了
注意:
思考:我們在13節(jié)中最終創(chuàng)建出來的makefile是否存在問題?
當(dāng).dep文件生成后,如果動態(tài)的改變文件間的依賴關(guān)系,那么make可能無法檢測到這個改變,進(jìn)而做出錯誤的判斷。
實(shí)例:
輸出結(jié)果:
解決方案:
將依賴文件的文件名作為目標(biāo)加入自動生成的依賴關(guān)系中,通過include加載依賴文件時判斷是否執(zhí)行規(guī)則,在規(guī)則執(zhí)行時重新生成依賴關(guān)系文件,最后加載新的依賴文件。
舉個栗子:當(dāng)我們前面編譯過之后(生成了依賴文件),又添加了新的頭文件,這時根據(jù)include的暗黑操作,要去檢查與include所包含的依賴文件同名的規(guī)則是否存在,如果存在,則檢查這個目標(biāo)所對應(yīng)的依賴是否被更新,如果更新,則執(zhí)行相應(yīng)規(guī)則。
最終方案:
.PHONY : all clean rebuild
MKDIR := mkdir
RM := rm -fr
CC := gcc
DIR_DEPS := deps
DIR_EXES := exes
DIR_OBJS := objs
DIRS := $(DIR_DEPS) $(DIR_EXES) $(DIR_OBJS)
EXE := app.out
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS := $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))
all : $(DIR_OBJS) $(DIR_EXES) $(EXE)
ifeq ("$(MAKECMDGOALS)", "all")
include $(DEPS)
endif
ifeq ("$(MAKECMDGOALS)", "")
include $(DEPS)
endif
$(EXE) : $(OBJS)
$(CC) -o $@ $^
@echo "Success! Target => $@"
$(DIR_OBJS)/%.o : %.c
$(CC) -o $@ -c $(filter %.c, $^)
# $(CC) -o $@ -c $(filter %.c, $^)
$(DIRS) :
$(MKDIR) $@
ifeq ("$(wildcard $(DIR_DEPS))", "")
$(DIR_DEPS)/%.dep : $(DIR_DEPS) %.c
else
$(DIR_DEPS)/%.dep : %.c
endif
@echo "Creating $@ ..."
@set -e; \
$(CC) -MM -E $(filter %.c, $^) | sed 's,\(.*\)\.o[ :]*,objs/\1.o $@ : ,g' > $@
clean :
$(RM) $(DIRS)
rebuild :
@$(MAKE) clean
@$(MAKE) all
總結(jié):
Makefile中可以將目標(biāo)的依賴拆分寫到不同的地方;
include關(guān)鍵字能夠觸發(fā)相應(yīng)的規(guī)則的執(zhí)行;
如果規(guī)則的執(zhí)行導(dǎo)致依賴更新,可能導(dǎo)致再次解釋執(zhí)行相應(yīng)的規(guī)則;
依賴文件可需要依賴源文件得到正確的編譯決策
自動生成文件的依賴關(guān)系能夠提高M(jìn)akefile的移植性。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
網(wǎng)頁標(biāo)題:makefile(05)_自動生成依賴關(guān)系-創(chuàng)新互聯(lián)
分享鏈接:http://www.chinadenli.net/article26/dcsjcg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護(hù)、用戶體驗(yàn)、ChatGPT、企業(yè)建站、微信公眾號、自適應(yīng)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容