makefile文件: 在應(yīng)用程序開(kāi)發(fā)過(guò)程中, makefile文件包含了所有命令, 宏定義,選項(xiàng)來(lái)編譯工程, 但是在wince的makefile文件中, 它僅僅包含了一個(gè)文件:makefile.def. makefile.def中包含了sources文件需要用到的宏定義,并傳遞一些標(biāo)志位給編譯器和連接器使用, 這些標(biāo)志位都將直接作用到DDK編譯環(huán)境,利用makefile.def可以使PB驅(qū)動(dòng)的建立單一化。 這也是PB的makefile與應(yīng)用程序的makefile不同的原因了。 值得注意的是, 一般不建議修改mekefile和makefile.def文件。
1 makefile入門(mén)
Windows CE的構(gòu)建系統(tǒng)大量使用了Nmake工具和makfile。在大多數(shù)微軟的軟件和驅(qū)動(dòng)開(kāi)發(fā)包中都會(huì)包含Nmake工具。因此,這里有必要介紹一下makefile和Nmake工具。
1.1 makefile簡(jiǎn)介
對(duì) 于許多Windows下的程序員來(lái)說(shuō),makefile可能還是個(gè)陌生的名詞。因?yàn)閃indows下的許多集成開(kāi)發(fā)環(huán)境(例 如:Microsoft Visual Studio和Borland C++ Builder等)可以幫助開(kāi)發(fā)人員完成makefile需要完成的功 能。通常只需要在集成開(kāi)發(fā)環(huán)境中按個(gè)按鈕,工具就可自動(dòng)幫助我們編譯、鏈接整個(gè)項(xiàng)目。想象如果沒(méi)有了集成開(kāi)發(fā)環(huán)境,那么就需要有另外一種方式來(lái)管理對(duì)項(xiàng)目 的構(gòu)建。
簡(jiǎn)單的來(lái)說(shuō),makefile負(fù)責(zé)幫助開(kāi)發(fā)人員簡(jiǎn)化代碼的編譯、鏈接等構(gòu)建工作。對(duì)于只包含幾個(gè)文件的簡(jiǎn)單的項(xiàng)目,開(kāi)發(fā)人員完全可以通過(guò) 手動(dòng)控制編譯器、鏈接器來(lái)完成對(duì)項(xiàng)目的構(gòu)建。但是想象一下對(duì)于一個(gè)擁有幾百個(gè)、甚至幾千個(gè)文件的大型項(xiàng)目,如果每次構(gòu)建都是通過(guò)手動(dòng)完成,那消耗的工作量 和復(fù)雜程度是不可想象的。在這種情況下,makefile就有了它的用武之地。
makefile關(guān)系到了整個(gè)工程的編譯規(guī)則。一個(gè)工程中的源文件 不計(jì)數(shù),其按類型、功能、模塊分別放在若干個(gè)目錄中,makefile定義了一系列的規(guī)則來(lái)指定,哪些文件需要先編譯,哪些文件需要后編譯,哪些文件需要 重新編譯,甚至于進(jìn)行更復(fù)雜的功能操作,因?yàn)閙akefile就像一個(gè)自動(dòng)化腳本一樣,其中也可以執(zhí)行操作系統(tǒng)的命令。
makefile帶來(lái)的最大的好處是“自動(dòng)化構(gòu)建”。寫(xiě)好makefile之后,在編譯的時(shí)候只需要一個(gè)命令,整個(gè)工程完全自動(dòng)編譯、鏈接,極大的提高了軟件開(kāi)發(fā)的效率。
makefile本質(zhì)上只是一個(gè)文本文件,本身并不能運(yùn)行。在運(yùn)行makefile的時(shí)候還是需要外部程序來(lái)對(duì)makefile進(jìn)行解釋執(zhí)行。NMake.exe就是用來(lái)解析并執(zhí)行makefile的工具。
當(dāng)用戶輸入nmake命令后,首先nmake會(huì)讀取makefile,然后解析makefile,并根據(jù)makefile的規(guī)則來(lái)確定要編譯哪些代碼。然后nmake會(huì)調(diào)用編譯器、鏈接器等一些開(kāi)發(fā)工具,完成對(duì)代碼的編譯鏈接。最終會(huì)生成可執(zhí)行文件。
圖:makefile的工作流程
值 得一提的是makefile既不是Windows CE特有的工具也不是微軟的發(fā)明創(chuàng)造。makefile是一種通用的自動(dòng)化構(gòu)建手段。在 UNIX/Linux平臺(tái)下有著廣泛而眾多的應(yīng)用。許多開(kāi)發(fā)工具都會(huì)提供NMake類似的工具。比如:Delphi的make,Visual C++的 nmake,Linux下GNU的make等等。
1.2 makefile的編
寫(xiě)規(guī)則
makefile是由一個(gè)個(gè)推導(dǎo)和規(guī)則構(gòu)成的,在NMake中這也被叫做描述塊(Description Blocks)。一個(gè)最基本的推導(dǎo)規(guī)則的語(yǔ)法如下。
targets... : dependents...
commands...
targets也就是一個(gè)目標(biāo)文件,可以是Object File,也可以是可執(zhí)行文件。還可以是一個(gè)標(biāo)簽(Label)。targets必須在一行的頂格寫(xiě),前面不能有空格。
dependents就是要生成target所需要的文件或是目標(biāo)依賴項(xiàng)。dependents與targets之間用冒號(hào)間隔。一個(gè)targets可以有多個(gè)dependents。
command也就是NMake需要執(zhí)行的命令。其中commands可以是任意的Windows命令行命令。
這 是一個(gè)文件的依賴關(guān)系,也就是說(shuō),target這一個(gè)或多個(gè)的目標(biāo)文件依賴于dependents中的文件,其生成規(guī)則定義在command中。也就是 說(shuō),dependents中如果有一個(gè)以上的文件比targets文件要新的話,command所定義的命令就會(huì)被執(zhí)行。這就是makefile的規(guī)則。 也就是Makefile中最核心的內(nèi)容。
掌握了makefile最核心的內(nèi)容,就可以嘗試編寫(xiě)第一個(gè)makefile了。但是僅僅知道了這一點(diǎn)還 遠(yuǎn)遠(yuǎn)不夠。makefile還有很多細(xì)節(jié)的內(nèi)容,下面會(huì)一點(diǎn)一點(diǎn)地介紹。在此之前,先看一個(gè)可以實(shí)際運(yùn)行的makefile。以便讀者對(duì)makefile 有個(gè)感性的認(rèn)識(shí)。
1.3 一個(gè)實(shí)際可以運(yùn)行的makefile
在%_WINCEROOT%\PBWorkspaces\MyPlatform\下新建立一個(gè)目錄hello,然后在hello目錄下創(chuàng)建hello.cpp,內(nèi)容如下:
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
MessageBox(NULL, L"Hello", L"bb", 0);
}
本部分內(nèi)容的目的就是使用NMake工具來(lái)把Hello.cpp構(gòu)建為Hello.exe可執(zhí)行文件。
為此,在hello目錄下再創(chuàng)建一個(gè)文本文件,名為makefile(沒(méi)有擴(kuò)展名)。然后用文本編輯器在makefile中輸入如下內(nèi)容:
#This is a demo makefile
hello.exe: hello.obj
@echo linking...
link -MACHINE:x86 -NODEFAULTLIB -subsystem:windowsce,5.00 -entry:WinMainCRTStartup -LIBPATH:E:\WINCE500\PBWorkspaces\MyPlatform\projroot\cesysgen\sdk\lib\x86\retail hello.obj coredll.lib corelibc.lib
hello.obj: hello.cpp
@echo compiling...
cl -nologo -c -I. -IE:\WINCE500\public\common\sdk\inc -DUNICODE -D_UNICODE -DUNDER_CE=500 -D_WIN32_WCE=500 -DWIN32 -DSTRICT -Dx86 -D_X86_ -DINTERNATIONAL -DL0804 -DINTLMSG_CODEPAGE=1252 hello.cpp
clean:
del hello.obj, hello.exe
第一行是注釋,在makefile中,用#表示該行內(nèi)容是注釋。
按照上文介紹的推導(dǎo)規(guī)則,這段makefile定義了兩個(gè)依賴規(guī)則。hello.exe依賴于hello.obj,hello.obj又依賴于hello.cpp。
要 從hello.cpp生成hello.obj,需要經(jīng)過(guò)編譯過(guò)程,執(zhí)行編譯命令。上文makefile代碼中定義了兩個(gè)命令,一個(gè)是操作系統(tǒng)的內(nèi)部命令 echo,作用是輸出一個(gè)字符串,另外一個(gè)是調(diào)用C++編譯器cl.exe,并用-c指示它只進(jìn)行編譯工作而不進(jìn)行鏈接。對(duì)于從hello.obj生成 hello.exe的過(guò)程也是一樣的。中間調(diào)用了鏈接器link.exe把幾個(gè)庫(kù)文件鏈接成hello.exe。
為了簡(jiǎn)單起見(jiàn),用到的路徑都直接采用了寫(xiě)死的絕對(duì)路徑。
從“開(kāi)始” ? “程序” ? “Microsoft Windows CE 5.0” 菜單打開(kāi)Windows CE的控制臺(tái)。然后cd到hello目錄,輸入nmake命令,控制臺(tái)的輸出結(jié)果如下:
E:\WINCE500\PBWorkspaces\MyPlatform\hello>nmake
compiling...
Windows CE Version (Release) (Built on Mar 1 2004 21:46:39)
cl -nologo -c -I. -IE:\WINCE500\public\common\sdk\inc -DUNICODE -D_UNICODE -DUNDER_CE=500 -D_WIN32_WCE=500 -DWIN32 -DSTRICT -Dx86 -D_X86_ -DINTERNATIONAL -DL0804 -DINTLMSG_CODEPAGE=1252 hello.cpp
hello.cpp
linking...
link -MACHINE:x86 -NODEFAULTLIB -subsystem:windowsce,5.00 -entry:WinMainCRTStartup -LIBPATH:E:\WINCE500\PBWorkspaces\MyPlatform\projroot\cesysgen\sdk\lib\x86\retail hello.obj coredll.lib corelibc.lib
Microsoft (R) Incremental Linker Version 7.10.4017
Copyright (C) Microsoft Corporation. All rights reserved.
這是使用dir命令查看hello目錄,可以看到多了hello.obj和hello.exe兩個(gè)文件。這說(shuō)明nmake已經(jīng)幫我們生成了目標(biāo)文件。
對(duì)于這個(gè)makefile,還有最后要介紹的一點(diǎn)。makefile中的clean是一個(gè)標(biāo)簽,通常所有的makefile中都會(huì)有clean這樣一個(gè)標(biāo)簽,用來(lái)清理生成的文件,以便重新編譯。為了調(diào)用這個(gè)標(biāo)簽可以在命令行下輸入如下指令
nmake clean
這樣,nmake就會(huì)幫我們調(diào)用系統(tǒng)的del命令,刪除構(gòu)建時(shí)生成的hello.obj和hello.exe文件了。
1.4 使用變量
讓 我們?cè)倩仡^來(lái)看看上一節(jié)中的示例makefile。如果我們希望把生成的文件由hello.exe改變?yōu)閚ihao.exe,那么僅僅這一丁點(diǎn)改動(dòng),在 makefile中需要修改的地方就多達(dá)五處。這對(duì)于makefile的維護(hù)非常不方便。如果能有一種類似于C語(yǔ)言中宏或者變量的機(jī)制,就可以解決這個(gè)問(wèn) 題了。
為了makefile的易維護(hù),在makefile中我們可以使用變量。makefile的變量也就是一個(gè)字符串,理解成C語(yǔ)言中的宏可能會(huì)更好。
比如,我們聲明一個(gè)變量,叫TARGETNAME,在makefile一開(kāi)始就這樣定義:
TARGETNAME = hello
于是,我們就可以很方便地在我們的makefile中以“$(TARGETNAME)”的方式來(lái)使用這個(gè)變量了,于是我們的改良版makefile就變成下面這個(gè)樣子:
TARGETNAME = hello
SOURCES = hello.cpp
TARGETLIBS = $(TARGETNAME).obj coredll.lib corelibc.lib
LINK = link
CPPFLAGS = -nologo -c -I. -IE:\WINCE500\public\common\sdk\inc -DUNICODE -D_UNICODE -DUNDER_CE=500 -D_WIN32_WCE=500 -DWIN32 -DSTRICT -Dx86 -D_X86_ -DINTERNATIONAL -DL0804 -DINTLMSG_CODEPAGE=1252
LFLAGS = -MACHINE:x86 -NODEFAULTLIB -subsystem:windowsce,5.00 -entry:WinMainCRTStartup -LIBPATH:E:\WINCE500\PBWorkspaces\MyPlatform\projroot\cesysgen\sdk\lib\x86\retail
$(TARGETNAME).exe: $(TARGETNAME).obj
@echo linking...
$(LINK) $(LFLAGS) $(TARGETLIBS)
$(TARGETNAME).obj: $(SOURCES)
@echo compiling...
$(CPP) $(CPPFLAGS) $(SOURCES)
clean:
del *.obj, *.exe
在 這一版本的makefile中,我們?cè)趍akefile的最開(kāi)始定義了六個(gè)變量。TARGETNAME表示要生成的文件名,SOURCES表示源代碼列 表。TARGETLIBS表示要鏈接的庫(kù)列表。LINK表示鏈接器的名稱。CPPFLAGS和LFLAGS分別表示編譯器和鏈接器的命令行參數(shù)。
有了這些變量定義之后,編譯和鏈接的命令就可以寫(xiě)的非常簡(jiǎn)潔了,例如編譯源代碼的命令就可以寫(xiě)成:$(CPP) $(CPPFLAGS) $(SOURCES)。nmake工具會(huì)自動(dòng)用變量替換這些標(biāo)記,最終的結(jié)果與第一個(gè)版本還是一樣的。
如 果讀者細(xì)心的話,可以發(fā)現(xiàn)我們?cè)趍akefile中并沒(méi)有定義CPP這個(gè)變量,但是在makefile中我們依然使用了CPP變量。這又是為什么呢?其實(shí) nmake工具默認(rèn)會(huì)設(shè)置一些變量的值,對(duì)于C++編譯器,nmake會(huì)默認(rèn)定義CPP變量,并把它的值賦為cl,因此,在使用的時(shí)候,makefile 代碼中就不需要重復(fù)定義了。
1.5 使用預(yù)處理
經(jīng)過(guò)上一節(jié)的改動(dòng),namefile已經(jīng)有了很大的靈活性。但是,依然達(dá)不到盡善盡美的地步。如果我們要把EXE文件的入口點(diǎn)從WinMainCRTStartup()函數(shù)修改到WinMain(),那么依然需要修改makefile。
使用預(yù)處理可以很好的解決上面的問(wèn)題。在makefile中,NMake工具允許使用預(yù)處理機(jī)制來(lái)完成如下功能:
按條件處理makefile
顯示錯(cuò)誤信息
包含其它的makefile
打開(kāi)/關(guān)閉某些nmake工具的命令行開(kāi)關(guān)
預(yù)處理指令以“!”開(kāi)頭,必須出現(xiàn)在每行的最開(kāi)始。最常用的預(yù)處理指令是條件處理。Nmake支持如下的條件預(yù)處理指令:
!IF
!IFDEF
!IFNDEF
!ELSE
!ELSEIF
!ELSEIFDEF
!ELSEIFNDEF
!ENDIF
它們的用法與C語(yǔ)言的預(yù)處理宏類似,相信讀者可以很容易的理解它們的含義。
使用預(yù)處理機(jī)制來(lái)修改上一個(gè)版本的makefile,得到的新makefile如下所示:
TARGETNAME = hello
SOURCES = hello.cpp
EXEENTRY = WinMain
TARGETLIBS = $(TARGETNAME).obj coredll.lib corelibc.lib
LINK = link
!IFDEF EXEENTRY
! MESSAGE EXEENTRY: $(EXEENTRY)
EXEENTRYOPTION=-entry:$(EXEENTRY)
!ELSE
EXEENTRYOPTION=-entry:WinMainCRTStartup
!ENDIF
CPPFLAGS = -nologo -c -I. -IE:\WINCE500\public\common\sdk\inc -DUNICODE -D_UNICODE -DUNDER_CE=500 -D_WIN32_WCE=500 -DWIN32 -DSTRICT -Dx86 -D_X86_ -DINTERNATIONAL -DL0804 -DINTLMSG_CODEPAGE=1252
LFLAGS = $(EXEENTRYOPTION) -MACHINE:x86 -NODEFAULTLIB -subsystem:windowsce,5.00 -LIBPATH:E:\WINCE500 \PBWorkspaces\MyPlatform\projroot\cesysgen\sdk\lib\x86\retail
$(TARGETNAME).exe: $(TARGETNAME).obj
@echo linking...
$(LINK) $(LFLAGS) $(TARGETLIBS)
$(TARGETNAME).obj: $(SOURCES)
@echo compiling...
$(CPP) $(CPPFLAGS) $(SOURCES)
clean:
del *.obj, *.exe
在 這個(gè)版本中,主要的改動(dòng)是新增加了一個(gè)變量EXEENTRY,并且增加了對(duì)于這個(gè)變量的預(yù)處理判斷。如果用戶定義了EXEENTRY變量,則把變量 EXEENTRYOPTION的值設(shè)置成-entry:EXEENTRY,否則就設(shè)置成默認(rèn)的CRT入口函數(shù) -entry:WinMainCRTStartup。修改相應(yīng)的LFLAGS,把EXEENTRYOPTION加到LFLAGS中,修改就生效了。
這樣,其實(shí)新增加的EXEENTRY是一個(gè)可選的變量,如果用戶沒(méi)有定義這個(gè)變量的值,構(gòu)建也不會(huì)出錯(cuò)。使用預(yù)處理技術(shù)對(duì)于維護(hù)makefile,保持它的向下兼容非常有效。
注意,代碼中出現(xiàn)的!MESSAGE也是一個(gè)宏,用來(lái)向標(biāo)準(zhǔn)輸出stdout輸出一個(gè)字符串。
1.6 包含其它文件
經(jīng)過(guò)上面的修改,makefile中的有些模塊已經(jīng)非常通用了,對(duì)于每個(gè)項(xiàng)目都建立一個(gè)makefile也是比較復(fù)雜的。為了增強(qiáng)代碼的重用性,可以考慮把makefile代碼中通用的部分抽取出來(lái),放在一個(gè)獨(dú)立的文件中。以便在多個(gè)項(xiàng)目中公用。
預(yù)處理的另外一個(gè)作用是包含其它makefile文件。語(yǔ)法是:
! INCLUDE [<] 文件名 [>]
使用這個(gè)功能,可以實(shí)現(xiàn)把makefile拆分的目的。
這次,我們把makefile拆分成三個(gè)文件,名字分別叫:sources、makefile和makefile.def,都放在hello目錄中。三個(gè)文件的內(nèi)容分別如下:
sources文件的內(nèi)容:
# This is a demo sources file
TARGETNAME = hello
SOURCES = hello.cpp
EXEENTRY = WinMain
TARGETLIBS = coredll.lib \
corelibc.lib
makefile文件的內(nèi)容:
! INCLUDE makefile.def
makefile.def文件的內(nèi)容:
! INCLUDE .\sources
TARGETLIBS = $(TARGETLIBS) \
$(TARGETNAME).obj
LINK = link
!IFDEF EXEENTRY
! MESSAGE EXEENTRY: $(EXEENTRY)
EXEENTRYOPTION=-entry:$(EXEENTRY)
!ELSE
EXEENTRYOPTION=-entry:WinMainCRTStartup
!ENDIF
CPPFLAGS = -nologo -c -I. -IE:\WINCE500\public\common\sdk\inc -DUNICODE -D_UNICODE -DUNDER_CE=500 -D_WIN32_WCE=500 -DWIN32 -DSTRICT -Dx86 -D_X86_ -DINTERNATIONAL -DL0804 -DINTLMSG_CODEPAGE=1252
LFLAGS = $(EXEENTRYOPTION) -MACHINE:x86 -NODEFAULTLIB -subsystem:windowsce,5.00 -LIBPATH:E:\WINCE500 \PBWorkspaces\MyPlatform\projroot\cesysgen\sdk\lib\x86\retail
$(TARGETNAME).exe: $(TARGETNAME).obj
@echo linking...
$(LINK) $(LFLAGS) $(TARGETLIBS)
$(TARGETNAME).obj: $(SOURCES)
@echo compiling...
$(CPP) $(CPPFLAGS) $(SOURCES)
clean:
del *.obj, *.exe
在 sources文件中我們只存放一些變量的定義,如果需要更改某些設(shè)置,只需要改動(dòng)sources文件就好了。在makefile中,僅有簡(jiǎn)單的一行,把 makefile.def文件導(dǎo)入進(jìn)來(lái)。在makefile.def文件中包含所有關(guān)聯(lián)推導(dǎo)和變量使用,并且還會(huì)把sources文件導(dǎo)入進(jìn)來(lái)。
在控制臺(tái)下輸入nmake和nmake clean,同樣可以順利地對(duì)hello.exe進(jìn)行構(gòu)建和清除。
好了,sources、makefile和makefile.def。至此為止,一個(gè)具體而微的Windows CE構(gòu)建系統(tǒng)就這么被我們給模擬出來(lái)了。Windows CE構(gòu)建系統(tǒng)中的DIRS文件是build.exe在進(jìn)行處理,NMake工具不會(huì)處理DIRS。
有了這些知識(shí),讀者在學(xué)習(xí)Windows CE的構(gòu)建系統(tǒng)時(shí),遇到makefile相關(guān)的內(nèi)容應(yīng)該不會(huì)再手足無(wú)措了。但是對(duì)于makefile本身的功能和作用而言,我們才剛剛開(kāi)始