- 論壇徽章:
- 0
|
第一章 FreeBSD's make
今天剛開始著手,弄了幾段,先來占個坑,偶認領(lǐng)的第一章的后續(xù)內(nèi)容將在這個坑里面陸續(xù)補充完整。請大家多提意見,比如文理不通、用詞不當、風格太差以及錯別字等等,
第一章 FreeBSD的make
雨絲風片@chinaunix.net
1.1 FreeBSD的make
作為常用的和基本的Unix軟件開發(fā)工具,make是一個可以跟蹤全部的文件依賴關(guān)系的非常好的簿記工具程序。要管理依賴關(guān)系這樣的項目細節(jié)常常需要花費很多的時間,甚至會拖延開發(fā)進度。當多個開發(fā)人員合作一個項目的時候,依賴關(guān)系的跟蹤就可能變得相當困難了。事實上,正確地使用make可以幫助我們加快應(yīng)用程序的開發(fā),從而提高生產(chǎn)效率。
雖然make最初的設(shè)計是用來對應(yīng)用程序版本構(gòu)建的維護過程進行管理的,我們實際上還可以通過創(chuàng)建一系列的基于目標依賴關(guān)系的Unix shell命令來讓make完成多種多樣的額外工作。這些依賴關(guān)系可以用很多種方式定義——包括需要進行編譯的源文件、所需的庫文件、shell命令以及其它的目標。
make有多種風格的版本,其中包括GNU make和System V make。并不是在每個make版本中都有我們接下來討論的那些特性,具體使用哪個版本完全取決于你的個人喜好。我們將主要關(guān)注跟隨FreeBSD一起發(fā)布的make(也叫做bmake或pmake),尤其是如何通過它來編譯和更新FreeBSD系統(tǒng),也就是所謂的make world。雖然我們關(guān)注的是FreeBSD make,但我們在這里討論的所有東西對于各種BSD版本來說都是適用的。
我們首先會講述一個Makefile的基本文件布局和語法。如果這對于你來說太簡單了,那你可以直接跳到本章結(jié)束處的示例部分去閱讀。(注意,我們給出的代碼示例只用于演示我們關(guān)于make目標和依賴關(guān)系的討論,它們并不一定是可以運行的代碼。)
當然,和其它工具程序一樣,最開始應(yīng)該先去看看man page,以對make提供的命令行選項的概要和細節(jié)有一個正式的了解。同時,和其它工具程序一樣,學(xué)習make的最好方法就是使用它。創(chuàng)建一些小型的源文件(可以使用任何語言),然后嘗試一些下面給出的例子。我們希望讀完本章之后你除了理解make的語法規(guī)則之外,還知道它是如何工作的。
1.2 Makefile布局
總的說來,你使用make的方式就是讓它去讀一個Makefile,你需要在Makefile里指定一個目標及其依賴關(guān)系。在運行的時候,make會按順序搜索名字為Makefile或makefile的文件。這個Makefile通常是放在一個工程的根目錄下的,如果想指定其它的Makefile,可以在命令行上用-f (filename)的選項給出。
1.3 語法
一個Makefile的結(jié)構(gòu)由四個基本行組成,它們都可以通過在行尾添加‘\’字符來擴展到下一行(和shell編程相似)。注釋是以‘#’號開始的,至行尾結(jié)束。
- ########################################
- # Simple Makefile with comment example #
- ########################################
- # when run, it will just echo hello
- all:
- echo "hello"
復(fù)制代碼
要使用make來編譯一個工程,首先需要確定在你的當前工作目錄中已有一個正確的Makefile,然后再通過下列命令之一來使用make:
- bash$ make
- bash$ make all
- bash$ make <target name>
復(fù)制代碼
1.4 目標
用來指定目標的方式有很多種,不過最常用的就是用目標文件或一個工程的名字。工程名字不應(yīng)當包含有空格或標點符號,不過這只是個慣例而已;少量的空格和標點符號也是允許的。這個名字必須寫在一個新行的開頭,必須以單冒號(:)、雙冒號(::)或感嘆號(!)三者之一結(jié)束。
- myprog:
- <some commands to the compile myprog target>
- another::
- <some commands to the compile another target>
- sample!
- <some commands to the compile sample target>
復(fù)制代碼
在這些目標名字之后是所需的依賴條件,包括名字、變量以及其它的目標等等。如果你的依賴條件太多的話,可以用一個‘\’和一個newline來將它們分開。所有的依賴條件都必須Makefile內(nèi)定義或者存在于某個外部文件中,否則make將無法知道如何去完成依賴操作。
一些示例如下:
- all: driver.cpp network_class.cpp file_io_class.cpp network_libs.cpp file_io_libs.cpp
- all: myprog.o
- myprog.o:
復(fù)制代碼
上例中,all和myprog.o是要make的目標。注意myprog.o既是一個目標又是一個依賴條件。make首先會到myprog.o那兒,執(zhí)行它的命令,然后返回到all那兒,再執(zhí)行它的命令。這種操作序列是make的功能基礎(chǔ)。
按照慣例,all:目標是你的目標中的最高者,這意味著make將從這兒開始去尋找要完成all:目標都需要哪些東西。不過all:目標并不是必需的,如果沒有的話,make就會簡單地選擇所有列出的目標中的第一個,只對其實施操作,除非你在命令行上指定了某個目標。對于那些有一個核心的應(yīng)用程序需要維護和構(gòu)建的工程來說,我們建議你使用all:目標;這是一個通用的慣例,有助于避免錯誤和不必要的任務(wù)。
上例所示的依賴序列只是很簡單的一個。下面是一個更為復(fù)雜和靈活的依賴序列,我們沒有給出用于具體目標的命令:
- all: myprog.o lib
- lib: lex
- lex:
- myprog.o: app.h
復(fù)制代碼
注意,在這個例子中,all:目標有兩個依賴條件:myprog.o和lib。這兩個依賴條件本身又都是目標,make將首先去編譯myprog.o。在make編譯myprog.o的時候,會發(fā)現(xiàn)有一個和app.h的依賴關(guān)系。app.h并沒有在這個makefile里定義,但app.h卻是一個在當前目錄中的頭文件。
用于myprog.o的命令完成之后,make即返回到all:處,繼續(xù)處理下一個依賴條件,在此例中是lib。依賴條件lib本身也有一個依賴條件lex,所以make在完成lib之前會先去完成lex:。
注意:正如你所看到的,這些依賴關(guān)系可能會非常長,或者嵌套得很深。如果你有一個很大的Makefile,那一定要好好地組織一下,把目標的順序弄好。
1.5 求值規(guī)則
依賴關(guān)系是按照依賴于目標名字結(jié)束符號的嚴格規(guī)則來求值的。一旦make認為滿足規(guī)則,它將通過執(zhí)行相應(yīng)的命令來創(chuàng)建特定的目標(比如編譯該目標)。例如,使用單冒號:可以讓你對需要進行編譯的目標進行更為精細的控制。也就是說,你可以指定某個特定的目標文件每次都需要重新編譯或者僅當它的源文件過時之后才編譯。這些規(guī)則都是基于目標名字的結(jié)束符號的,如下:
如果目標名字以單冒號(:)結(jié)束,它將根據(jù)以下兩個規(guī)則來創(chuàng)建:
- 如果目標尚未存在,就像我們在上面舉的例子里的all:一樣,make就會創(chuàng)建它。
- 如果任意一個源文件具有比當前目標更新的時間戳。在上例中如果app.h或myprog.c具有更新的時間戳,myprog.o就會被make。這種情況只需簡單地用一下touch命令即可出現(xiàn)
如果目標名字以雙冒號(::)結(jié)束,它將根據(jù)以下三個規(guī)則來創(chuàng)建:
- 如果任意一個源文件具有比當前目標更新地時間戳。
- 該目標不存在。
- 該目標沒有與之關(guān)聯(lián)的源文件。
如果目標名字以感嘆號(!)結(jié)束,只要make把它所需的全部依賴條件都創(chuàng)建完畢就會來創(chuàng)建它。
你只能在目標或源文件的最后一個組成部分中使用通配表達式?、*和[],而且只能用于描述已經(jīng)存在的文件。比如:
而使用花括號{}的表達式則不一定非得描述已經(jīng)存在的文件。比如:
- {mypgog,test}.o
- # the expression above would match myprog.o test.o only
復(fù)制代碼
最后需要注意一點:可變表達式是按照目錄順序來處理的,而非字母順序,就跟在shell表達式中一樣。例如,如果你的目標有某些基于字母順序的依賴條件,下面這個表達式可能就不對了:
- {dprog,aprog,bprog,cprog}.cpp
復(fù)制代碼
1.6 變量
make能夠使用變量這一點是非常重要的。例如,你有一個名字為a.c的源文件,由于某種原因你想把它的名字改成b.c。通常情況下,你得把你的makefile里的每個a.c的實例都改成b.c。但是,如果你寫成以下方式:
你只需要把這一行更新成新的名字即可,如下:
你因此而節(jié)省了時間,這也正是make的首要角色:項目管理。
變量可用$(<變量名字> 或就用一個$來引用,但后者未被廣泛使用,因此建議別那樣寫。
- $(GCC) = /usr/local/bin/gcc
復(fù)制代碼
make有四種不同類型的變量,下面將按照被搜索的順序列出它們。(make的搜索將一直進行到發(fā)現(xiàn)某個數(shù)值的第一個實例為止。)
- 局部變量:這些是賦給特定目標的數(shù)值。
- 命令行:命令行變量是在命令行上傳給make的。
- 全局變量:全局變量是在該Makefile或任何所包含的Makefile內(nèi)賦值的。你在一個Makefile內(nèi)最常看到的就是這些變量。
- 環(huán)境變量:環(huán)境變量是在Makefile之外,也就是運行make的shell里設(shè)置的。
這些變量可以在Makefile里用以下五種操作符進行定義:
- 等號“=”是最常用的操作符,這和shell的情況是類似的。數(shù)值被直接賦給了變量。例如:
- 加號等號“+=”的意思是附加賦值,通過把所賦數(shù)值附加到當前數(shù)值的后面來完成對變量的賦值。例如:
- VAR += < value to append >
復(fù)制代碼
- 問號等號“?=”的意思是條件賦值,僅當該變量從未賦值時才進行賦值。條件賦值在向一個字符串數(shù)值添加前綴的時候非常有用。例如:
- VAR ?= < value if unset >
復(fù)制代碼
- 冒號等號“:=”的意思是擴展賦值,在賦值前會對所賦數(shù)值進行擴展;通常這種擴展是在所賦變量被引用的時候才進行的。例如:
- VAR := < value to expand >
復(fù)制代碼
- 感嘆號等號“!=”的意思是shell命令賦值。在命令被擴展并發(fā)給shell執(zhí)行完畢之后,將命令結(jié)果賦給變量。結(jié)果中的newline字符將被空格取代。例如:
- VAR != < shell command to execute >
復(fù)制代碼
注意:有些變量是在外部的系統(tǒng)級的Makefile內(nèi)定義的(在/etc/make.conf或/etc/defaults/make.conf中),如果你在設(shè)置環(huán)境變量方面遇到了問題,就去檢查一下系統(tǒng)級文件里的設(shè)置。
一個完整的例子如下:
- #####################
- # Example make file #
- #####################
- CURDIR != pwd
- CFLAGS ?= -g
- CFLAGS += -Wall -O2
-
- all:
- echo $CFLAGS
- #######################
-
- bash$ CFLAGS="-g -Wall" make
復(fù)制代碼
在上例中,CURDIR被設(shè)置成了shell命令pwd的結(jié)果。(注意,這種賦值并不需要backtick命令(即' ')。)CFLAGS如果從未設(shè)置,會首先被設(shè)置成-g,然后不管它的當前值是什么,都會被附加上-Wall -O2。
1.7 命令
如果沒有命令,那make什么都不是,只有把命令告訴make它才能完成它的工作。make只能運行這些命令,然后基于shell的退出狀態(tài)判斷這些命令是否成功。因此,如果命令失敗,shell返回一個錯誤,make將會因錯誤而退出并于該處終止。make可以就被看作是另外一個命令——除了運行其它命令之外,它和那些命令并沒有什么實際的交互。
命令必須與一個目標相關(guān)聯(lián),任何一個目標都可以有多個命令。例如:
- # example for an all-install target
- all-install:
- $(CC) $(CFLAGS) $(MYSRC)
- cp $(MYPROG) $(INSTALL_DIR)
- echo "Finished the build and install"
復(fù)制代碼
每個命令都必須在一個目標之后以新行開始,在實際命令起始位置之前必須要有一個tab鍵,如上所示。
對于大多數(shù)情況而言,Makefile里的命令只要是個有效的shell命令就行,命令還經(jīng)常會包括變量。例如:
- CPP = -g++
- CFLAGS = -Wall -O2
- myprog.o:
- $(CPP) $(CFLAGS) -c myprog.c
復(fù)制代碼
下面這個例子告訴make使用給定值編譯myprog.c。這些命令可能會比一行要長,它們也可以被寫來完成其它的任務(wù)。這是非常重要的,因為編譯器可以接受相當多的命令行選項、環(huán)境設(shè)置以及定義等等,比如:
- CLFAGS = $(LINK_FLAGS) $(LINK_LIBS) $(OTHER_LIBS) \
- $(OPTIMIZER_FLAGS) $(DEFINES) $(NO_KERNEL) $(OBJS) \
- $(CPU_TYPE_FLAGS) $(USE_MY_MALLOC) $(UDEFINES) \
- $(SRC_FILE) $(OTHER_SRC_FILES)
復(fù)制代碼
下面這個例子告訴make刪除所有的object文件、core文件以及應(yīng)用程序本身,然后把log文件移走,這些操作都可以很方便的完成。
- CPP = -g++
- CFLAGS = -Wall -O2
- APP = myapp
- DATE != date +%m%d%y_%H_%M
- LOG = debug
- myprog.o:
- $(CPP) $(CFLAGS) -c myprog.c
- rm -f *.o *.core $(APP)
- mv $(LOG).log $(LOG)_$(DATE).log
- clean:
- rm -f *.o *.core $(APP)
- mv $(LOG).log $(LOG)_$(DATE).log
復(fù)制代碼
但是,如果并不存在log文件,make就會因錯誤而退出。為了避免這種情況,可以在命令之前加上“-”號。通過在命令前面加上減號,你就告訴了make忽略執(zhí)行命令時遇到的錯誤。(不過make仍然會打印出錯誤信息。)因此,在某個命令出現(xiàn)錯誤之后make仍將繼續(xù)。例如:
- clean:
- -rm -f *.o *.core $(APP)
- -mv $(LOG).log $(LOG)_$(DATE).log
復(fù)制代碼
這將使得make忽略掉rm和mv命令可能遇到的錯誤。
你還可以讓make禁止掉“echo”之類的命令的輸出。echo首先告訴make打印出包括echo語句在內(nèi)的整個命令,然后再執(zhí)行命令并將字符串打印到屏幕上:
- echo $(SOME_DEFINED_STRING)
復(fù)制代碼
要避免這種情況,可以在echo命令之前加上“@”符號,這將告訴make只打印字符串,比如:
- @echo $(SOME_DEFINED_STRING)
復(fù)制代碼
“-”號和“@”號都既可用于變量,又可用于形為字符串的命令,但需要確定你對變量命令的引用是正確的。下例演示了如何對命令使用@操作符:
- ECHO = echo
- MESSAGE = "Print this message"
- msg::
- @$(ECHO) $(MESSAGE)
復(fù)制代碼
1.8 條件語句(#if,#ifndef等等)
如果你對C和C++比較熟悉,那你肯定知道條件預(yù)處理命令。功能繁多的make也有一個類似的特性。條件語句使你可以選擇Makefile里的哪個部分需要被處理。這些條件語句最多可以嵌套30層,并且可以放在Makefile里的任何地方。每條語句都必須以一個圓點(.)開始,而且條件語句塊必須以.endif結(jié)束。
條件語句允許使用邏輯操作符,比如邏輯AND“&&”、邏輯OR“||”,整條語句還可以用“!”操作符取反!!”操作符具有最高優(yōu)先級,其后依次是邏輯AND和邏輯OR。括號可以被用來指定優(yōu)先級順序。關(guān)系運算符也是可以使用的,比如“>”、“>=”、“<”、“<=”、“==”和“!=”。這些操作符可被用于十進制和十六進制的數(shù)值。對于字符串則可以使用“==”和“!=”操作符。如果沒有給出操作符,那么將會把數(shù)值和0進行比較。
在下面的例子中,在對VER變量進行賦值之后對條件進行測試。注意,如果VER變量未被賦值,則最后的.else條目將被求值為真,TAG將被賦成2.4_stable的值。
- .if $(VER) >= 2.4
- TAG = 2.4_current
- .elif $(VER) == 2.3
- TAG = 2.3_release
- .else
- TAG = 2.4_stable
- .endif
復(fù)制代碼
條件語句可用于測試變量,也可以用于下面這種函數(shù)風格的表達式。
這些用法中有些是有簡寫形式的。出于兼容性方面的考慮,我們列出了簡寫形式。非簡寫形式意思更為確定,也更容易被理解,但需要你敲入更多的字符。
在使用簡寫形式的時候是不必使用括號的。此外,簡寫形式還可以和if/else語句以及其它的簡寫形式混合在一起:
make( < arg > ) short hand [ .ifmake, .ifnmake, .elifmake, .elifnmake ]
在上例中,make將以一個目標名字作為它的參數(shù)。如果這個目標已在命令行上給出,或者它就是缺省進行make的目標,那么該值就為真。下例中將根據(jù)make()表達式的規(guī)則給CFLAGS賦值:
- .if make(debug)
- CFLAGS += -g
- .elif make(production)
- CFLAGS += -O2
- .endif
復(fù)制代碼
下面是用簡寫形式表示的相同代碼:
- .ifmake debug
- CFLAGS += -g
- .elifmake production
- CFLAGS += -O2
- .endif
復(fù)制代碼
target( < arg > )
這種形式將以一個目標名字作為參數(shù)。僅當該target已被定義時該值才為真。這個表達式?jīng)]有簡寫形式。例如:
- .if target(debug)
- FILES += $(DEBUG_FILES)
- .endif
復(fù)制代碼
在上例中,如果debug目標返回真的話就會對FILES變量進行附加操作。
empty ( < arg > )
這種形式以一個變量為參數(shù),并允許使用修飾符。當變量被擴展之后是一個空字符串時該值為真。這個表達式?jīng)]有簡寫形式。此外需要注意的是,你在使用這個表達式的時候并不需要對數(shù)值進行引用,記住是VAR而不是$(VAR)。例如:
- .if empty (CFLAGS)
- CFLAGS = -Wall -g
- .endif
復(fù)制代碼
defined( < arg > ) short hand [ .ifdef , .ifndef , .elifdef, elifndef ]
下面這個例子采用一個變量作為參數(shù)。僅當變量已被定義時該值為真。
- .if defined(OS_VER)
- .if $(OS_VER) == 4.4
- DIRS += /usr/local/4.4-STABLE_src
- .endif
- .else
- DIRS += /usr/src
- .endif
復(fù)制代碼
下面是簡寫形式:
- .ifdef OS_VER
- . if $(OS_VER) == 4.4
- DIRS += /usr/local/4.4-STABLE_src
- . endif
- .else
- OS_VER = 3.2
- DIRS += /usr/src
- .endif
復(fù)制代碼
正如你所看到的,make允許嵌套的條件和define表達式。和C不同的是,你不能對if語句和變量賦值語句進行縮進。如果想讓你的條件語句塊更清晰一點的話,你可以在圓點之后、if之前加一些空格。示例如下:
- .if $(DEBUG) == 1
- $(CFLAGS) = -g
- . ifndef $(DEBUG_FLAGS)
- $(FLAGS) = $(DEBUG_FLAGS)
- . endif
- .endif
復(fù)制代碼
exists( < arg > )
下面的例子演示了如何使用exists,以及如何給一個目標添加條件語句。如果存在tmp目錄,make將運行-rm tmp/*.o命令。正如你在這個例子中看到的,.if語句僅在clean目標中被求值;所運行的命令必須遵從常規(guī)的命令語法。
- clean:
- -rm -f *.o *.core
- .if exists(tmp)
- -rm tmp/*.o
- .endif
復(fù)制代碼
1.9 系統(tǒng)Makefiles,模板以及.include指令
C的一個重要特性就是它的簡單明了的預(yù)處理指令,也就我們常說的#include。這個特性也在make中實現(xiàn)了。所不同的是,如果你想包含另外一個Makefile,那你得在文件末尾包含它,而不是像C那樣在文件頭包含,這是因為變量是按順序進行賦值的。Makefile中又有Makefile可能會造成混淆,不過包含一個Makefile的基本語法卻是很簡單的。注意,在include單詞前面必須要加一個圓點(.);菊Z法如下:
如果要指定一個位于系統(tǒng)make目錄中的Makefile,可以使用尖括號:
如果所指定的文件位于當前目錄或者是由-I命令行選項指定的目錄中,則使用雙引號,這和C的#include是類似的:
在下面的例子中,如果已在project.mk中定義了CFLAGS變量,它將被這里的新聲明所取代。當包含多個Makefile時這就可能造成一些麻煩。
- .include "../project.mk"
- CFLAGS = -g
復(fù)制代碼
FreeBSD系統(tǒng)有很多的系統(tǒng)Makefile可供包含,與之對應(yīng)的是完成各種任務(wù)的例程。在大多數(shù)的FreeBSD系統(tǒng)中,這些Makefile位于/usr/share/mk/目錄里。除此之外,還有一個/etc/defaults/make.conf,它是可以被/etc/make.conf取代的。(細節(jié)請參見man make.conf。)
下面是一個常用的Makefile的簡單列表。如果你正準備進行大量的內(nèi)核編程或應(yīng)用程序移植,那就好好利用Makefile吧。這些Makefile的形式為bsd.<type>.mk,其中<type>表示這些Makefile的用途。
- bsd.dep.mk:這是一個用來處理Makefile依賴關(guān)系的非常有用的包含文件。
- bsd.port.mk:這是一個在構(gòu)建應(yīng)用程序的ports的時候使用的包含文件。
使用.include指令的好處就是你可以把你的工程的Makefile分成很多的片斷。比如,你的工程可以有一個主Makefile,由所有其它的子Makefile進行包含。這個主Makefile可以包含編譯器變量及其所需的選項。這樣一來,就不必在每個Makefile里都指定編譯器和所需選項了,對編譯器名字的引用也因此而得到了簡化。這些公共使用的片斷之后還可以用于其它的Makefile,對編譯程序等例程的修改也將隨之在所有的Makefile中生效。
1.10 高級選項
高級的make選項主要為了增加靈活性。我們建議你仔細閱讀一下make的man page,以便對make有一個深入的了解。下面這些選項是我們最常使用的。
局部變量
make可以使用專門定義的局部變量,其范圍僅限于當前目標。下面列出了七個這樣的局部變量,同時還給出了它們的System V兼容的老的表示形式。(不建議使用System V的老的表示形式,之所以列出它們,只是出于向后兼容的考慮。)
這個變量就是目標的名字:
- .TARGET
- old style notation: '@'
復(fù)制代碼
這個變量包含了當前目標的全部源文件的列表:
- .ALLSRC
- old style notation: '>'
復(fù)制代碼
這個變量是當前目標所隱含的源文件。它的數(shù)值是這個目標的源文件的名字和路徑。(示例見下面的.SUFFIX部分。)
- .IMPSRC
- old style notation: '<'
復(fù)制代碼
這個變量保存的是已經(jīng)被確定為過期的源文件的列表:
- .OODATE
- old style notation: '?'
復(fù)制代碼
這個變量的值是不包括后綴和路徑的文件名:
- .PREFIX
- old style notation: '*'
復(fù)制代碼
這個變量的值是檔案文件的名字:
- .ARCHIVE
- old style notation: '!'
復(fù)制代碼
這個變量的值是檔案成員的名字:
- .MEMBER
- old style notation: '%'
復(fù)制代碼
當在依賴關(guān)系行中使用這些局部變量的時候,只有.TARGET、.PREFIX、.ARCHIVE和.MEMBER具有該目標的值。
另外還有一個非常不錯的指令:
這會讓你非常方便地取消對一個變量的定義。注意,只有全局變量才能取消定義。例如:
- .ifdef DEBUG
- .undef RELEASE
- .endif
復(fù)制代碼
1.11 轉(zhuǎn)換規(guī)則(后綴規(guī)則)
轉(zhuǎn)換規(guī)則規(guī)定了如何去創(chuàng)建一個目標。你可以利用這些規(guī)則,省下為每個目標文件編寫規(guī)則的時間。語法很簡單:.SUFFIXES: (suffix list)
注意,可能有多個不同的后綴共享相同的轉(zhuǎn)換后綴(.cpp、.cc、.c都可以轉(zhuǎn)換成a.o)。如果沒有列出后綴,make將把之前的所有后綴都刪除。這可能會在你有多個.SUFFIXES規(guī)則的時候造成很大的混亂。我們建議你只使用一個規(guī)則,并把它放在Makefile的底部。把.SUFFIXES語句塊中列出的東西和target進行比較,你就容易理解它的結(jié)構(gòu)了。
- .SUFFIXES: .cpp .c .o .sh
- .c.o:
- $(CC) $(CFLAGS) -c ${.IMPSRC}
- .cpp.o:
- $(CPP) $(CXXFLAGS) -c ${.IMPSRC}
- .sh:
- cp ${.IMPSRC} ${.TARGET}
- chmod +x ${.TARGET}
復(fù)制代碼
上面給出的這些規(guī)則將會編譯C和C++源文件。不過對于.sh:規(guī)則而言,它也可以告訴make去創(chuàng)建相應(yīng)的shell腳本。注意,如果想在這個例子中列出某個shell腳本作為目標,那就不能添加.sh擴展名,不過那個shell腳本本身卻必須是帶有.sh后綴的。
如果我們把不帶.sh后綴的install_script列出來作為一個依賴條件,那就必須存在一個帶有.sh后綴的shell腳本,名字為install_script.sh。也就是說,如果某個文件可以被列出來作為一個目標,那么在這個文件的存在期間,僅當make認為那個目標和該文件比起來過了期之后才會去創(chuàng)建它,make并不會去創(chuàng)建這個文件。示例見下;更多的信息請參見apps.h的例子:
- all: install_script $(OBJS)
- $(CPP) $(CFLAGS) -o $(APP) $(OBJS) $(LINK)
復(fù)制代碼
1.12 有用的命令行選項
下面給出了一些非常容易學(xué)習和使用的命令行選項。這并不是一個完整的列表,要想知道其它的選項還得去看man page。
-D <variable name to define>
這個選項將在命令行上定義一個變量,如果你的Makefile里面有.ifdef語句,那么使用這個選項就非常方便了。例如:
- .ifdef DEBUG
- CFLAGS += -g -D__DEBUG__
- .endif
復(fù)制代碼
于是,當你運行命令make -D DEBUG的時候,make就會正確的設(shè)置CFLAGS,在編譯你的應(yīng)用程序的時候也把調(diào)試語句編進去。
-E < variable name to override >
這個選項將用環(huán)境變量的值取代Makefile中給變量賦的值。在使用這個選項之前,需要先設(shè)置你的shell中的環(huán)境變量。例如:
- bash $ CFLAGS="-O2 -Wall" make -E CFLAGS
復(fù)制代碼
-e
和大寫形式類似,-e將用環(huán)境變量取代Makefile中的所有變量。如果某個變量沒有定義相應(yīng)的環(huán)境變量,則按正常方式賦值。
-f <makefile to use>
這個選項使你可以在命令行上指定Makefile,這一點在你需要多個Makefile時很有用。例如:
- bash$ make -f Makefile.BSD
復(fù)制代碼
-j < number of max_jobs >
這個選項使你可以指定make能夠派生出多少個job來。一般來說make只會派生出一個,但對于一個非常大的工程而言,如果想讓你的硬件物盡其用的話,那就指定4個吧,如下:
如果超過了4個,有時反而會延長執(zhí)行時間,不過有些人倒是可能覺得CPU被六個或更多的job折騰的樣子很有趣。
-n
這個選項對于Makefile的調(diào)試很有用,它可以讓你看到make究竟會執(zhí)行哪些命令,但又不會實際去執(zhí)行它們。對于有很多命令的大型工程,最好是把輸出重定向到一個外部文件中,否則就會有浩如煙海的命令顯示出來。示例如下:
- bash $ make -n >> make_debug.log 2>&1
復(fù)制代碼
-V < variable name >
這個選項將基于全局的上下文打印變量的值。與前一個選項類似,make也不會去構(gòu)建任何目標。你可以在命令行上指定多個-V選項,如下:
- make -V CFLAGS -V LINK -V OBJS
復(fù)制代碼
1.13 一個最后的例子
下面列出的Makefile是一個可以重復(fù)使用的Makefile的例子。在你包含了它之后,它就知道從列出的.SUFFIXES規(guī)則中得知如何去編譯C++源文件。它還知道如何去安裝應(yīng)用程序和清除開發(fā)目錄。這顯然并不是一個很好理解的Makefile,但它卻是一個很好的創(chuàng)建通用模板風格的Makefile的范例,這種Makefile包含了那些用于開發(fā)的公共例程。這不只是節(jié)省了在創(chuàng)建每個Makefile時重復(fù)輸入這些公共規(guī)則的時間,它還能讓開發(fā)人員重復(fù)使用已知的好例程。
- ########################################################
- #
- # FILE: Makefile
- #
- # AUTHOR: Nathan Boeger
- #
- # NOTES:
- # This is a generic Makefile for *BSD make, you will
- # need to customize the listed variables below inside
- # the Makefile for your application.
- #
- # INSTALL_DIR = name of the directory that you want to install
- # this applicaion (Ex: /usr/local/bin/ )
- #
- # APP = name of the application
- #
- # C_SRC = C source files (Ex: pstat.c )
- #
- # CPP_SRC = CPP source files (Ex: node.cpp)
- #
- #
- # $Id: ch01.html,v 1.5 2004/08/10 14:41:39 nathan Exp $
- #########################################################
- # Make the OBJ's from our defined C & C++ files
- .ifdef CPP_SRC
- OBJS = ${CPP_SRC:.cpp=.o}
- .endif
- .ifdef C_SRC
- OBJS += ${C_SRC:.c=.o}
- .endif
- # define the Compiler. The compiler flags will be appended to
- # if defined, else they are just assigned the values below
- CPP = g++
- CFLAGS += -Wall -Wmissing-prototypes -O
- LINK += -lc
- # Add a debug flag.
- .ifdef DEBUG
- CFLAGS += -g
- .endif
- # Targets
- all: ${OBJS}
- $(CPP) $(CFLAGS) -o $(APP) ${OBJS} $(LINK)
- depend:
- $(CPP) -E -MM ${C_SRC} ${CPP_SRC} > .depend
- #######################################################
- #
- # INSTALL SECTION
- #
- # install will copy the defined application (APP) into the
- # directory INSTALL_DIR and chmod it 0755
- # for more information on install read MAN(1) install
- ########################################################
- install: all
- install -b -s $(APP) $(INSTALL_DIR)
-
- clean
- rm -f $(APP) *.o *.core
- # SUFFIX RULES
- .SUFFIXES: .cpp .c .o
- .c.o:
- $(CPP) $(CFLAGS) -c ${.IMPSRC}
- .cpp.o:
- $(CPP) $(CFLAGS) -c ${.IMPSRC}
復(fù)制代碼
下面給出的Makefile是需要你在工程目錄內(nèi)部創(chuàng)建的:
- #######################################################
- # PROJECT Makefile
- #
- # This is what the programs makefile would look like
- # These are the only variables you will need to define
- ######################################################
- APP = myapp
- C_SRC = debug_logger.c
- CPP_SRC = myapp.cpp base_classes.cpp
- INSTALL_DIR = /usr/local/bin/
- # And include the template Makefile, make sure its
- # path is correct.
-
- .include "../../bsd_make.mk"
復(fù)制代碼
[ 本帖最后由 雨絲風片 于 2006-3-4 08:24 編輯 ] |
|