Makefile使用指南

转载请标明出处:http://blog.csdn.net/shensky711/article/details/52231202
本文出自: 【HansChen的博客】

什么是Makefile

Makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,文件之间有哪些依赖等。Makefile有自己的书写格式、关键字、函数。像C 语言有自己的格式、关键字和函数一样。而且在Makefile中可以使用系统shell所提供的任何命令来完成想要的工作。
Makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率

makefile的文件名

默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名中,最好不要用“GNUmakefile”,这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,但是基本上来说,大多数的make都支持“makefile”和“Makefile”这两种默认文件名。

当然,你可以使用别的文件名来书写Makefile,比如:“Make.Linux”,“Make.Solaris”,“Make.AIX”等,如果要指定特定的Makefile,你可以使用make的“-f”和“–file”参数,如:

make -f Make.Linux
make --file Make.AIX

Makefile的规则

显示规则

target ... : prerequisites ...
    command
  • target:要生成的目标,也可以是一个标签
  • prerequisites:目标所依赖的列表
  • command:任意shell指令,一般用于生成target。必须以[Tab键]开头,也可以和prerequisites在一行,用分号做为分隔

这是一个文件的依赖关系,也就是说,target依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行(command一定要以Tab键开始,否则编译器无法识别command)。这就是Makefile的规则,也是Makefile中最核心的内容。

举个栗子:

test : test1.o test2.o
    cc -o test test1.o test2.o

test1.o : test1.c test1.h
    cc -c test1.c

test2.o : test2.c test2.h
    cc -c test2.c

clean :
    rm test test1.o test2.o

在这里,test是最终的目标,生成test,依赖于test1.o和test2.o也就是说,有以下情况发生时,会执行cc -o test test1.o test2.o指令,以重新生成target

  • test不存在
  • test存在,但test1.o的修改时间比test新
  • test存在,但test2.o的修改时间比test新

把所有依赖关系都在makefile里列举出来之后,执行make命令的时候,就会根据依赖关系自动编译链接了

但是这里的clean又是干什么的呢?它并没有其他依赖,那么,make就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后明显得指出这个lable的名字,比如make clean。这样的方法非常有用,我们可以在一个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

隐晦规则

每个.o文件的依赖文件默认会有同名的.c文件,比如有一个target是test.o,那么test.c默认就是test.O的依赖文件,这个是makefile的隐晦规则,是make会自动推导出来的

make是怎么工作的?

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

  1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“test”这个文件,并把这个文件作为最终的目标文件
  3. 如果test文件不存在,或是test所依赖的后面的 .o 文件的文件修改时间要比test这个文件新,那么,他就会执行后面所定义的命令来生成test这个文件
  4. 如果test所依赖的.o文件也不存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件

这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件

makefile中使用变量

变量的基础

为了makefile的易维护,在makefile中我们可以使用变量。makefile的变量也就是一个字符串,比如我们可以定义一个objects变量,并通过$(objects)的方式来使用这个变量。变量类似与C语言的宏定义,在执行的时候,变量的值会被扩展到被使用的地方

objects = test1.o test2.o   
test : $(objects)
    cc -o test $(objects)
# --------------------------------------
test : test1.o test2.o
    cc -o test test1.o test2.o

第一种写法和第二种写法的作用完全是一样的

变量中的变量

在定义变量的值时,我们可以使用其它变量来构造变量的值,在Makefile中有两种方式来在用变量定义变量的值。

先看第一种方式,也就是简单的使用“=”号,在“=”左侧是变量,右侧是变量的值,右侧变量的值可以定义在文件的任何一处,也就是说,右侧中的变量不一定非要是已定义好的值,其也可以使用后面定义的值。如:

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

all:
    echo $(foo) #我们执行“make all”将会打出变量$(foo)的值是“Huh?”

这个功能有好的地方,也有不好的地方,好的地方是,我们可以把变量的真实值推到后面来定义,不好的地方,那就是递归定义:

A = $(B)
B = $(A)
#这会让make陷入无限的变量展开过程中去,当然,我们的make是有能力检测这样的定义,并会报错

为了避免上面的这种方法,我们可以使用make中的另一种用变量来定义变量的方法。这种方法使用的是“:=”操作符:

x := foo
y := $(x) bar
x := later

#这种方法,前面的变量不能使用后面的变量,只能使用前面已定义好了的变量,上下两种方式是等价的

y := foo bar
x := later

追加变量值

我们可以使用“+=”操作符给变量追加值,如:

objects = main.o foo.o bar.o utils.o
objects += another.o

于是,我们的$(objects)值变成:“main.o foo.o bar.o utils.o another.o”(another.o被追加进去了)

如果变量之前没有定义过,那么,“+=”会自动变成“=”,如果前面有变量定义,那么“+=”会继承于前次操作的赋值符。如果前一次的是“:=”,那么“+=”会以“:=”作为其赋值符,如:

variable := value
variable += more

等价于:

variable := value
variable := $(variable) more

但如果是这种情况:

variable = value
variable += more

由于前次的赋值符是“=”,所以“+=”也会以“=”来做为赋值,那么岂不会发生变量的递补归定义,这是很不好的,所以make会自动为我们解决这个问题,我们不必担心这个问题。

变量的高级用法

变量值的替换

我们可以替换变量中的共有的部分,其格式是“(var:a=b){var:a=b}”,其意思是,把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符还是看一个示例吧:

foo := a.o b.o c.o
bar := $(foo:.o=.c)
# 把“$(foo)”中所有以“.o”字串“结尾”全部替换成“.c”,最终bar的值是a.c b.c c.c

另外一种变量替换的技术是以“静态模式”(参见前面章节)定义的,如:

foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
# 这依赖于被替换字串中的有相同的模式,模式中必须包含一个“%”字符,这个例子同样让$(bar)变量的值为“a.c b.c c.c”

把变量的值再当成变量

x = y
y = z
a := $($(x))
# 在这个例子中,$(x)的值是“y”,所以$($(x))就是$(y),于是$(a)的值就是“z”

让make自动推导

GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令。

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

清空目标文件的规则

每个Makefile中都应该写一个清空目标文件(.o和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁,一般风格是:

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

makefile注释

Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,如:

# 这是在makefile中的注释1
# 这是在makefile中的注释2

引用其它的Makefile

在Makefile使用include关键字可以把别的Makefile包含进来,make命令开始时,会把找寻include所指出的其它Makefile,并把其内容安置在当前的位置。这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是:

include <filename>
# 在include前面可以有一些空字符,但是绝不能是[Tab]键开始
# filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)

-include <filename>
# 无论include过程中出现什么错误,都不要报错继续执行。上面那条指令若是找不到include的目标文件,会报错

伪目标

最早先的一个例子中,我们提到过一个“clean”的目标。

clean:
    rm *.o temp

伪目标不会自动被执行,只能显式地调用执行。但是上面伪目标的写法有一个缺陷,若是当前目录下存在有一个文件名为”clean”,那么根据我们的规则,command将不会被执行,因为目标已经存在了,为了解决这个问题,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”

.PHONY : clean
clean:
    rm *.o temp

通过.PHONY,无论是否存在“clean”文件,我们的command都将会被执行了

命令出错

每当命令运行完后,make会检测每个命令的返回码,如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。如果一个规则中的某个命令出错了(命令退出码非零),那么make就会终止执行当前规则,这将有可能终止所有规则的执行。

有些时候,命令的出错并不表示就是错误的。例如mkdir命令,我们一定需要建立一个目录,如果目录不存在,那么mkdir就成功执行,万事大吉,如果目录存在,那么就出错了。我们之所以使用mkdir的意思就是一定要有这样的一个目录,于是我们就不希望mkdir出错而终止规则的运行。

为了做到这一点,忽略命令的出错,我们可以在Makefile的命令行前加一个减号“-”(在Tab键之后),标记为不管命令出不出错都认为是成功的。如:

clean:
    -rm -f *.o

转载于:https://www.cnblogs.com/hanschen-coder/p/6528834.html

对“info make”的翻译整理,不是一个纯粹的语言翻译版本,其对GNU make的一些语法和用法进行了一些详细分析和说明,也加入了一些个人的观点和实践总结。 本书的所有的例子都可以在支持V3.8版本的GNU make的系统正确执行。 文于册 伪目标 强制目标(没有命令或依赖的规则) 空目标文件 的特殊目标 多目标 多规则目标 静态模式 静态模式规则的语法 静态模式和隐含规则 双冒号规则 自动产生依赖 第五章:规则的命令 为规则书写命令 命令回显 命令的执行 并发执行命令 命令执行的错误 断的执行 的递归执行 变量 变量和递归 命令行选项和递归 选项 定义命令包 第六章 的变量 使用变量 变量的引用 两种变量定义(赋值) 归展开式变量 直接展开式变量 定义一个空格 ”操作符 变量的高级用法 变量的替换引用 变量的套嵌引用 变量取值 如何设置变量 追加变量值 指示符 多行定义 系统环境变量 目标指定变量 模式指定变量 第七章 的条件执行 的条件判断 个例子 条件判断的基本语法 标记测试的条件语句 笫八章:的内嵌函数 的函数 年月日 文于册 函数的调用语法 文本夂理函数 文件名处理函数 函数 函数 西数 函数 函数 函数 西数 的控制函数 第九章:执行 执行 指定 文件 指定终极日标 替代命令的执行 防止特定文件重建 替换变量定义 使用 进行编译测试 的命令行选项 第十章: 的隐含规则 使用隐含规则 隐含规则的使用 的隐含规则一览 隐含变量 代表命令的变量 命令参数的变量 隐含规则链 模式规 模式规则介绍 模式规则示例 自动化变量 年月日 文于册 模式的匹配 万用规则 重建内嵌隐含规则 缺省规则 后缀规则 隐含规则搜索算法 笫十一章:使用更新静态库文件 更新静态库文件 库成员作为目标 静态库的更新 更新静态庠的符号索引表 静态库的注意享项 静态库的后缀规则 第十二章: 的特点 的一些特点 源自 的特点 源自其他版本的特点 自身的特点 第十三章和其它版本的兼容 不兼容性 第十四章 的约定 书写约定 基本的约定 规则命令行的约定 代表命令变量 安装目录变量 的标准目标名 安装命令分类 第十五章的常见错误信息 产生的错误信息 附录:关键字索引 可识别的指示符 函数 的自动化变量 环境变量 后序 年月日 文于册 关于本书 本文瑾献给所有热爱 的程序员!本文文档版权所有 本文比较完整的讲述 工具,涵盖 的用法、语法。同时重 讨论如何为一个工程编写 作为一个程序员, 工具的使用以及编 写 是必嚅的。系统、详细讲述的文资料比较少,出于对广大文 的支持,本人在工作之余,花了个多月时间完成对“ 的翻译整理,完成 这个文版手册。夲书不是一个纯粹的语言翻译版本,其对 的一些语法 和用法根据我个人的工作经验进行了一些详细分析和说明,也加入了一些个人的观点和 实践总结。本书的所有的例子都可以在支持版本的 的系统正确执行。 由于个人水平限制,本文在一些地方存在描述不准确之处。恳请大家在阅读过程 提出您宝贵的意见,也是对我个人的帮助。我的个人电子邯箱地址: 非常愿意和大家交流!共同学习 阅读本书之前,读者应该对 的工具链和 的一些常用编程工具有一定的 了解。诸如: 等;同时在书写 时,需要能够进行一些 基本的编程。这些工具是维护一个工程的基础。如果大家对这些工具的用法不是 很熟悉,可参考项目资料 阅读本文的几点建议: 如果之前你对 没有了解、当前也不想深入的学习 的读 者。可只阅读本文各章节前半部分的内容(作为各章节的基础知识) 如果你已经对 比较熟悉,你更霄要关心此版本的新增特点、功能、 和之前版本不兼容之处;也可以作为开发过程过程的参考手册。 之前你对 没有概念、或者刚开始接触,本身又想成为一个 下 的专业程序员,那么建议:完整学习本文的各个章节,包括了基础知识和高级 用法、技巧。它会为你在 下的工程开发、工程管理提供非常有用的帮助。 此文文档当前版本 本文的所有勘误和最新版本可在主 页 上获取!! 谢谢! 徐海兵 年月日 文于册 第一章:概述 概既述 环境下的程序员如果不会侠用 来构建和管理自己的工程,应该 不能算是一个合柊的专业程序员,至少不能称得上是程序员。在 )环 境下侠用 的 工具能够比较容易的构建一个属于你自己的工程,整个工程的 编译只需要一个命令就可以完成编译、连接以至于最后的执行。不过这需要我们投入 些时间去完成一个或者多个称之为 文件的编写。此文件正是 正常工作 的基础 所要完成的 文件描述了整个工程的编译、连接等规则。其包括:工程 的哪些源文件需要编译以及如何编译、需要创建那些库文件以及如何创建这些库文 件、如何最后产生我们想要得可执行文件。尽管看起来可能是很复杂的事情,但是为工 程编写
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值