Make命令完全詳解教程_第1頁
Make命令完全詳解教程_第2頁
Make命令完全詳解教程_第3頁
Make命令完全詳解教程_第4頁
Make命令完全詳解教程_第5頁
已閱讀5頁,還剩56頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

1、Make命令完全詳解教程無論是在Linux還是在Unix環(huán)境中,make都是一個非常重要的編譯命令。不管是自己進行項目開發(fā)還是安裝應(yīng)用軟件,我們都經(jīng)常要用到make或makeinstall。利用make工具,我們可以將大型的開發(fā)項目分解成為多個更易于管理的模塊,對于一個包括幾百個源文件的應(yīng)用程序,使用make和makefile工具就可以簡潔明快地理順各個源文件之間紛繁復(fù)雜的相互關(guān)系。而且如此多的源文件,如果每次都要鍵入gcc命令進行編譯的話,那對程序員來說簡直就是一場災(zāi)難。而make工具則可自動完成編譯工作,并且可以只對程序員在上次編譯后修改過的部分進行編譯。因此,有效的利用make和make

2、file工具可以大大提高項目開發(fā)的效率。同時掌握make和makefile之后,您也不會再面對著Linux下的應(yīng)用軟件手足無措了。一、Make程序的命令行選項和參數(shù)Make命令參數(shù)的典型序列如下所示:make-fmakefile文件名選項宏定義目標這里用括起來的表示是可選的。命令行選項由破折號“-”指明,后面跟選項,如make-e如果需要多個選項,可以只使用一個破折號,如make-kr也可以每個選項使用一個破折號,如make-k-r甚至混合使用也行,如make-e-krMake命令本身的命令行選項較多,這里只介紹在開發(fā)程序時最為常用的三個,它們是:-k:如果使用該選項,即使make程序遇到錯誤

3、也會繼續(xù)向下運行;如果沒有該選項,在遇到第一個錯誤時make程序馬上就會停止,那么后面的錯誤情況就不得而知了。我們可以利用這個選項來查出所有有編譯問題的源文件。-n:該選項使make程序進入非執(zhí)行模式,也就是說將原來應(yīng)該執(zhí)行的命令輸出,而不是執(zhí)行。-f:指定作為makefile的文件的名稱。如果不用該選項,那么make程序首先在當前目錄查找名為makefile的文件,如果沒有找到,它就會轉(zhuǎn)而查找名為Makefile的文件。如果您在Linux下使用GNUMake的話,它會首先查找GNUmakefile,之后再搜索makefile和Makefile。按照慣例,許多Linux程序員使用Makefil

4、e,因為這樣能使Makefile出現(xiàn)在目錄中所有以小寫字母命名的文件的前面。所以,最好不要使用GNUmakefile這一名稱,因為它只適用于make程序的GNU版本。當我們想構(gòu)建指定目標的時候,比如要生成某個可執(zhí)行文件,那么就可以在make命令行中給出該目標的名稱;如果命令行中沒有給出目標的話,make命令會設(shè)法構(gòu)建makefile中的第一目標。我們可以利用這一特點,將all作為makefile中的第一個目標,然后將讓目標作為all所依賴的目標,這樣,當命令行中沒有給出目標時,也能確保它會被構(gòu)建。二、Makefile概述上面提到make命令對于構(gòu)建具有多個源文件的程序有很大的幫助。事實上,只有

5、make命令還是不夠的,前面說過還必用須makefile告訴它要做什么以及怎么做才行,對于程序開發(fā)而言,就是告訴make命令應(yīng)用程序的組織情況。我們現(xiàn)在對makefile的位置和數(shù)量簡單說一下。一般情況下,makefile會跟項目的源文件放在同一個目錄中。另外,系統(tǒng)中可以有多個makefile,一般說來一個項目吏用一個makefile就可以了;如果項目很大的話,我們就可以考慮將它分成較小的部分,然后用不同的makefile來管理項目的不同部分。make命令和Makefile配合使用,能給我們的項目管理帶來極大的便利,除了用于管理源代碼的編譯之外,還用于建立手冊頁,同時還能將應(yīng)用程序安裝到指定的

6、目錄。因為Makefile用于描述系統(tǒng)中模塊之間的相互依賴關(guān)系,以及產(chǎn)生目標文件所要執(zhí)行的命令,所以,一個makefile由依賴關(guān)系和規(guī)則兩部分內(nèi)容組成。下面分別加以解釋。依賴關(guān)系由一個目標和一組該目標所依賴的源文件組成。這里所說的目標就是將要創(chuàng)建或更新的文件,最常見的是可執(zhí)行文件。規(guī)則用來說明怎樣使用所依賴得文件來建立目標文件。當make命令運行時,會讀取makefile來確定要建立的目標文件或其他文件,然后對源文件的日期和時間進行比較,從而決定使用那些規(guī)則來創(chuàng)建目標文件。一般情況下,在建立起最終的目標文件之前,肯定免不了要建立一些中間性質(zhì)的目標文件。這時,Make命令也是使用makefil

7、e來確定這些目標文件的創(chuàng)建順序,以及用于它們的規(guī)則序列。Makefile中的依賴關(guān)系make程序自動生成和維護通常是可執(zhí)行模塊或應(yīng)用程序的目標,目標的狀態(tài)取決于它所依賴的那些模塊的狀態(tài)。Make的思想是為每一塊模塊都設(shè)置一個時間標記,然后根據(jù)時間標記和依賴關(guān)系來決定哪一些文件需要更新。一旦依賴模塊的狀態(tài)改變了,make就會根據(jù)時間標記的新舊執(zhí)行預(yù)先定義的一組命令來生成新的目標。依賴關(guān)系規(guī)定了最終得到的應(yīng)用程序跟生成它的各個源文件之間的關(guān)系。如下面的圖1描述了可執(zhí)行文件main對所有的源程序文件及其編譯產(chǎn)生的目標文件之間的依賴關(guān)系,見下圖:圖1模塊間的依賴關(guān)系就圖1而言,我們可以說可執(zhí)行程序ma

8、in依賴于main.o、f1.o和ff1.o。與此同時,main.0依賴于main.c和defl.h;f1.o依賴于fl.c、defl.h和def2.h;而ffl.o則依賴于ffl.c、def2.h和def3.h。在makefile中,我們可以用目標名稱,加冒號,后跟空格鍵或tab鍵,再加上由空格鍵或tab鍵分隔的一組用于生產(chǎn)目標模塊的文件來描述模塊之間的依賴關(guān)系對于上例來說,可以作以下描述:main:main.of1.of2.omain.o:main.cdef1.hf1.o:f1.cdef1.hdef2.hf2.o:f2.cdef2.hdef3.h不難發(fā)現(xiàn),上面的各個源文件跟各模塊之間的關(guān)系

9、具有一個明顯的層次結(jié)構(gòu),如果def2.h發(fā)生了變化,那么就需要更新f1.。和f2.o,而f1.。和f2.。發(fā)生了變化的話,那么main也需要隨之重新構(gòu)建。默認時,make程序只更新makefile中的第一個目標,如果希望更新多個目標文件的話,可以使用一個特殊的目標all,假如我們想在一個makefile中更新main和hello這兩個程序文件的話,可以加入下列語句達到這個目的:all:mainhello實際上,makefile是以相關(guān)行為基本單位的,相關(guān)行用來描述目標、模塊及規(guī)則(即命令行)三者之間的關(guān)系。一個相關(guān)行格式通常為:冒號左邊是目標(模塊)名;冒號右邊是目標所依賴的模塊名;緊跟著的規(guī)

10、則(即命令行)是由依賴模塊產(chǎn)生目標所使用的命令。相關(guān)行的格式為:目標:依賴模塊;命令習慣上寫成多行形式,如下所示:目標:依賴模塊命令命令目標(TARGET)程序產(chǎn)生的文件,如可執(zhí)行文件和目標文件;目標也可以是要執(zhí)行的動作,如“clean”。依賴(DEPENDENCIES)是用來產(chǎn)生目標的輸入文件,個目標通常依賴于多個文件。命令(COMMAND)是make執(zhí)行的動作,一個可以有多個命令,每個占一行。注意:每個命令行的起始字符必須為TAB字符!有依賴關(guān)系規(guī)則中的命令通常在依賴文件變化時負責產(chǎn)生target文件,make執(zhí)行這些命令更新或產(chǎn)生target。規(guī)則可以沒有依賴關(guān)系,如包含target“c

11、lean”的規(guī)則。規(guī)則解釋如何和何時重做該規(guī)則中的文件,make根據(jù)依賴關(guān)系執(zhí)行產(chǎn)生或更新目標;規(guī)則也說明如何和何時執(zhí)行動作。有的規(guī)則看起來很復(fù)雜,但都符合上述模式。需要注意的是,如果相關(guān)行寫成一行,“命令”之前用分號“;”隔開,如果分成多行書寫的話,后續(xù)的行務(wù)必以tab字符為先導(dǎo)。對于makefile而言,空格字符和tab字符是不同的。所有規(guī)則所在的行必須以tab鍵開頭,而不是空格鍵。初學者一定對此保持警惕,因為這是新手最容易疏忽的地方,因為幾個空格鍵跟一個tab鍵在肉眼是看不出區(qū)別的,但make命令卻能明察秋毫。此外,如果在makefile文件中的行尾加上空格鍵的話,也會導(dǎo)致make命令運

12、行失敗。所以,大家一定要小心了,免得耽誤許多時間。例,一個名為prog的程序由三個C源文件filea.c、fileb.c和filec.c以及庫文件LS編譯生成,這三個文件還分別包含自己的頭文件a.h、b.h和c.h。通常情況下,C編譯器將會輸出三個目標文件filea.o、fileb.o和filec.o。假設(shè)filea.c和fileb.c都要聲明用到一個名為defs的文件,旦filec.c不用。即在filea.c和fileb.c里都有這樣的聲明:#includedefs那么下面的文檔就描述了這些文件之間的相互聯(lián)系:line#Itisaexamplefordescribingmakefilepro

13、g:filea.ofileb.ofilec.occfilea.ofileb.ofilec.o-LS-oprogfilea.o:filea.ca.hdefscc-cfilea.cfileb.o:fileb.cb.hdefscc-cfileb.cfilec.o:filec.cc.hcc-cfilec.c這個描述文檔就是一個簡單的makefile文件。從上面的例子注意到,第一個字符為#的行為注釋行。第一個非注釋行指定prog由三個目標文件filea.o、fileb.o和filec.o鏈接生成。第三行描述了如何從prog所依賴的文件建立可執(zhí)行文件。接下來的4、6、8行分別指定三個目標文件,以及它們所依

14、賴的.C和.h文件以及defs文件。而5、7、9行則指定了如何從目標所依賴的文件建立目標。當filea.c或a.h文件在編譯之后又被修改,則make工具可自動重新編譯filea.o,如果在前后兩次編譯之間,filea.C和a.h均沒有被修改,而且test.o還存在的話,就沒有必要重新編譯。這種依賴關(guān)系在多源文件的程序編譯中尤其重要。通過這種依賴關(guān)系的定義,make工具可避免許多不必要的編譯工作。當然,利用Shell腳本也可以達到自動編譯的效果,但是,Shell腳本將全部編譯任何源文件,包括哪些不必要重新編譯的源文件,而make工具則可根據(jù)目標上一次編譯的時間和目標所依賴的源文件的更新時間而自動

15、判斷應(yīng)當編譯哪個源文件。Makefile文件舉例根據(jù)圖1的依賴關(guān)系,這里給出了一個完整的makefile文件,這個例子很簡單,由四個相關(guān)行組成,我們將其命名為mymakefile1。文件內(nèi)容如下所示:main:main.of1.of2.ogcc-omainmain.of1.of2.omain.o:main.cdef1.hgcc-cmain.cf1.o:f1.cdef1.hdef2.hgcc-cf1.cf2.o:f2.cdef2.hdef3.hgcc-cf2.c注意,由于我們這里沒有使用缺省名makefile或者Makefile,所以一定要在make命令行中加上-f選項。如果在沒有任何源碼的目錄

16、下執(zhí)行命令“make-fMymakefile1”的話,將收到下面的消息:make:*Noruletomaketargetmain.c,neededbymain.。.Stop.Make命令將makefile中的第一個目標即main作為要構(gòu)建的文件,所以它會尋找構(gòu)建該文件所需要的其他模塊,并判斷出必須使用一個稱為main.c的文件。因為迄今尚未建立該文件,而makefile又不知道如何建立它,所以只好報告錯誤。好了,現(xiàn)在建立這個源文件,為簡單起見,我們讓頭文件為空,創(chuàng)建頭文件的具體命令如下:$touchdefl.h$touchdef2.h$touchdef3.h我們將main函數(shù)放在main.c文

17、件中,讓它調(diào)用function2和function3,但將這兩個函數(shù)的定義放在另外兩個源文件中。由于這些源文件含有#include命令,所以它們肯定依賴于所包含的頭文件。如下所示:/*main.c*/#includevSTDLIDEF2.H#include“defl.h”externvoidfunction2();externvoidfunction3();intmain()function2();function3();exit(EXIT_SUCCESS);/*f1.c*/#include“defl.h”#include“def2.h”voidfunction2()/*f2.c*/#incl

18、ude“def2.h”#include“def3.h”voidfunction3()建好源代碼后,再次運行make程序,看看情況如何:$make-fMymakefilelgcc-cmain.cgcc-cfl.cgcc-cf2.cgcc-omainmain.ofl.of2.o$好了,這次順利通過了。這說明Make命令已經(jīng)正確處理了makefile描述的依賴關(guān)系,并確定出了需要建立哪些文件以及它們的建立順序。雖然我們在makefile中首先列出的是如何建立main,但是make還是能夠正確的判斷出這些文件的處理順序,并按相應(yīng)的順序調(diào)用規(guī)則部分規(guī)定的相應(yīng)命令來創(chuàng)建這些文件。當這些命令執(zhí)行時,make

19、程序會按照執(zhí)行情況來顯示這些命令。如今,我們對def2.h加以變動,來看看makefile能否對此作出相應(yīng)的回應(yīng):$touchdef2.h$make-fMymakefile1gcc-cf1.cgcc-cf2.cgcc-omainmain.of1.of2.o$這說明,當Make命令讀取makefile后,只對受def2.h的變化的影響的模塊進行了必要的更新,注意它的更新順序,它先編譯了C程序,最后連接生產(chǎn)了可執(zhí)行文件。現(xiàn)在,讓我們來看看刪除目標文件后會發(fā)生什么情況,先執(zhí)行刪除,命令如下:$rmfl.o然后運行make命令,如下所示:$make-fMymakefilelgcc-cf1.cgcc-o

20、mainmain.of1.of2.o$很好,make的行為讓我們非常滿意。Makefile中的宏在makefile中可以使用諸如XLIB、UIL等類似于Shell變量的標識符,這些標識符在makefile中稱為“宏,它可以代表一些文件名或選項。宏的作用類似于C語言中的define,利用它們來代表某些多處使用而又可能發(fā)生變化的內(nèi)容,可以節(jié)省重復(fù)修改的工作,還可以避免遺漏。Make的宏分為兩類,類是用戶自己定義的宏,類是系統(tǒng)內(nèi)部定義的宏。用戶定義的宏必須在makefile或命令行中明確定義,系統(tǒng)定義的宏不由用戶定義。我們首先介紹第一種宏。這里是一個包含宏的makefile文件,我們將其命名為mym

21、akefile2,如下所示:all:main#使用的編譯器CC=gcc#包含文件所在目錄INCLUDE=.#在開發(fā)過程中使用的選項CFLAGS=-g-Wall-ansi#在發(fā)行時使用的選項#CFLAGS=-O-Wall-ansimain:main.ofl.of2.o$(CC)-omainmain.ofl.of2.omain.o:main.cdefl.h$(CC)-I$(INCLUDE)$(CFLAGS)-cmain.cfl.o:fl.cdefl.hdef2.h$(CC)-I$(INCLUDE)$(CFLAGS)-cfl.cf2.o:f2.cdef2.hdef3.h$(CC)-I$(INCLUD

22、E)$(CFLAGS)-cf2.c我們看到,在這里有一些注釋。在makefile中,注釋以#為開頭,至行尾結(jié)束。注釋不僅可以幫助別人理解我們的makefile,如果時間久了,有些東西我們自己也會忘掉,它們對makefile的編寫者來說也是很有必要的?,F(xiàn)在言歸正傳,先看一下宏的定義。我們既可以在make命令行中定義宏,也可以在makefile中定義宏。2.4在makefile中定義宏的基本語法是:宏標識符二值列表其中,宏標識符即宏的名稱通常全部大寫,但它實際上可以由大、小寫字母、阿拉伯數(shù)字和下劃線構(gòu)成。等號左右的空白符沒有嚴格要求,因為它們最終將被make刪除。至于值列表,既可以是零項,也可以是

23、一項或者多項。如:LIST_VALUE=onetwothree當一個宏定義之后,我們就可以通過$(宏標識符)或者$宏標識符來訪問這個標識符所代表的值了。在makefile中,宏經(jīng)常用作編譯器的選項。很多時候,處于開發(fā)階段的應(yīng)用程序在編譯時是不用優(yōu)化的,但是卻需要調(diào)試信息;而正式版本的應(yīng)用程序卻正好相反,沒有調(diào)試信息的代碼不僅所占內(nèi)存較小,進過優(yōu)化的代碼運行起來也更快。對于Mymakefile1來說,它假定所用的編譯器是gcc,不過在其他的UNIX系統(tǒng)上,更常用的編譯器是cc或者c89,而非gcc。如果你想讓自己的makefile適用于不同的UNIX操作系統(tǒng),或者在一個系統(tǒng)上使用其他種類的編譯器

24、,這時就不得不對這個makefile中的多處進行修改。但對于mymakefile2來說則不存在這個問題,我們只需修改一處,即宏定義的值就行了。除了在makefile中定義宏的值之外,我們還可以在make命令行中加以定義,如:$makeCC=c89當命令行中的宏定義跟makefile中的定義有沖突時,以命令行中的定義為準。當在makefile文件之外使用時,宏定義必須作為單個參數(shù)進行傳遞,所以要避免使用空格,但是更妥當?shù)姆椒ㄊ鞘褂靡枺纾?make“CC=c89”這樣就不必擔心空格所引起的問題了?,F(xiàn)在讓我們將前面的編譯結(jié)果刪掉,來測試一下mymakefile2的工作情況。命令如下所示:$rm*

25、.omain$make-fMymakefile2gcc-I.-g-Wall-ansi-cmain.cgcc-I.-g-Wall-ansi-cf1.cgcc-I.-g-Wall-ansi-cf2.cgcc-omainmain.of1.of2.o$就像我們看到的那樣,Make程序會用相應(yīng)的定義來替換宏引用$(CC)、$(CFLAGS)和$(INCLUDE),這跟C語言中的宏的用法比較相似。上面介紹了用戶定義的宏,現(xiàn)在介紹make的內(nèi)部宏。常用的內(nèi)部宏有:$?:比目標的修改時間更晚的那些依賴模塊表。$:當前目標的全路徑名??捎糜谟脩舳x的目標名的相關(guān)行中。$:比給定的目標文件時間標記更新的依賴文件名

26、。$*:去掉后綴的當前目標名。例如,若當前目標是pro.o,則$*表示pro。Makefile文件作為一種描述文檔一般需要包含以下內(nèi)容:宏定義源文件之間的相互依賴關(guān)系可執(zhí)行的命令Makefile中允許使用簡單的宏指代源文件及其相關(guān)編譯信息,在Linux中也稱宏為變量。在引用宏時只需在變量前加$符號,但值得注意的是,如果變量名的長度超過一個字符,在引用時就必須加圓括號()。下面都是有效的宏引用:$(CFLAGS)$2$Z$(Z)其中最后兩個引用是完全一致的。需要注意的是一些宏的預(yù)定義變量,在Unix系統(tǒng)中,$*、$、$?和$四個特殊宏的值在執(zhí)行命令的過程中會發(fā)生相應(yīng)的變化,而在GNUmake中則

27、定義了更多的預(yù)定義變量。關(guān)于預(yù)定義變量的詳細內(nèi)容,宏定義的使用可以使我們脫離那些冗長乏味的編譯選項,為編寫makefile文件帶來很大的方便。#DefineamacrofortheobjectfilesOBJECTS=filea.ofileb.ofilec.oDefineamacroforthelibraryfileLIBES=-LSusemacrosrewritemakefileprog:$(OBJECTS)cc$(OBJECTS)$(LIBES)-oprog此時如果執(zhí)行不帶參數(shù)的make命令,將連接三個目標文件和庫文件LS;但是如果在make命令后帶有新的宏定義:makeLIBES=-LL

28、-LS則命令行后面的宏定義將覆蓋makefile文件中的宏定義。若LL也是庫文件,此時make命令將連接三個目標文件以及兩個庫文件LS和LL。在Unix系統(tǒng)中沒有對常量NULL作出明確的定義,因此我們要定義NULL字符串時要使用下述宏定義:STRINGNAME=Make命令在make命令后不僅可以出現(xiàn)宏定義,還可以跟其他命令行參數(shù),這些參數(shù)指定了需要編譯的目標文件。其標準形式為:target1target2dependent.;commands#.(tab)commands#.方括號中間的部分表示可選項。Targets和dependents當中可以包含字符、數(shù)字、句點和7符號。除了引用,com

29、mands中不能含有#,也不允許換行。在通常的情況下命令行參數(shù)中只含有一個:,此時commanc序列通常和makefile文件中某些定義文件間依賴關(guān)系的描述行有關(guān)。如果與目標相關(guān)連的那些描述行指定了相關(guān)的command序列,那么就執(zhí)行這些相關(guān)的command命令,即使在分號和(tab)后面的aommand字段甚至有可能是NULL。如果那些與目標相關(guān)連的行沒有指定command,那么將調(diào)用系統(tǒng)默認的目標文件生成規(guī)則。如果命令行參數(shù)中含有兩個冒號:,則此時的command序列也許會和makefile中所有描述文件依賴關(guān)系的行有關(guān)。此時將執(zhí)行那些與目標相關(guān)連的描述行所指向的相關(guān)命令。同時還將執(zhí)行bu

30、ild-in規(guī)則。如果在執(zhí)行command命令時返回了一個非0的出錯信號,列如makefile文件中出現(xiàn)了錯誤的目標文件名或者出現(xiàn)了以連字符打頭的命令字符串,make操作一般會就此終止,但如果make后帶有-i參數(shù),則make將忽略此類出錯信號。Make命本身可帶有四種參數(shù):標志、宏定義、描述文件名和目標文件名。其標準形式為:MakeflagsmacrodefinitionstargetsUnix系統(tǒng)下標志位flags選項及其含義為:-ffile指定file文件為描述文件,如果file參數(shù)為-符,那么描述文件指向標準輸入。如果沒有-f參數(shù),則系統(tǒng)將默認當前目錄下名為makefile或者名為Ma

31、kefile的文件為描述文件。在Linux中,GNUmake工具在當前工作目錄中按照GNUmakefile、makefile、Makefile的順序搜索makefile文件。-i忽略命令執(zhí)行返回的出錯信息。-s沉默模式,在執(zhí)行之前不輸出相應(yīng)的命令行信息。-r禁止使用build-in規(guī)則。-n非執(zhí)行模式,輸出所有執(zhí)行命令,但并不執(zhí)行。-t更新目標文件。-qmake操作將根據(jù)目標文件是否已經(jīng)更新返回0或非0的狀態(tài)信息。-p輸出所有宏定義和目標文件描述。-dDebug模式,輸出有關(guān)文件和檢測時間的詳細信息。Linux下make標志位的常用選項與Unix系統(tǒng)中稍有不同,下面我們只列出了不同部分:-cd

32、ir在讀取makefile之前改變到指定的目錄dir。-Idir當包含其他makefile文件時,利用該選項指定搜索目錄。-hhelp文擋,顯示所有的make選項。-w在處理makefile之前和之后,都顯示工作目錄。Linux下make標志位的常用選項與Unix系統(tǒng)中稍有不同,下面我們只列出了不同部分:-cdir在讀取makefile之前改變到指定的目錄dir。-Idir當包含其他makefile文件時,利用該選項指定搜索目錄。-hhelp文擋,顯示所有的make選項。-w在處理makefile之前和之后,都顯示工作目錄。通過命令行參數(shù)中的target,可指定make要編譯的目標,并且允許同

33、時定義編譯多個目標操作時按照從左向右的順序依次編譯target選項中指定的目標文件。如果命令行中沒有指定目標,則系統(tǒng)默認target指向描述文件中第一個目標文件。通常,makefile中還定義有clean目標,可用來清除編譯過程中的中間文件,例如:clean:rm-f*.o運行makeclean時,將執(zhí)行rm-f*.o命令,最終刪除所有編譯過程中產(chǎn)生的所有中間文件。2.5.清理編寫規(guī)則不至于編譯程序。Makefile通常描述如何做其它事情:比如刪除目錄中的目標文件和可執(zhí)行文件來清理目錄。例子中是這樣寫的:clean:rmedit$(objects)實際情況是,我們需要處理一些意外事件:存在一個

34、叫做clean的文件;如果rm出錯,并不希望make過程停止下來,修改過的版本如下:.PHONY:cleanclean:-rmedit$(objects)這樣的規(guī)則當然不能放在makefile的開始,因為這并不是我們?nèi)笔∫龅墓ぷ?。由于clean并不是edit的依賴,在運行make時沒有參數(shù)時,這條規(guī)則不會執(zhí)行;要執(zhí)行這個規(guī)則,必須運行makeclean。三、規(guī)則makefile中的規(guī)則描述如何生成特定的文件,即規(guī)則的目標。規(guī)則列出了目標的依賴文件,指定生成或更新目標的命令。規(guī)則的次序是不重要的,除非是確定缺省目標:缺省目標是第一makefile中的第一個規(guī)則;如果第一個規(guī)則有多個目標,第一個

35、目標是缺省的。有兩個例外:以.開頭的目標不是缺省目標;模式規(guī)則對缺省目標沒有影響。通常我們所寫的地一個規(guī)則是編譯整個或makefile中指定的所有程序。例子foo.o:foo.cdefs.h#modulefortwiddlingthefrobscc-c-gfoo.c它的目標是foo.o,依賴于foo.c和defs.h,有一命令cc-c-gfoo.c。命令行以TAB字符開始標識它是一個命令。這條規(guī)則說明兩件事:8如何決定foo.o是舊的:如果它不存在,或者foo.c或者defs.h比它新。9如何更新foo.o文件:通過運行cc程序。命令未提及defs.h,擔可以猜想foo.c包含了它,這是def

36、s.h被置于依賴關(guān)系中的理由。規(guī)則的語法語法如下:TARGETS:DEPENDENCIESCOMMAND或者TARGETS:DEPENDENCIES;COMMANDCOMMANDTARGETS是以空格隔開的文件名,統(tǒng)配符可以使用。通常一個規(guī)則只有一個目標,偶爾也有多個。命令行以TAB鍵開始。第一條命令可在依賴關(guān)系的下一行;或者在同一行,在分號后面;兩種方式效果相同。因為$符號被用做變量引用,如果要在規(guī)則中使用$符號,必須寫兩個:$??梢杂梅杹矸指钜粋€長行,這不是必須的,因為ake對行的長度沒有限制。通配符規(guī)則中的文件名可以包含統(tǒng)配符,如*,?。文件名前的字符有特殊的含義。單獨使用,或跟隨一個

37、/,代表用戶的home目錄,比如/bin擴展為/home/you/bin;如果跟隨一單詞,表示單詞指示的那個用戶的home目錄,如john/bin擴展為/home/john/bin。通配符在目標,依賴關(guān)系,命令中自動擴展,其它情況下,統(tǒng)配符的擴展除非顯式使用wildcard函數(shù)。通配符的特殊意義可以使用符號關(guān)閉。例子:clean:rm-f*.o和print:*.clpr-p$?touchprint通配符在定義變量時并不擴展,例如:objects=*.o貝objects的值是字符串*.o;但是如果你將objects用于目標,依賴或命令中,擴展會進行。要將objects設(shè)置成擴展過的內(nèi)容,使用:o

38、bjects:=$(wildcard*.o)通配符的缺陷這是一個使用通配符的例子,但結(jié)果不是你所期望的。假設(shè)可執(zhí)行文件foo是從當前目錄中的所有.o文件生成的:objects=*.ofoo:$(objects)cc-ofoo$(CFLAGS)$(objects)objects變量的值是字符串*.o。通配符擴展在規(guī)則foo中進行,于是所有存在的.o文件成為foo的依賴而且在需要時重新編譯。但如果刪除了所有的.o文件呢?當通配符不匹配任何文件時,一切都保持原樣:則foo依賴于一個叫做*.o的文件;由于這個文件不大可能存在,make程序會報告一個無法生成*.o文件的錯誤,這不是期待的結(jié)果。實際上可以

39、用通配符獲得期望結(jié)果,但是需要復(fù)雜的技術(shù),包括wildcard函數(shù)和字符串替換函數(shù)。332wildcard函數(shù)通配符自動在規(guī)則中進行。但是在變量賦值的和函數(shù)的參數(shù)中通配符不會擴展,如果在這些情況下需要通配符擴展,必須使用wildcard函數(shù)。語法如下:$(wildcardPATTERN.)這個在makefile任何地方出現(xiàn)的字符串,會被匹配任何一個文件名格式的以空格隔開的現(xiàn)有文件列表替換。如果沒有任何文件匹配一個模式,這個模式從wildcard的輸出中忽略,注意,這和上述的通配符的處理是不一樣的。wildcard函數(shù)的一個功能是找出目錄中所有的.c文件:$(wildcard*.c)可以通過替換

40、后綴.c為.o從C文件列表得到目標文件的列表:$(patsubst%.c,%.o,$(wildcard*.c)這樣,上節(jié)中的makefile改寫為:objects:=$(patsubst%.c,%.o,$(wildcard*.c)foo:$(objects)cc-ofoo$(objects)這個makefile利用了編譯C程序的隱含規(guī)則,所以不需要對編譯寫出顯式的規(guī)則。(:=是=的一個變體)注意:PATTERN是大小寫敏感的。目錄搜索對于大的系統(tǒng),通常將源文件和目標文件放在不同的目錄中。目錄搜索功能可以讓make自動在多個目錄中搜尋依賴文件,當你將文件重新分布是,不需要改變規(guī)則,更改搜索路徑即

41、可。VPATHmake變量VPATH列出make應(yīng)當搜索的目錄列表。很多情況下,當前目錄不包含依賴文件,VPATH描述一個對所有文件的搜索列表,包含那些是規(guī)則的目標的文件。如果一個目標或者依賴文件在當前目錄沒找到的話,make在VPATH中列出的目錄中查找同名的文件。如果找到的話,那個文件成為依賴文件;規(guī)則可以象這些文件在當前目錄中一樣來使用他們。在VPATH變量中,目錄名以冒號或空格隔開;目錄列出的順序決定make查找的順序。(注:在pSOSystem2.5移植到Win32的GNUmake目錄名必須使用分號隔開,以下均簡稱Win32GNUmake)。舉例說明:VPATH=src:./head

42、ers則規(guī)則foo.o:foo.c被解釋為foo.o:src/foo.c假設(shè)foo.c在當前目錄不存在,在src目錄中可以找到。選擇性搜索與VPATH變量相似但更具選擇性的是vpath指令(注意是小寫),可以指定對于符合特定模式文件的查找路徑。這樣可以為不同類型的文件指定不同的搜索路徑。vpath指令共有三中形式:vpathPATTERNDIRECTORIES為匹配PATTERN的文件名指定搜索路徑DIRECTORIES,目錄的分隔和VPATH的相同vpathPATTERN清除為匹配PATTERN的文件名指定的搜索路徑vpath清除所有以前用vpath指定的搜索路徑vpath的模式是包含%的字

43、符串:這個字符串必須匹配需要搜索的依賴文件名,%字符匹配0個或多個任意字符。例如:.h匹配任何以.h結(jié)尾的文件如果沒有貝SPATTERN必須和依賴文件完全一致這種用法不太多)。當當前目錄中不存在依賴文件時,如果vpath中的PATTERN匹配依賴文件名,則指令中DIRECTORIES列出的目錄和VPATH中同樣處理。舉例:vpath%.h./headers告訴make在當前目錄中未找到的.h文件在./headers目錄中查找。如果多個vapth的模式匹配依賴文件名,make將逐一處理,在所有指定的目錄中搜索。Make按照vapth在makefile中的次序;來處理它們,多個相同模式的vapth

44、是相互獨立的。vpath%.cfoovpath%blishvpath%.cbar將按照foo,blish,bar的次序查找.c文件。而vpath%.cfoo:barvpath%blish按照foo,bar,blish的順序搜索。使用自動變量目錄搜索的結(jié)果并不改變規(guī)則中的命令:命令按原樣被執(zhí)行。因此,必須寫出與目錄搜索功相適應(yīng)的命令。這可以通過使用$人這樣的自動變量來完成。$A表示規(guī)則中的所有依賴文件,包含它們所在的目錄名(參見目錄搜索);$表示目標。例如:foo.o:foo.ccc-c$(CFLAGS)$A-o$通常情況下,依賴文件也包含頭文件,但命令中并不提及這些文件:變量$表示第一個依賴文

45、件:VPATH=src:./headersfoo.o:foo.cdefs.hhack.hcc-c$(CFLAGS)$bigoutput:text.ggeneratetext.g-bigbigoutputlittleoutput:text.ggeneratetext.g-littlelittleoutput等同。這里假設(shè)程序generate產(chǎn)生兩種輸出:一種使用-big選項,一種使用-little選項。如果想象使用$變化命令那樣來變化依賴關(guān)系,不能通過多目標的普通規(guī)則實現(xiàn),但是可以通過模式規(guī)則來實現(xiàn)。一個目標多條規(guī)則一個文件可以是多條規(guī)則的目標,所有規(guī)則的依賴關(guān)系被合并。如果目標比任一個依賴文件

46、舊,命令被執(zhí)行。一個文件只能有一組命令執(zhí)行。如果多個規(guī)則對于同一個文件都給出了命令,make使用最后一組并打印錯誤信息(特殊情況:如果文件名以開始,并不打印錯誤信息,這一點是為了和其它make兼容)。沒有任何理由需要將makefile寫成這樣,這是make給出錯誤信息的理由。一條只有依賴關(guān)系的附加規(guī)則可以一次給出許多文件的附加依賴文件。例如objects變量表示系統(tǒng)中編譯器的所有輸出.,說明當config.h更改時所有文件必須重做的簡單方法如下:objects=foo.obar.ofoo.o:defs.hbar.o:defs.htest.h$(objects):config.h不用改變實際目標

47、文件生成的規(guī)則,這條規(guī)則可以在需要增刪附加的依賴關(guān)系時插入或提出。另一個訣竅是附加的依賴關(guān)系可以用變量表示,在make執(zhí)行時,可以給變量賦值:extradeps=$(objects):$(extradeps)當命令makeextradeps二foo.h執(zhí)行時會認為foo.h是每個目標文件的依賴文件,但簡單的make命令不是這樣。靜態(tài)模式規(guī)則靜態(tài)模式規(guī)則(staticpatternrules)可以指定多個目標,并且使用目標名字來建議依賴文件的名字;比普通多目標規(guī)則更通用因為不需要依賴關(guān)系是相同的:依賴關(guān)系必須類似但不需要相同。語法TARGETS.:TARGET-PATTERN:DEP-PATTE

48、RNS.COMMANDSTARGETS列表指出規(guī)則應(yīng)用的目標,可以包含通配符,于普通規(guī)則的目標相同。TARGET-PATTERN和DEP-PATTERNS來表明目標的依賴關(guān)系如何計算:匹配TARGET-PATTERN的目標從名字中抽出一部分,叫做詞干(stem),詞干被替換到DEP-PATTERNS來形成依賴文件名。每個模式通常包含一個%字符。當TARGET-PATTER匹配一個目標時,%字符可以匹配目標名中的任何部分;這部分即是詞干,模式的其余部分必須完全匹配。例如foo.o匹配.0,foo是詞干;目標foo.c和foo.out并不匹配這個模式。目標的依賴文件名通過將DEP-PATTERNS

49、中的替換為詞干形成:如果依賴模式為.c,在替換詞干foo可以得到foo.c。依賴模式中不包含也是合法的,此依賴文件對所有的目標均有效。如果需要在模式規(guī)則中使用%字符,必須在其前面加字符,如果%前的字符是有實際意義的,必須在其前面加,其它的不必如此處理。如the%weird%pattern在有效的前是the%weird,其后是pattern。最后的保持原樣是因為其并不影響字符。以下例子從相應(yīng)的.c文件編譯foo.o和bar.o:objects=foo.obar.o$(objects):%.o:%.c$(CC)-c$(CFLAGS)$-o$每個目標必須匹配目標模式,對于不匹配的目標會給出警告。如果

50、列表中只有部分文件匹配模式,可以使用filter函數(shù)移去不匹配的文件名:files=foo.elcbar.olose.o$(filter%.o,$(files):%.o:%.c$(CC)-c$(CFLAGS)$-o$(filter%.elc,$(files):%.elc:%.elemacs-fbatch-byte-compile$命令generate執(zhí)行時,$*擴展為詞干big或little。靜態(tài)模式規(guī)則和隱式規(guī)則靜態(tài)模式規(guī)則和隱式規(guī)則在作為模式規(guī)則是具有很多共同點,都有目標模式和構(gòu)造依賴文件名的模式,不同之處在于make決定何時應(yīng)用規(guī)則的方法。隱式規(guī)則可應(yīng)用于匹配其模式的任何目標,但只限于沒

51、有指定命令的目標,如果有多條可應(yīng)用的隱式規(guī)則,只有一條被使用,取決于規(guī)則的順序。反之,靜態(tài)模式規(guī)則適用于規(guī)則中明確目標列表,不適用于其它目標且總是適用于指定的每個目標。如果有兩條沖突的規(guī)則,且都有命令,這是一個錯誤。靜態(tài)模式規(guī)則比隱式規(guī)則優(yōu)越之處如下:可為一些不能按句法分類,但可以顯式列出的文件重載隱式規(guī)則不能判定目錄中的精確內(nèi)容,些無關(guān)的文件可能導(dǎo)致make適用錯誤的隱式規(guī)則;最終結(jié)果可能依賴于隱式規(guī)則的次序。適用靜態(tài)模式規(guī)則時,這種不確定性是不存在的:規(guī)則適用于明確指定的目標。雙冒號規(guī)則雙冒號規(guī)則(Double-colonrules)的目標后是::而不是:,當一個目標出現(xiàn)在多條規(guī)則中時,其

52、處理和普通規(guī)則的處理不同。當個目標出現(xiàn)在多條規(guī)則中時,所有規(guī)則必須是相同類型的:都是普通的或者都是雙冒號的。如果是雙冒號,規(guī)則之間相互獨立;如果目標需要更新,則規(guī)則的命令被執(zhí)行;結(jié)果可能是沒有執(zhí)行,或者執(zhí)行了其中一些,或者所有的規(guī)則都執(zhí)行了。同一目標的雙冒號規(guī)則事實是完全孤立的,每條規(guī)則被被單獨處理,就象不同目標的規(guī)則一樣;規(guī)則按照在makefile中出現(xiàn)的次序被處理,此類規(guī)則真正有意義的是那些于命令執(zhí)行次序無關(guān)的。這種規(guī)則有時比較晦澀不是特別有用;它提供了一種機制:通過不同依賴文件的更新來對目標進行不同的處理,這種情形很罕見。每個這種規(guī)則應(yīng)當提供命令,如果沒有,適用的隱式規(guī)則將使用。自動生成

53、依賴關(guān)系在makefile中,許多規(guī)則都是一些目標文件依賴于一些頭文件。例如:main.c通過#include使用defs.h,這樣規(guī)則:main.o:defs.h告訴make在defs.h變化時更新main.o。在程序比較大時,需要寫許多這樣的規(guī)則;而且當每次增刪#include時,必須小心的更新makefile。許多現(xiàn)代的編譯器可以幫你寫這些規(guī)則,通常這是通過編譯器的-M選項,例如命令:cc-Mmain.c輸出以下內(nèi)容:main.o:main.cdefs.h這樣就不必寫這些規(guī)則,有編譯器代勞了。注意這樣的依賴關(guān)系中提及main.o,不會被隱式規(guī)則認為是中間文件,這意味這make在使用過它之

54、后不會將其刪除。使用老的make程序時,習慣做法是使用makedepend命令利用編譯器的功能產(chǎn)生依賴關(guān)系,該命令會產(chǎn)生一個depend文件包含所有自動產(chǎn)生的依賴關(guān)系,然后在makefile中使用include將其讀入。使用GNU的make時,重新生成makefile的功能使得這種做法變得過時:從不需要顯式請求更新依賴關(guān)系,因為它總是重新生成任何過時的makefile。自動依賴關(guān)系生成推薦的做法是對每個源文件做一個makefile。對每個源文件NAME.c,有一個makefileNAME.d,其中列出了目標文件NAME.。依賴的所有文件,這樣在源文件更新時,需要掃描來產(chǎn)生新的依賴關(guān)系。例子是一

55、個從NAME.c產(chǎn)生依賴關(guān)系文件NAME.d的模式規(guī)則:%.d:%.c$(SHELL)-ec$(CC)-M$(CPPFLAGS)$-e選項是當$(CC)命令失敗時(exit狀態(tài)非O),shell立刻退出。通常shell的返回值是管道中最后一條命令(sed)的返回值,這樣make不會注意到編譯器出錯。使用GNU的C編譯器時(gcc),可以用-MM選項來代替-M選項,這樣省略系統(tǒng)頭文件的依賴關(guān)系。sed命令的目的是將main.o:main.cdefs.h轉(zhuǎn)換為main.omain.d:main.cdefs.h這樣使得每個.d文件依賴于.o文件相應(yīng)源文件和頭文件,make則可以在原文間或頭文件變化時

56、更新依賴關(guān)系文件。如果定義了生成.d文件的規(guī)則,可以使用include指令來讀入所有的文件:sources=foo.cbar.cinclude$(sources:.c=.d)例中使用替換變量來將源文件列表foo.cbar.c轉(zhuǎn)換為依賴關(guān)系文件的列表。因為.d文件和其它文件一樣,不需要更多工作,make會在需要時重新生成它們。規(guī)則的命令是由一一執(zhí)行的shell命令組成。除了以分號隔開寫在依賴關(guān)系后的命令,每個命令行必須以tab字符開始空行和注釋行可以出現(xiàn)在命令行中,處理時被忽略(注意:以tab字符開始的空行不是空行,是一條空命令)??梢栽诿钪惺褂萌魏纬绦?,但這些程序是(SHELL)來執(zhí)行的。4

57、.1.回顯通常make打印出要執(zhí)行的命令,稱之為回顯,這和親自敲命令的現(xiàn)象是一樣的。當行之前有字符時,命令不再回顯,字符在傳遞給shell前丟棄。典型的用法是只對打印命令有效,比如echo命令:echoAbouttomakedistributionfiles當make使用-n或Tust-print選項時,顯示要發(fā)生的一切,但不執(zhí)行命令。只有在這種情況下,即使命令以開始,命令行仍然顯示出來。這個選項對查看make實際要執(zhí)行的動作很有用。-s或一silent選項阻止make所有回顯,就象所有命令以開始一樣;一條沒有依賴關(guān)系的.SILENT規(guī)則有相同的作用,但是更加靈活。執(zhí)行在需要執(zhí)行命令更新目標時

58、,make為每一行創(chuàng)建一個子shell來執(zhí)行。這意味著諸如為進程設(shè)置局部變量的shell命令cd(改變進程的當前目錄)不會影響以后的命令。如果需要cd影響下一個命令,將它們放在一行上用分號隔開,這樣make認為是一條命令傳遞給shell程序(注意:這需要shell支持):foo:bar/losecdbar;gobblelose./foo另一個形式使用續(xù)行符:foo:bar/losecdbar;gobblelose./fooshell程序的名字是通過SHELL變量來取得的。(*UNIX)不象大多數(shù)變量,SHELL變量不是通過環(huán)境來設(shè)置的(即需要在makefile中設(shè)置),因為SHELL環(huán)境是個人

59、選擇的,如果不同人的選擇會影響makefile的功能的話,這樣很糟糕。并行執(zhí)行GNUmake可以一次執(zhí)行幾條命令。通常make-次執(zhí)行一條命令,等待其返回,再執(zhí)行下一條。使用j或Tobs可以同時執(zhí)行多條命令。如果j后梗一個正數(shù),表示一次可以執(zhí)行的命令條數(shù);如果-j之后沒有參數(shù),則不限制可執(zhí)行的命令數(shù)。缺省的數(shù)量是一。一個討厭的問題是如果同時執(zhí)行多條命令,它們的輸出會混在一起;另一個問題是兩個進程不能從同一個設(shè)備獲得輸入。錯誤每條shelI命令返回時,make會檢查其返回狀態(tài)。如果命令執(zhí)行成功,則下一條命令被執(zhí)行,最后一條命令執(zhí)行完后,規(guī)則執(zhí)行結(jié)束。如果有錯誤(返回非0狀態(tài)),make放棄當前規(guī)

60、則,也可能是所有規(guī)則。有時候命令執(zhí)行錯誤并不是問題,比如使用mkdir命令確保目錄存在:如果目錄一存在,貝mkdir會報告錯誤,但仍希望make繼續(xù)。要忽略命令的錯誤,在命令之前使用-字符,-字符在傳遞給shell之前被丟棄:clean:-rm-f*.o如果使用-i或一ignore-errors選項,make會忽略所有命令產(chǎn)生的錯誤;一條沒有依賴關(guān)系的.IGNORE規(guī)則有相同的作用,但-更靈活。在忽略錯誤時,make將錯誤也認為是成功,只是通知你命令的退出狀態(tài)和和錯誤被忽略。如果make并未告知忽略錯誤,在錯誤發(fā)生時,表明該目標不能成功更新,直接或間接依賴于此的目標當然也不能成功;這些目標的命

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論