简介make命令和makefile文件
一、为什么要用到 make 命令和 makefile 文件
在 Linux 下编写一个程序,每次编译都需要在命令行一行一行的敲命令。如果是一个很小的程序还好说,命令不怎的复杂,编译速度也挺快,但是对于大型程序来说,这样无疑很麻烦,且不说可能会敲错命令,有时候仅仅改动了一个小地方,却需要将整个程序全部重新编译一遍,显然很浪费时间。Linux 提供了 make 命令来解决上述问题,它会在必要时重新编译所有受改动影响的源文件。同时,还提供了一个 makefile 文件,它告诉 make 命令如何构建应用程序。这里用一个简单的例子提前演示一下:
/* hello.c */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> int main()
{
printf("hello world!\n");
exit();
}
/* Makefile */
hello: hello.c
gcc -o hello.s -S hello.c
gcc -o hello.o -c hello.s
gcc -o hello hello.o
clean:
-rm hello hello.s hello.o
这里提供了两段代码,第一段代码是一个简单的 HelloWorld 程序,第二段代码是为这个程序编写的一个 makefile 文件。此时,只需要在命令行输入 make 命令,就可以对源文件 hello.c 进行编译,如下:

执行 make 命令时,make 命令会读取 makefile 文件,并按照 makefile 文件中给出的命令来创建文件,同时会在执行时将命令打印到标准输出。执行完 make 命令后,源文件所在目录下多了三个文件:hello、hello.o 和 hello.s,其中 hello 是可执行文件,使用命令 ./hello 即可查看程序的输出结果。这和直接在命令行使用 gcc 命令所得到的结果是一样的,而且,当你修改了源文件时,也只需要再次使用 make 命令即可重新编译,十分方便。
二、make 命令
make 命令用于从一个名为 makefile 的文件中获得构建一个程序的依赖关系。make 命令会根据 makefile 文件来确定目标文件的创建顺序以及正确的规则调用顺序。
make 命令的一些常用参数
1)-k 参数:
使用 -k 参数可以让 make 命令在发现错误时仍然继续执行,而不是在检测到第一个错误时就停下来。利用这个选项可以在一次操作中发现所有未编译成功的源文件;
2)-n 参数:
使用 -n 参数,让 make 命令输出将要执行的操作步骤,而不是真正执行这些操作;
3)-f 参数:
使用 -f 参数,后面可以接一个文件名,用于指定一个文件作为 makefile 文件。如果没有使用 -f 选项,则 make 命令会在当前目录下查找名为 makefile 的文件,如果该文件不存在,则查找名为 Makefile 的文件。
三、makefile 文件
makefile 文件由一组依赖关系和规则构成。每个依赖关系都由一个目标(即将要创建的文件)和一个该目标所依赖的源文件组成;规则描述了如何通过这些依赖文件创建目标。简单的来说,makefile 文件的写法如下:
target: prerequisites
command1
command2
...
其中,target 是即将要创建的目标(通常是一个可执行文件),target 后面紧跟一个冒号,prerequisite 是生成该目标所需要的源文件(依赖),一个目标所依赖的文件可以有多个,依赖文件与目标之间以及各依赖文件之间用空格或制表符 Tab 隔开,这些元素组成了一个依赖关系。随后的命令 command 就是规则,也就是 make 需要执行的命令,它可以是任意的 shell 命令。另外,makefile 文件中,注释以 # 号开头,一直延续到该行的结束。
3.1 依赖关系
依赖关系定义了最终应用程序里的每个文件与源文件之间的关系。一个依赖关系列表由目标和该目标的零个或多个依赖组成,语法是:先写目标,然后接一个冒号,再用一个空格或制表符隔开,最后是用空格或制表符隔开的依赖文件列表,如下:
target: prerequisite1 prerequisite2 prerequisite3 ...
依赖关系表明了这样一件事:目标文件 target 依赖于文件 prerequisite1、prerequisite2、prerequisite3 ...,即,要生成 target,需要有这几个依赖文件的存在,而且,若其中一个依赖文件发生了改变,则需要重新生成 target。目标所依赖的文件可以有一个或多个,也可以没有依赖文件 —— 该目标总被认为是过时的,在执行 make 命令时,若指定了该目标,则该目标所对应的规则将总被执行(如目标 clean)。
makefile 文件中可以有很多个目标,每个目标都有自己对应的规则。make 命令默认创建的是 makefile 文件中的第一个目标。也可以自己指定一个目标让 make 命令去创建,只需要将该目标的名字作为参数放到 make 命令之后即可(如常用的 make clean)。实际上,更好的做法是,将 makefile 文件中的第一个目标定义为 all,然后再 all 后面列出其他从属目标,这将告诉 make 命令,在未指定特定目标时,默认情况下将创建哪个目标。此外,使用目标 all ,还可以使 make 命令一次性创建多个文件,这取决于 all 后面所接的从属目标的个数。
举个例子说明一下文件与文件之间的依赖关系:
/* sum.c */
#include <stdio.h>
#include <stdlib.h> extern int add(int i,int j); int main()
{
printf("%d\n",add(,));
exit();
} /* add.c */
#include <stdio.h> int add(int i,int j)
{
int k;
k = i + j;
return k;
}
这是一个简单的加法程序,包含两个文件:sum.c 和 add.c,其中,sum.c 中的 main 函数调用了 add.c 中的 add 函数。这个程序的依赖关系表如下:
sum: sum.o add.o
sum.o: sum.c stdio.h stdlib.h
add.o: add.c stdio.h
其中,最终所需要的目标文件是 sum,sum.o 和 add.o 是依赖 —— 要生成目标文件 sum ,需要先生成 sum.o 和 add.o。同样的,作为目标的 sum.o 依赖于 sum.c、stdio.h 和 stdlib.h;add.o 依赖于 add.c 和 stdio.h。这组依赖关系形成了一个层次结构,它显示了源文件之间的关系。
可以看出来,如果 add.c 发生了改变,那么就需要重新编译 add.o,而由于 add.o 发生了改变,目标文件 sum 也需要被重新创建,同时,由于 add.c 的改变并没有影响到 sum.o(sum.o 不依赖于 add.c),因此,sum.o 并不需要被重新编译。也就是说,通过使用 makefile 文件和 make 命令,我们可以实现,只重新编译所有受到改动影响的源文件,没有受到影响的源文件不必重新编译。这比把整个程序全部重新编译一遍显然要快上很多,尤其是对于大型程序。
3.2 规则
makefile 文件里另一部分内容是规则,它们定义了目标的创建方式。 规则的内容可以是任意的 shell 命令。关于规则,有以下两点需要注意:
1)规则所在行必须以制表符 tab 开头,不能用空格;
2)规则所在行最好不要以空格结尾,可能会导致 make 命令执行失败;
3)如果一行不足以写下所有内容,需要在每行代码的结尾加上一个反斜杠符 “\”,以让所有的命令在逻辑上处于同一行。
两个特殊字符 - 和 @:
1)在规则中,若命令之前加上了符号 “-”,则表明 make 命令将忽略该命令产生的所有错误;
2)若在命令之前加上了符号“@”,则表明 make 在执行该命令前,不会将该命令显示在标准输出上。
/* Makefile */
all: sum sum: sum.o add.o
gcc -o sum add.o sum.o
sum.o: sum.c
gcc -c sum.c
add.o: add.c
gcc -c add.c
clean:
-rm sum sum.o add.o
这是 3.1 中 sum.c 程序的 makefile 文件。其中 gcc 、rm 命令等行就是规则,它们告诉了 make 命令将如何去创建目标。
两个特殊的目标:clean 和 install
目标 clean 和 install 是两个特殊的目标,它们并不用于创建文件,而是有其他用途。
目标 clean 在前面已经提到过,它使用 rm 命令来删除目标文件。rm 命令通常以减号 - 开头,表示让 make 命令忽略该命令的执行结果,这意味着,即使由于文件不存在而导致 rm 命令返回错误,命令 make clean 也能成功执行。
目标 install 用于按照命令的执行顺序将应用程序安装到指定的目录,还是用上面的 sum.c 程序来演示一下目标 install 的用法:
all: sum # 安装目录
INSTDIR = /tmp sum: sum.o add.o
gcc -o sum add.o sum.o
sum.o: sum.c
gcc -c sum.c
add.o: add.c
gcc -c add.c
clean:
rm sum sum.o add.o install: sum
@if [ -d $(INSTDIR) ];\
then\
cp sum $(INSTDIR);\
chmod a+x $(INSTDIR)/sum;\
chmod og-w $(INSTDIR)/sum;\
echo "Installed in $(INSTDIR)";\
else\
echo "The directory $(INSTDIR) dose not exist!";\
fi
使用这个 makefile 文件,make 命令将会把 sum 安装到目录 /tmp 下(实际上,应用程序一般是安装在 /usr/local/bin 下的,这里为了方便就放到 /tmp 下了) 。执行 make install 命令,将得到如下结果:

输出结果显示 sum 已被成功安装到了 /tmp 目录下(实际上就是把可执行文件 sum 复制到 /tmp 目录下)。再进入 /tmp 目录查看,可以看到可执行文件 sum,其文件权限是 rwxr-xr-x,与 makefile 文件中所设置的一致。

3.3 makefile 文件中的宏
在 makefile 文件中定义一个宏很简单,如下:
MACRONAME=value
这里定义了一个宏 MACRONAME,引用宏的方法是使用 $(MACRONAME) 或 ${MACRONAME} 。使用宏定义,可以让 makefile 文件的可移植性更强。除了自己定义一些宏以外,make 命令还内置了一些特殊的宏定义,使得 makefile 文件变得更加简洁:
| 宏 | 说明 |
| $? | 当前目标所依赖的文件列表中比当前目标文件还要新的文件 |
| $@ | 当前目标的名字 |
| $< | 当前依赖文件的名字 |
| $* | 不包括后缀名的当前依赖文件的名字 |
除了在 makefile 文件里面定义宏以外,还可以调用 make 命令时,在命令行上给出宏定义。命令行上的宏定义将 覆盖在 makefile 文件中的宏定义。需要注意的是,在 make 命令后接宏定义时,宏定义必须以单个参数的形式传递,因此,需要避免在宏定义中使用空格或加引号。
参考资料:
《Linux 程序设计 第四版》
https://www.ibm.com/support/knowledgecenter/zh/ssw_aix_71/com.ibm.aix.cmds3/make.htm
简介make命令和makefile文件的更多相关文章
- make命令和makefile文件
make命令和makefile文件的结合提供了一个在项目管理领域十分强大的工具,它不仅常被用于控制源代码的编译,而且还用于手册页的编写以及将应用程序安装到目标目录. makefile文件由一组依赖关系 ...
- Shell脚本——make命令和Makefile文件【转】
https://blog.csdn.net/twc829/article/details/72729799 make命令是一个常用的编译命令,尤其在C/C++开发中,make命令通过makefile文 ...
- 【C编程基础】make命令和makefile文件
1.关于程序的编译和链接 一般来说,无论是C.C++首先要把源文件编译成中间目标文件即 Object File(windows为.obj文件,unix为.o文件),这个动作叫做编译(compile). ...
- gcc命令以及makefile文件
(一)makefile里涉及到的gcc命令 gcc -I./inc:指定头文件寻找目录 将按照 ./inc --> /usr/include --> /usr/local/include的 ...
- 面试题----makefile文件的作用
make工具和makefile文件 make工具和makefile文件简介 make命令和makefile文件的结合提供了一个在项目管理领域十分强大的工具.它不仅常被用于控制源代码的编译和链接,而且还 ...
- 利用 autoconf 和 automake 生成 Makefile 文件
一.相关概念的介绍 什么是 Makefile?怎么书写 Makefile?竟然有工具可以自动生成 Makefile?怎么生成啊?开始的时候,我有这么多疑问,所以,必须得先把基本的概念搞个清楚. 1.M ...
- make命令以及makefile
make命令以及makefile使用RCS与CVS进行源代码控制编写手册页使用patch与tar发布软件开发环境 多源代码的问题 当我们编写小程序时,许多人都是简单的在编辑后通过重新编译所有的文件重新 ...
- MakeFile 文件详解
GNU的make工作时的执行步骤入下:(想来其它的make也是类似) 1.读入所有的Makefile. 2.读入被include的其它Makefile. 3.初始化文件中 ...
- 工程管理之makefile与自动创建makefile文件过程
(风雪之隅 http://www.laruence.com/2009/11/18/1154.html) Linux Makefile自动编译和链接使用的环境 想知道到Linux Makefile系统的 ...
随机推荐
- (十)T检验-第一部分
介绍T分布.T检验.Z检验与T检验.P值.相依样本以及配对样本的非独立T检验. T分布 在到目前为止举的所有例子中,我们都假设我们知道总体参数 μ 和 σ,但很多时候,我们并不知道,我们通常只有样本, ...
- Zookeeper入门(一)之概述
今天主要讲这么几个方面? 1.分布式应用: 2.什么是Zookeeper: 3.使用Zookkeeper有什么好处: ZooKeeper是一种分布式协调服务,用于管理大型主机.在分布式环境中协调和管理 ...
- Python:基础知识(二)
常用模块 urllib2 :用于发送网络请求,获取数据 (Pyhton2中的urllib2工具包,在Python3中分拆成了urllib.request和urllib.error两个包.) json: ...
- linux 修改时间永久
date -s "20190421 16:36:20" &&hwclock --systohc
- gulp插件 run-sequence(同步执行任务)
功能描述 gulp默认使用最大并发数执行任务,也就是说所有的任务几乎都是同时执行,而不会等待其它任务.但很多时候,任务是需要有先后次序的,比如要先清理目标目录,然后再执行打包. run-sequenc ...
- Linux的常用基础命令
初入运维界,从Linux开始学起.首先掌握的是Linux的部分常用命令,总结如下: 1. ls ls = list 查看当前目录下所有文件信息,常用参数组合:ls -al (l 指详细信息 a指所有文 ...
- webdriver 获取页面response
在selenium webdriver实现自动化抓取数据过程中,发现无法从webdriver获取页面response 查来查去最终在 stackoverflow 上找到了这一篇文章 文章中说:webd ...
- 不安分的android开发者(小程序初尝试,前后台都自己做)
前言 作为一个稍微有点想法的程序员来说,拥有一个自己开发,自己运营,完全属于自己的应用,应该是很多人的梦想.刚毕业那会,自己的工作是做游戏,于是也和朋友业余时间开发一些小游戏玩玩,可是终究不成气候,而 ...
- 20155222 c语言实现pwd命令
20155222 c语言实现linux的pwd命令 1.学习pwd命令在Linux层次结构中,用户可以在被授权的任意目录下利用mkdir命令创建新目录,也可以利用cd命令从一个目录转换到另一个目录.然 ...
- AGC 018 F - Two Trees
F - Two Trees 链接 题意: 给定两棵都是N个节点的有根树,节点均从1~N标号.给每个标号定一个权值(类似一号点的权值是x,那么两棵树中1号点的权值都是x),使在两棵树满足以任意节点为根的 ...