Linux嵌入式开发——Makefile基本语法

2023-02-10 14:46:00 浏览数 (1)

文章目录

  • Linux嵌入式开发——Makefile基本语法
    • 一、Makefile 规则格式
    • 二、Makefile 变量
      • 2.1、赋值符“=”
      • 2.2、赋值符“:=”
      • 2.3、赋值符“?=”
      • 2.4、变量追加“ =”
    • 三、Makefile 模式规则
    • 四、Makefile 自动化变量
    • 五、Makefile 伪目标

Linux嵌入式开发——Makefile基本语法

接下来我们来详细介绍一下基本的Makefile语法,以便于我们以后使用make工具。

一、Makefile 规则格式

Makefile文件里面其实就是由一系列的规则组成的,所以我们掌握这些规则格式非常重要,这些规则格式如下:

代码语言:javascript复制
目标... : 依赖文件集合...
	命令 1
	命令 2
	......

我们接下来详细介绍一下这些规则格式到底代表着什么意思。首先就是目标,也可以说是目的,比如说:“写读书笔记”,这就是我们的一个目标。

其次就是依赖文件集合,就相当于,我们要完成这个目标所需要依赖什么。比如“写读书笔记”这个事情,就需要依赖于“笔”,“书”,“书桌”等等。

最后就是命令,就相当于,在依赖的东西都存在的时候,需要执行哪些步骤,比如“写读书笔记”这个目标,在有书,有书桌的情况下,就该坐上去,然后打开书,开始用笔记录,这就是命令。

但是如果依赖文件集合更新了,那么目标也要更新,比如你看的书换了一本,那么这个读书笔记自然也要更新。接下来,我们来分析一下上篇文章的Makefile文件里面的代码。

代码语言:javascript复制
# 要得到main目标,需要main.o input.o calcu.o,需要执行
# gcc -o main main.o input.o calcu.o命令,后面的都是这样的。
main: main.o input.o calcu.o
	gcc -o main main.o input.o calcu.o
	
main.o: main.c
	gcc -c main.c
	
input.o: input.c
	gcc -c input.c
	
calcu.o: calcu.c
	gcc -c calcu.c

# clear需要执行的命令
clean:
	rm *.o
	rm main

我们具体来分析一下make 命令在执行这些 Makefile语句的时候其执行步骤。

首先更新第一条规则中的 main,我们一般把第一条规则的目标当作默认目标,只要默认目标更新了那 么就认为 Makefile 的工作完成了。在第一次编译的时候由于 main 还不存在,因此第一条规则会执行, 第一条规则依赖于文件 main.o、input.o 和 calcu.o 这个三个.o 文件,这三个.o 文件目前还都没有,因此必须先更新这三个文件。

然后make 会查找以这三个.o 文件为目标的规则并执行。以 main.o 为例,发现更新 main.o 的是第二条规则,因此会执行第二条规则,其它两个.o 文件同理。

最后一个规则目标是 clean,它没有依赖文件,因此会默认为依赖文件都是最新的,所以其对应的命令不会执行,当我们想要执行 clean 的话可以直接使用命令“make clean”,执行以后就会删除当前目录下所有的.o 文件以及 main,因此 clean 的功能就是完成工程的清理。

我们在来总结一下 Make 的执行过程:

  1. make 命令会在当前目录下查找以 Makefile命名的文件。
  2. 当找到 Makefile 文件以后就会按照 Makefile 中定义的规则去编译生成最终的目标文件。
  3. 当发现目标文件不存在,或者目标所依赖的文件比目标文件新的话就会执行后面的命令来更新目标。

命令列表中的每条命令必须以 TAB 键开始,不能使用空格!

二、Makefile 变量

接下来呢,我们要介绍一下Makefile里面的变量,Makefile不同于C语言,变量有许多类型,比如 int、char 等各种类型,Makefile 中的变量都是字符串。那我们就开始看一下如何使用,Makefile 中的变量吧!

首先我们先看一下这样一段代码:

代码语言:javascript复制
main: main.o input.o calcu.o
	gcc -o main main.o input.o calcu.o

我们可以看到,里面“main.o input.o calcu.o”这一大段重复了,那么怎么使用变量去解决这个问题呢?且看下面的代码:

代码语言:javascript复制
objects = main.o input.o calcu.o
main: $(objects)
	gcc -o main $(objects)

Makefile 中变量的使用方法就是直接赋值,引用方法是“ $ (变量名) ” ,还算是比较简单的,这两段代码是等效的。

2.1、赋值符“=”

我们首先看一下下面这段代码,大家猜一下他的输出值为多少。

代码语言:javascript复制
name = ygr
curname = $(name)
name = yegaorui

print:
	@echo curname: $(curname)

根据我们所学的C语言,我们可以得知,最后的输出结果肯定是“ygr”,但是,我们来具体看一下输出的是什么:

代码语言:javascript复制
curname: yegaorui

非常的Amazing,结果竟然是这个,这是为什么呢?我们具体来看一看赋值符“=”的具体含义:**通过赋值符“=”赋值的变量的真实值取决于它所引用的变量的最后一次有效值。**所以最后的结果就是这个。

我们可以看到“echo”前面加了个“@”符号,这是因为 Make 在执行的过程中会自动输出命令执行过程,在命令前面加上“@”的话就不会输出命令执行过程,这样便于我们直观地去看结果。

2.2、赋值符“:=”

我们接下来来看一下赋值符“:=”,他的输出就比较正常了,和我们的C语言输出一样。

代码语言:javascript复制
name = ygr
curname := $(name)
name = yegaorui

print:
	@echo curname: $(curname)

输出:

代码语言:javascript复制
curname: ygr

2.3、赋值符“?=”

这个赋值符还是比较好玩的,我管他叫判断赋值符,他的作用就是:如果变量前面没有被赋值,那么此变量就给他赋这个值, 如果前面已经赋过值了,那么就使用前面赋的值。具体代码如下所示:

代码语言:javascript复制
name = ygr
curname ?= yegaorui
name = yegaorui

print:
	@echo curname: $(curname)

输出:

代码语言:javascript复制
curname: yegaorui

2.4、变量追加“ =”

其实就相当于字符串连接符,比较简单,就不详细介绍了。

代码语言:javascript复制
# 两者是相等的
objects = main.o input.o calcu.o

objects = main.o inpiut.o
objects  = calcu.o

三、Makefile 模式规则

我们看前面的代码也能看到,代码中有很多类似的语句,比如下面的这几条语句,长得极其相似,那么我们有什么办法,能够简化这几条语句呢?答案是肯定的,不过我们需要引入我们接下来要介绍的Makefile 模式规则了。好啦,就让我们开始吧!!!

代码语言:javascript复制
main.o: main.c
	gcc -c main.c
	
input.o: input.c
	gcc -c input.c
	
calcu.o: calcu.c
	gcc -c calcu.c

其实我们所谓的模式规则就是在目标中引入了一个通配符“%”,目标中的“%” 表示对文件名的匹配,“%”表示长度任意的非空字符串,比如“%.c”就是所有的以.c 结尾的文件。

而当“%”出现在目标中的时候,目标中“%”所代表的值决定了依赖中的“%”值,比如下面代码就是等价的:

代码语言:javascript复制
# 等价
main.o: main.c

%.o : %.c

但是后面的“gcc -c main.c”,却不能写成“gcc -c %.c”,因为gcc后面一定要跟着具体的文件。那么怎么解决这个问题呢?我们马上来揭晓。

四、Makefile 自动化变量

自动化变量:这种变量会把模式中所定义的一系列的文件自动的挨个取出,直至所有的符合模式的文件都取完,自动化变量只应该出现在规则的命令中。

我们来具体看一看都有哪些自动化变量:

自动化变量

描述

$@

规则中的目标集合,在模式规则中,如果有多个目标的话,“$@”表示匹配模式中定义的目标集合。

$%

当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空。

$<

依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%”)定义的,那么“$<”就是符合模式的一系列的文件集合。

$?

所有比目标新的依赖目标集合,以空格分开。

$^

所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,“$^”会去除重复的依赖文件,值保留一份。

$

和“$^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件。

$*

表示目标模式中"%"及其之前的部分,如果目标是 test/a.c,目标模式为 %.c,那么“$*”就是 test/a。

根据我们刚刚学习的自动化变量,我们可以得到下面的等价语句:

代码语言:javascript复制
# 等价
main.o: main.c
	gcc -c main.c
	
input.o: input.c
	gcc -c input.c
	
calcu.o: calcu.c
	gcc -c calcu.c
	
# $<就相当与依赖文件的第一个文件
%.o : %.c
	gcc -c $<

那么接下来,我们的代码就可以修改为下面的代码:

代码语言:javascript复制
objects = main.o input.o calcu.o
main: $(objects)
	gcc -o main $(objects)
	
%.o : %.c
	gcc -c $<

clean:
	rm *.o
	rm main

五、Makefile 伪目标

接下来我们要来介绍一下伪命令,其实我们之前就接触过,那就是我们的clear命令,接下来我们就来详细介绍一下吧!!!

我们知道,当我们输入“make clean”时,就能执行clear下面的语句,但是当我们有这个“clear”文件时,是不会执行的,因为clear文件一直都是最新的。所以我们需要定义一下伪命令,具体语法如下所示:

代码语言:javascript复制
.PHONY : clean

接下来。我们就能将我们的代码改成如下的代码了:

代码语言:javascript复制
objects = main.o input.o calcu.o
main: $(objects)
	gcc -o main $(objects)
	
%.o : %.c
	gcc -c $<

.PHONY : clean
clean:
	rm *.o
	rm main

好了,这就是关于markfile的基本语法了,后面我们还会再详细深入地介绍。

0 人点赞