Makefile 简述
定义
Linux 环境下的程序员如果不会使用GNU make来构建和管理自己的工程,应该不能算是一个合格的专业程序员,至少不能称得上是 Unix程序员。在 Linux(unix )环境下使用GNU 的make工具能够比较容易的构建一个属于你自己的工程,整个工程的编译只需要一个命令就可以完成编译、连接以至于最后的执行。不过这需要我们投入一些时间去完成一个或者多个称之为Makefile 文件的编写。
make是一个命令工具,它解释Makefile 中的指令(应该说是规则)。在Makefile文件中描述了整个工程所有文件的编译顺序、编译规则。Makefile 有自己的书写格式、关键字、函数。像C 语言有自己的格式、关键字和函数一样。而且在Makefile 中可以使用系统shell所提供的任何命令来完成想要的工作。Makefile(在其它的系统上可能是另外的文件名)在绝大多数的IDE 开发环境中都在使用,已经成为一种工程的编译方法。
下面都用C语言举例子
程序编译链接过程
C语言的编译和链接就是要把编写的C程序转换成可执行程序。编译是把文本形源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作系统的启动代码和用到的库文件进行组织形成最终生成可执行代码的过程。
编译
编译是读取源程序,并进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。
链接
链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。
Makfile
make执行命令的时候需要一个Makefile文件,来告诉make命令如何编译链接程序。
这里先给一个例子来简单的理解Makefile的书写规则,在示例中,工程有11个C文件,和3个头文件。我们要写一个Makefile来告诉make命令如何编译链接这几个文件。编译规则如下:
1、如果所有的C文件都没有编译过,那么要将所有C文件都编译和链接。
2、如果有一个以上的C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
2、如果头文件被修改,那么编译引用头文件的C文件,并链接目标程序。
我们只要写好Makefile文件,告诉make命令如何编译链接,我们就可以很简单的用一个make命令完成这个工作。
Makefile 规则
我们还是先大体的看一下Makefile的基本编写规则。
target ... : prerequisites ...
command
...
target 是一个目标文件,这个目标文件可以是Object File,也可以是可执行文件,还可以是一个标签(伪目标)。
prerequisites 是要生成 target 所需要的文件或是目标。
command 是要执行的命令(任意Shell命令)
这其实就是一个依赖关系, target 依赖于prerequisites 中的文件,它生成的规则定义在 command 中。如果 prerequisites 有被修改的文件,那么command 中的命令就会被执行。
Makefile 示例
我们前面提到要举一个例子,这个例子中有11个C文件和两个头文件,为了能够实现前面提到的规则,我们编写了一个Makefile。
wordSystem : wordSysPat.o watchRecited.o wordSqlLogin.o \
wordSysSetPlan.o wordSysExit.o wordSysShow.o \
my_dil.o wordRecite.o wordSysLogin.o \
wordSysMain.o wordTest.o
cc -o wordSystem wordSysPat.o watchRecited.o wordSqlLogin.o \
wordSysSetPlan.o wordSysExit.o wordSysShow.o \
my_dil.o wordRecite.o wordSysLogin.o \
wordSysMain.o wordTest.o
wordSysPat.o : wordSysPat.c wordSystem.h
cc -c wordSysPat.c
watchRecite.o : watchRecite.c mydil.h
cc -c watchRecite.c
wordSysSetPlan.o : wordSysSetPlan.c mydil.h
cc -c wordSysSetPlan.c
wordSysShow.o : wordSysShow.c wordSystem.h
cc -c wordSysShow.c
wordRecite.o : wordRecite.c mydil.h
cc -c wordRecite.c
wordSysLogin.o : wordSysLogin.c wordSystem.h mydil.h
cc -c wordSysLogin.c
wordSysMain.o : wordSysMain.c wordSystem.h
cc -c wordSysMain.c
clean :
rm wordSystem wordSysPat.o watchRecited.o wordSqlLogin.o \
wordSysSetPlan.o wordSysExit.o wordSysShow.o \
my_dil.o wordRecite.o wordSysLogin.o \
wordSysMain.o wordTest.o
只要在保存文件的目录中执行
make
就可以生成最终的可执行文件 wordSystem ,执行make clean
可以删除所有可执行文件和中间目标文件。
Makefile 中的 \ 是换行的意思,这样让 Makefile 看起来整洁了一些,wordSystem 是最终生成的可执行文件,冒号后面的
wordSysPat.o watchRecited.o wordSqlLogin.o \
wordSysSetPlan.o wordSysExit.o wordSysShow.o \
my_dil.o wordRecite.o wordSysLogin.o \
wordSysMain.o wordTest.o
是它生成所需要的依赖文件,其实就是 target 是由 prerequisites 中的文件生成的。 command 就是生成 target 要执行的命令。这里还要说一下书写的格式,
command 的前面必须要有一个Tab键作为开头(不可以是其他的空白符,用空格凑出来一个类似Tab键的效果是不可以的),否则就会出现类似这样的提示,Makefile:10: *** 遗漏分隔符 。 停止。
同时不是命令的前面不可以用Tab键,如果使用了Tab键开头会出现类似这样的提示,Makefile:1: *** recipe commences before first target。 停止。
Makefile 最后的 clean 它并不是一个文件,只是一个动作的名称,它并有依赖的文件,所以冒号后面没有 prerequisites ,它只有要执行的命令,但是这个命令并不会自动的执行,这个命令要在make命令的后面写出这个标签来执行命令,
这样就非常有用了,Makefile 中可以写很多与编译无关或者不用编译的命令,比如程序的打包,备份,清除等等。
make工作过程
当你输入 make ,会在当前目录寻找名字叫 makefile 或 Makefile 的文件,一般还是使用 Makefile 这样更容易区分,
当然如果你使用其它的名称也可以,只要加上-f
选项,如:make -f Linux_make
如果找到了文件,它会找到第一个目标作为最终的生成目标,例子中的就是 wordSystem ,其实最后生成的文件的名称是和第一目标生成的命令中的文件名决定的,如下:
wordSystem : wordSysPat.o watchRecited.o wordSqlLogin.o \
wordSysSetPlan.o wordSysExit.o wordSysShow.o \
my_dil.o wordRecite.o wordSysLogin.o \
wordSysMain.o wordTest.o
cc -o my_wordSystem wordSysPat.o watchRecited.o wordSqlLogin.o \
wordSysSetPlan.o wordSysExit.o wordSysShow.o \
my_dil.o wordRecite.o wordSysLogin.o \
wordSysMain.o wordTest.o
......
最后生成可执行文件的名称是 my_wordSystem ,这也就说明其实 target 真的就只是一个目标 ,只是编译链接的中间的一个过程而已,再如下:
wordSystem : wordSysPat_new.o watchRecited.o wordSqlLogin.o \
wordSysSetPlan.o wordSysExit.o wordSysShow.o \
my_dil.o wordRecite.o wordSysLogin.o \
wordSysMain.o wordTest.o
cc -o wordSystem wordSysPat.o watchRecited.o wordSqlLogin.o \
wordSysSetPlan.o wordSysExit.o wordSysShow.o \
my_dil.o wordRecite.o wordSysLogin.o \
wordSysMain.o wordTest.o
wordSysPat_new.o : wordSysPat.c wordSystem.h
cc -c wordSysPat.c
watchRecite.o : watchRecite.c mydil.h
cc -c watchRecite.c
wordSysSetPlan.o : wordSysSetPlan.c mydil.h
cc -c wordSysSetPlan.c
wordSysShow.o : wordSysShow.c wordSystem.h
cc -c wordSysShow.c
wordRecite.o : wordRecite.c mydil.h
cc -c wordRecite.c
wordSysLogin.o : wordSysLogin.c wordSystem.h mydil.h
cc -c wordSysLogin.c
wordSysMain.o : wordSysMain.c wordSystem.h
cc -c wordSysMain.c
clean :
rm wordSystem wordSysPat.o watchRecited.o wordSqlLogin.o \
wordSysSetPlan.o wordSysExit.o wordSysShow.o \
my_dil.o wordRecite.o wordSysLogin.o \
wordSysMain.o wordTest.o
我将 wordSysPat.o 的目标改成了 wordSysPat_new.o ,最后的效果都是一样,生成的中间文件也是 wordSysPat.o 而不是 wordSysPat_new.o ,
当然不要这么写,不然你自己也可能写蒙了,只是理解一下标签的实际效果。
继续说工作的过程,如果 wordSystem 不存在那么就寻找它的依赖,并执行命令,如果 wordSystem 已经存在了,并且它依赖的文件的修改时间要新,那么就执行命令。
如果头文件被修改了,那么所有依赖了这个头文件的目标都会被重新编译,然后再和目标进行链接。
如果这个依赖文件也不存在,那么就去找这个依赖文件的目标,并重复这个操作。
make 只会在文件中一层一层的去寻找文件依赖关系,而编译的错误就不去关心,如果寻找依赖关系出现问题,那么它就会直接退出并报错。
Makefile 使用变量
我们再看一下 wordSystem 的依赖
wordSystem : wordSysPat.o watchRecited.o wordSqlLogin.o \
wordSysSetPlan.o wordSysExit.o wordSysShow.o \
my_dil.o wordRecite.o wordSysLogin.o \
wordSysMain.o wordTest.o
cc -o wordSystem wordSysPat.o watchRecited.o wordSqlLogin.o \
wordSysSetPlan.o wordSysExit.o wordSysShow.o \
my_dil.o wordRecite.o wordSysLogin.o \
wordSysMain.o wordTest.o
我们看到这里很多的 [.o] 的文件重复的出现了两次,这个例子中的文件还比较少,而且重复的次数比较少,但是如果是一个大的工程,我们就不能保证重复多次这样的事情不会出错,而且显得很繁琐,那么有没有简化的方法那?
当然是有的,这就使用到了 Makefile 中的变量。就是定义一段固定的字符串,其实就像是C语言的宏定义。
那么我们就要声明这个变量,这个变量你可以给它起各种你喜欢的名称了,只有不发生冲突就好,我们就暂时给这个变量起名叫 objects 。
我们用 $(objects) 来使用这个变量,那么我们的将 Makefile 就可以改良成下面这样。
objects = wordSysPat.o watchRecited.o wordSqlLogin.o \
wordSysSetPlan.o wordSysExit.o wordSysShow.o \
my_dil.o wordRecite.o wordSysLogin.o \
wordSysMain.o wordTest.o
wordSystem : $(objects)
cc -o wordSystem $(objects)
wordSysPat_new.o : wordSysPat.c wordSystem.h
cc -c wordSysPat.c
watchRecite.o : watchRecite.c mydil.h
cc -c watchRecite.c
wordSysSetPlan.o : wordSysSetPlan.c mydil.h
cc -c wordSysSetPlan.c
wordSysShow.o : wordSysShow.c wordSystem.h
cc -c wordSysShow.c
wordRecite.o : wordRecite.c mydil.h
cc -c wordRecite.c
wordSysLogin.o : wordSysLogin.c wordSystem.h mydil.h
cc -c wordSysLogin.c
wordSysMain.o : wordSysMain.c wordSystem.h
cc -c wordSysMain.c
clean :
rm wordSystem $(objects)
是不是看起来就简化了一些,以后在增减或减少一些目标文件都简单了很多,只要更改变量就可以了。
make自动推导
GNU的make还是很强大的,它能够自动推导文件和依赖关系后的命令,所以我们就没有必要在每个目标后面都写的那么复杂,于是就可以修改成如下:
objects = wordSysPat.o watchRecited.o wordSqlLogin.o \
wordSysSetPlan.o wordSysExit.o wordSysShow.o \
my_dil.o wordRecite.o wordSysLogin.o \
wordSysMain.o wordTest.o
wordSystem : $(objects)
cc -o wordSystem $(objects)
wordSysPat_new.o : wordSystem.h
watchRecite.o : mydil.h
wordSysSetPlan.o : mydil.h
wordSysShow.o : wordSystem.h
wordRecite.o : mydil.h
wordSysLogin.o : wordSystem.h mydil.h
wordSysMain.o : wordSystem.h
clean :
rm wordSystem $(objects)
这就是make的 “隐晦规则” ,所以之前那种玩笑的写法是万万不可的。
##清除目标文件的的规则
>每一个 Makefile 都应该写一个清除目标文件和可执行文件的规则,这不仅便于重编译,也很利于保持文件的清洁。
一般的风格都是这样的:
```
clean :
rm wordSystem $(objects)
```
>更稳妥的方法:
```
.PHONY : clean
clean :
-rm wordSystem $(objects)
```
>`.PHONY` 的意思是 clean 是个 ‘伪目标’ ,而 rm 命令前面的 `-`是如果某些文件出现问题先不管它,继续向后执行,clean 有一个不成文的规则 “clean从来都放在文件最后” 。
>>这些就是Makefile的简单的概述,都是一些基础,如果需要更多的细节请见:
Makefile 简述的更多相关文章
- Linux内核配置、编译及Makefile简述
Hi,大家好!我是CrazyCatJack.最近在学习Linux内核的配置.编译及Makefile文件.今天总结一下学习成果,分享给大家^_^ 1.解压缩打补丁 首先是解压缩你获取到的Linux内核. ...
- linux内核makefile概览
linux内核makefile概览 本博客参照内核官方英文文档 linux的内核makefile主要用于编译整个内核源码,按照用户的需求生成各种目标文件,对于用户来说,编译内核时非常简单的,只需要几个 ...
- Linux内核
Linux内核配置.编译及Makefile简述 Hi,大家好!我是CrazyCatJack.最近在学习Linux内核的配置.编译及Makefile文件.今天总结一下学习成果,分享给大家^_^ 1.解压 ...
- u-boot 学习系列 1 - SPL
u-boot这个东西从自我N年前使用到现在,变化好多,今天开始重新研究下,本系列的研究都是基于BeagleBoneBlack(bbb)开发板和 u-boot v201801版本的. SPL介绍 在源代 ...
- unix automake 使用,快速生成你的Makefile
使用automake快速生成编译的Makefile 1,确保自己装有的软件automake autoconf 2, 1)执行autoscan 并将生成的configure.scan重命名为config ...
- 简述configure、pkg-config、pkg_config_path三者的关系
简述configure.pkg-config.pkg_config_path三者的关系 一.什么是configure 源码安装过程中大多会用到configure这个程序,一般的configure都是一 ...
- [转帖]Qemu 简述
Qemu 简述 记得KVM 就是 底层用的qemu https://www.cnblogs.com/bakari/p/7858029.html 本文首发于我的公众号 Linux云计算网络(id: cl ...
- ./configure,make,make install的作用(configure一般用来生成 Makefile,相当于qmake)
这些都是典型的使用GNU的AUTOCONF和AUTOMAKE产生的程序的安装步骤. ./configure是用来检测你的安装平台的目标特征的.比如它会检测你是不是有CC或GCC,并不是需要CC或GCC ...
- 说说Makefile那些事儿
说说Makefile那些事儿 |扬说|透过现象看本质 工作至今,一直对Makefile半知半解.突然某天幡然醒悟,觉得此举极为不妥,只得洗心革面从头学来,以前许多不明觉厉之处顿时茅塞顿开,想想好记性不 ...
随机推荐
- Monkey King(左偏树 可并堆)
我们知道如果要我们给一个序列排序,按照某种大小顺序关系,我们很容易想到优先队列,的确很方便,但是优先队列也有解决不了的问题,当题目要求你把两个优先队列合并的时候,这就实现不了了 优先队列只有插入 删除 ...
- python单元测试框架-unittest(四)之用例综合框架管理
简述为何如要框架? 前面测试用例与执行都是写在一个文件,当用例数量不断增加的时候,用例的执行与管理变得非常麻烦,因此需要对用例根据具体的功能模块来使用单独的模块来管理.就像一所学校要根据不同年级进行分 ...
- [转]javascript实现限制上传文件的大小
本文转自:http://www.micmiu.com/lang/javascript/js-check-filesize/ 目录 基本思路 示例 [一].基本思路 在FireFox.Chrome浏览器 ...
- Murano Weekly Meeting 2015.10.06
Meeting time: 2015.October.6th 1:00~2:00 Chairperson: Kirill Zaitsev, from Mirantis Meeting summar ...
- Hadoop2.X分布式集群部署
本博文集群搭建没有实现Hadoop HA,详细文档在后续给出,本次只是先给出大概逻辑思路. (一)hadoop2.x版本下载及安装 Hadoop 版本选择目前主要基于三个厂商(国外)如下所示: 基于A ...
- JavaScript控制流及关键字与C语言之比较
学习JavaScript控制流及关键字概念前,对有过C语言学习经验的同学来说,那么关键字,控制语句概念并不陌生.我们先来看看C语言吧: C语言的32个关键字和9种控制语句 9种控制语句: if.if- ...
- angularjs ui-grid如何动态设置行高
自己开发的公众号,可以领取淘宝内部优惠券 在用ui-grid的时候我们可以用rowHeight设置行高,可是每一行的高度都是一样的,无法根据行内的内容进行自适应.如下图 为了解决这个问题,google ...
- 随机练习:C#实现维吉尼亚加密与解密(解密前提为已知密匙)
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- Windows到Ubuntu免密登陆
Windows到Ubuntu免密登陆 首先检查C盘用户文件夹下是否有.ssh文件夹,同时检查该文件夹中是否有至少两个文件,一个是xxx_rsa和xxx_rsa.pub,一个是私钥文件一个是公钥文件. ...
- Mono For Android如何在VS2012 中打开设计界面
刚接触 Mono For Android 没几天,不知不觉把设计界面弄丢了.辛辛苦苦才把设计界面弄出来,如果你在 Layout 下打开 *.xaml 的文件打开的却是 xml 文档,那么你可以按照 ...