在linux/unix开发环境中,makefile文件描述了一个特定编译系统所需要的策略,而make工具则是通过解析makefile文件并执行相应的命令来帮助我们构建其编译系统。大致原理如下:
1.makefile文件帮助我们记录了整个项目工程的所有需要编译的文件列表,这样我们在编译时仅需要输入简单的make命令就能编译出我们期望的结果;
2.makefile文件反映了整个项目中各个模块的依赖关系,这样我们改动了某些源文件后,仅需简单的输入make命令,make工具就会根据makefile文件里描述的依赖关系帮助我们分析哪些模块需要重新编译,并执行相应的操作。
makefile就是一个简单的文本文件,它基本上就是由一条条的规则构成。下面,我们就来看一下makefile里的最基本的语法单元:规则。一条makefile的规则构成如下:
target:prerequisites
<tab> command1
<tab> command2
.....
<tab> commandN
target: 规则的目标,可以简单理解为这条规则存在的目的是什么。通常是程序中间或者最后需要生成的文件的文件名,也可以不对应具体的文件,而仅仅就是个概念上的规则目标。
prerequisites: 规则的依赖列表,可以简单的理解为要达到本条规则的目标所需要的先决条件是什么。可以是文件名,也可以是其他规则的目标。
command: 规则的命令,可以简单地理解为当目标所需要的先决条件的满足了之后,需要执行什么动作来达成规则的目标。规则的命令其实就是shell命令。一条规则中可以有多行命令。(特别注意:每行命令都必须以tab键开始!)
要解释make的工作机理,需要解决以下3个问题:
1.make命令如何使用 ;
2.make从哪读取makefile;
3.make如何解析执行makefile文件的规则。
make命令的基本使用范式如下:
$ make [ -f makefile ] [ options ] ... [ targets ] ...
使用make命令的最简单的方式主要有以下四种形式:
1.不带任何参数,直接执行make:
$ make
2.指定makefile文件:
$ make -f
3.指定 makefile 目标:
$ make
4.到指定目录下执行make:
$ make -C
在执行make的时候,我们可以带上 -f
在读取完makefile的内容后,make工具并不是一条一条地去执行makefile里的规则,而是以某条规则为突破口,以“多米诺骨牌”的方式去执行makefile里的规则。而这条作为突破口的规则的目标,称为终极目标 。我们可以在执行make时以参数的形式指定终极目标,从而执行作为突破口的规则。如果我们不显式地指定终极目标,make一般情况下将选择makefile的第一条规则的目标作为终极目标。
下面介绍一下make解析makefile的流程。
假设有一个mekefile文件,其文件框架如下所示:
终极目标:依赖A 依赖B 依赖C
终极目标命令
依赖A:子依赖A1 子依赖A2
依赖A命令
依赖B:子依赖B1 子依赖B2
依赖B命令
依赖C:子依赖C1 子依赖C2
依赖C命令
步骤一: 以终极目标为树根,解析出整颗依赖树:
步骤二: 对整颗依赖树以从底到上,从左到右的顺序,解析执行每一条规则:
本节将通过构建一个简单的c语言项目工程project_demo1来理解makefile的基本概念。
先来看一下project_demo1的整体目录结构。
构建项目的步骤如下:
步骤一: 创建工程目录
步骤二: 创建main.c文件,代码如下:
extern void hello(); //声明hello()函数是在文件外部定义的
int main()
{
hello();
return 0;
}
步骤三: 创建hello.c文件,代码如下:
#include
void hello()
{
printf("Hello, csdn!\n");
}
步骤四: 编写makefile
在每次编写makefile之前都需要先分析清楚整个工程各个模块的依赖关系,及相应的生成命令。project_demo1工程最终生成的是一个叫hello的可执行文件,而该可执行文件是使用gcc命令编译main.c和hello.c生成的。整个工程的关系依赖图如下:
hello: main.c hello.c
gcc hello.c main.c -o hello
接下来在终端里输入make,终端打印出了makefile里的依赖命令,并生成了可执行文件hello.
运行hello,终端成功打印出我们所期望的语句,说明makefile编写正确。
本节将创建另一个项目project_demo2,其文件之间的依赖关系相比上一个项目会更复杂一点。操作过程与上一个项目类似。先来看一下项目的整体目录结构:
main.c的代码如下:
#include
#include "hello.h"
int main()
{
printf("%s\n", HELLO);
hello();
return 0;
}
hello.c的代码如下:
#include
#include "hello.h"
void hello()
{
printf("%s\n", HELLO);
}
hello.h的代码如下:
#ifndef __HELLO_H__
#define __HELLO_H__
#define HELLO "Hello, csdn!"
#define GREET "How are you?"
extern void hello();
#endif
由此编写makefile如下:
hello: main.o hello.o
gcc main.o hello.o -o hello
main.o: main.c
gcc -c main.c -o main.o
hello.o: hello.c
gcc -c hello.c -o hello.o