嵌入式Linux应用开发基础知识(四)——Makefile语法

前面我们介绍了Makefile运行规则,这一章我们介绍Makefile的语法。

1. 通配符

什么是通配符?学过其他编程语言的可能只要什么叫通配符,通配符是一种特殊语句,主要有星号(*)和问号(?),用来模糊搜索文件。当查找文件夹时,可以使用它来代替一个或多个真正字符。

1.1 Makefile常见通配符

Makefile具有较多通配符,但是常见的也就几种。如下所示:

  • %.o或%.c : 该通配符表示任意的.o文件或者.c文件
  • $@ : 表示目标
  • $< : 表示第一个依赖文件
  • $^ : 表示所有依赖文件

1.2 Makefile常见通配符实验

以下为上节课我们使用的Makefile文件

test : main.o sub.o
        gcc -o test main.o sub.o
main.o : main.c
        gcc -c -o main.o main.c
sub.o : sub.c
        gcc -c -o sub.o sub.c

我们将使用通配符进行改进,实现我们使用%.o和%.c文件进行改进,

test : main.o sub.o
        gcc -o test main.o sub.o
%.o : %.c
        gcc -c -o main.o main.c

运行逻辑:

  • 当一开始运行程序时,没有main.o,那么会向下查找,发现通配符%.o, 那么此时将%替换为main,执行: gcc -c -o main.o main.c 命令
  • 生成main.o后,再次查找依赖sub.o, 发现没有改文件,那么需要往下查找,此时将%替换为sub, 执行gcc -c -o main.o main.c命令,不过意外的是,这里还是生成main.o, 最后还会编译不了 test, 系统报错,相应过程见下图。
    在这里插入图片描述
    需要对上述Makefile进行修改,使用
test : main.o sub.o
        gcc -o test main.o sub.o
%.o : %.c
        gcc -c -o $@ $<

$@表示目标 , $<表示第一个依赖文件, 那么当%为main时,此时 $@ 表示main.o, $<表示main.c。当%为sub时,此时 $@ 表示sub.o, $<表示sub.c。 具体运行过程见下图:

在这里插入图片描述
这样就能正常运行了, 不过是否考虑到一个问题,如果有很多OBJ文件呢? 难道我们需要一个一个加上XX.o吗,那么此时gcc -o test main.o sub.o这条语句得使劲加?显然这样是不智能的,我们可以使用$^ : 通配符,表示所有依赖文件,改进的程序如下:

test : main.o sub.o
        gcc -o test $^
%.o : %.c
        gcc -c -o $@ $<

当运行 gcc -o test $^ 命令时,程序会将 $^ 替换为main.o sub.o, 这样的话我们有多个源文件,只需要往sub.o后添加就可以了。下图为运行过程图:
在这里插入图片描述

2. 假想目标

我们先不提什么是假想目标,我们慢慢引入。

2.1 清除中间文件

我们希望删除中间文件(.o文件)和目标文件(test), 以确保我们将编译过程产生的文件全部删除,我们可以在Makefile文件中加入 rm *.o test命令,其中 *.o表示全部的OBJ文件,test为可执行程序。那这条语句应该怎么加入到Makefile文件中呢?我们可以仿造一下,直接写clean: (不需要依赖文件)。当我们输入make时(不带任何参数),此时系统会找到文件的第一个目标文件(第一行的开头的目标:test), 默认执行make test, 此时系统运行第一行,在清楚时,输入make clean, 系统会跳转到clean处,由于不需要依赖文件,所以任何时间都会运行,此时执行rm *.o test 以下为所有代码

test : main.o sub.o
        gcc -o test $^
%.o : %.c
        gcc -c -o $@ $<
clean:
		rm *.o test

下图为实验过程:
在这里插入图片描述

2.2 本地存在clean文件

是否想过一个问题,如果在当前目录存在clean文件,那么当我们运行make clean时会出现什么情况?
在这里插入图片描述

如上图所示:我们使用 > clean命令创建一个空的clean文件,然后再执行make clean, 此时发现显示:make: ‘clean’ is up to date. 这个报错表示clean文件是最新的, 不需要再执行了,这样就不能执行make clean操作了,此时我们可以使用.PHONY: clean 设置clean为假想目标,详细代码如下:

test : main.o sub.o
        gcc -o test $^
%.o : %.c
        gcc -c -o $@ $<
clean:
		rm *.o test
.PHONY: clean

下图为详细的试验过程
在这里插入图片描述

3. 变量

3.1 分类及定义

Makefile的变量分为即时变量(也称简单变量)、延时变量,即时变量是在定义时就确定值,而延时变量是在使用时才确定的值,在没使用 时一直为空,以下为使用举例:

  • A := xxx # A的值即刻确定,在定义时即确定
  • B = xxx # B的值使用到时才确定

3.2 实验

3.2.1 使用变量

Makefile中使用变量使用$(变量名称),代码中的all和之前讲的clean类似, 由于代码中只有一个目标文件all,所以在运行时输入make和make all的效果是一样的,echo是Makefile中的打印函数,测试代码如下:

A := abc
B = 123

all:
        echo $(A)
        echo $(B)

运行结果:
在这里插入图片描述
上述图中echo abc和echo 123是打印命令,如果不想打印,可以在echo前面加入@,代码如下:

A := abc
B = 123

all:
        @echo $(A)
        @echo $(B)

运行结果:
在这里插入图片描述
此时我们还是看不出即时变量和延时变量的区别,接着我们引入一个新变量C,代码如下:

A := $(C)
B = $(C)
C = abc

all:
        @echo $(A)
        @echo $(B)

运行结果:
在这里插入图片描述
有上图可知,A变量值为空, B变量的值为abc。
运行机制如下:运行A := $©时,由于C变量未定义,而且A为即时变量,此时A的值已经确定(为空),运行B = $©时,即使C变量未定义,但是B为延时变量,在引用时才确定值,运动C = abc,定义了延时变量C,运行@echo $(A)直接打印A的值(空),运行@echo $(B)此时先引用B,在引用时C变量已经定义,有值(abc),故此时B的值也确定了(也为abc),故打印abc。
为了更加清晰的看出变量的值,我们加上辅助信息,代码如下:

A := $(C)
B = $(C)
C = abc

all:
        @echo A = $(A)
        @echo B = $(B)

运行结果:
在这里插入图片描述

有人会想是否和C = abc的位置有关呢?我们修改代码如下:

A := $(C)
B = $(C)
# C = abc

all:
        @echo A = $(A)
        @echo B = $(B)
C = abc

运行结果:
在这里插入图片描述
由运行结果可知,可位置是没关系的,是因为Makefile文件在运行前会将所有代码读入,当运行make或make all时才生产目标文件。那么我们是否会考虑代码加载的顺序?是从上到下加载吗?我们不妨做个实验:

A := $(C)
B = $(C)
C = abc

all:
        @echo A = $(A)
        @echo B = $(B)
C = 123

观察以上代码,如果打印B=123,表示是顺序执行的,并且后面的值覆盖了前面的值
运行结果:
在这里插入图片描述
从上图结果可以看出,的确和我们的猜想是一样的。

3.3 变量运算及赋值方式

3.3.1 常见的赋值方式

  • ?= : 也是延时变量赋值的一种,区别在于如果是第一次定义才起效,如果在前面该变量已定义则忽略这句。
  • +=:附加,它是即时变量还是延时变量取决于前面的定义

3.3.2 赋值方式实验

我们做 += 的实验,运行如下代码:

A := $(C)
B = $(C)
C = abc

all:
        @echo A = $(A)
        @echo B = $(B)
C += 123

运行结果:
在这里插入图片描述
由结果可知,C还是为即时变量,因为之前是采用 =进行定义的。

我们再做 ?= 的实验,运行如下代码:

A := $(C)
B = $(C)
C = abc

D = LingTu
D ?= LLLS

all:
        @echo A = $(A)
        @echo B = $(B)
        @echo D = $(D)
C += 123

运行结果:
在这里插入图片描述
由上图可知,D = LingTu在D ?= LLLS之前定义了, 故D ?= LLLS定义不起效果。
我们再做一个对比,代码如下:

A := $(C)
B = $(C)
C = abc

D ?= LLLS

all:
        @echo A = $(A)
        @echo B = $(B)
        @echo D = $(D)
C += 123

运行结果:
在这里插入图片描述
此时D ?= LLLS为第一次定义变量,故起效果。
除了在代码中幅值外,我们也可以在输入命令时赋值,命令行的赋值先于代码中的赋值,实验如下:
在这里插入图片描述
注意命名行赋值时参数不要输入空格,命令行是默认以空格间隔参数的,第一次加入空格报错了。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零涂

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值