makefile (適用於Visual Studio)入門

1.Dos下運行VC++自帶的Nmake,設置路徑

==========================================

●C/C++ 編譯器需要的環境變數設定

古早以來,PC 上的 C 編譯器,就需要兩個環境變數:

LIB:這個環境變數告訴編譯器說,必要的 libraries 在哪裏(哪個磁碟目錄下)
INCLUDE:告訴編譯器說,必要的 header files 在哪裏(哪個磁碟目錄下)

另外,爲了讓我們能夠在任何 working directory 都叫得到編譯器,當然我們必須設定 PATH。

從古早以來,一直到現在,C/C++ 編譯器都需要這三個環境變數。


●以 Visual C++ 爲例

以 Visual C++ 爲例,如果安裝後的檔案佈局如下:

C:/MSDEV/VC98/BIN : 這裏放有編譯器 CL.EXE
C:/MSDEV/VC98/INCLUDE : 這裏放有 C/C++ header files
C:/MSDEV/VC98/LIB : 這裏放有 C/C++ standard libraries

那麼你可以寫一個批次檔如下:

set PATH=C:/MSDEV/VC98/BIN;C:/MSDEV/COMMON/MSDEV98/BIN
set INCLUDE=C:/MSDEV/VC98/INCLUDE
set LIB=C:/MSDEV/VC98/LIB

之所以需要另外設定 PATH=C:/MSDEV/COMMON/MSDEV98/BIN,是因爲編譯器 CL.EXE 執行時需要 MSPDB60.DLL,而它被安裝於 C:/MSDEV/COMMON/MSDEV98/BIN 之中。

如果你寫的程式不只是單純的 C/C++ 程式,還用到了 MFC,一樣可以在 console mode 下編譯,這時候你的環境變數應該如此設定:

set PATH=C:/MSDEV/VC98/BIN;C:/MSDEV/COMMON/MSDEV98/BIN
set INCLUDE=C:/MSDEV/VC98/INCLUDE;C:/MSDEV/VC98/MFC/INCLUDE
set LIB=C:/MSDEV/VC98/LIB;C:/MSDEV/VC98/MFC/LIB

多指定了 MFC/INCLUDE 和 MFC/LIB,就可以讓編譯器和聯結器找到 MFC 的 header files 和 libraries。如果你還需要用到 ATL,就得在 INCLUDE 環境變數中再加上 C:/MSDEV/VC98/ATL/INCLUDE。

=========================================

我的VC++安裝在D:/Program Files/Microsoft Visual Studio下,所以改寫批次檔如下:

set PATH=D:/Program Files/Microsoft Visual Studio/VC98/Bin;D:/Program Files/Microsoft Visual Studio/Common/MSDev98/Bin
set INCLUDE=D:/Program Files/Microsoft Visual Studio/VC98/Include;D:/Program Files/Microsoft Visual Studio/VC98/MFC/Include
set LIB=D:/Program Files/Microsoft Visual Studio/VC98/Lib;D:/Program Files/Microsoft Visual Studio/VC98/MFC/Lib

然後運行cmd,將以上設置複製粘貼到鼠標閃爍處。如果想要確認路徑更改正確,可以鍵入set命令查看。

注:這樣的環境變量修改,僅對本次命令行窗口有效,因爲它是一個虛擬設備。如果想要每次進入時,不做這個工作。可以運行VCVARS32.BAT然後設置你的環境變量。爲了不影響VC++的原本設置方便集成環境的使用,我並沒有實際操作,一個簡單的複製粘貼也不見得麻煩。另外還可以在我的電腦-屬性-高級-環境變量裏直接修改,這個修改也是永久性的。到這裏,路徑就設置好了。下面試操作一下:

我在F:/盤保存了一個test.cpp文件作爲測試文件。文件內容如下:

=======================================
#include <iostream.h>
void main()
{
 cout<<"hello"<<endl;
}
=========================================

使用cd命令把當前命令行窗口路徑切換到F:/>,然後執行cl test.cpp命令,在F:/盤路徑下生成了兩個文件,test.obj和test.exe。然後再運行test.exe,就可以看到結果了(輸出hello)。

下面再舉一個例子,也就是下面要學習的makefile文件。測試文件名爲mypath.mak(你可以任意取名),依舊存儲在當前路徑下。

===========================
all:  
  @echo   $(PATH)
==========================

運行命令nmake mypath.mak,你可以看到輸出結果爲你剛纔設置過的路徑(我的結果是D:/Program Files/Microsoft Visual Studio/VC98/Bin;D:/Program Files/Microsoft Visual Studio/Common/MSDev98/Bin)。

2.產生自己的makefile文件

下面是我在網上找到的一個簡單的例子及其解說

==================================
下面來看一個簡單的例子(以下內容均以Win32平臺爲例):  
  文件名:makefile  
  1.   #   makefile  
  2.   #   this   is   a   example   of   make   file  
  3.   all:a1   a2  
  4.       @echo   this   is   all!  
  5.   a1:  
  6.       @echo   this   is   a1!  
  7.   a2:  
  8.       @echo   this   is   a2!  
   
  運行make後,結果如下:  
  this   is   a1!  
  this   is   a2!  
  this   is   all!  
   
 現在讓我們來分析一下這個簡單的規則文件。
  
       第1、2行不用說,一眼就可以看出是註釋。在Make規則文件中,註釋是以“#”開始,是行註釋,和C++中的“//”功能一樣。不過你可不能把它放到其它的語句之後,否則就錯了。第3行就是規則開始了!all:a1   a2一行中,規則的名字就是all,它通常是目標名(target)。一條規則可以有不止一個名字,像這一行,你也可以把它寫成all   all2:a1   a2。這時,規則就有了兩個名稱—all和all2。當然,還可以有更多,都看你自己。後面的5、7兩行也分別是兩條規則的起始。在“:”之後的,就是依賴項。在這一行裏,依賴項有兩個,分別是a1和a2。這些依賴項可以是其它的規則名(目標名),也可以是文件名。依賴和目標之間的關係就是“依賴關係”。一條規則中,可以有零個(像後面的兩條規則)、一個或多個依賴。第4行@echo   this   is   all!是命令行。它是執行all規則時要執行的命令。要注意的是,一條規則內的命令要以tab爲一行的起始,以表示命令是屬於一個規則。一條規則也可以有多條命令,每條命令佔一行(要以tab開頭)。至於可以使用哪些命令,這完全取決於你使用的OS和SHELL。
 
        當執行make時,它會找到第一條規則。然後,make就會檢查依賴和目標之間的關係。如果目標比依賴舊,就執行規則,以更新目標。執行完規則就結束。如何判定目標和依賴的新舊呢?如果目標(文件)不存在,目標的時間就爲0;如果目標(文件存在),目標的時間就爲文件的修改時間。如果依賴項是一條規則,就執行依賴的規則(這裏是一個遞歸),然後依賴的時間就是當前最新時間;如果是一個存在的文件,就爲文件的修改時間,否則就報錯。之後,就可以比較目標和依賴之間的關係。不過,有一點特殊的是,在沒有依賴項時,依賴的時間爲1。
 
        在這個例子中,make先找到規則“all”,發現目標不存在,所以目標的時間爲0;然後在查找依賴“a1”,結果“a1”不存在;於是,執行規則“a1”。“a1”不存在,所以它的時間爲0,而“a1”沒有依賴,它的依賴時間爲1;1>0,所以,執行規則“a1”。然後返回規則“all”,再檢查依賴“a2”。“a2”執行過程同“a1”。這時,“all”的目標時間爲0,依賴時間爲最新時間。於是,執行規則“all”的命令。
 
       當然,大家也可以指定一條規則讓make執行,比如:make   a1這個命令就是告訴make程序不去找第一條規則,而是規則“a1”來執行。並且我們還可以一次執行多條規則,比如:執行make   a1   a2就會連續執行“a1”、“a2”兩條規則。  

      OK,雖然講得很混亂,但也費了我半天的力氣。大家應該有一點了解make規則的執行過程了吧。  
==============================================

依賴的基本寫法如下:
Target:Dependence
            Command

==============================================
Target可以是文件名。Dependence可以是其它的target名或文件名。Command就是操作系統所運行的命令行。  
  變量  
      一個make規則文件有這些內容就已經基本可以工作了。可是,當我們在編譯一個程序時,如果有些內容要反覆用到,每次都要寫一長串的話,是很麻煩的。於是,make就引入了宏這個概念(其實也可以看成簡單的腳本語言)。  
  宏變量的定義如下:  
  var1   =   this   is   a   macro   demo!  
  var1就是變量名,它的值就是“this   is   a   macro   demo!”  
  如果我們要使用這個變量的值,那只有通過$這個運算符才行—$(var1)代表的就是“this   is   a   macro   demo!”。  
  如下makefile  
  1.   var1   =   this   is   a   macro   demo!  
  2.   all:  
  3.       @echo   $(var1)  
  結果輸出:  
  this   is   a   macro   demo!  
  用戶在執行命令行時也可以定義宏變量。其形式如下:  
  make   all   var1=”this   is   a   test”  
  執行結果爲:  
  this   is   a   test  
  我們不僅可以使用自定義變量,還可以通過這種方式使用系統環境變量。這樣可以大大方便我們建議靈活的規則。如下makefile  
  1.   all:  
  2.       @echo   $(WINDIR)  
  執行結果爲:  
  C:/WINDOWS
  (注意:makefile中的變量是大小寫敏感的)  
==========================================

下面介紹makefile文件的一些內置變量:

==========================================
  $@代表規則中的目標名(也就是規則名)。  
  $<代表規則中的依賴項目。注意,它只代表規則所有依賴項目中的第一項!  
  其它還有:  
  $^代表規則中所有的依賴項目。  
  $?代表規則中時間新於目標的依賴項目。  
  不僅如此,還可以給這些特殊的變量加一些限制。  
  如:  
  在規則  
  debug/out.exe   :   out.obj  
  中,$@代表debug/out.exe,而$(@D)代表目錄名debug,$(@F)代表文件名out.exe。其它如$(<D)、$(<F)、$(^D)、$(^F)、$(?D)、$(?F)與此類似。  
=============================================

舉個例子說明,下面是原始makefile文件內容:

==============================================
myprog : foo.o bar.o
     gcc foo.o bar.o -o myprog
    
    foo.o : foo.c foo.h bar.h
     gcc -c foo.c -o foo.o
    
    bar.o : bar.c bar.h
     gcc -c bar.c -o bar.o
==============================================

用變量代替後的文件內容:

==============================================
OBJS = foo.o bar.o
    CC = gcc
    CFLAGS = -Wall -O -g
    
    myprog : $(OBJS)
     $(CC) $^ -o $@
    
    foo.o : foo.c foo.h bar.h
     $(CC) $(CFLAGS) -c $<-o $@
    
    bar.o : bar.c bar.h
     $(CC) $(CFLAGS) -c $<-o $@
==============================================

更多見
http://www.china-askpro.com/msg22/qa83.shtml

 

下面是另外一篇文章,主要是講解一些語法規則

●註釋和換行
① makefile 文件註釋以“#”開頭,一直到行尾的字符
② 當一行的內容過長的時候,可以用換行符“/”來繼續。※在使用換行符的時候在“/” 後面不要再加上其他任何字符,包括註釋和空格。因爲只有“/”出現在一行的最後的 時候,nmake才把它當成換行符來解釋。
●宏定義(也可稱之爲變量定義)
① 宏定義的語法如下:
        變量名=變量內容
② 宏的引用
在引用宏時只需在變量前加“$”,被引用的變量名要加圓括號,如下:
        #定義
        EXE = Test.exe
        #引用
        $(EXE)
③ 用宏定義的好處不言而喻,但最大的好處是是可以直接在命令行中用新的宏定義覆蓋:
比如在原makefile中有如下宏定義:
        #編譯選項
        ML_FLAG =  /c  /coff
        #在命令行中輸入:
        nmake ML_FLAG=" /c  /coff  /F1"
這裏有必要解釋一下,此處“覆蓋”的意思並不是執行本條命令後makefile文件中宏ML_FLAG的值被物理的改變成“ /c  /coff  /F1”,執行本命令後查看makefile文件,發現宏ML_FLAG的值仍爲“/c  /coff ”。此處“覆蓋”的真正含義是:僅在執行本條命令遇到makefile文件中宏ML_FLAG時,以“ /c  /coff  /F1”來替換執行。
④ 不過關於上述③的使用中有兩點需要注意:
        ■宏名稱要區分大小寫,ML_FLAG與ml_flag是不一樣的,一般習慣用大寫
        ■定義值中有空格的時候要用雙引號引起來(沒有空格時可以不用雙引號。(makfile中的宏定義不用遵守這種規則))
        這使臨時使用不同的參數編譯文件時可以不必修改makefile
●顯示規則
① makefile中包含有一些規則,這些規則定義了文件之間的依賴關係和產生命令,一個規則的格式是這樣的:

        目標文件:依賴文件;命令 (方法一)
        或
        目標文件:依賴文件  (方法二)
       (Tab)命令
  說明:
        ■規則可以用這兩種方法,用方法二的時候,命令可以從第二行開始,第一行的“;”省略,但是這時命令前面必須有一個“Tab”字符,否則nmake無法區分這究竟是命  令還是別的定義。
        ■在同一個規則中目標文件可以有多個,依賴文件也可以有多個,同時命令也可以由多個命令行組成,當然這時候就必須用第二種方法定義了,否則無法在同一行中寫入多   條命令。
        ■nmake默認將整個描述文件的第一條規則中的目標文件認爲是最終文件,所以我們必須把最終需要生成的文件放在第一條規則定義
  (當用戶要求nmake去建造一個目標時,make會去找到這個目標的依賴規則,這時規則中定義的命令並不會立刻被執行,而是首先要做一些事情:▲nmake首先去檢查依賴文件是否是另一條規則的目標文件,如果是,則先處理這一條規則,依次類推;▲如果不是,nmake再檢查各個依賴文件的時間,看這些文件有沒有比目標文件更新的,如果沒有,nmake會決定不再建造目標文件,並給出提示:‘xxx文件’is up-to-date,如果依賴文件有比目標文件更新的,才執行命令、生成目標文件。一旦放在第一條的目標文件生成,如果還有其它尚未生成的目標文件沒有生成的話,nmake並不去理會,這就說明了爲什麼我們要把我們想要生成的最終目標文件放在第一條的原因。)
        ■目標也可以沒有依賴文件,而且目標也可以不是一個真正的文件,比如
……
              Clean:
              (Tab)del *.obj
              del *.res
  Clean是一個目標,但我們並不是要生成一個clean文件,而是希望在文件調試完畢後用nmake來清除臨時文件,當我們鍵入nmake clean的時候,工作目錄下並沒有clean   文件,那麼nmake就會去執行clean定義中的命令,因爲nmake把每一個不存在的目   標當做是一個過時(out-of-date)的目標(需要重新生成),如此一來,就會執行clean   定義中的命令,如此一來,就會刪除中間過程中的文件*.obj和*.res
               ■指出了目標文件全名的規則稱爲顯示規則,便有些類別的文件的編譯方法可以是雷同的,如從asm文件產生obj文件的命令總是用ml,從rc文件產生res文件的命令總   是用rc ,對於每個文件都寫一條規則有些多餘,這時候就要用到隱含規則。

●隱含規則
① 隱含規則可以爲某一類的文件指出建立的命令,它具體定義瞭如何將帶一個特定擴展名的文件轉換成具有另一種擴展名的文件,定義的格式是:
             .源擴展名.目標擴展名:;命令(方法一)
             或
             .源擴展名.目標擴展名: (方法二)
            (Tab)命令
② 隱含規則不能有依賴文件,所以“:”下面沒有內容。
             .asm.obj:
             Ml $(ML_FLAG) $<
             .rc.res:
             rc $<

隱含規則中無法指定確定的輸入文件名,因爲輸入文件名是泛指的有相同擴展名的一整類文件,這時候就要用到幾個特殊的內定宏來指定文件名:
            ▲ $@-----全路徑的目標文件。
            ▲ $*-----除去擴展名的全路徑的目標文件。
            ▲ $?-----所有源文件名。
            ▲ $<-----源文件名(只能用在隱含規則中)
所以上述的rc $<用於x.rc時就是 rc x.rc ,而用於 y.rc 時就是 rc y.rc 了。

發佈了9 篇原創文章 · 獲贊 33 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章