參考鏈接:
【FreeBSD system programming 】中文翻譯計劃及所有異義提交處
FreeBSD系統編程
Copyright(C) 2001~2006 Nathan Boeger 和Mana Tominaga 版權所有,簡體中文版由ChinaUnix論壇提供翻譯。
目錄
第一章: FreeBSD的Make
第二章: BSD自舉
第三章: 進程和內核服務
第四章: 高級進程控制和信號
第五章: 基本I/O
第六章: 高級I/O
第七章: 進程資源和系統限制
第八章: FreeBSD 5.x
第一章 FreeBSD的make
譯者:雨絲風片@chinaunix.net
1.1 FreeBSD的make
作為常用的和基本的Unix軟件開發工具,make是一個可以跟蹤全部的文件依賴關系的非常好的簿記工具程序。要管理依賴關系這樣的項目細節常常需要花費很多的時間,甚至會拖延開發進度。當多個開發人員合作一個項目的時候,依賴關系的跟蹤就可能變得相當困難了。事實上,正確地使用make可以幫助我們加快應用程序的開發,從而提高生產效率。
雖然make最初的設計是用來對應用程序版本構建的維護過程進行管理的,我們實際上還可以通過創建一系列的基於目標依賴關系的Unix shell命令來讓make完成多種多樣的額外工作。這些依賴關系可以用很多種方式定義——包括需要進行編譯的源文件、所需的庫文件、shell命令以及其它的目標。
make有多種風格的版本,其中包括GNU make和System V make。並不是在每個make版本中都有我們接下來討論的那些特性,具體使用哪個版本完全取決於你的個人喜好。我們將主要關注跟隨FreeBSD一起發布的make(也叫做bmake或pmake),尤其是如何通過它來編譯和更新FreeBSD系統,也就是所謂的make world。雖然我們關注的是FreeBSD make,但我們在這裡討論的所有東西對於各種BSD版本來說都是適用的。
我們首先會講述一個Makefile的基本文件布局和語法。如果這對於你來說太簡單了,那你可以直接跳到本章結束處的示例部分去閱讀。(注意,我們給出的代碼示例只用於演示我們關於make目標和依賴關系的討論,它們並不一定是可以運行的代碼。)
當然,和其它工具程序一樣,最開始應該先去看看man page,以對make提供的命令行選項的概要和細節有一個正式的了解。同時,和其它工具程序一樣,學習make的最好方法就是使用它。創建一些小型的源文件(可以使用任何語言),然後嘗試一些下面給出的例子。我們希望讀完本章之後你除了理解make的語法規則之外,還知道它是如何工作的。
1.2 Makefile布局
總的說來,你使用make的方式就是讓它去讀一個Makefile,你需要在Makefile裡指定一個目標及其依賴關系。在運行的時候,make會按順序搜索名字為Makefile或makefile的文件。這個Makefile通常是放在一個工程的根目錄下的,如果想指定其它的Makefile,可以在命令行上用-f (filename)的選項給出。
CODE:[Copy to clipboard]make -f OtherMakefile
1.3 語法
一個Makefile的結構由四個基本行組成,它們都可以通過在行尾添加‘\’字符來擴展到下一行(和shell編程相似)。注釋是以‘#’號開始的,至行尾結束。
CODE:[Copy to clipboard]########################################
# Simple Makefile with comment example #
########################################
# when run, it will just echo hello
all:
echo "hello"
要使用make來編譯一個工程,首先需要確定在你的當前工作目錄中已有一個正確的Makefile,然後再通過下列命令之一來使用make:
CODE:[Copy to clipboard]bash$ make
bash$ make all
bash$ make
1.4 目標
用來指定目標的方式有很多種,不過最常用的就是用目標文件或一個工程的名字。工程名字不應當包含有空格或標點符號,不過這只是個慣例而已;少量的空格和標點符號也是允許的。這個名字必須寫在一個新行的開頭,必須以單冒號(:)、雙冒號(::)或感歎號(!)三者之一結束。
CODE:[Copy to clipboard]myprog:
another::
sample!
在這些目標名字之後是所需的依賴條件,包括名字、變量以及其它的目標等等。如果你的依賴條件太多的話,可以用一個‘\’和一個newline來將它們分開。所有的依賴條件都必須Makefile內定義或者存在於某個外部文件中,否則make將無法知道如何去完成依賴操作。
一些示例如下:
CODE:[Copy to clipboard]all: driver.cpp network_class.cpp file_io_class.cpp network_libs.cpp file_io_libs.cpp
all: myprog.o
myprog.o:
上例中,all和myprog.o是要make的目標。注意myprog.o既是一個目標又是一個依賴條件。make首先會到myprog.o那兒,執行它的命令,然後返回到all那兒,再執行它的命令。這種操作序列是make的功能基礎。
按照慣例,all:目標是你的目標中的最高者,這意味著make將從這兒開始去尋找要完成all:目標都需要哪些東西。不過all:目標並不是必需的,如果沒有的話,make就會簡單地選擇所有列出的目標中的第一個,只對其實施操作,除非你在命令行上指定了某個目標。對於那些有一個核心的應用程序需要維護和構建的工程來說,我們建議你使用all:目標;這是一個通用的慣例,有助於避免錯誤和不必要的任務。
上例所示的依賴序列只是很簡單的一個。下面是一個更為復雜和靈活的依賴序列,我們沒有給出用於具體目標的命令:
CODE:[Copy to clipboard]all: myprog.o lib
lib: lex
lex:
myprog.o: app.h
注意,在這個例子中,all:目標有兩個依賴條件:myprog.o和lib。這兩個依賴條件本身又都是目標,make將首先去編譯myprog.o。在make編譯myprog.o的時候,會發現有一個和app.h的依賴關系。app.h並沒有在這個makefile裡定義,但app.h卻是一個在當前目錄中的頭文件。
用於myprog.o的命令完成之後,make即返回到all:處,繼續處理下一個依賴條件,在此例中是lib。依賴條件lib本身也有一個依賴條件lex,所以make在完成lib之前會先去完成lex:。
注意:正如你所看到的,這些依賴關系可能會非常長,或者嵌套得很深。如果你有一個很大的Makefile,那一定要好好地組織一下,把目標的順序弄好。
1.5 求值規則
依賴關系是按照依賴於目標名字結束符號的嚴格規則來求值的。一旦make認為滿足規則,它將通過執行相應的命令來創建特定的目標(比如編譯該目標)。例如,使用單冒號:可以讓你對需要進行編譯的目標進行更為精細的控制。也就是說,你可以指定某個特定的目標文件每次都需要重新編譯或者僅當它的源文件過時之後才編譯。這些規則都是基於目標名字的結束符號的,如下:
如果目標名字以單冒號(:)結束,它將根據以下兩個規則來創建:
如果目標尚未存在,就像我們在上面舉的例子裡的all:一樣,make就會創建它。
如果任意一個源文件具有比當前目標更新的時間戳。在上例中如果app.h或myprog.c具有更新的時間戳,myprog.o就會被make。這種情況只需簡單地用一下touch命令即可出現
CODE:[Copy to clipboard] touch myprog.c
如果目標名字以雙冒號(::)結束,它將根據以下三個規則來創建:
如果任意一個源文件具有比當前目標更新地時間戳。
該目標不存在。
該目標沒有與之關聯的源文件。
如果目標名字以感歎號(!)結束,只要make把它所需的全部依賴條件都創建完畢就會來創建它。
你只能在目標或源文件的最後一個組成部分中使用通配表達式?、*和[],而且只能用於描述已經存在的文件。比如:
CODE:[Copy to clipboard]myprog.[oc]
而使用花括號{}的表達式則不一定非得描述已經存在的文件。比如:
CODE:[Copy to clipboard]{mypgog,test}.o
# the expression above would match myprog.o test.o only
最後需要注意一點:可變表達式是按照目錄順序來處理的,而非字母順序,就跟在shell表達式中一樣。例如,如果你的目標有某些基於字母順序的依賴條件,下面這個表達式可能就不對了:
CODE:[Copy to clipboard]{dprog,aprog,bprog,cprog}.cpp
1.6 變量
make能夠使用變量這一點是非常重要的。例如,你有一個名字為a.c的源文件,由於某種原因你想把它的名字改成b.c。通常情況下,你得把你的makefile裡的每個a.c的實例都改成b.c。但是,如果你寫成以下方式:
CODE:[Copy to clipboard]MYSRC = a.c
你只需要把這一行更新成新的名字即可,如下:
CODE:[Copy to clipboard]MYSRC = b.c
你因此而節省了時間,這也正是make的首要角色:項目管理。
變量可用$(<變量名字>)或就用一個$來引用,但後者未被廣泛使用,因此建議別那樣寫。
CODE:[Copy to clipboard]$(GCC) = /usr/local/bin/gcc
make有四種不同類型的變量,下面將按照被搜索的順序列出它們。(make的搜索將一直進行到發現某個數值的第一個實例為止。)
局部變量:這些是賦給特定目標的數值。
命令行:命令行變量是在命令行上傳給make的。
全局變量:全局變量是在該Makefile或任何所包含的Makefile內賦值的。你在一個Makefile內最常看到的就是這些變量。
環境變量:環境變量是在Makefile之外,也就是運行make的shell裡設置的。
這些變量可以在Makefile裡用以下五種操作符進行定義:
等號“=”是最常用的操作符,這和shell的情況是類似的。數值被直接賦給了變量。例如:
CODE:[Copy to clipboard]VAR = < value >
加號等號“+=”的意思是附加賦值,通過把所賦數值附加到當前數值的後面來完成對變量的賦值。例如:
CODE:[Copy to clipboard]VAR += < value to append >
問號等號“?=”的意思是條件賦值,僅當該變量從未賦值時才進行賦值。條件賦值在向一個字符串數值添加前綴的時候非常有用。例如:
CODE:[Copy to clipboard]VAR ?= < value if unset >
冒號等號“:=”的意思是擴展賦值,在賦值前會對所賦數值進行擴展;通常這種擴展是在所賦變量被引用的時候才進行的。例如:
CODE:[Copy to clipboard]VAR := < value to expand >
感歎號等號“!=”的意思是shell命令賦值。在命令被擴展並發給shell執行完畢之後,將命令結果賦給變量。結果中的newline字符將被空格取代。例如:
CODE:[Copy to clipboard]VAR != < shell command to execute >
注意:有些變量是在外部的系統級的Makefile內定義的(在/etc/make.conf或/etc/defaults/make.conf中),如果你在設置環境變量方面遇到了問題,就去檢查一下系統級文件裡的設置。
一個完整的例子如下:
CODE:[Copy to clipboard] #####################
# Example make file #
#####################
CURDIR != pwd
CFLAGS ?= -g
CFLAGS += -Wall -O2
all:
echo $CFLAGS
#######################
bash$ CFLAGS="-g -Wall" make
在上例中,CURDIR被設置成了shell命令pwd的結果。(注意,這種賦值並不需要backtick命令(即' ')。)CFLAGS如果從未設置,會首先被設置成-g,然後不管它的當前值是什麼,都會被附加上-Wall -O2。
1.7 命令
如果沒有命令,那make什麼都不是,只有把命令告訴make它才能完成它的工作。make只能運行這些命令,然後基於shell的退出狀態判斷這些命令是否成功。因此,如果命令失敗,shell返回一個錯誤,make將會因錯誤而退出並於該處終止。make可以就被看作是另外一個命令——除了運行其它命令之外,它和那些命令並沒有什麼實際的交互。
命令必須與一個目標相關聯,任何一個目標都可以有多個命令。例如:
CODE:[Copy to clipboard]# example for an all-install target
all-install:
$(CC) $(CFLAGS) $(MYSRC)
cp $(MYPROG) $(INSTALL_DIR)
echo "Finished the build and install"
每個命令都必須在一個目標之後以新行開始,在實際命令起始位置之前必須要有一個tab鍵,如上所示。
對於大多數情況而言,Makefile裡的命令只要是個有效的shell命令就行,命令還經常會包括變量。例如:
CODE:[Copy to clipboard]CPP = -g++
CFLAGS = -Wall -O2
myprog.o:
$(CPP) $(CFLAGS) -c myprog.c
下面這個例子告訴make使用給定值編譯myprog.c。這些命令可能會比一行要長,它們也可以被寫來完成其它的任務。這是非常重要的,因為編譯器可以接受相當多的命令行選項、環境設置以及定義等等,比如:
CODE:[Copy to clipboard]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)
下面這個例子告訴make刪除所有的object文件、core文件以及應用程序本身,然後把log文件移走,這些操作都可以很方便的完成。
CODE:[Copy to clipboard]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
但是,如果並不存在log文件,make就會因錯誤而退出。為了避免這種情況,可以在命令之前加上“-”號。通過在命令前面加上減號,你就告訴了make忽略執行命令時遇到的錯誤。(不過make仍然會打印出錯誤信息。)因此,在某個命令出現錯誤之後make仍將繼續。例如:
CODE:[Copy to clipboard]clean:
-rm -f *.o *.core $(APP)
-mv $(LOG).log $(LOG)_$(DATE).log
這將使得make忽略掉rm和mv命令可能遇到的錯誤。
你還可以讓make禁止掉“echo”之類的命令的輸出。echo首先告訴make打印出包括echo語句在內的整個命令,然後再執行命令並將字符串打印到屏幕上:
CODE:[Copy to clipboard] echo $(SOME_DEFINED_STRING)
要避免這種情況,可以在echo命令之前加上“@”符號,這將告訴make只打印字符串,比如:
CODE:[Copy to clipboard] @echo $(SOME_DEFINED_STRING)
“-”號和“@”號都既可用於變量,又可用於形為字符串的命令,但需要確定你對變量命令的引用是正確的。下例演示了如何對命令使用@操作符:
CODE:[Copy to clipboard]ECHO = echo
MESSAGE = "Print this message"
msg::
@$(ECHO) $(MESSAGE)
1.8 條件語句(#if,#ifndef等等)
如果你對C和C++比較熟悉,那你肯定知道條件預處理命令。功能繁多的make也有一個類似的特性。條件語句使你可以選擇Makefile裡的哪個部分需要被處理。這些條件語句最多可以嵌套30層,並且可以放在Makefile裡的任何地方。每條語句都必須以一個圓點(.)開始,而且條件語句塊必須以.endif結束。
條件語句允許使用邏輯操作符,比如邏輯AND“&&”、邏輯OR“||”,整條語句還可以用“!”操作符取反。“!”操作符具有最高優先級,其後依次是邏輯AND和邏輯OR。括號可以被用來指定優先級順序。關系運算符也是可以使用的,比如“>”、“>=”、“<”、“<=”、“==”和“!=”。這些操作符可被用於十進制和十六進制的數值。對於字符串則可以使用“==”和“!=”操作符。如果沒有給出操作符,那麼將會把數值和0進行比較。
在下面的例子中,在對VER變量進行賦值之後對條件進行測試。注意,如果VER變量未被賦值,則最後的.else條目將被求值為真,TAG將被賦成2.4_stable的值。
CODE:[Copy to clipboard].if $(VER) >= 2.4
TAG = 2.4_current
.elif $(VER) == 2.3
TAG = 2.3_release
.else
TAG = 2.4_stable
.endif
條件語句可用於測試變量,也可以用於下面這種函數風格的表達式。
這些用法中有些是有簡寫形式的。出於兼容性方面的考慮,我們列出了簡寫形式。非簡寫形式意思更為確定,也更容易被理解,但需要你敲入更多的字符。
在使用簡寫形式的時候是不必使用括號的。此外,簡寫形式還可以和if/else語句以及其它的簡寫形式混合在一起:
make( < arg > ) short hand [ .ifmake, .ifnmake, .elifmake, .elifnmake ]
在上例中,make將以一個目標名字作為它的參數。如果這個目標已在命令行上給出,或者它就是缺省進行make的目標,那麼該值就為真。下例中將根據make()表達式的規則給CFLAGS賦值:
CODE:[Copy to clipboard].if make(debug)
CFLAGS += -g
.elif make(production)
CFLAGS += -O2
.endif
下面是用簡寫形式表示的相同代碼:
CODE:[Copy to clipboard].ifmake debug
CFLAGS += -g
.elifmake production
CFLAGS += -O2
.endif
target( < arg > )
這種形式將以一個目標名字作為參數。僅當該target已被定義時該值才為真。這個表達式沒有簡寫形式。例如:
CODE:[Copy to clipboard].if target(debug)
FILES += $(DEBUG_FILES)
.endif
在上例中,如果debug目標返回真的話就會對FILES變量進行附加操作。
empty ( < arg > )
這種形式以一個變量為參數,並允許使用修飾符。當變量被擴展之後是一個空字符串時該值為真。這個表達式沒有簡寫形式。此外需要注意的是,你在使用這個表達式的時候並不需要對數值進行引用,記住是VAR而不是$(VAR)。例如:
CODE:[Copy to clipboard].if empty (CFLAGS)
CFLAGS = -Wall -g
.endif
defined( < arg > ) short hand [ .ifdef , .ifndef , .elifdef, elifndef ]
下面這個例子采用一個變量作為參數。僅當變量已被定義時該值為真。
CODE:[Copy to clipboard].if defined(OS_VER)
.if $(OS_VER) == 4.4
DIRS += /usr/local/4.4-STABLE_src
.endif
.else
DIRS += /usr/src
.endif
下面是簡寫形式:
CODE:[Copy to clipboard].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
正如你所看到的,make允許嵌套的條件和define表達式。和C不同的是,你不能對if語句和變量賦值語句進行縮進。如果想讓你的條件語句塊更清晰一點的話,你可以在圓點之後、if之前加一些空格。示例如下:
CODE:[Copy to clipboard].if $(DEBUG) == 1
$(CFLAGS) = -g
. ifndef $(DEBUG_FLAGS)
$(FLAGS) = $(DEBUG_FLAGS)
. endif
.endif
exists( < arg > )
下面的例子演示了如何使用exists,以及如何給一個目標添加條件語句。如果存在tmp目錄,make將運行-rm tmp/*.o命令。正如你在這個例子中看到的,.if語句僅在clean目標中被求值;所運行的命令必須遵從常規的命令語法。
CODE:[Copy to clipboard]clean:
-rm -f *.o *.core
.if exists(tmp)
-rm tmp/*.o
.endif
1.9 系統Makefiles,模板以及.include指令
C的一個重要特性就是它的簡單明了的預處理指令,也就我們常說的#include。這個特性也在make中實現了。所不同的是,如果你想包含另外一個Makefile,那你得在文件末尾包含它,而不是像C那樣在文件頭包含,這是因為變量是按順序進行賦值的。Makefile中又有Makefile可能會造成混淆,不過包含一個Makefile的基本語法卻是很簡單的。注意,在include單詞前面必須要加一個圓點(.)。基本語法如下:
CODE:[Copy to clipboard].include file name
如果要指定一個位於系統make目錄中的Makefile,可以使用尖括號:
CODE:[Copy to clipboard].include
如果所指定的文件位於當前目錄或者是由-I命令行選項指定的目錄中,則使用雙引號,這和C的#include是類似的:
CODE:[Copy to clipboard].include "file name"
在下面的例子中,如果已在project.mk中定義了CFLAGS變量,它將被這裡的新聲明所取代。當包含多個Makefile時這就可能造成一些麻煩。
CODE:[Copy to clipboard].include "../project.mk"
CFLAGS = -g
FreeBSD系統有很多的系統Makefile可供包含,與之對應的是完成各種任務的例程。在大多數的FreeBSD系統中,這些Makefile位於/usr/share/mk/目錄裡。除此之外,還有一個/etc/defaults/make.conf,它是可以被/etc/make.conf取代的。(細節請參見man make.conf。)
下面是一個常用的Makefile的簡單列表。如果你正准備進行大量的內核編程或應用程序移植,那就好好利用Makefile吧。這些Makefile的形式為bsd..mk,其中表示這些Makefile的用途。
bsd.dep.mk:這是一個用來處理Makefile依賴關系的非常有用的包含文件。
bsd.port.mk:這是一個在構建應用程序的ports的時候使用的包含文件。
使用.include指令的好處就是你可以把你的工程的Makefile分成很多的片斷。比如,你的工程可以有一個主Makefile,由所有其它的子Makefile進行包含。這個主Makefile可以包含編譯器變量及其所需的選項。這樣一來,就不必在每個Makefile裡都指定編譯器和所需選項了,對編譯器名字的引用也因此而得到了簡化。這些公共使用的片斷之後還可以用於其它的Makefile,對編譯程序等例程的修改也將隨之在所有的Makefile中生效。
1.10 高級選項
高級的make選項主要為了增加靈活性。我們建議你仔細閱讀一下make的man page,以便對make有一個深入的了解。下面這些選項是我們最常使用的。
局部變量
make可以使用專門定義的局部變量,其范圍僅限於當前目標。下面列出了七個這樣的局部變量,同時還給出了它們的System V兼容的老的表示形式。(不建議使用System V的老的表示形式,之所以列出它們,只是出於向後兼容的考慮。)
這個變量就是目標的名字:
CODE:[Copy to clipboard].TARGET
old style notation: '@'
這個變量包含了當前目標的全部源文件的列表:
CODE:[Copy to clipboard].ALLSRC
old style notation: '>'
這個變量是當前目標所隱含的源文件。它的數值是這個目標的源文件的名字和路徑。(示例見下面的.SUFFIX部分。)
CODE:[Copy to clipboard].IMPSRC
old style notation: '<'
這個變量保存的是已經被確定為過期的源文件的列表:
CODE:[Copy to clipboard].OODATE
old style notation: '?'
這個變量的值是不包括後綴和路徑的文件名:
CODE:[Copy to clipboard].PREFIX
old style notation: '*'
這個變量的值是檔案文件的名字:
CODE:[Copy to clipboard].ARCHIVE
old style notation: '!'
這個變量的值是檔案成員的名字:
CODE:[Copy to clipboard].MEMBER
old style notation: '%'
當在依賴關系行中使用這些局部變量的時候,只有.TARGET、.PREFIX、.ARCHIVE和.MEMBER具有該目標的值。
另外還有一個非常不錯的指令:
CODE:[Copy to clipboard].undef
這會讓你非常方便地取消對一個變量的定義。注意,只有全局變量才能取消定義。例如:
CODE:[Copy to clipboard].ifdef DEBUG
.undef RELEASE
.endif
1.11 轉換規則(後綴規則)
轉換規則規定了如何去創建一個目標。你可以利用這些規則,省下為每個目標文件編寫規則的時間。語法很簡單:.SUFFIXES: (suffix list)
注意,可能有多個不同的後綴共享相同的轉換後綴(.cpp、.cc、.c都可以轉換成a.o)。如果沒有列出後綴,make將把之前的所有後綴都刪除。這可能會在你有多個.SUFFIXES規則的時候造成很大的混亂。我們建議你只使用一個規則,並把它放在Makefile的底部。把.SUFFIXES語句塊中列出的東西和target進行比較,你就容易理解它的結構了。
CODE:[Copy to clipboard].SUFFIXES: .cpp .c .o .sh
.c.o:
$(CC) $(CFLAGS) -c ${.IMPSRC}
.cpp.o:
$(CPP) $(CXXFLAGS) -c ${.IMPSRC}
.sh:
cp ${.IMPSRC} ${.TARGET}
chmod +x ${.TARGET}
上面給出的這些規則將會編譯C和C++源文件。不過對於.sh:規則而言,它也可以告訴make去創建相應的shell腳本。注意,如果想在這個例子中列出某個shell腳本作為目標,那就不能添加.sh擴展名,不過那個shell腳本本身卻必須是帶有.sh後綴的。
如果我們把不帶.sh後綴的install_script列出來作為一個依賴條件,那就必須存在一個帶有.sh後綴的shell腳本,名字為install_script.sh。也就是說,如果某個文件可以被列出來作為一個目標,那麼在這個文件的存在期間,僅當make認為那個目標和該文件比起來過了期之後才會去創建它,make並不會去創建這個文件。示例見下;更多的信息請參見apps.h的例子:
CODE:[Copy to clipboard]all: install_script $(OBJS)
$(CPP) $(CFLAGS) -o $(APP) $(OBJS) $(LINK)
1.12 有用的命令行選項
下面給出了一些非常容易學習和使用的命令行選項。這並不是一個完整的列表,要想知道其它的選項還得去看man page。
-D
這個選項將在命令行上定義一個變量,如果你的Makefile裡面有.ifdef語句,那麼使用這個選項就非常方便了。例如:
CODE:[Copy to clipboard].ifdef DEBUG
CFLAGS += -g -D__DEBUG__
.endif
於是,當你運行命令make -D DEBUG的時候,make就會正確的設置CFLAGS,在編譯你的應用程序的時候也把調試語句編進去。
-E < variable name to override >
這個選項將用環境變量的值取代Makefile中給變量賦的值。在使用這個選項之前,需要先設置你的shell中的環境變量。例如:
CODE:[Copy to clipboard]bash $ CFLAGS="-O2 -Wall" make -E CFLAGS
-e
和大寫形式類似,-e將用環境變量取代Makefile中的所有變量。如果某個變量沒有定義相應的環境變量,則按正常方式賦值。
-f
這個選項使你可以在命令行上指定Makefile,這一點在你需要多個Makefile時很有用。例如:
CODE:[Copy to clipboard]bash$ make -f Makefile.BSD
-j < number of max_jobs >
這個選項使你可以指定make能夠派生出多少個job來。一般來說make只會派生出一個,但對於一個非常大的工程而言,如果想讓你的硬件物盡其用的話,那就指定4個吧,如下:
CODE:[Copy to clipboard]make -j 4 world
如果超過了4個,有時反而會延長執行時間,不過有些人倒是可能覺得CPU被六個或更多的job折騰的樣子很有趣。
-n
這個選項對於Makefile的調試很有用,它可以讓你看到make究竟會執行哪些命令,但又不會實際去執行它們。對於有很多命令的大型工程,最好是把輸出重定向到一個外部文件中,否則就會有浩如煙海的命令顯示出來。示例如下:
CODE:[Copy to clipboard]bash $ make -n >> make_debug.log 2>&1
-V < variable name >
這個選項將基於全局的上下文打印變量的值。與前一個選項類似,make也不會去構建任何目標。你可以在命令行上指定多個-V選項,如下:
CODE:[Copy to clipboard]make -V CFLAGS -V LINK -V OBJS
1.13 一個最後的例子
下面列出的Makefile是一個可以重復使用的Makefile的例子。在你包含了它之後,它就知道從列出的.SUFFIXES規則中得知如何去編譯C++源文件。它還知道如何去安裝應用程序和清除開發目錄。這顯然並不是一個很好理解的Makefile,但它卻是一個很好的創建通用模板風格的Makefile的范例,這種Makefile包含了那些用於開發的公共例程。這不只是節省了在創建每個Makefile時重復輸入這些公共規則的時間,它還能讓開發人員重復使用已知的好例程。
CODE:[Copy to clipboard]########################################################
#
# 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}
下面給出的Makefile是需要你在工程目錄內部創建的:
CODE:[Copy to clipboard]#######################################################
# 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"