简介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系统的 ...
随机推荐
- 1051. [HAOI2006]受欢迎的牛【强连通分量】
Description 每一头牛的愿望就是变成一头最受欢迎的牛.现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎. 这 种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也 ...
- 【bzoj2693】jzptab 莫比乌斯反演+线性筛
题目描述 输入 一个正整数T表示数据组数 接下来T行 每行两个正整数 表示N.M 输出 T行 每行一个整数 表示第i组数据的结果 样例输入 1 4 5 样例输出 122 题解 莫比乌斯反演+线性筛 由 ...
- Zookeeper学习之路 (二)集群搭建
ZooKeeper 软件安装须知 鉴于 ZooKeeper 本身的特点,服务器集群的节点数推荐设置为奇数台.我这里我规划为三台, 为别为 hadoop1,hadoop2,hadoop3 ZooKeep ...
- 《信息安全技术》实验二 Windows口令破解
<信息安全技术>实验二 Windows口令破解 实验目的 了解Windows口令破解原理 对信息安全有直观感性认识 能够运用工具实现口令破解 实验环境 实验机Windows Server ...
- PAT乙级1008
1008 数组元素循环右移问题 (20 分) 一个数组A中存有N(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥0)个位置,即将A中的数据由(A0A1⋯A ...
- cgroup测试存储设备IOPS分配
1 使用:创建树并且attach子系统 首先要创建文件系统的挂载点作为树的根 mkdir /cgroup/name mkdir /cgroup/cpu_and_mem Mount这个挂载点到一个或者多 ...
- js随笔记录
1.当我们尝试优化一段程序的时候,必须要同时了解语言本身和运行环境就比如说,可能教科书上写移位操作比乘法运算要快,但是这是因为CPU指令的问题,所以对于C语言成立,对于跑在VM上的语言来说则不一定了 ...
- Yosimite 系统 “发生意外错误(错误代码-50)” (记一次macbook pro(mid2012) 自主维修排错经历)
电脑型号: Macbook Pro(Mid 2012) A1278 问题描述: 上周,电脑偶尔弹出提示框"发生意外错误(错误代码-50)",弹出这个提示之后硬盘好像变成只读模式 ...
- iOS/OSX漏洞分析和再现:CVE-2019-7286
iOS 12.1.4是2019年2月8日发布的iOS的最新版本.该版本修补了iOS上发现的四个漏洞.根据Project Zero的Ben Hawkes的推文,其中至少有两个0day还是处于在野状态…… ...
- ios学习路线—Objective-C(Runtime消息机制)
RunTime简称运行时.就是系统在运行的时候的一些机制,其中最主要的是消息机制.对于C语言,函数的调用在编译的时候会决定调用哪个函数( C语言的函数调用请看这里 ).编译完成之后直接顺序执行,无任何 ...