寫你的第一個makefile

Make 命令教程
最簡明的見阮一峰的博客,以下是我整理的寫makefile的筆記

從小例子說起

foo.o : foo.c defs.h       # foo模塊
    cc -c -g foo.c

第一條規(guī)則中的目標(biāo)將被確立為最終的目標(biāo)刹碾。
規(guī)則說明了

  1. foo.o依賴于foo.c和defs.h的文件
  2. 如何生成foo.o這個文件

規(guī)則的語法

targets : prerequisites
    command
    ...

targets是文件名故觅,以空格分開厂庇,可以使用通配符。
目標(biāo)可以是一個文件输吏,但也有可能是多個文件权旷。
如果命令太長,你可以使用反斜框(‘\’)換行贯溅。

偽目標(biāo)
前置條件

自動找尋源文件中包含的頭文件拄氯,并生成一個依賴關(guān)系
大多數(shù)的C/C++編譯器都支持一個“-M”的選項,如果你使用GNU的C/C++編譯器它浅,你得用“-MM”參數(shù)译柏。

  • 執(zhí)行g(shù)cc -MM main.c
  • 輸出main.o : main.c defs.h

gcc為每一個“name.c”的文件都生成一個“name.d”的Makefile文件
于是可以寫出[.c]文件和[.d]文件的依賴關(guān)系,并讓make自動更新[.d]文件罚缕,并把其包含在Makefile中艇纺,這樣,就可以自動化地生成每個文件的依賴關(guān)系了邮弹。

給出了一個模式規(guī)則來產(chǎn)生[.d]文件

%.d: %.c
    @set -e; rm -f $@; \
         $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
         sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
         rm -f $@.$$$$

這個規(guī)則的意思是:

  • 所有的[.d]文件依賴于[.c]文件
  • “rm -f $@”的意思是刪除所有[.d]文件
  • 第二行命令的意思是黔衡,為每個依賴文件[.c]文件生成依賴文件
    第二行生成的文件有可能是“name.d.12345”
  • 第三行使用sed命令做替換
  • 第四行刪除臨時文件。

自動找.c文件所含的.h文件腌乡,可以參見如下:
http://blog.csdn.net/haoel/article/details/2890
http://wiki.ubuntu.org.cn/%E8%B7%9F%E6%88%91%E4%B8%80%E8%B5%B7%E5%86%99Makefile:MakeFile%E4%BB%8B%E7%BB%8D

使用函數(shù)
$(<function> <arguments>)
或者
${<function> <arguments>}
參數(shù)間以逗號,分隔盟劫,而函數(shù)名和參數(shù)之間以空格分隔。
例子:

comma:= ,
empty:=
space:= $(empty) $(empty)
foo:= a b c
bar:= $(subst $(space),$(comma),$(foo))

這里的函數(shù)subst(替換substitution)与纽,把foo里的space用comma替換

格式:$(patsubst <pattern>,<replacement>,<text> )
名稱:模式字符串替換函數(shù)——patsubst侣签。
功能:查找<text>中的單詞(單詞以“空格”塘装、“Tab”或“回車”“換行”分隔)是否符合模式<pattern>,如果匹配的話影所,則以<replacement>替換蹦肴。

這里,<pattern>可以包括通配符“%”猴娩,表示任意長度的字串阴幌。
如果<replacement>中也包含“%”,那么卷中,<replacement>中的這個“%”將是<pattern>中的那個“%”所代表的字串
例子:
$(patsubst %.c,%.o,x.c.c bar.c)
模式替換函數(shù)patsubst(pattern substitution)
把x.c.c bar.c里面的符合%.c模式的矛双,換成%.o模式
所以最后返回的是x.c.o bar.o

例子:
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
filter函數(shù),保留符合patter的word
上面例子的模式是%.c %.s蟆豫,最后保留foo.c bar.c baz.s

下面開始為一個簡單的項目寫makefile吧议忽!
(參考:http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/

假設(shè)你的C項目包含如下三個文件:
(1) hellomake.c

#include <hellomake.h>

int main() {
  // call a function in another file
  myPrintHelloMake();

  return(0);
}

(2) hellofunc.c

#include <stdio.h>
#include <hellomake.h>

void myPrintHelloMake(void) {

  printf("Hello makefiles!\n");

  return;
}

(3) hellomake.h

/*
example include file
*/

void myPrintHelloMake(void);

手動在終端輸命令如下可以得到可執(zhí)行文件hellomake
gcc -o hellomake hellomake.c hellofunc.c -I.

手打這串命令的不方便之處是,當(dāng)你重新打開終端十减,得從頭(from scratch)再輸栈幸,此外,如果你只是修改了一個.c文件帮辟,你需要重新編譯所有文件侦镇,浪費(fèi)時間沒效率。

version 1

于是你可以構(gòu)建第一個版本的自動化makefile
創(chuàng)建一個名為makefile或者M(jìn)akefile的文件织阅,敲如下命令:

hellomake: hellomake.c hellofunc.c
     gcc -o hellomake hellomake.c hellofunc.c -I.

第一行是規(guī)則壳繁,知道hellomake依賴于哪些文件,
第二行是命令荔棉,注意命令前面必須有tab

version 2

CC=gcc
CFLAGS=-I.

hellomake: hellomake.o hellofunc.o
     $(CC) -o hellomake hellomake.o hellofunc.o -I.

現(xiàn)在定義了常數(shù)CC和CLAGS闹炉。
宏CC指定用那個編譯器
CFLAGS是flag列表傳遞給編譯命令

通過將目標(biāo)文件hellomake.o 和 hellofunc.o放在依賴性列表,make知道它需要首先編譯.c润樱,然后構(gòu)建可執(zhí)行的hellomake渣触。

用上面這種形式的makefile對于大部分小型工程足夠了,但遺漏對include文件的依賴性的規(guī)定壹若。如果你要修改hellomake.h嗅钻,make不會重新編譯.c文件,即使這些.c文件需要重新編譯店展。所以养篓,我們需要告訴make所有的.c文件依賴于哪些特定的header file。

version 3

CC=gcc
CFLAGS=-I.
DEPS = hellomake.h

%.o: %.c $(DEPS)
    $(CC) -c -o $@ $< $(CFLAGS)

hellomake: hellomake.o hellofunc.o 
    gcc -o hellomake hellomake.o hellofunc.o -I.

這里增加了宏DEPS赂蕴,定義.c文件依賴的header files柳弄。
接著我們定義了應(yīng)用于所有(注意%符號).o文件的規(guī)則,規(guī)則是.o文件依賴于所有.c文件以及頭文件概说。
規(guī)則然后規(guī)定碧注,make需要編譯.c文件以產(chǎn)生.o文件嚣伐。
-c flag(這里flag和option是同義詞)指產(chǎn)生obj文件
-o $@指將編譯結(jié)果放在:左邊名字命名的文件里(也即是%.o)
$<是list里面的第一項(也即是 %.c)

  • $@——:的左邊
  • $^ ——:的右邊
自動變量

再舉個例子:
all: library.cpp main.cpp
在這種情形下
$@ 指all
$< 指library.cpp
$^ 指library.cpp main.cpp

version 4

CC=gcc
CFLAGS=-I.
DEPS = hellomake.h
OBJ = hellomake.o hellofunc.o 

%.o: %.c $(DEPS)
    $(CC) -c -o $@ $< $(CFLAGS)

hellomake: $(OBJ)
    gcc -o $@ $^ $(CFLAGS)

version 5

如果我們這樣組織我們的項目文件,header files在一個include目錄萍丐,source code在src目錄轩端,本地庫在一個lib目錄,當(dāng)?shù)玫搅丝蓤?zhí)行文件逝变,.o文件也不需要了船万,如何處理它們呢?

IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)

ODIR=obj
LDIR =../lib

LIBS=-lm

_DEPS = hellomake.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
# hellomake.h整個用../include/hellomake.h替換

_OBJ = hellomake.o hellofunc.o 
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
#  hellomake.o hellofunc.o分別用obj/hellomake.o和obj/hellofunc.o替換

$(ODIR)/%.o: %.c $(DEPS)
    $(CC) -c -o $@ $< $(CFLAGS)

hellomake: $(OBJ)
    gcc -o $@ $^ $(CFLAGS) $(LIBS)

.PHONY: clean

clean:
    rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~ 

這個makefile應(yīng)該放在src目錄里面骨田。
這里也包含了clean up你的source和obj目錄的規(guī)則,如果你敲make clean的話声怔。

注意态贤,本文makefile里面的通配符%和都是表示匹配任意字符,但是%是make層的醋火,是shell層的悠汽。

兩種口味的變量

上面的例子給出了兩種設(shè)置變量值的方法,
VAR=VAL VS VAR:=VAL
下面做個簡單的說明

遞歸展開變量

foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:;echo $(foo)

這里foo->bar->ugh
最后在終端打印Huh芥驳?

簡單展開變量: 使用在之前定義的值柿冲,而不會按照引用去遞歸地找最終的值。

細(xì)節(jié)請參見GNU make手冊
本文是http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/ 的整理版

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末兆旬,一起剝皮案震驚了整個濱河市假抄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌丽猬,老刑警劉巖宿饱,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異脚祟,居然都是意外死亡谬以,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門由桌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來为黎,“玉大人,你說我怎么就攤上這事行您∶” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵娃循,是天一觀的道長片橡。 經(jīng)常有香客問我,道長淮野,這世上最難降的妖魔是什么捧书? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任吹泡,我火速辦了婚禮,結(jié)果婚禮上经瓷,老公的妹妹穿的比我還像新娘爆哑。我一直安慰自己,他們只是感情好舆吮,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布揭朝。 她就那樣靜靜地躺著,像睡著了一般色冀。 火紅的嫁衣襯著肌膚如雪潭袱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天锋恬,我揣著相機(jī)與錄音屯换,去河邊找鬼。 笑死与学,一個胖子當(dāng)著我的面吹牛彤悔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播索守,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼晕窑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了卵佛?” 一聲冷哼從身側(cè)響起杨赤,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎截汪,沒想到半個月后望拖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挫鸽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年说敏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丢郊。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡盔沫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出枫匾,到底是詐尸還是另有隱情架诞,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布干茉,位于F島的核電站谴忧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜沾谓,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一委造、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧均驶,春花似錦昏兆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至腾它,卻和暖如春跑筝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瞒滴。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工曲梗, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人逛腿。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像仅颇,于是被迫代替她去往敵國和親单默。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容