1. 程式人生 > >Makefile詳解——從入門到精通

Makefile詳解——從入門到精通

--------------------------------------------------------------------------------------------------

寫的相當不錯很詳細,只是有點多,當作一個參考手冊非常不錯,所以轉過來,順便整理一下。


make命令

make -h 幫助。更詳細幫助可以檢視 man make

[email protected]:~# make -h
Usage: make [options] [target] ...
Options:
  -b, -m                      Ignored for compatibility.
  -B, --always-make           Unconditionally make all targets.
  -C DIRECTORY, --directory=DIRECTORY
                              Change to DIRECTORY before doing anything.
  -d                          Print lots of debugging information.
  --debug[=FLAGS]             Print various types of debugging information.
  -e, --environment-overrides
                              Environment variables override makefiles.
  --eval=STRING               Evaluate STRING as a makefile statement.
  -f FILE, --file=FILE, --makefile=FILE
                              Read FILE as a makefile.
  -h, --help                  Print this message and exit.
  -i, --ignore-errors         Ignore errors from recipes.
  -I DIRECTORY, --include-dir=DIRECTORY
                              Search DIRECTORY for included makefiles.
  -j [N], --jobs[=N]          Allow N jobs at once; infinite jobs with no arg.
  -k, --keep-going            Keep going when some targets can't be made.
  -l [N], --load-average[=N], --max-load[=N]
                              Don't start multiple jobs unless load is below N.
  -L, --check-symlink-times   Use the latest mtime between symlinks and target.
  -n, --just-print, --dry-run, --recon
                              Don't actually run any recipe; just print them.
  -o FILE, --old-file=FILE, --assume-old=FILE
                              Consider FILE to be very old and don't remake it.
  -O[TYPE], --output-sync[=TYPE]
                              Synchronize output of parallel jobs by TYPE.
  -p, --print-data-base       Print make's internal database.
  -q, --question              Run no recipe; exit status says if up to date.
  -r, --no-builtin-rules      Disable the built-in implicit rules.
  -R, --no-builtin-variables  Disable the built-in variable settings.
  -s, --silent, --quiet       Don't echo recipes.
  -S, --no-keep-going, --stop
                              Turns off -k.
  -t, --touch                 Touch targets instead of remaking them.
  --trace                     Print tracing information.
  -v, --version               Print the version number of make and exit.
  -w, --print-directory       Print the current directory.
  --no-print-directory        Turn off -w, even if it was turned on implicitly.
  -W FILE, --what-if=FILE, --new-file=FILE, --assume-new=FILE
                              Consider FILE to be infinitely new.
  --warn-undefined-variables  Warn when an undefined variable is referenced.

make命令格式:make [-f Makefile] [option] [target]

make是一個解釋makefile中指令的命令工具。Make工具最主要也是最基本的功能就是通過makefile檔案來描述源程式之間的相互關係並自動維護編譯工作。而makefile 檔案需要按照某種語法進行編寫,檔案中需要說明如何編譯各個原始檔並連線生成可執行檔案,並要求定義原始檔之間的依賴關係。

編譯工具make、gmake、cmake、nmake和Dmake的區別

1. gamke, make, nmake, dmake, cmake 異同


現有編譯工具
--------------------------------------------------------------------
gmake是GNU Make的縮寫。
Linux系統環境下的make就是GNU Make,之所以有gmake,是因為在別的平臺上,make一般被佔用,GNU make只好叫gmake了。
比如在安裝二進位制檔案進行編譯時要使用make命令,但如果在Solaris或其他非GNU系統中執行,必須使用GNU make,而不是使用系統自帶的make版本,這時要用gmake代替make進行編譯。
Solaris包含兩種make工具:GCC (GNU Compiler Collection)工具套裝包括一個 C 編譯器和一個 C++ 編譯器,Solaris自身的 make 實用程式。

freebsd自帶的make是BSD make。兩者的不同在於Makefile部分不相容。即使是部分,有時候也不能完全把軟體給make出來。

另外,windows下自帶的make叫做nmake喲。(http://wtqy.blogbus.com/logs/40756172.html)


跨平臺的編譯工具

--------------------------------------------------------------------

        Dmake是同GNU Make類似的一個工具。其命令格式自成一體,但是可以適用於Linux, Solaris, and Win32 and other platforms。Dmake有一個變種,被OpenOffice.org使用。dmake 是一個命令列工具,與 make(1) 相容。dmake 能夠以網格、分佈、並行或序列模式生成目標。如果使用的是標準 make(1) 實用程式,在對 makefile 進行任何更改時可以毫不費力地過渡到使用 dmake。dmake 是 make 實用程式的超集。
        跨平臺的編譯工具,其中最有名的兩個是 cmake 和 scons,cmake 之所以出名估計是因為 KDE 4 從 autotools 轉向用 cmake 來編譯。而 scons 則是 lighttpd 原來用的編譯工具,它現在也轉向 Python 了。
        cmake 和 python 大概代表了新一代跨平臺編譯工具的兩種方向。第一種 (cmake) 是延續並改良傳統 automake, autoconf 工具鏈,將之合為一體,但最終仍然生成 Makefile, Visual Studio 的 .sln,Xcode 的 .xcodebuild 檔案,依賴現有編譯工具 (make, nmake, vcbuild, xcodebuild) 來編譯;第二種則是完全消除現有編譯工具的呼叫,直接呼叫編譯器,scons 就屬於這一類 (scons 還有一個特點是完全不用專門的語言,控制編譯的指令碼就是 Python)。
        從人氣上 來說,反倒是走改良路線的 cmake 比 scons 好一些,有幾個原因:scons 基於 Python,可能有些程式碼不是很照顧速度,於是類似 KDE 這樣的大專案編譯起來會很慢;scons 開發比較慢,最近一直只是 bugfix。不過相對 cmake,scons 的優點是文件非常細緻可讀,而 cmake 的文件則非常少,可以在網上找到的只有幾篇介紹性的文章和參考手冊,不像 scons 有一本 User Guide。
        與之相關的工具還有 Jam (包括它的變體 FTJam, Boost.Build), Waf, Bakefile 等。其中比較新的 Waf 是一個 scons 的改進,在它的提供的 benchmark 中,顯示通過快取方式可以大大改進編譯的速度。不過因為這個專案還很新,目前沒有什麼軟體用它作為編譯系統。
        Bakefile 走的則是 cmake 的路子,從名稱上也可以看出,它最終也是通過生成 Makefile 一類的檔案來完成編譯的。不過不同的地方在於 cmake 用的語法很像 autotools 用的 m4 的傳統語法,而 bakefile 則完全用 XML 來定義編譯規則了,這一點倒很像 ant。Bakefile 倒是有不少著名的專案使用,比如 wxWidgets, WebKit, VCF, libxml。

2. 交叉編譯器製作流程

"交叉編譯器”(cross compiler),用作跨平臺來編譯程式!做交叉編譯器要弄清楚3個概念:host, build, target:
build -- 你在什麼平臺上編譯的這個編譯器
host -- 這個編譯器將來要在什麼平臺上執行
target -- 編譯器最終會生成在哪個平臺上執行的可執行程式碼
詳見:http://blog.csdn.net/colin719/article/details/758000

3. CMake(http://www.cmake.org/)用法

CMake 是個開源的跨平臺自動化建構系統,它用組態檔控制建構過程(build process)的方式和 Unix 的 Make 相似,只是 CMake 的組態檔取名為 CmakeLists.txt。Cmake 並不直接建構出最終的軟體,而是產生標準的建構檔(如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces),然後再依一般的建構方式使用。這使得熟悉某個整合開發環境(IDE)的開發者可以用標準的方式建構他的軟體,這種可以使用各平臺的原生建構系統的能力是 CMake 和 SCons 等其他類似系統的區別之處。CMake 可以編譯原始碼、製做程式庫、產生介面卡(wrapper)、還可以用任意的順序建構執行檔。CMake 支援 in-place 建構(二進檔和原始碼在同一個目錄樹中)和 out-of-place 建構(二進檔在別的目錄裡),因此可以很容易從同一個原始碼目錄樹中建構出多個二進檔。CMake 也支援靜態與動態程式庫的建構。
“CMake”這個名字是 "cross platform make" 的縮寫。雖然名字中含有 "make",但是 CMake 和 Unix 上常見的“make”系統是分開的,而且更為高階。

詳見:
http://zh.wikipedia.org/wiki/CMake
http://blog.csdn.net/dbzhang800/article/details/6314073 (cmake 學習筆記)
http://digdeeply.info/archives/0421949.html (cmake命令 安裝、用法簡介)
http://sinojelly.blog.51cto.com/479153/319173 (CMake入門指南)
http://hi.baidu.com/black/item/18af7d4423de2b2111ee1e26(cmake 簡介CMake 使用方法)

4.cmake的大致介紹

大家都知道,寫程式大體步驟為:
        1.用編輯器編寫原始碼,如.c檔案。
2.用編譯器編譯程式碼生成目標檔案,如.o。
3.用連結器連線目的碼生成可執行檔案,如.exe。
但如果原始檔太多,一個一個編譯時就會特別麻煩,於是人們想到,為什麼不設計一種類似批處理的程式,來批處理編譯原始檔呢,於是就有了make工具,它是一個自動化編譯工具,你可以使用一條命令實現完全編譯。但是你需要編寫一個規則檔案,make依據它來批處理編譯,這個檔案就是makefile,所以編寫makefile檔案也是一個程式設計師所必備的技能。

對於一個大工程,編寫makefile實在是件複雜的事,於是人們又想,為什麼不設計一個工具,讀入所有原始檔之後,自動生成makefile呢,於是就出現了cmake工具,它能夠輸出各種各樣的makefile或者project檔案,從而幫助程式設計師減輕負擔。但是隨之而來也就是編寫cmakelist檔案,它是cmake所依據的規則。所以在程式設計的世界裡沒有捷徑可走,還是要腳踏實地的。

概述

什麼是makefile?或許很多Winodws的程式設計師都不知道這個東西,因為那些Windows的IDE都為你做了這個工作,但我覺得要作一個好的和professional的程式設計師,makefile還是要懂。這就好像現在有這麼多的HTML的編輯器,但如果你想成為一個專業人士,你還是要了解HTML的標識的含義。特別在Unix下的軟體編譯,你就不能不自己寫makefile了,會不會寫makefile,從一個側面說明了一個人是否具備完成大型工程的能力。

因為,makefile關係到了整個工程的編譯規則。一個工程中的原始檔不計數,其按型別、功能、模組分別放在若干個目錄中,makefile定義了一系列的規則來指定,哪些檔案需要先編譯,哪些檔案需要後編譯,哪些檔案需要重新編譯,甚至於進行更復雜的功能操作,因為makefile就像一個Shell指令碼一樣,其中也可以執行作業系統的命令。

makefile帶來的好處就是——“自動化編譯”,一旦寫好,只需要一個make命令,整個工程完全自動編譯,極大的提高了軟體開發的效率。make是一個命令工具,是一個解釋makefile中指令的命令工具,一般來說,大多數的IDE都有這個命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可見,makefile都成為了一種在工程方面的編譯方法。

現在講述如何寫makefile的文章比較少,這是我想寫這篇文章的原因。當然,不同產商的make各不相同,也有不同的語法,但其本質都是在“檔案依賴性”上做文章,這裡,我僅對GNU的make進行講述,我的環境是RedHat Linux 8.0,make的版本是3.80。必竟,這個make是應用最為廣泛的,也是用得最多的。而且其還是最遵循於IEEE 1003.2-1992 標準的(POSIX.2)。

在這篇文件中,將以C/C++的原始碼作為我們基礎,所以必然涉及一些關於C/C++的編譯的知識,相關於這方面的內容,還請各位檢視相關的編譯器的文件。這裡所預設的編譯器是UNIX下的GCC和CC。

程式的編譯和連結

在此,我想多說關於程式編譯的一些規範和方法,一般來說,無論是C、C++、還是pas,首先要把原始檔編譯成 中間程式碼檔案,在Windows下也就是 .obj 檔案,UNIX下是 .o 檔案,即 Object File,這個動作叫做 編譯(compile)。然後再把大量的Object File合成執行檔案,這個動作叫作 連結(link)

編譯時,編譯器需要的是語法的正確,函式與變數的宣告的正確。對於後者,通常是你需要告訴編譯器標頭檔案的所在位置(標頭檔案中應該只是宣告,而定義應該放在C/C++檔案中),只要所有的語法正確,編譯器就可以編譯出中間目標檔案。一般來說,每個原始檔都應該對應於一箇中間目標檔案(O檔案或是OBJ檔案)。

連結時,主要是連結函式和全域性變數,所以,我們可以使用這些中間目標檔案(O檔案或是OBJ檔案)來連結我們的應用程式。連結器並不管函式所在的原始檔,只管函式的中間目標檔案(Object File),在大多數時候,由於原始檔太多,編譯生成的中間目標檔案太多,而在連結時需要明顯地指出中間目標檔名,這對於編譯很不方便,所以,我們要給中間目標檔案打個包,在Windows下這種包叫“庫檔案”(Library File),也就是 .lib 檔案,在UNIX下,是Archive File,也就是 .a 檔案。

總結一下,原始檔首先會生成中間目標檔案,再由中間目標檔案生成執行檔案。在編譯時,編譯器只檢測程式語法,和函式、變數是否被宣告。如果函式未被宣告,編譯器會給出一個警告,但可以生成Object File。而在連結程式時,連結器會在所有的Object File中找尋函式的實現,如果找不到,那到就會報連結錯誤碼(Linker Error),在VC下,這種錯誤一般是:Link 2001錯誤,意思說是說,連結器未能找到函式的實現。你需要指定函式的Object File.

好,言歸正傳,GNU的make有許多的內容,閒言少敘,還是讓我們開始吧。

Makefile 介紹

make命令執行時,需要一個 Makefile 檔案,以告訴make命令需要怎麼樣的去編譯和連結程式。

首先,我們用一個示例來說明Makefile的書寫規則。以便給大家一個感興認識。這個示例來源於GNU的make使用手冊,在這個示例中,我們的工程有8個C檔案,和3個頭檔案,我們要寫一個Makefile來告訴make命令如何編譯和連結這幾個檔案。我們的規則是:

  • 1)如果這個工程沒有編譯過,那麼我們的所有C檔案都要編譯並被連結。 
  • 2)如果這個工程的某幾個C檔案被修改,那麼我們只編譯被修改的C檔案,並連結目標程式。 
  • 3)如果這個工程的標頭檔案被改變了,那麼我們需要編譯引用了這幾個標頭檔案的C檔案,並連結目標程式。 
只要我們的Makefile寫得夠好,所有的這一切,我們只用一個make命令就可以完成,make命令會自動智慧地根據當前的檔案修改的情況來確定哪些檔案需要重編譯,從而自己編譯所需要的檔案和連結目標程式。

一、Makefile的規則

規則的格式:

targetsprerequisites
    command
    command

    ......

目標依賴

    命令
    命令

    ......

+++++命令需要以[TAB]鍵開始++++

在講述這個Makefile之前,還是讓我們先來粗略地看一看Makefile的規則。 (三要素: 目標、條件、命令)

target ... : prerequisites ... 
        command 
        ... 
        ... 

makefile的構成
(1)target 也就是一個目標檔案,需要由make工具建立的目標體(target)。可以是Object File,也可以是執行檔案。還可以是一個標籤(Label),對於標籤這種特性,在後續的“偽目標”章節中會有敘述。
(2)prerequisites 就是要生成那個target所需要的檔案或是目標。即要建立的目標體所依賴的檔案(dependency_file)。
(3)command 也就是make需要執行的命令(任意的Shell命令)。即建立每個目標體時需要執行的命令(command)。

makefile 檔案其實就是一個檔案的依賴關係。也就是說,target這一個或多個的目標檔案依賴於prerequisites中的檔案,其生成規則定義在command中。說白一點就是說,prerequisites中如果有一個以上的檔案比target檔案要新的話,command所定義的命令就會被執行。這就是Makefile的規則。也就是Makefile中最核心的內容。說到底,Makefile的東西就是這樣一點,好像我的這篇文件也該結束了。呵呵。還不盡然,這是Makefile的主線和核心,但要寫好一個Makefile還不夠,我會以後面一點一點地結合我的工作經驗給你慢慢到來。內容還多著呢。:)

在書寫Makefile的時候,將單獨在一個目錄中編譯軟體,以避免生成的中間檔案散在原始檔中,亂花了眼。vpath(VPATH)可以指定搜尋目錄。auto tools在編譯樹中生成Makefile時就使用了vpath命令,來達到前面我們所說的目的。
例子,目錄結構
src
   file1.cpp file2.cpp
   Makefile
build
   Makefile

上面的結構中,src目錄結構中的Makefile可以正常編寫,如 all : file1.o file2.o。而build目錄下的Makefile則使用如下規則
VPATH = ../src
include ../src/Makefile
在build目錄下,進行make的時候,生成的中間檔案就放在build目錄下了。是不是很簡單呢。

vpath與VPATH的區別在於VPATH指定全域性的搜尋路徑,而vpath可以針對特定的檔案搜尋路徑。

vpath命令有三種形式:
        vpath pattern path : 為符合模式<pattern>的檔案指定搜尋目錄<directories>
vpath pattern         : 清除符合模式<pattern>的檔案的搜尋目錄。
vpath                    : 清除所有已被設定好了的檔案搜尋目錄。

        <pattern>需要包含“%”字元。“%”的意思是匹配零或若干字元,例如,“%.h”表示所有以“.h”結尾的檔案。<pattern>指定了要搜尋的檔案集,而<directories>則指定了<pattern>的檔案集的搜尋的目錄。
例如:vpath %.h ../headers        #該語句表示,要求make 在“../headers”目錄下搜尋所有以“.h”結尾的檔案。

因為Makefile、hello.h hello.cpp main.cpp沒有在同一個路徑,所以要考慮路徑的問題。同時,路徑也有兩種,一種是針對Makefile來說在執行make命令的時候,要尋找目標檔案和依賴檔案的路徑。另一個就是原始檔所要包含的標頭檔案等相關檔案的路徑。對於第一種來說,Makefile 提供了兩種方式,一種是設定全域性訪問路徑VAPTH:即在執行make命令時可以從該路徑中查詢目標和依賴make可識別一個特殊變數“VPATH”。通過變數“VPATH”可以指定依賴檔案的搜尋路徑,在規則的依賴檔案在當前目錄不存在時,make會在此變數所指定的目錄下去尋找這些依賴檔案。一般我們都是用此變數來說明規則中的依賴檔案的搜尋路徑。

首先說明一下makefile的執行步驟:
        1、讀入所有的Makefile。
        2、讀入被include的其它Makefile。
        3、初始化檔案中的變數。
        4、推導隱晦規則,並分析所有規則。
        5、為所有的目標檔案建立依賴關係鏈。
        6、根據依賴關係,決定哪些目標要重新生成。
        7、執行生成命令

Makefile中所有檔案的搜尋路徑,包括依賴檔案和目標檔案。 
變數“VPATH”的定義中,使用空格或者冒號(:)將多個目錄分開。make 搜尋的目錄順序按照變數“VPATH”定義中順序進行(當前目錄永遠是第一搜索目錄)。
例如:
VPATH = src:../headers
它指定了兩個搜尋目錄,“src”和“../headers”。對於規則“foo:foo.c”如果“foo.c”在“src”目錄下,此時此規則等價於“foo:src:/foo.c”

對於第二種來說:當需要為不型別的檔案指定不同的搜尋目錄時需要這種方式

vpath:關鍵字
它所實現的功能和上一小節提到的“VPATH”變數很類似,但是它更為靈活。它可以為不同型別的檔案(由檔名區分)指定不同的搜尋目錄。

它的使用方法有三種
1、vpath PATTERN DIRECTORIES 
        為符合模式“PATTERN”的檔案指定搜尋目錄“DIRECTORIES”。多個目錄使用空格或者冒號(:)分開。類似上一小節的“VPATH” 
2、vpath PATTERN 
        清除之前為符合模式“PATTERN”的檔案設定的搜尋路徑
3、vpath
        清除所有已被設定的檔案搜尋路徑。
對於vpath的詳細說明待續。

在執行make命令的時候,根據makefile執行步驟,首先讀入所有的makefile檔案,那麼
VPATH = include:src       //指定了makefile的搜尋路徑
或者
vpath %.h include    //指定.h型別檔案的搜尋路徑是include
vpath %.cpp src      //指定.cpp型別檔案的搜尋路徑是src
這僅僅是對於makefile來說搜尋目標和依賴檔案的路徑,但是對於命令列來說是無效的,也就是說在執行g++或者gcc時不會自動從VPATH 或者vpath中自動搜尋要包含的標頭檔案等資訊檔案。此時要用到了 -I 或者--incude +路徑
例如依賴是:
main.o:main.cpp hello.h
即 g++  -c  $<  -Iinclude,這時候,g++會自動從include目錄中搜索要包含的hello.h標頭檔案

*************(命令以Tab開頭)*************

1)所有檔案都在一個目錄中
示例
st_work : main.o  st_work.o  fun.o
    gcc  main.o  st_work.o  fun.o  -o  st_work main 
st_work.o : st_work.c
    gcc  -c st_work.c  -o st_work
main.o : main.c  st_work.h
    gcc -c main.c -o  main.o
fun.o : fun.c fun.h
    gcc -c  fun.c -o fun.o
clean:
    rm -f st_work *.o


2)多目錄的寫法
我們這裡,在工作目錄下有4個資料夾
分別是 sources(原始檔) obj(中間檔案) headers(標頭檔案) bin(目標檔案)
sources  裡面有 main.c  st_work.c fun.c
obj      裡面最初沒有檔案
headers  裡面有 fun.h st_work.h
最終目標取名為 st_work,它應存放到bin裡面

預備知識:
gcc 的3個引數:
1. -o 指定目標檔案
    gcc sources/main.c -o bin/main
2. -c 編譯的時候只生產目標檔案不連結
    gcc -c sources/main.c -o obj/main.o
3. -I 主要指定標頭檔案的搜尋路徑
    gcc -I headers -c main.c -o main.o
4. -l 指定靜態庫
    gcc -lpthread ...

示例(********命令以Tab開頭********)
bin/st_work : obj/main.o  obj/st_work.o  obj/fun.o  
   gcc  obj/main.o obj/st_work.o  obj/fun.o  -o bin/st_work  
obj/st_work.o : sources/st_work.c
    gcc  -I  headers -c sources/st_work.c  -o  obj/st_work.o
obj/main.o : sources/main.c
    gcc  -I  headers -c sources/main.c    -o  obj/main.o
obj/fun.o  : sources/fun.c
    gcc  -I  headers -c sources/fun.c     -o  obj/fun.o
clean:
    rm -f bin/st_work obj/*.o

3)隱式規則的引入
    3個預定義變數介紹:
        1.  [email protected]     表示要生成的目標
        2.  $^     表示全部的依賴檔案
        3.  $<     表示第一個依賴檔案

bin/st_work : obj/main.o  obj/st_work.o  obj/fun.o  
    gcc  $^  -o [email protected]
obj/st_work.o : sources/st_work.c
    gcc  -I  headers   -c $< -o  [email protected]
obj/main.o : sources/main.c
    gcc  -I  headers   -c $< -o  [email protected]
obj/fun.o  : sources/fun.c
    gcc  -I  headers   -c $< -o  [email protected]
clean:
    rm -f bin/st_work obj/*.o

4)變數的引入
變數的引入和應用:

CC=gcc -Wall
HEAD=-I headers  # 標頭檔案目錄
SRC=-c $<        # 第一個依賴檔案
OBJ=-o [email protected]        # 目標檔案

bin/st_work : obj/main.o  obj/st_work.o  obj/fun.o
    gcc  $^  -o [email protected]
obj/st_work.o : sources/st_work.c
    $(CC) $(HEAD) $(SRC) $(OBJ)
obj/main.o : sources/main.c
    $(CC) $(HEAD) $(SRC) $(OBJ)
obj/fun.o  : sources/fun.c
    $(CC) $(HEAD) $(SRC) $(OBJ)
clean:
    rm -f bin/st_work file_o/*.o
可以看到,依賴關係由上到下。

More:
(1)如此定義變數後,
    objects = main.o,
    foo = $(bar)
    使用$(object)來代替變數的值,用“$$”來表示$。
(2)變數替換
    其格式是“$(var:a=b)”或是“${var:a=b}”,
    其意思是,把變數“var”中所有以“a”字串“結尾”的“a”替換成“b”字串。
    這裡的“結尾”意思是“空格”或是“結束符”。
    foo := a.o b.o c.o
    bar := $(foo:.o=.c)
(3)變數可以巢狀
    $($(x))
(4)可以使用“+=”操作符給變數追加值。
(5)當make巢狀呼叫時,上層Makefile中定義的變數會以系統環境變數的方式傳遞到下層的Makefile中。這裡的系統環境變數與本makefile中定義的變數的關係有點類似於全域性變數與區域性變數的關係。預設情況下,只有通過命令列設定的變數會被傳遞。而定義在檔案中的變數,如果要向下層Makefile傳遞,則需要使用exprot關鍵字來宣告。

5)資訊顯示
    在執行make時,會把所有這些資訊都輸出來。在命令前面加個@,就不會把相關資訊輸出螢幕了。

6)函式的使用
    函式呼叫後,函式的返回值可以當做變數來使用。
    呼叫格式如下所示:  $(<function> <arguments>;)
    函式呼叫以“$”開頭,以圓括號或花括號把函式名和引數括起。
    引數間以逗號“,”分隔,而函式名和引數之間以“空格”分隔。引數可以使用變數。
    bar:= $(subst $(space),$(comma),$(foo))
    上面的函式subst是把第三個引數中包含第一個引數中的字串替換成第二個引數中的字串。

7)語句的使用
    $(if <condition>;,<then-part>;) 
    $(if <condition>;,<then-part>;,<else-part>;)
    make支援三各萬用字元:“*”,“?”和“[...]”。
    波浪號“~”字元在檔名中也有比較特殊的用途。
    如果是“~/test”,這就表示當前使用者的$HOME目錄下的test目錄。
    而“~hchen/test”則表示使用者hchen的宿主目錄下的test目錄

8)、其他注意點
1. 在大多數時候,由於原始檔太多,編譯生成的中間目標檔案太多,而在連結時需要明顯地指出中間目標檔名,這對於編譯很不方便,所以,通常要給中間目標檔案打個包,在Windows 下這種包叫“庫檔案”(Library File),也就是 .lib 檔案,在UNIX 下,是Archive File,也就是 .a 檔案。編譯階段,主要解決語法問題,如函式,變數等宣告是否正確,而連結階段,因為要生成可執行檔案了,要明確具體的地址關係,及尋找真實的定義等。
2. 將長行用 “\” 分開便於閱讀。
3. 和目標沒有依賴關係的規則不會被處理,除非指定make 處理(如make clean)。
4. 將原始檔分門別類地放置時,編譯時要進行尋找。通過VPATH實現。
如果沒有指明這個變數,make 只會在當前的目錄中去找尋依賴檔案和目標檔案。如果定義了這個變數,那麼,make就會在當前目錄找不到的情況下,然後到所指定的目錄中去找尋檔案了。
VPATH = src:../headers

5. 有些情況下,則規則失效,沒有依賴檔案,檔案clean命令永遠不會執行;為避免這個問題,可使用".PHONY"指明該目標。如:
    .PHONY : clean
這樣,在make clean是會無視clean是否存在,直接執行其命令。
    與上面等效的另外一個表達:
clean: FORCE
    rm $(objects)
    FORCE:
6. 此外,還有很少使用的雙冒號規則。
7. 使用include關鍵字可以把別的Makefile包含進來。

8. objects = *.o
     萬用字元同樣可以用在變數中。並不是說[*.o]會展開,objects的值就是“*.o”。Makefile 中的變數其實就是 C/C++中的巨集。如果你要讓萬用字元在變數中展開,也就是讓 objects 的值是所有[.o]的檔名的集合,那麼,你可以這樣:objects := $(wildcard *.o),這種用法由關鍵字“wildcard”指出。
9. 通過makefile編譯,自動推導,如果發現當前的檔案沒有做過更新,則不會編譯,只編譯更新過了的。

預定義變數選項

  預定義變數  

    含義

        $* 

    不包含副檔名的目標檔名稱。

        $+ 

    所有的依賴檔案,以空格分開,並以出現的先後為序,可能包含重複的依賴檔案。

        $< 

    第一個依賴檔案的名稱。

         $? 

    所有的依賴檔案,以空格分開,這些依賴檔案的修改日期比目標的建立日期晚。

        [email protected] 

    目標的完整名稱。

        $^ 

    所有的依賴檔案,以空格分開,不包含重複的依賴檔案。

        $% 

    如果目標是歸檔成員,則該變量表示目標的歸檔成員名稱。例如,如果目標名稱為 mytarget.so(image.o),則 [email protected] 為 mytarget.so,而 $% 為 image.o。

        AR  

    歸檔維護程式的名稱,預設值為 ar。

  ARFLAGS

    歸檔維護程式的選項。

        AS 

    彙編程式的名稱,預設值為 as。

  ASFLAGS

    彙編程式的選項。

        CC 

    C 編譯器的名稱,預設值為 cc。

  CCFLAGS 

    C 編譯器的選項。

      CPP 

    C 預編譯器的名稱,預設值為 $(CC) -E。

  CPPFLAGS

    C 預編譯的選項。

        CXX 

    C++ 編譯器的名稱,預設值為 g++。

  CXXFLAGS

    C++ 編譯器的選項。


make 選項

命令列選項

含義

-C DIR

在讀取 makefile之前改變到指定的目錄DIR

-f FILE

以指定的 FILE檔案作為makefile

-h

顯示所有的 make選項。

-i

忽略所有的命令執行錯誤。

-I DIR

當包含其他 makefile檔案時,可利用該選項指定搜尋目錄。

-n

只打印要執行的命令,但不執行這些命令。

-p

顯示 make變數資料庫和隱含規則。

-s

在執行命令時不顯示命令。

-w

在處理 makefile之前和