在寫大量的程式時,通常要編譯、組譯~
可能要打一堆指令,這一個時候就出現了Makefile的東西了~
先寫一個簡單的東西吧,以下是一個簡單的Makefile的內容
food.h、food.cpp、main.cpp請參考這裡
Makefile
# Program, flags, etc.
ASM = g++
ASMFLAGS =
OBJ=food.o main.o
TARGET=main
.PHONY: everything clean all
everything: $(TARGET)
clean:
-rm -fv $(OBJ) $(TARGET)
all: clean everything
food.o: food.cpp food.h
$(ASM) -c $< -o $@
main.o: main.cpp
$(ASM) -c $< -o $@
main: $(OBJ)
@$(ASM) $(OBJ) -o $@
這裡要解釋一下,
target : prerequisites
command
0. 想要得到target,需要執行命令command
1. target依賴於prerequisites,當prerequisites中至少有一個檔案比target檔新時,command才會被執行,而且command前面的空格是用Tab鍵產生的
2. 以字元#開頭的行是注釋。
3. =是用來定義變數,這裡ASM與ASMFLAGS就是兩個變數
4. .PHONY應該指的是make後面可以接的參數指令,那就表示它後面的名字並不是檔案,而僅僅是一種行為的標號,例如make clean、make everything、make all
5. $@表示target的第一個名字,也就是代表目前的標的(target)
6. $<表示prepequisites的第一個名字
7. $?同一個規則的所有先決條件名,但是只有原始程式碼改過的比obj檔新才會符合,也就是比target還新的先決條件檔案。
8. $*同一個規則的第一個先決條件,但是不包含副檔名
9. $^所有先決條件,但是有的make像solaris make, M$ nmake可能不認得這個自動變數。(在nmake是用$**表示)
10. 使用變數用$(VAR)或者${VAR}都可以。
11. 如果要用$,請多加一個$變成$,在Shell Command會用到Shell 變數此時就要加$。
12. 在要執行的指令前面加@,則不會顯示編譯的過程喔~像上面的例子的最後一行,就不會出現編譯的過程哩
13. 若直接輸入make,這時make程式會從第一個名字所代表的動作執行。在本例中,第一個標號是everything,所以make和make everything是一樣的。
14. 在要執行的指令前面加-,表示即使該行指令出錯,也不會中斷Makefile的執行。
當我們要產生執行檔時或是有修改檔案後,要重新編譯,我們就打以下指令
[root@host ~]# make
當我們要刪除執行檔和物件檔時,我們就打以下指令
[root@host ~]# make clean
你看,是不是就很節省時間啊~
而且,現在有很多程式都可以幫我們產生makefile,像是QT就是啦,不過,我們至少要知道它們是怎麼運作的,這樣之後要改也比較方便啊~
[2009.02.28 補充]
若只在指令列上打
$ make
則會尋找Makefile的內容來執行。
若有多個makefile的檔案,則可以使用-f的參數來指定是要使用哪一個makefile
$ make -f makefile
若目錄中有多個資料夾,而每一個資料夾均有一個makefile,則也可以使用make -f的方式由上一層的makefile來控制下面的所有的makefile
下面有一個例子:
情況是makefile.server負責產生server的執行檔
而makefile.client負責產生client的執行檔
我們們直接透過Makefile來完成所有的動作
Makefile
.PHONY: everything all clean clean_target
everything:
make -f makefile.server
make -f makefile.client
all: clean_target everything
clean:
make -f makefile.server clean
make -f makefile.client clean
clean_target:
make -f makefile.server clean_target
make -f makefile.client clean_target
[2009.04.01 補充]
可以使用include的指令喔~
自己的內隱規則
因為可能有的時候你希望做些dependency檢查,或者加上一些gcc 用的旗標,不是很單純的編譯而已你可以給make自訂的內隱規則
樣式規則(pattern rule)
你可以用pattern rule來做一些自定的內隱規則。像這樣
%.o : %.c prog.h
$(CC) $(CFLAGS) $(DEBUG_FLAG) -c -o $@ $<
%表示所有相對於後面先決條件的檔名的意思,他不是*,因為他 有一對一的相對應關係,foo.o 就要找foo.c,foo1.o就要找foo1.c, 所以他不是*。所以上面的意思是所有碰到要.o的target時,去找相 對應的.c檔,並根據先決條件prog.h做檢查,如果找不到prog.h就不 做下去了。%不只可以表現主檔名,其實可以表現任一個相對應的字串, 所以叫pattern,你可以用s.%.c,不只用&.c,其中對應到%的子字串叫stem
[2009.04.02 補充]
-i 忽略指令執行返回的出錯資訊。 -s 沉默模式,在執行之前不輸出相應的指令行資訊。 -r 禁止使用build-in規則。 -n 非執行模式,輸出所有執行指令,但並不執行。 -t 更新目標文件。 -q make操作將根據目標文件是否已經更新返回"0"或非"0"的狀態資訊。 -p 輸出所有巨集定義和目標文件描述。 -d Debug模式,輸出有關文件和檢測時間的詳細資料。 Linux下make標誌位的常用選項與Unix系統中稍有不同,下面我們只列出了不同部分: -C dir 在讀取 makefile 之前改變到指定的目錄dir。 -I dir 當包含其他 makefile文件時,利用該選項指定搜尋目錄。 -M dir 改變目錄後,再回到原本的目錄 -h help文擋,顯示所有的make選項。 -w 在處理 makefile 之前和之後,都顯示工作目錄。
遞迴make
在Makefile裡面叫make來用,請用$(MAKE),例如我們常在source code的 最上層打個make, 它會自動跑到底下所有的目錄去,裡面都有個Makefile,然後每個都做make。 例如
all:
cd user;$(MAKE) $@
cd kernel;$(MAKE) $@
[2009.04.03 補充]
進入每一個子目錄執行Makefile
Makefile例:
SUBDIR = B C
.PHONY: alldir $(SUBDIR)
alldir: $(SUBDIR)
$(SUBDIR):
$(MAKE) -C $@
B: C
這個 Makefile 會依照 SUBDIR 變數裡所包含的資料夾名稱, 一一進去執行make, 需要注意的是第 9 行的敘述, 這是一個處理相依性的例子, "B: C"的意思是, 編譯 B 之前, 要先編譯 C.
[2009.04.06 補充]
以下這一個例子是說明如何在Makefile中使用shell script到達到進入每一個子目標執行其目錄的Makefile,要注意的一點是若要使用到shell的$必需使用$$來表達
# 列出所有的子目錄
SUBDIRS = lib client/version01 server/01_iterative_server server/02_concurrent_server
# 列出所有的make後面可接的指令
INSTR = everything all clean distclean
.PHONY: $(INSTR)
# 透過shell的指令到每一個子目錄尋找Makefile並執行其makefile
$(INSTR): $(SUBDIRS)
for i in $(SUBDIRS); do (cd $$i && make $@);done
[2009.04.12 補充]
指派巨集的優先順序(Priority of Macro Assignments)
如果以不同方式定義的巨集互相有衝突時(也就是不同的來源定義了相同的巨集名稱),make會採用哪個定義呢? 比如說,如果在描述檔裡頭就定義了DIR,其內容為/usr/proj,但是在目前的環境變數中,也有一個叫做DIR的環境變數,其內容為/usr/proj/lib,那麼make會採用哪一個定義呢? 下面就是優先權的順序,從優先權最大到優先權最小:
1.在使用make指令時,所定義的巨集,但是是位於make指令之後,相較於第2項,雖然同樣都是在命令列上,但是因為順序上的差異引發了微小的不同.
2.在描述檔裡頭的巨集定義.
3.目前的環境變數. 在Korn shell和Bourne shell裡頭,如果把巨集定義在make指令之前的話,此巨集相當於環境變數.
4.make內部的(default,內定的)已有的定義.
說明define-endef指令
在Makefile中若有一些指令複合指令常常一起出現,那麼我們就可以透過define-endef指令它們合併成一個指令,例:
define run-yacc
echo "Hello World"
mv $< $@
endef
foo.c:foo.y
$(run-yacc)
如此一來,當我要執行run-yacc時,他就會轉換成執行
echo "Hello World"
與
mv $< $@ 這兩個指令 控制語法
除了ifeq, ifneq還有ifdef, ifndef, endif, else, define-endef, include,很像c裡面的前置處理
例子:
ifeq ($(CC),gcc)
libs = "good"
else
libs = "not good"
endif
ifneq ($(CC),gcc)
lib = "bad"
else
lib = "not bad"
endif
default:
echo $(libs)
echo $(lib)
echo $(CC)
[2015.03.18 補充]
今天追了Makefile的code,
記錄一下兩個小技巧
因為makefile在編譯的過程中,
會有很多指令,
就算全部讓他顯示出來,
仍會出現太多資訊的情況,能不能像windows一樣,有pause的指令呢?
事情是是有的。
read -p "Press [Enter] key to start backup..."
另外一個是一個很神奇的東西,
就是原本指令只可以執行在command這一個區塊裡面,
然而,我今天看到一個非正規的方式,我覺得是非常不好的,
但是,還是記錄下來。
CP_UICONFIG := $(shell cp $(ROOTFS_PATH)/glue/mk.uicompo $(shell pwd))
這一道指令看似是在做變數給值的動作,實際上是在做cp的動作,
重點是,這一道指令可以不用放在command的區域,
可以放在Makefile一開始設定變數的區域~
害我今天一直找不到檔案在哪裡被更動的@@
另外同時記錄Makefile的四種等號
=
直接assign變數
:=
Makefile會先把整個Makefile展開後,再決定Makefile的變數值。
例如:
x = foo
y = $(x) bar
x = xyz
最後結果y變數會是xyz bar
若要以當時的變數值來設定的話,就要用:=來處理
x := foo
y := $(x) bar
x := xyz
此例最後y變數為foo bar
?=
當變數沒有被定義時,則使用右邊的數值,
若有被定義時,則使用原本的數值
參考資料:
跟我一起寫Makefile
參考資料:文魁資訊-自己動手寫作業系統
[2008.09.19 補充]
Makefile撰寫
Makefile 語法簡介
make 命令和 makefile
Makefile學習教程: 跟我一起寫 Makefile
如何寫一個簡單的 Makefile [1][2]
Tutorial - Makefile
Make - a tutorial
Linux/Unix環境下的make和makefile詳解
GNU make中文手册
沒有留言:
張貼留言