|
決心學(xué)習(xí)Makefile,一方面是為了解決編譯開源代碼時(shí)需要跨編譯平臺(tái)的問題(發(fā)現(xiàn)一些開源代碼已經(jīng)在使用VS2010開發(fā),但我還沒安裝VS2010,我想在VS2008下編譯這些代碼);另一方面源碼在服務(wù)器端編譯的話,使用IDE的方式編譯還是不太方便。
本文主要分為三部分:第一部分講述namke工具使用makefile的用法;第二部分講述makefile的主要語法;第三部分講述自己動(dòng)手實(shí)踐學(xué)習(xí)寫makefile文件。第四部分是編寫一個(gè)工具將vc工程文件轉(zhuǎn)化為Makefile文件。
首先要清楚的是在VS環(huán)境下使用Makefile的工具是nmake。因此我們需要弄明白nmake的使用Makefile文件常用命名行用法。nmake使用Makefile文件常用命名行用法是:
其中makefile為makefile文件,/x stderrfile為可選參數(shù),即把namke錯(cuò)誤存儲(chǔ)到文件stderrfile。
接著介紹makefile的主要語法。makefile的注釋以#開頭,如:
Makefile的一個(gè)重要組成部分是宏。Makefile中的宏和C語言的中宏類似,其實(shí)質(zhì)就是字符串替換。其語法很簡(jiǎn)單,如下:
macro name = macro value
直譯就是宏名 = 宏的值
VS預(yù)定義了很多宏,如OUTDIR,你可以在你的Makefile重新定義這些宏以覆蓋原來的值。
宏可以使用環(huán)境變量,如你的系統(tǒng)有一個(gè)OPEN_SOURCE的環(huán)境變量,然后你可以這樣定義宏: THIRD_PARTY = $(OPEN_SOURCE)
宏的引用用法是 $(宏名)。
接著介紹Makefile的第二個(gè)重要組成部分預(yù)處理指令。Makefile的預(yù)處理指令和C語言的預(yù)處理指令類似,其常用指令如下: !ERROR string —— 顯示錯(cuò)誤“string”, 然后停止執(zhí)行,錯(cuò)誤代碼為U1050 !MESSAGE string —— 顯示字符串,這個(gè)一般用于信息顯示C語言的#pragma message !INCLUDE [<]filename[>] —— 包含makefile。 !IF const —— 如果成立(非零),則處理!F和下一個(gè)!ELSE或!ENDIF之間的語句 還有諸如!IFDEF macroname、!IFNDEF macroname、!ELSE、!ELSEIF、!ELSEIFDEF、!ELSEIFNDEF、!ENDIF和C語言的#if之類的指令的意義是一致的,這里就不一一詳述了。
Makefile的第三個(gè)主要組成部分是描述塊。描述塊的結(jié)構(gòu)如下: 目標(biāo):依賴項(xiàng) 命令
這里略微解釋下什么叫目標(biāo)、依賴項(xiàng)和命令。所謂目標(biāo)就是用戶最終希望得到的結(jié)果,也就是nmake需要生成的結(jié)果。目標(biāo)可以是一個(gè)文件、目錄,也可以什么都不是。如果目標(biāo)不存在或者目標(biāo)的時(shí)間戳(文件的最后修改時(shí)間)比依賴項(xiàng)早,或者目標(biāo)類型不是文件,nmake將運(yùn)行描述塊中的“命令”。
依賴項(xiàng)是指在生成目標(biāo)所需要使用到的對(duì)象。一個(gè)目標(biāo)可以有一個(gè)或多個(gè)依賴項(xiàng),也可以沒有依賴項(xiàng)。多個(gè)依賴項(xiàng)以空格分隔。如果指定的依賴項(xiàng)不存在,則在其他描述塊的目標(biāo)中尋找,但首先需要生成這個(gè)目標(biāo)。
命令是nmake在生成目標(biāo)時(shí)所調(diào)用的命令。與用戶自己在命令行中執(zhí)行效果是一樣的。
在使用namke進(jìn)行程序構(gòu)建時(shí),nmake采用了時(shí)間戳判斷機(jī)制。在生成一個(gè)目標(biāo)時(shí),會(huì)判斷目標(biāo)文件是否存在或目標(biāo)的最后修改時(shí)間是否晚于所有依賴項(xiàng)的最后修改時(shí)間。如果所有依賴項(xiàng)的最后修改時(shí)間都比目標(biāo)的最后修改時(shí)間晚,則說明當(dāng)前的目標(biāo)文件是使用現(xiàn)有的依賴項(xiàng)生成,是最新的,沒有必要再進(jìn)行生成。 介紹到這里,可能你對(duì)Mdakefile的語法細(xì)節(jié)有了大致的了解,但估計(jì)你對(duì)Makefile的常用文件結(jié)構(gòu)還不了解。如果缺少對(duì)這一層的理解,你還是對(duì)如何編寫Makefile文件一頭霧水。下面介紹一下常用的Makefile文件結(jié)構(gòu)。Makefile文件結(jié)構(gòu)可以是如下的結(jié)構(gòu): # 宏定義 …… # 描述塊
學(xué)了這么多,我們來實(shí)踐一下。首先我們來一個(gè)簡(jiǎn)單的控制臺(tái)工程——ConsoleTest。一切根據(jù)工程向?qū)Р捎媚J(rèn)設(shè)置即可。然后在main函數(shù)中添加幾句簡(jiǎn)單代碼(這個(gè)用于判斷我們生成的程序是否成功),具體如下:
然后我們?cè)贑onsoleTest文件夾下新建一個(gè)makefile.vc。我們開始正式編寫一個(gè)makefile文件了。這時(shí)我們的大腦可能會(huì)一片空白,雖然你學(xué)了很多makefile語法,但邁出第一步依然是困難,這是正常的反應(yīng)。好吧,讓我們一步步來吧。首先要告訴你makefile的一個(gè)基本原則:以終為始,這個(gè)似乎和我們平時(shí)進(jìn)行的過程式編程的原則相悖。所謂以終為始,就是你通過makefile文件首先告訴編譯器這個(gè)工程是想生成一個(gè)exe還是一個(gè)dll還是一個(gè)靜態(tài)庫(kù)。然后告訴編譯器要生成這個(gè)exe之類需要生成哪些obj文件。在這個(gè)例子中,我們要生成一個(gè)exe,所以我們?cè)趍akefile文件的第一行就是:
接下來就是編譯器的一般生成過程:編譯加鏈接命令,具體是:
其中cl語句是VC編譯器的編譯器的命令行編譯,link語句是VC鏈接器的命令行用法,這里只簡(jiǎn)單敘述cl和link的用法。 cl的一些常用選項(xiàng): -c: 編譯但不鏈接 -D: 定義預(yù)處理器,如-D_X86=1:指定在x86平臺(tái)上編譯,-D_DEBUG:定義預(yù)處理器_DEBUG, -I:包含的頭文件 cl的最后一個(gè)參數(shù)是所編譯的文件。
link的一些常用選項(xiàng): /INCREMENTAL:是否啟用增量鏈接,YES為啟用,NO為不啟用, /NOLOGO: 取消顯示啟動(dòng)版權(quán)標(biāo)志 /SUBSYSTEM:指定子系統(tǒng),在PC桌面程序上一般是兩個(gè)選項(xiàng):console(控制臺(tái)程序)和WINDOWS(非控制臺(tái)程序)。 /out: 指定輸出的文件。 link最后的參數(shù)是需要鏈接的obj文件和庫(kù)文件。
cl和link的詳細(xì)用法請(qǐng)參考MSDN和參考文獻(xiàn)2《VC命令行編譯C++》。
我們看到生成的obj文件和ConsoleTest.exe是放到當(dāng)前的源碼文件夾下。一般我們想把它放到debug文件夾下。那么我們?cè)撛趺醋瞿??這時(shí)就可以用到makefile中的一個(gè)常用部分——宏。我們可以這樣定義一個(gè)宏,然后創(chuàng)建debug文件夾,具體代碼是: OUTDIR = .\Debug
#這里增加了一個(gè)輸出:$(OUTDIR)
#假如不存在$(OUTDIR)文件夾,就創(chuàng)建它
相應(yīng)地,生成的obj文件和exe文件都需要加上輸出文件的路徑,具體如下:
這里cl工具增加了兩個(gè)選項(xiàng) /Fo:指定obj文件的放置路徑 /Fd:指定pdb文件的放置路徑
這里需要值得注意的,Windows平臺(tái)下文件反斜杠應(yīng)該采用\,而不是跨平臺(tái)的/,因?yàn)槲以袿UTDIR = .\Debug寫成OUTDIR = ./Debug,結(jié)果造成if not exist不識(shí)別$(OUTDIR)而造成語法錯(cuò)誤。/在windows平臺(tái)下的makefile中大多地方可以識(shí)別,但在一些地方不能識(shí)別(例如if not exist語句),而\在任何地方都能識(shí)別的。
還有就是命令語句必須至少空出一格,而不能頂格寫。如果if not exist"$(OUTDIR)" mkdir $(OUTDIR)頂格,就會(huì)出現(xiàn)錯(cuò)誤: makefile.vc(5) : fatal error U1034: 語法錯(cuò)誤 : 缺少分隔符 Stop.
除開命令語句,其它語句都應(yīng)該頂格寫。
我們繼續(xù)完善這個(gè)makefile。我們想增加一個(gè)清理輸出文件的指令,就是常用的clean指令。我們可以在描述塊all后面加一個(gè)描述塊:clean,clean描述塊的代碼如下:
如果makefile文件中不存在clean這個(gè)描述塊,而你運(yùn)行下面的命令: nmake /f makefile.vc clean 會(huì)出現(xiàn)下面的錯(cuò)誤提示: NMAKE : fatal error U1052: 未找到文件“clean” Stop.
我們繼續(xù)完善這個(gè)makefile。因?yàn)楝F(xiàn)在只能編譯debug版本,我們想用戶能指定編譯debug版本或release版本,用戶只需要輸入“debug”或“release”來指定。我們想到可以設(shè)定一個(gè)宏標(biāo)記來指定,當(dāng)用戶輸入正確時(shí)就編譯相應(yīng)的版本,錯(cuò)誤時(shí)就提示使用方法。同時(shí)我們想到前面提到nmake工具的命令行用法是:
其中macrodefs就是允許我們定義一些自定義宏來控制編譯輸出的。這次我們可以定義兩個(gè)宏debug和release。具體不再詳述,下面列出代碼:
該makefile的用法是:
參考文獻(xiàn):
1. MSDN 2008,Microsoft Corporation 2. VC命令行編譯C++ 3. 精通Windows API,范文慶、周彬彬、安靖編著 |
|
|