目录
- 为什么要学习使用make工具?
- 什么是make工具?
- make工具的学习过程
- 1. 安装make:sudo apt install make;并学习使用make
- 安装make流程
- 学习使用make指令
- make指令的相关特点
- make只会对修改过的或者可执行目标文件不存在的.c文件进行编译
- 使用make时,若不加 -f 选项,make会依次寻找,GNUmakefile、makefile、Makefile这三个文件
- 2. 学习编写Makefile文件
- 命名规则
- 基本规则
- 变量说明
- 变量种类介绍
- 1. 自定义变量
- 扩展介绍:“=” 、“ := ” 、“ ?= "、” += ” 四种赋值的区别
- 2. 系统预定义变量
- 3. 自动化变量
- 静态规则
为什么要学习使用make工具?
在实际项目开发过程中,我们都会选择对项目进行模块化编程,即把可以重复使用的函数接口、数据结构等封装为源文件和头文件,这样可以大大提升项目程序的可移植、可读性和可维护性。但是当项目的源码数量较大时,就会导致封装的源文件和头文件的数量较多,此时就大大提高了项目的编译难度。为了提高项目的开发效率,我们必须学习使用make工具。
什么是make工具?
Make工具最早由斯图尔特·费尔德曼(Stuart Feldman)在1977年开发,作为UNIX系统的一部分推出。费尔德曼的Make工具解决了当时构建大型软件项目的复杂性问题,通过自动化依赖管理和构建过程,显著提高了开发效率。
为了获得更高的项目开发效率,所以GNU组织就在Linux系统中集成了Make工具,而Makefile就属于Make工具的一部分。Makefile是一个强大而灵活的工具,广泛应用于各种编程语言和项目中。通过定义目标、依赖和命令,Makefile可以自动化管理项目的构建过程,处理复杂的依赖关系,简化开发和部署工作。无论是编译大型C/C++项目,还是自动化生成文档和部署应用,Makefile都能提供极大的便利和效率提升。
Makefile可以理解为是一个脚本文件,用于管理项目的自动化构建过程,尤其在编译和链接复杂的源代码项目中非常有用。 Makefile文件的主要目的是简化和自动化项目的构建过程,包括编译源代码、链接生成可执行文件、清理中间文件等任务。
make工具的学习过程
1. 安装make:sudo apt install make;并学习使用make
安装make流程
虽然linux系统是支持使用make的,但是make工具在linux系统中是默认未安装,所以我们需要确认linux系统联网的情况下,通过指令:sudo apt install make 完成安装。
学习使用make指令
安装完成之后,可以选择阅读man手册来了解make指令的使用规则和注意事项,如下所示:
经过阅读man手册,我们可知:
在Linux系统中,可以通过make指令完成Makefile文件的执行,make指令是一个命令工具,是一个解释Makefile文本的命令工具,也可以说它是一个工程管理器软件工具。
总而言之,Makefile是一个类似配置文件的文本(必须命名为Makefile或者makefile),用于描述如何去编译项目中的源文件以及完成一些其他操作,而make是解释该配置文件的命令工具。
make指令的相关特点
make只会对修改过的或者可执行目标文件不存在的.c文件进行编译
思考:如何判断“修改过”?
答:通过文件修改时间来判断,即可执行文件的修改时间早于.c文件,则代表.c文件进行了修改,make便会对其重新编译生成可执行文件。具体举例说明如下:
假设一个工程有四个源文件,分别为a.c、b.c、x.c和y.c,他们最终将会链接生成可执行文件image,如下图:
在开发的过程当中,如果修改了x.c源文件,就须重新生成x.o,再重新编译链接生成image文件,但是在由成千上万个源文件组成的庞大工程,比如Linux源码,一旦对若干个源文件进行了修改,则需要花费时间挑选出需重新编译的文件,否则就需要整体工程编译必将会浪费大量的时间;而这个挑选文件的任务可以交给make工程管理器,让make按照Makefile配置文件进行挑选所需文件进行编译处理。
使用make时,若不加 -f 选项,make会依次寻找,GNUmakefile、makefile、Makefile这三个文件
make是一个shell命令,用于执行Makefile文件中的命令(gcc xxx.c -o xxx),前提是执行make指令的时候,当前路径下需要有Makefile文件,如果没有Makefile文件,则指令make指令时会报错。如下图所示:
2. 学习编写Makefile文件
命名规则
我们从man手册对于make 指令的解释说明中,可知:
用户在编写makefile文件的时候,该文件的名称应该是makefile或者Makefile,建议大家使用Makefile作为该脚本文件的名称,注意:该文件是没有拓展名的!!!
基本规则
通过GNU组织提供的Make手册可以知道,Makefile文件的基本规则是有四部分组成,分别是目标、先决条件、制表符、命令,具体的规则如下所示:
注意:
- 一套完整的规则,目标、先决条件(依赖)、制表符、命令,四个部分缺一不可,但是根据实际情况,先决条件与shell命令可以不填写。
- Makefile中可以有多套规则存在,但是应该把最终目标的规则设置为第一套规则。
- 一个目标的多个依赖文件之间,使用隔开。
- 一个目标生成可能需要多条指令,但每条命令最好独立一行写,并且必须以Tab制表符开头;制表符的个数无强制要求,但是一般以一个Tab制表符开头。
- 即是制表符,即Tab键,*不能用空格代替*;也是因为这个制表符,make才知道后面是一个需要执行的shell命令。
- 如果想要在一行编写多条执行命令,则需要使用“;”分号将每条命令隔开。如无特殊情况,建议还是一条命令写一行。
- Makefile文件中允许存在“伪目标”,其意义为,如果不在make 后面添加指定伪目标名称,则不会执行伪目标内部的命令,一般多用于清除.o文件和可执行文件。“伪目标”如上图右边所示,且不能省略“.PHONY: 伪目标名称”。
- 经过实际测试,一个Makefile文件中不应该只存在伪目标,否则使用make时,会执行文件中的第一个伪目标。
- 如果make在执行Makefile中规则时,发现其依赖条件不存在,则会在Makefile文件中进行递归查找,该查找顺序与规则编写顺序无关,只要存在,make便会继续执行;没有找的则会报错。
Makefile文件实现原理进一步阐述
- image:a.o b.o x.o y.o
- gcc a.o b.o x.o y.o -o image
-
- a.o:a.c
- gcc a.c -o a.o -c
-
- b.o:b.c
- gcc b.c -o b.o -c
- x.o:x.c
- gcc x.c -o x.o -c
- y.o:y.c
- gcc y.c -o y.o -c
复制代码 这个简单的Makefile文件总共有11行,具有5套规则,其中第1行中的image是第1个目标,冒号后面是这个目标的依赖列表(四个.o可重定位文件)。第2行的行首是一个制表符,后面紧跟着一句shell命令。
下面从第4行到第11行,也都是这样的“目标-依赖”,及其相关的Shell命令。但是这里必须注意一点:虽然这个Makefile总共出现了5个目标,但是第一个规则的目标(即image)被称之为终极目标,终极目标指的是当你执行make的时候,默认生成的那个可执行文件。
注意:如果第一个规则有多个目标,则只有第一个才是终极目标。另外,以圆点.开头的目标不在此讨论范围内,流程如下:- 1.找到由终极目标构成的一套规则。(第1行和第2行)。
- 2.如果终极目标及其依赖列表都存在,则判断他们的时间戳关系,只要目标比任何一个依赖文件旧,就会执行其下面的Shell命令。(目标与执行命令中生成的可执行文件名要一致,否则无法对比依赖列表与目标的时间戳)
- 3.如果有任何一个依赖文件不存在,或者该依赖文件比该依赖文件的依赖文件要旧,则需要执行以该依赖文件为目标的规则的Shell命令。(比如a.o如果不存在或者比a.c要旧,则会找到a.o所在行的这一套规则,并执行其下一行的Shell命令)
- 4.如果依赖文件都存在并且都最新,但是目标不存在,则执行其下面的Shell命令。---对应执行文件不存在,make会生成的规则
复制代码 变量说明
编写工程Makefile文件是为了简化编译流程的,但是目前编写的Makefile文件仍然不能满足该要求。
假设:在现有工程基础上再添加一个z.c源文件,要放在一起编译,对于上述的Makefile文件而言,可能需要重新修改一遍,另外,假设工程有1000个文件,貌似就要写1000套规则,这样是不现实的。其实Makefile提供了很多机制,比如变量、函数等来帮助我们更好更方便地组织工作(简化Makefile的编写)。在Makefile中变量的特征有以下几点:
<ol>变量和函数的展开(除规则的命令行以外),是在make读取Makefile文件时进行的,这里的变量包括了使用“=”定义和使用指示符“define”定义的变量。
变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列表、编译输出的目录列表和所有我们能够想到的事物。这里需要与C语言中的变量作区分,不再是“容器”决定“承载内容”,而是“承载内容”决定“容器”。
变量名不能包括“:”、“#”、“=”、前置空白和尾空白的任何字符串。前置空白和尾空白会造成系统在执行相应命令的语法错误,且很难直接通过观察Makefile文件找出,需要特别注意。还需要注意的是,尽管在GNU make中没有对变量的命名有其它的限制,但定义一个包含除字母、数字和下划线以外的变量的做法也是不可取的,因为除字母、数字和下划线以外的其它字符可能会在以后的make版本中被赋予特殊含义,并且这样命名的变量对于一些Shell来说不能作为环境变量使用。
变量名是大小写敏感的。变量“foo”、“Foo”和“FOO”指的是三个不同的变量。Makefile传统做法是变量名是全采用大写的方式。推荐的做法是在对于内部定义的一般变量(例如:目标文件列表objects)使用小写方式,而对于一些参数列表(例如:编译选项CFLAGS)采用大写方式,这并不是要求的。但需要强调一点:对于一个工程,所有Makefile中的变量命名应保持一种风格,否则会显得你是一个蹩脚的开发者(就像代码的变量命名风格一样),随时有被鄙视的危险。
另外有一些变量名只包含了一个或者很少的几个特殊的字符(符号)。称它们为自动化变量。自动化变量也能够大幅度提升开发效率,像“ |