写你的第一个makefile

Make 命令教程
最简明的见阮一峰的博客,以下是我整理的写makefile的笔记

从小例子说起

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

第一条规则中的目标将被确立为最终的目标。
规则说明了

  1. foo.o依赖于foo.c和defs.h的文件
  2. 如何生成foo.o这个文件

规则的语法

targets : prerequisites
    command
    ...

targets是文件名,以空格分开,可以使用通配符。
目标可以是一个文件,但也有可能是多个文件。
如果命令太长,你可以使用反斜框(‘\’)换行。

伪目标
前置条件

自动找寻源文件中包含的头文件,并生成一个依赖关系
大多数的C/C++编译器都支持一个“-M”的选项,如果你使用GNU的C/C++编译器,你得用“-MM”参数。

  • 执行gcc -MM main.c
  • 输出main.o : main.c defs.h

gcc为每一个“name.c”的文件都生成一个“name.d”的Makefile文件
于是可以写出[.c]文件和[.d]文件的依赖关系,并让make自动更新[.d]文件,并把其包含在Makefile中,这样,就可以自动化地生成每个文件的依赖关系了。

给出了一个模式规则来产生[.d]文件

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

这个规则的意思是:

  • 所有的[.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

使用函数
$(<function> <arguments>)
或者
${<function> <arguments>}
参数间以逗号,分隔,而函数名和参数之间以空格分隔。
例子:

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

这里的函数subst(替换substitution),把foo里的space用comma替换

格式:$(patsubst <pattern>,<replacement>,<text> )
名称:模式字符串替换函数——patsubst。
功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。

这里,<pattern>可以包括通配符“%”,表示任意长度的字串。
如果<replacement>中也包含“%”,那么,<replacement>中的这个“%”将是<pattern>中的那个“%”所代表的字串
例子:
$(patsubst %.c,%.o,x.c.c bar.c)
模式替换函数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函数,保留符合patter的word
上面例子的模式是%.c %.s,最后保留foo.c bar.c baz.s

下面开始为一个简单的项目写makefile吧!
(参考:http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/

假设你的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);

手动在终端输命令如下可以得到可执行文件hellomake
gcc -o hellomake hellomake.c hellofunc.c -I.

手打这串命令的不方便之处是,当你重新打开终端,得从头(from scratch)再输,此外,如果你只是修改了一个.c文件,你需要重新编译所有文件,浪费时间没效率。

version 1

于是你可以构建第一个版本的自动化makefile
创建一个名为makefile或者Makefile的文件,敲如下命令:

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

第一行是规则,知道hellomake依赖于哪些文件,
第二行是命令,注意命令前面必须有tab

version 2

CC=gcc
CFLAGS=-I.

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

现在定义了常数CC和CLAGS。
宏CC指定用那个编译器
CFLAGS是flag列表传递给编译命令

通过将目标文件hellomake.o 和 hellofunc.o放在依赖性列表,make知道它需要首先编译.c,然后构建可执行的hellomake。

用上面这种形式的makefile对于大部分小型工程足够了,但遗漏对include文件的依赖性的规定。如果你要修改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。
接着我们定义了应用于所有(注意%符号).o文件的规则,规则是.o文件依赖于所有.c文件以及头文件。
规则然后规定,make需要编译.c文件以产生.o文件。
-c flag(这里flag和option是同义词)指产生obj文件
-o $@指将编译结果放在:左边名字命名的文件里(也即是%.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目录,当得到了可执行文件,.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应该放在src目录里面。
这里也包含了clean up你的source和obj目录的规则,如果你敲make clean的话。

注意,本文makefile里面的通配符%和都是表示匹配任意字符,但是%是make层的,是shell层的。

两种口味的变量

上面的例子给出了两种设置变量值的方法,
VAR=VAL VS VAR:=VAL
下面做个简单的说明

递归展开变量

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

all:;echo $(foo)

这里foo->bar->ugh
最后在终端打印Huh?

简单展开变量: 使用在之前定义的值,而不会按照引用去递归地找最终的值。

细节请参见GNU make手册
本文是http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/ 的整理版

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容