組成元素

變數(variables)

簡介

像寫程式一樣,make規則裡面的組成可以有動態的值,這時就需要用變數來 設值來取代與轉換以及日後維護。另外還有就是有時字太多了,打字打到 可能會出錯,這樣除錯起Makefile很不好除錯,用變數代換值可以減少一 些打字錯誤。變數也有一些規定,

  • 字母大小有差。

  • 不要用字母數字底線以外的字元。可以有空格在前面後面。 (像shell script就不行有空格在變數前後。Var = value跟 Var=value是不一樣的)。

  • 使用變數用$(VAR)或者${VAR}都可以。

  • 如果要用$,請多加一個$變成$$,在Shell Command會用到Shell 變數此時就要加$。

Example 5-2. 設objects

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

變數代換

變數代換有兩種很重要的不同代換。遞迴的變數代換 (recursively expand)只要變數的值又是另一個變數值時, 就會一直代換下去。簡單的變數代換(simple expand)只代換一次變數的值。

變數代換

遞迴的變數代換

var = value

簡單的變數代換

var := value

Example 5-3. 例子

foo = $(bar)
bar = $(ugh)
ugh = Huh?
	      
all:
	echo $(foo)
	    
會echo `Huh?'

遞迴的方法有個壞處,不能夠加東西上去,例如
CFLAGS = -Isrc/include
CFLAGS = $(CFLAGS) -O
	  
不過這可以用另一種方法來解決
CFLAGS += -O
	  
變數通常都是Global的,也就是到處都可以適用的,除非你重設值。 但是在某些地方我們希望設一些值是根據某種情況的target或者些規則。

Shell變數與Makefile變數

wildcard在變數中可以展成所有的意義,但是在Makefile中使用要小心 例如
OBJECTS = *.o
foo: $(OBJECTS)
	cc -o foo $(OBJECTS)
	  
如果目前目錄下面沒有.o檔,則make並不知道你要的target是那些.o檔 他以為要去找一個叫*.o的target,這時就會跟你說找不到。如果目前目錄下有.o檔, 則會自動用這些.o檔作target不過這樣沒有任何意義了。 所以OBJECTS要設成prog1.o prog2.o這樣的形式。另外每一行command其實是喚起一個 sub shell來執行所以*.o會被shell解讀而沒有問題。 cc -o foo $(OBJECTS)的$(OBJECTS)是可以展成所有的.o檔的。

所以如果要傳Shell的變數給Shell就不要讓make對$這個符號做解釋, 所以要多加$。例如
linuxsubdirs: dummy
	  set -e; for i in $(Subdirs); do $(MAKE) -C $$i; done
	  
裡面的$$i就是這個意思。

傳變數給Shell可以用
export var1 var2 var3....
	  
而想要把shell變數的值傳給Makefile變數有兩個情況, 一個是原本的環境變數自動會變成同樣的Makefile變數名, 可以直接使用。如果兩個有相同的變數名,用make -e 則 Shell的會蓋掉Makefile裡的定義。 另一個狀況是想要根據一個shell的執行傳回的字串來設變數, 這時需要用內建函式,$(shell shell_command)
VAR := $(shell shell_command)
	  

特定Target的變數

例如
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
	  
當要編譯`prog'這個target時,才設CFLAGS為`-g',同時當要編譯 prog.o foo.o,bar.o時也會同時設CFLAGS=-g,這是因為某種設定只為了 某個特定的target才須要,例如程式除錯時就很有用。

目標(target)

簡介

目標可以是檔名或者是一個代表動作的識別符號,如果不是檔名的Target叫 phony target。make根據指定的target來做相關動作。

要完成一個目標前會先檢查他所需要的檔案或要先做的phnoy target,即 相依性檔案或先決條件目標(dependency or prerequiste) 如果要的相依或先決目標不存在,則make會失敗。如果這裡的先決目標是 phony target則PHONY TARGET每次都會被執行。

如果你在shell prompt只下make命令而已,第一個rule永遠被執行。 這叫default goal。如果你有指定target名字,例如make clean,則會 去執行這個target的動作,以上面例子看就是會執行 rm *.o *~ 這個動作。

一些目標規定

有些phony目標是GNU建議的,不見得一定要有啦只是建議目標。例如
all		:內定的編譯動作
install		:安裝binary檔的動作
clean		:清除obj檔的動作
dist		:產生configure的動作
distclean	:清除configure所產生的檔
	  

特別的內定目標(built-in target)

有些目標名稱已經有特別規定了,例如

一些內定目標

.PHONY:

在這個後面的target無條件執行。因為例如
clean:
	  rm *.o
		  
如果萬一真的有一個叫clean的檔案在make的目錄下, 偏偏這個檔沒有update,日期沒變,所以當你make clean時, make認為這個clean已經有了,也沒有相依性檔案需要重新編譯, 於是就不執行rm *.o了 。 所以我們要把它寫在.PHONY,則每次make clean就無條件執行, 不會把clean看成是檔名。

.SUFFIXS:

make有一些內定方法編譯特別副檔名,這些副檔名規則的副檔名 (名單)list,是在SUFFIXS這個變數裡, 可能有.c .o .cpp 等等。 用
.SUFFIXS:
		  
清掉內定副檔名list。 用
.SUFFIXS: .sgml .hack  
		  
加上.sgml .hack到內定list。

.SILENT:

這裡面的target執行時 命令(command)將不會印出來

.EXPORT_ALL_VARIABLES:

把所有變數告訴後來sub shell的子程序

命令(command)

命令就是要完成一個目標所要做的動作, 有幾個比較重要的規定要清楚