GUN Makefile

GUN Make介紹

1 GUN Make 和 makefile簡介

在大型的開發項目中,通常有幾十到上百個的源文件,如果每次均手工鍵入 gcc 命令進行編譯的話,則會非常不方便。因此,人們通常利用 make 工具來自動完成編譯工作。這些工作包括:如果僅修改了某幾個源文件,則只重新編譯這幾個源文件;如果某個頭文件被修改了,則重新編譯所有包含該頭文件的源 文件。
利用這種自動編譯可大大簡化開發工作,避免不必要的重新編譯。
實際上,make 工具通過一個稱爲 makefile 的文件來完成並自動維護編譯工作。makefile 需要按照某種語法進行編寫,其中說明了如何編譯各個源文件並連接生成可執行文件,並定義了源文件之間的依賴關係。
當修改了其中某個源文件時,如果其他源文件依賴於該文件,則也要重新編譯所有依賴該文件的源文件。
makefile 文件是許多編譯器,包括 Windows NT 下的編譯器維護編譯信息的常用方法,只是在集成開發環境中,用戶通過友好的界面修改 makefile 文件而已。
默認情況下,GNU make 工具在當前工作目錄中按如下順序搜索 makefile:
* GNUmakefile
* makefile
* Makefile
在 UNIX 系統中,習慣使用 Makefile 作爲 makfile 文件。如果要使用其他文件作爲 makefile,則可利用類
似下面的 make 命令選項指定 makefile 文件:
$ make -f Makefile.debug

2 GNU Make 工具
~~~~~~~~~~~~~~~~
 
2.1 基本 makefile 結構
GNU Make 的主要工作是讀進一個文本文件, makefile 。這個文件裏主要是有關哪些文件(‘target’目的文件)是從哪些別的 文件(‘dependencies’依靠文件)中產生的,用什麼命令來進行這個產生過程。有了這些信息, make 會檢查磁碟上的文件,如果 目的文件的時間戳(該文件生成或被改動時的時間)比至少它的一個依靠文件舊的話, make 就執行相應的命令,以便更新目的文件。 (目的文件不一定是最後的可執行檔,它可以是任何一個文件。)
 
makefile 一般被叫做“makefile”或“Makefile”。當然你可以 在 make 的命令行指定別的文件名。如果你不特別指定,它會尋 找“makefile”或“Makefile”,因此使用這兩個名字是最簡單 的。
一個 makefile 主要含有一系列的規則,如下:


(tab)
<command>
(tab)
<command>
.
.
.


 
例如,考慮以下的 makefile :

複製代碼
=== 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
=== makefile 結束 ===
複製代碼


  
這 是一個非常基本的 makefile —— make 從最上面開始,把上面第一個目的,‘myprog’,做爲它的主要目標(一個它需要保 證其總是最新的最終目標)。給出的規則說明只要文件‘myprog’ 比文件‘foo.o’或‘bar.o’中的任何一箇舊,下一行的命令將 會被執行。
 
但 是,在檢查文件 foo.o 和 bar.o 的時間戳之前,它會往下查找那些把 foo.o 或 bar.o 做爲目標文件的規則。它找到的關於 foo.o 的規則,該文件的依靠文件是 foo.c, foo.h 和 bar.h 。 它從下面再找不到生成這些依靠文件的規則,它就開始檢查磁碟 上這些依靠文件的時間戳。如果這些文件中任何一個的時間戳比 foo.o 的新,命令 'gcc -o foo.o foo.c' 將會執行,從而更新 文件 foo.o 。
 
接下來對文件 bar.o 做類似的檢查,依靠文件在這裏是文件 bar.c 和 bar.h 。
 
現在, make 回到‘myprog’的規則。如果剛纔兩個規則中的任 何一個被執行,myprog 就需要重建(因爲其中一個 .o 檔就會比 ‘myprog’新),因此連接命令將被執行。
 
希 望到此,你可以看出使用 make 工具來建立程序的好處——前一章中所有繁瑣的檢查步驟都由 make 替你做了:檢查時間戳。 你的源碼文件裏一個簡單改變都會造成那個文件被重新編譯(因 爲 .o 文件依靠 .c 文件),進而可執行文件被重新連接(因爲 .o 文件被改變了)。其實真正的得益是在當你改變一個 header 檔的時候——你不再需要記住那個源碼文件依靠它,因爲所有的 資料都在 makefile 裏。 make 會很輕鬆的替你重新編譯所有那些因依靠這個 header 文件而改變了的源碼文件,如有需要,再 進行重新連接。
 
當然,你要確定你在 makefile 中所寫的規則是正確無誤的,只 列出那些在源碼文件中被 #include 的 header 檔……
 
2.2 編寫 make 規則 (Rules)
 
最 明顯的(也是最簡單的)編寫規則的方法是一個一個的查看源碼文件,把它們的目標文件做爲目的,而C源碼文件和被它 #include 的 header 檔做爲依靠文件。但是你也要把其它被這些 header 檔 #include 的 header 檔也列爲依靠文件,還有那些被 包括的文件所包括的文件……然後你會發現要對越來越多的文件進行管理,然後你的頭髮開始脫落,你的脾氣開始變壞,你的臉 色變成菜色,你走在路上開始跟電線杆子碰撞,終於你搗毀你的電腦顯示器,停止編程。到低有沒有些容易點兒的方法呢?
 
當然有!向編譯器 要!在編譯每一個源碼文件的時候,它實在應該知道應該包括什麼樣的 header 檔。使用 gcc 的時候,用 -M 開關,它會爲每一個你給它的C文件輸出一個規則,把目標文件做爲目的,而這個C文件和所有應該被 #include 的 header 文 件將做爲依靠文件。注意這個規則會加入所有 header 文件,包括被角括號(`<', `>')和雙引號(`"')所包圍的文件。其實我們可以 相當肯定系統 header 檔(比如 stdio.h, stdlib.h 等等)不會 被我們更改,如果你用 -MM 來代替 -M 傳遞給 gcc,那些用角括 號包圍的 header 檔將不會被包括。(這會節省一些編譯時間)
 
由 gcc 輸出的規則不會含有命令部分;你可以自己寫入你的命令 或者什麼也不寫,而讓 make 使用它的隱含的規則(參考下面的 2.4 節)。
 
2.3 Makefile 變量
 
上面提到 makefiles 裏主要包含一些規則。它們包含的其它的東 西是變量定義。
 
makefile 裏的變量就像一個環境變量(environment variable)。 事實上,環境變量在 make 過程中被解釋成 make 的變量。這些變量是大小寫敏感的,一般使用大寫字母。它們可以從幾乎任何 地方被引用,也可以被用來做很多事情,比如:
i) 貯存一個文件名列表。在上面的例子裏,生成可執行文件的 規則包含一些目標文件名做爲依靠。在這個規則的命令行 裏同樣的那些文件被輸送給 gcc 做爲命令參數。如果在這 裏使用一個變數來貯存所有的目標文件名,加入新的目標 文件會變的簡單而且較不易出錯。
 
ii) 貯存可執行文件名。如果你的項目被用在一個非 gcc 的系 統裏,或者如果你想使用一個不同的編譯器,你必須將所 有使用編譯器的地方改成用新的編譯器名。但是如果使用一 個變量來代替編譯器名,那麼你只需要改變一個地方,其 它所有地方的命令名就都改變了。
 
iii) 貯存編譯器旗標。假設你想給你所有的編譯命令傳遞一組 相同的選項(例如 -Wall -O -g);如果你把這組選項存 入一個變量,那麼你可以把這個變量放在所有呼叫編譯器 的地方。而當你要改變選項的時候,你只需在一個地方改 變這個變量的內容。
要設定一個變量,你只要在一行的開始寫下這個變量的名字,後 面跟一個 = 號,後面跟你要設定的這個變量的值。以後你要引用 這個變量,寫一個 $ 符號,後面是圍在括號裏的變量名。比如在 下面,我們把前面的 makefile 利用變量重寫一遍:
 

複製代碼
=== makefile 開始 ===
OBJS 
= foo.o bar.o
CC 
= gcc
CFLAGS 
= -Wall --g
 
myprog : $(OBJS)
 $(CC) $(OBJS) 
-o myprog
 
foo.o : foo.c foo.h bar.h
 $(CC) $(CFLAGS) 
-c foo.c -o foo.o
 
bar.o : bar.c bar.h
 $(CC) $(CFLAGS) 
-c bar.c -o bar.o
=== makefile 結束 ===
複製代碼


 
還 有一些設定好的內部變量,它們根據每一個規則內容定義。三個 比較有用的變量是 $@, $< 和 $^ (這些變量不需要括號括住)。 $@ 擴展成當前規則的目的文件名, $< 擴展成依靠列表中的第一個依靠文件,而 $^ 擴展成整個依靠的列表(除掉了裏面所有重 復的文件名)。利用這些變量,我們可以把上面的 makefile 寫成:
 

複製代碼
=== makefile 開始 ===
OBJS 
= foo.o bar.o
CC 
= gcc
CFLAGS 
= -Wall --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 $@
=== makefile 結束 ===
複製代碼


 
你可以用變量做許多其它的事情,特別是當你把它們和函數混合 使用的時候。如果需要更進一步的瞭解,請參考 GNU Make 手冊。 ('man make', 'man makefile')
 
2.4 隱含規則 (Implicit Rules)
 
請 注意,在上面的例子裏,幾個產生 .o 文件的命令都是一樣的。 都是從 .c 文件和相關文件裏產生 .o 文件,這是一個標準的步 驟。其實 make 已經知道怎麼做——它有一些叫做隱含規則的內置的規則,這些規則告訴它當你沒有給出某些命令的時候,應該 怎麼辦。
 
如果你把 生成 foo.o 和 bar.o 的命令從它們的規則中刪除, make 將會查找它的隱含規則,然後會找到一個適當的命令。它的命令會 使用一些變量,因此你可以按照你的想法來設定它:它使用變量 CC 做爲編譯器(象我們在前面的例子),並且傳遞變量 CFLAGS (給 C 編譯器,C++ 編譯器用 CXXFLAGS ),CPPFLAGS ( C 預 處理器旗標), TARGET_ARCH (現在不用考慮這個),然後它加 入旗標 '-c' ,後面跟變量 $< (第一個依靠名),然後是旗 標 '-o' 跟變量 $@ (目的文件名)。一個C編譯的具體命令將 會是:
 
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
 
當然你可以按照你自己的需要來定義這些變量。這就是爲什麼用 gcc 的 -M 或 -MM 開關輸出的碼可以直接用在一個 makefile 裏。

假象目的 (Phony Targets)
 
假 設你的一個項目最後需要產生兩個可執行文件。你的主要目標是產生兩個可執行文件,但這兩個文件是相互獨立的——如果一 個文件需要重建,並不影響另一個。你可以使用“假象目的”來達到這種效果。一個假象目的跟一個正常的目的幾乎是一樣的, 只是這個目的文件是不存在的。因此, make 總是會假設它需要被生成,當把它的依賴文件更新後,就會執行它的規則裏的命令 行。
 
如果在我們的 makefile 開始處輸入:
 
all : exec1 exec2
 
其 中 exec1 和 exec2 是我們做爲目的的兩個可執行文件。 make 把這個 'all' 做爲它的主要目的,每次執行時都會嘗試把 'all' 更新。但既然這行規則裏沒有哪個命令來作用在一個叫 'all' 的實際文件(事實上 all 並不會在磁碟上實際產生),所以這個規 則並不真的改變 'all' 的狀態。可既然這個文件並不存在,所以 make 會嘗試更新 all 規則,因此就檢查它的依靠 exec1, exec2 是否需要更新,如果需要,就把它們更新,從而達到我們的目的。
 
假象目的也可以用來描述一組非預設的動作。例如,你想把所有由 make 產生的文件刪除,你可以在 makefile 裏設立這樣一個規則:
 
veryclean :
 rm *.o
 rm myprog
 
前提是沒有其它的規則依靠這個 'veryclean' 目的,它將永遠 不會被執行。但是,如果你明確的使用命令 'make veryclean' , make 會把這個目的做爲它的主要目標,執行那些 rm 命令。
 
如 果你的磁碟上存在一個叫 veryclean 文件,會發生什麼事?這時因爲在這個規則裏沒有任何依靠文件,所以這個目的文件一定是 最新的了(所有的依靠文件都已經是最新的了),所以既使用戶明 確命令 make 重新產生它,也不會有任何事情發生。解決方法是標 明所有的假象目的(用 .PHONY),這就告訴 make 不用檢查它們是否存在於磁碟上,也不用查找任何隱含規則,直接假設指定的目 的需要被更新。在 makefile 里加入下面這行包含上面規則的規則:
 
.PHONY : veryclean
 
就可以了。注意,這是一個特殊的 make 規則,make 知道 .PHONY 是一個特殊目的,當然你可以在它的依靠里加入你想用的任何假象 目的,而 make 知道它們都是假象目的。
 
2.6 函數 (Functions)
 
makefile 裏的函數跟它的變量很相似——使用的時候,你用一個 $ 符號跟開括號,函數名,空格後跟一列由逗號分隔的參數,最後 用關括號結束。例如,在 GNU Make 裏有一個叫 'wildcard' 的函數,它有一個參數,功能是展開成一列所有符合由其參數描述的文 件名,文件間以空格間隔。你可以像下面所示使用這個命令:
 
SOURCES = $(wildcard *.c)
 
這行會產生一個所有以 '.c' 結尾的文件的列表,然後存入變量 SOURCES 裏。當然你不需要一定要把結果存入一個變量。
 
另一個有用的函數是 patsubst ( patten substitude, 匹配替 換的縮寫)函數。它需要3個參數——第一個是一個需要匹配的 式樣,第二個表示用什麼來替換它,第三個是一個需要被處理的由空格分隔的字列。例如,處理那個經過上面定義後的變量,
 
OBJS = $(patsubst %.c,%.o,$(SOURCES))
 
這 行將處理所有在 SOURCES 字列中的字(一列文件名),如果它的 結尾是 '.c' ,就用 '.o' 把 '.c' 取代。注意這裏的 % 符號將匹 配一個或多個字符,而它每次所匹配的字串叫做一個‘柄’(stem) 。在第二個參數裏, % 被解讀成用第一參數所匹配的那個柄。
 
2.7 一個比較有效的 makefile
 
利用我們現在所學的,我們可以建立一個相當有效的 makefile 。 這個 makefile 可以完成大部分我們需要的依靠檢查,不用做太大 的改變就可直接用在大多數的項目裏。
 
首先我們需要一個基本的 makefile 來建我們的程序。我們可以讓它搜索當前目錄,找到源碼文件,並且假設它們都是屬於我們的項 目的,放進一個叫 SOURCES 的變量。這裏如果也包含所有的 *.cc 文件,也許會更保險,因爲源碼文件可能是 C++ 碼的。
 
SOURCES = $(wildcard *.c *.cc)
 
利用 patsubst ,我們可以由源碼文件名產生目標文件名,我們需 要編譯出這些目標文件。如果我們的源碼文件既有 .c 文件,也有 .cc 文件,我們需要使用相嵌的 patsubst 函數呼叫:
 
OBJS = $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(SOURCES)))
 
最裏面一層 patsubst 的呼叫會對 .cc 文件進行後綴替代,產生的結 果被外層的 patsubst 呼叫處理,進行對 .c 文件後綴的替代。
 
現在我們可以設立一個規則來建可執行文件:
 
myprog : $(OBJS)
 gcc -o myprog $(OBJS)
 
進一步的規則不一定需要, gcc 已經知道怎麼去生成目標文件 (object files) 。下面我們可以設定產生依靠信息的規則:
 
depends : $(SOURCES)
 gcc -M $(SOURCES) > depends
 
在 這裏如果一個叫 'depends' 的文件不存在,或任何一個源碼文件比一個已存在的 depends 文件新,那麼一個 depends 文件會被生 成。depends 文件將會含有由 gcc 產生的關於源碼文件的規則(注 意 -M 開關)。現在我們要讓 make 把這些規則當做 makefile 檔 的一部分。這裏使用的技巧很像 C 語言中的 #include 系統——我 們要求 make 把這個文件 include 到 makefile 裏,如下:
 
include depends
 
GNU Make 看到這個,檢查 'depends' 目的是否更新了,如果沒有, 它用我們給它的命令重新產生 depends 檔。然後它會把這組(新) 規則包含進來,繼續處理最終目標 'myprog' 。當看到有關 myprog 的規則,它會檢查所有的目標文件是否更新——利用 depends 文件裏的規則,當然這些規則現在已經是更新過的了。
 
這個系統其實效率很低,因爲每當一個源碼文件被改動,所有的源碼 文件都要被預處理以產生一個新的 'depends' 文件。而且它也不是 100% 的安全,這是因爲當一個 header 檔被改動,依靠信息並不會 被更新。但就基本工作來說,它也算相當有用的了。

2.8 一個更好的 makefile
 
這是一個我爲我大多數項目設計的 makefile 。它應該可以不需要修改的用在大部分項目裏。我主要把它用在 djgpp 上,那是一個 DOS 版的 gcc 編譯器。因此你可以看到執行的命令名、 'alleg' 程序包、 和 RM -F 變量都反映了這一點。
 

複製代碼
=== makefile 開始 ===
 
######################################
#
# Generic makefile
#
# by George Foot
# email: [email protected]
#
# Copyright (c) 
1997 George Foot
# All rights reserved.
# 保留所有版權
#
# No warranty, no liability;
# you use 
this at your own risk.
# 沒保險,不負責
# 你要用這個,你自己擔風險
#
# You are free to modify and
# distribute 
this without giving
# credit to the original author.
# 你可以隨便更改和散發這個文件
# 而不需要給原作者什麼榮譽。
# (你好意思?)
#
######################################
 
### Customising
# 用戶設定
#
# Adjust the following 
if necessary; EXECUTABLE is the target
# executable
's filename, and LIBS is a list of libraries to link in
# (e.g. alleg, stdcx, iostr, etc). You can override these on make's
# command line of course, if you prefer to do it that way.
#
# 如果需要,調整下面的東西。 EXECUTABLE 是目標的可執行文件名, LIBS
# 是一個需要連接的程序包列表(例如 alleg, stdcx, iostr 等等)。當然你
# 可以在 make 的命令行覆蓋它們,你願意就沒問題。
#
 
EXECUTABLE :
= mushroom.exe
LIBS :
= alleg
 
# Now alter any 
implicit rules' variables if you like, e.g.:
#
# 現在來改變任何你想改動的隱含規則中的變量,例如
 
CFLAGS :
= --Wall -O3 -m486
CXXFLAGS :
= $(CFLAGS)
 
# The next bit checks to see whether rm 
is in your djgpp bin
# directory; 
if not it uses del instead, but this can cause (harmless)
# `File not found
' error messages. If you are not using DOS at all,
set the variable to something which will unquestioningly remove
# files.
#
# 下面先檢查你的 djgpp 命令目錄下有沒有 rm 命令,如果沒有,我們使用
# del 命令來代替,但有可能給我們 
'File not found' 這個錯誤信息,這沒
# 什麼大礙。如果你不是用 DOS ,把它設定成一個刪文件而不廢話的命令。
# (其實這一步在 UNIX 類的系統上是多餘的,只是方便 DOS 用戶。 UNIX
# 用戶可以刪除這5行命令。)
 
ifneq ($(wildcard $(DJDIR)
/bin/rm.exe),)
RM
-F := rm -f
else
RM
-F := del
endif
 
# You shouldn
't need to change anything below this point.
#
# 從這裏開始,你應該不需要改動任何東西。(我是不太相信,太NB了!)
 
SOURCE :
= $(wildcard *.c) $(wildcard *.cc)
OBJS :
= $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(SOURCE)))
DEPS :
= $(patsubst %.o,%.d,$(OBJS))
MISSING_DEPS :
= $(filter-out $(wildcard $(DEPS)),$(DEPS))
MISSING_DEPS_SOURCES :
= $(wildcard $(patsubst %.d,%.c,$(MISSING_DEPS)) \
$(patsubst 
%.d,%.cc,$(MISSING_DEPS)))
CPPFLAGS 
+= -MD
 
.PHONY : everything deps objs clean veryclean rebuild
 
everything : $(EXECUTABLE)
 
deps : $(DEPS)
 
objs : $(OBJS)
 
clean :
 @$(RM
-F) *.o
 @$(RM
-F) *.d
 
veryclean: clean
 @$(RM
-F) $(EXECUTABLE)
 
rebuild: veryclean everything
 
ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
 @$(RM
-F) $(patsubst %.d,%.o,$@)
endif
 
-include $(DEPS)
 
$(EXECUTABLE) : $(OBJS)
 gcc 
-o $(EXECUTABLE) $(OBJS) $(addprefix -l,$(LIBS))
 
=== makefile 結束 ===
複製代碼


 
有幾個地方值得解釋一下的。首先,我在定義大部分變量的時候使 用的是 := 而不是 = 符號。它的作用是立即把定義中參考到的函 數和變量都展開了。如果使用 = 的話,函數和變量參考會留在那 兒,就是說改變一個變量的值會導致其它變量的值也被改變。例 如:
 
A = foo
B = $(A)
# 現在 B 是 $(A) ,而 $(A) 是 'foo' 。
A = bar
# 現在 B 仍然是 $(A) ,但它的值已隨着變成 'bar' 了。
B := $(A)
# 現在 B 的值是 'bar' 。
A = foo
# B 的值仍然是 'bar' 。
 
make 會忽略在 # 符號後面直到那一行結束的所有文字。
 
ifneg...else...endif 系統是 makefile 裏讓某一部分碼有條件的 失效/有效的工具。 ifeq 使用兩個參數,如果它們相同,它把直 到 else (或者 endif ,如果沒有 else 的話)的一段碼加進 makefile 裏;如果不同,把 else 到 endif 間的一段碼加入 makefile (如果有 else )。 ifneq 的用法剛好相反。
 
'filter-out' 函數使用兩個用空格分開的列表,它把第二列表中所 有的存在於第一列表中的項目刪除。我用它來處理 DEPS 列表,把所 有已經存在的項目都刪除,而只保留缺少的那些。
 
我 前面說過, CPPFLAGS 存有用於隱含規則中傳給預處理器的一些 旗標。而 -MD 開關類似 -M 開關,但是從源碼文件 .c 或 .cc 中 形成的文件名是使用後綴 .d 的(這就解釋了我形成 DEPS 變量的步驟)。DEPS 裏提到的文件後來用 '-include' 加進了 makefile 裏,它隱藏了所有因文件不存在而產生的錯誤信息。
 
如果任何依靠文件不存在, makefile 會把相應的 .o 文件從磁碟 上刪除,從而使得 make 重建它。因爲 CPPFLAGS 指定了 -MD , 它的 .d 文件也被重新產生。
 
最後, 'addprefix' 函數把第二個參數列表的每一項前綴上第一 個參數值。
 
這個 makefile 的那些目的是(這些目的可以傳給 make 的命令行 來直接選用):
 
everything:(預設) 更新主要的可執行程序,並且爲每一個 源碼文件生成或更新一個 '.d' 文件和一個 '.o' 文件。
 
deps: 只是爲每一個源碼程序產生或更新一個 '.d' 文件。
 
objs: 爲每一個源碼程序生成或更新 '.d' 文件和目標文件。
 
clean: 刪除所有中介/依靠文件( *.d 和 *.o )。
 
veryclean: 做 `clean' 和刪除可執行文件。
 
rebuild: 先做 `veryclean' 然後 `everything' ;既完全重建。
 
除了預設的 everything 以外,這裏頭只有 clean , veryclean , 和 rebuild 對用戶是有意義的。
 
我 還沒有發現當給出一個源碼文件的目錄,這個 makefile 會失敗的情況,除非依靠文件被弄亂。如果這種弄亂的情況發生了,只要輸入 `make clean' ,所有的目標文件和依靠文件會被刪除,問題就應該被解決了。當然,最好不要把它們弄亂。如果你發現在某種情況下這 個 makefile 文件不能完成它的工作,請告訴我,我會把它整好的。
 

3 總結
~~~~~~~~~~~~~~~
 
我希望這篇文章足夠詳細的解釋了多文件項目是怎麼運作的,也說明了 怎樣安全而合理的使用它。到此,你應該可以輕鬆的利用 GNU Make 工 具來管理小型的項目,如果你完全理解了後面幾個部分的話,這些對於 你來說應該沒什麼困難。

4 轉自:http://oss.org.cn/ossdocs/gnu/linux/gmake.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章