Makefile详解

有感于网络上很多技术博客的排版方式不够统一,并趋向于随意,影响学习的效率和动力,故而根据自己的理解将其重新编排如下,不算原创,算作是自己的学习心得吧,也可供自己日后翻看。

一个Makefile例子

make 命令执行时,需要根据一些规则来决定按照怎么样的方式去编译和链接程序,这些规则就由 makefile 文件所指定。如果我们 makefile 文件写的足够好,make 命令会自动地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。

首先,本文将给出一个makefile文件的示例,以便大家能有一个直观感受,这个例子来源于GNU的make使用手册。在这个例子中,我们的工程有8个c文件,和3个头文件,我们要写一个makefile来告诉make命令如何编译和链接这几个文件。例子如下:

edit : main.o kbd.o command.o display.o \
         insert.o search.o files.o utils.o
       cc -o edit main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o
main.o: main.c defs.h
   cc -c main.c
kbd.o: kbd.c defs.h command.h
   cc -c kbd.c
command.o: command.c defs.h command.h
   cc -c command.c
display.o: display.c defs.h buffer.h
   cc -c display.c
insert.o: insert.c defs.h buffer.h
   cc -c insert.c
search.o: search.c defs.h buffer.h
   cc -c search.c
files.o: files.c defs.h buffer.h command.h
   cc -c files.c
utils.o: utils.c defs.h
   cc -c utils.c
clean:
   rm edit main.o kbd.o command.o display.o \
   insert.o search.o files.o utils.o

这个例子里 make 的编码规则如下:

a. 如果这个工程没有编译过,那么我们的所有c文件都要编译并被链接。
b. 如果这个工程的某几个c文件被修改,那么我们只编译被修改的c文件,并链接目标程序。
c. 如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的c文件,并链接目标程序。

Makefile规则

在详细拆解上一节的 Makefile 之前,先来看下 Makefile 的基本范式。

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

target可以是一个 1) object file(可执行文件),2) 可执行文件,还可以是个3) label(标签),关于标签这个特性,在后面的伪目标章节还会有叙述。

prerequisites 就是,要生成那个target所需要的文件或是目标。command 也就是 make 需要执行的命令,可以是任意的
shell 命令。

这是一个文件的依赖关系,也就是说,target 这一个或多个的目标文件依赖于 prerequisites 中的文件,其生成规则定义在 command中。同时,prerequisites 中如果有一个以上的文件比target文件要新的话,command 所定义的命令就会被执行。这就是Makefile的规则,也是 Makefile最核心的内容。

有了这些规则后,再来分析上面的例子。在这个 makefile 中,目标文件(target)包含:

  • 执行文件edit
  • 中间目标文件(*.o)

依赖文件(prerequisites)就是冒号后面的那些 .c 文件和 .h文件。每一个 .o文件都有一组依赖文件,而这些 .o文件又是执行文件 edit 的依赖文件。

在定义好依赖关系后,后续的那一行定义了如何生成目标文件的系统命令,一定要以一个tab键作为开头make会比较
targets 文件和 prerequisites 文件的修改日期,如果 prerequisites 文件的日期要比targets文件的日期要新,或者 target 不存在的话,那么,make就会执行后续定义的命令。

我们可以把这个内容保存在名字为makefileMakefile的文件中,然后在该目录下直接输入命令 make 就可以生成可执行文件edit。如果要删除执行文件和所有的中间目标文件,那么,只要简单地执行一下 make clean就可以了。注:反斜线(\)是换行符的意思,这样比较便于阅读。

这里要说明一点的是,clean 不是一个文件,它只不过是一个动作名字,有点像C语言中的 lable 一样,其冒号后什么也没有,那么,make就不会去找它的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令(不仅用于 clean,其他 lable 同样适用),就要在 make 命令后显式指出这个 lable 的名字。这样的方法非常有用,我们可以在一个 makefile 中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

Make是如何工作的

在默认的方式下,也就是我们只输入make命令。那么,

  1. make 会在当前目录下找名字叫Makefilemakefile的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,它会找到 edit 这个文件,并把这个文件作为最终的目标文件。
  3. 如果 edit 文件不存在,或是 edit 所依赖的后面的.o文件的文件修改时间要比 edit 这个文件新,那么,它就会执行后面所定义的命令来生成 edit 这个文件。
  4. 如果 edit 所依赖的.o文件也不存在,那么 make 会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(有点像是堆栈的过程)
  5. 当然,你的.c文件和.h文件是存在的啦,于是 make 会生成 .o 文件,然后再用.o文件生成 make 的终极任务,也就是执行文件 edit 了。

这就是整个 make 的依赖性,make 会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,这些都不在 make 职责范围内。

通过上述分析,我们知道,像 clean 这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要 make 执行。即命令make clean,以此来清除所有的目标文件,以便重编译。

在Makefile中使用变量

在上面的例子中可以看到,后缀为.o的一大串文件名写了两次,这样比较费时费力,而且如果文件有所增减,要修改的地方也非常多,对以后的维护造成困难。在这种情形下,我们可以在Makefile里使用变量代替这一大串依赖文件,这里变量的使用方式基本类似于shell脚本里变量的使用方法。

我们可以在makefile一开始就这样定义:

objects = main.o kbd.o command.o display.o \
        insert.o search.o files.o utils.o

那么接下来我们就可以很方便地在我们的Makefile中以$(objects)的方式来使用这个变量了,于是如果有新的.o 文件加入,我们只需简单地修改一下 objects 变量就可以了。

让 make 自动推导

GNU的 make 很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个.o文件后都写上类似的命令。因为,我们的make会自动识别,并自己推导命令。

只要make看到一个.o文件,它就会自动的把.c文件加在依赖关系中,如果make找到一个FILENAME.o,那么 FILENAME.c,就会是FILENAME.o的依赖文件。并且 cc -c FILENAME.c 也会被推导出来,于是,我们的makefile 再也不用写得这么复杂。我们的新makefile就可以这么写了。

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o
cc = gcc

edit: $(objects)
      cc -o edit $(objects)

main.o: defs.h
kbd.o: defs.h command.h
command.o: defs.h command.h
display.o: defs.h buffer.h
insert.o: defs.h buffer.h
search.o: defs.h buffer.h
files.o: defs.h buffer.h command.h
utils.o: defs.h

.PHONY: clean
clean:
    rm edit $(objects)

这种方法,也就是make的**。上面文件内容中,“.PHONY”表示,clean是个伪目标文件。

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

推荐阅读更多精彩内容

  • makefile关系到整个工程的编译规则,一个工程中的源文件不计其数,按其类型、功能、模块分别放在若干的目录当中,...
    Joe_HUST阅读 1,879评论 0 3
  • 由于用的是Chrome,上一篇的末尾不知道为何突然退出,点进去好多次,依然是一有动作就自动退出。之前用插件马克飞象...
    神的第57个名字阅读 506评论 0 2
  • 来自陈浩的一片老文,但绝对营养。 示例工程:3 个头文件*.h,和 8 个 C 文件*.c。 初 编译过程,源文件...
    周筱鲁阅读 4,694评论 0 17
  • @(linux 编程)[开发技能, 工具使用] What is GNU Make Make 是控制工程中通过源码生...
    orientlu阅读 11,339评论 0 26
  • makefile的规则 组成 target prerequisites commandtarget这一个或多个的目...
    Neucrack阅读 2,754评论 0 3