Make 命令教程
最簡明的見阮一峰的博客,以下是我整理的寫makefile的筆記
從小例子說起
foo.o : foo.c defs.h # foo模塊
cc -c -g foo.c
第一條規(guī)則中的目標(biāo)將被確立為最終的目標(biāo)刹碾。
規(guī)則說明了
- foo.o依賴于foo.c和defs.h的文件
- 如何生成foo.o這個文件
規(guī)則的語法
targets : prerequisites
command
...
targets是文件名故觅,以空格分開厂庇,可以使用通配符。
目標(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/ 的整理版