Makefile中3个常用自动化变量

在Makefile,有三个常用也很好用的自动化变量:$@、$^、$<,所谓自动化变量是在模式规则中定义的一系列文件自动挨个的去除,直至所有的符合模式的文件都取完。这么说可能比较绕,我们可以简单直白的理解,我们写的应用程序,稍微简单的也要有几个文件(.c、.h等),那么复杂的可能有几百,几千,甚至几十万(kernel),我们在编写Makefile时,如果逐个文件去编译,链接,那是绝不可能的,所以我们需要一种自动规则,一条命令就能干很多重复的事儿,在C编程里,我们有各种循环操作,比如for、while、do等等。那么自动化变量就是用于Makefile中能够自动循环执行命令的变量,而且这些自动化变量还特别“智能”,会自动识别、切换。

先简单的说明一下这3个自动化变量的含义:

$@

表示目标集,“集”的意思就是组合,全部,有多个目标,$@就是目标集合。

$^

所有依赖目标的集合,注意,这里说的是“依赖”,也就是目标的组成元素。

$<

依赖目标中的第一个目标名字,也就是上面说的$^中的第一个元素。

特别注意:我们可能会有一个疑惑,这个自动化变量的应用范围是什么?是整个Makefile吗?答案是:不是!

我简单的认为:自动化变量的应用范围是当前目标,Makefile中可以有很多的目标,但是终极目标只有1个,就是第一个,而其他的组成目标,也是需要编译的,所以我们在使用自动化变量时,它的应用范围只限于当前目标,这一点我们详细举例来说:

假设我们有一个应用工程,包含5个文件,分别为 main.c func1.c func1.h func2.c func2.h,代码如下:

/*------------------- main.c ---------------------*/
#include "func1.h"
#include "func2.h"

int main(int argc, char **arcv)
{

func1("hello");

func2("hello");
}

/*------------------- func1.c ---------------------*/
#include "func1.h"
#include <stdio.h>

void func1(char *print_str)
{
printf("this is func1 %s \n", print_str);
}

/*------------------- func1.h ---------------------*/
void func1(char *print_str);

/*------------------- func2.c ---------------------*/
#include "func2.h"
#include <stdio.h>

void func1(char *print_str)
{
printf("this is func2 %s \n", print_str);
}

/*------------------- func2.h ---------------------*/
void func2(char *print_str);

Makefile编写如下:

main : main.o func1.o func2.o
gcc main.o func1.o func2.o -o main

main.o : main.c func1.h func2.h
gcc -c main.c

func1.o : func1.c func1.h
gcc -c func1.c

func2.o : func2.c func2.h
gcc -c func2.c

.PHONY : clean
clean :
rm *.o

这么编写是比较中规中矩的,如果考虑到Makefile的隐晦规则(潜规则),只需要前两行就行了,因为make会自动推导。

如果文件不是5个,而是50、500个或者更多,我们很显然不能这么干,这个时候就需要自动化变量了,我们可以使用这3个自动化变量先把上面的Makefile初步简化一下,如下所示:

main : main.o func1.o func2.o
gcc $^ -o $@

main.o : main.c func1.h func2.h
gcc -c $<

func1.o : func1.c func1.h
gcc -c $<

func2.o : func2.c func2.h
gcc -c $<

.PHONY : clean
clean :
rm *.o main

看到这里,可能会疑惑,这叫哪门子简化,不就是符号替换吗,别着急,上面的代码只是为了说明这3个自动化变量代表什么,因为能替代,恰恰就说明了他们的作用,这里再重复,分析下:

$@ —— 目标集,首先必须是“目标”,其次这是个集,可以是1个,也可以是多个。

$^—— 所有的依赖对象集,如果觉得拗口,可以理解为,所有的组成元素,这是个集,可以是1个,也可以是多个。

$< —— 依赖对象集中的第一个,这里要说明一下,gcc只能编译*.c和.S文件,不能编译*.h头文件的,所以上面的

gcc -c $< 表示的是 gcc -c *.c,如果我们写成 gcc -c $^,会报错,因为这个展开来变成了gcc -c *.c *.h

从这里也可以验证之前说的话,自动化变量很智能,其应用范围仅限于当前目标,不是全范围,如果是全范围就乱套了。所以每个目标都可以使用这些自动化变量。

那么我们进一步简化Makefile,如下:

1 main : main.o func1.o func2.o
2 gcc $^ -o $@
3 
4 .c : .o
5 gcc -c $<
6 
7 .PHONY : clean
8 clean :
9 rm *.o main

第一个目标的命令中$^代表所有依赖元素集,也就是main.o func1.o func2.o,$@代表最终的目标集,这里只有一个:main

.c : .o命令是Makefile中 静态模式的用法,这个名字我觉得翻译的不够形象,还不如叫自动循环命令呢,这里简单的说下,就是告诉make,所有的.o文件有对应名字的.c文件编译获得,所以下面的命令中 $<表示第一个依赖元素,到这里才会豁然开朗,

.c:.o会执行很多次(取决于有多少个.c文件),每一次执行,“$<”都代表第一个元素,也就是所谓的.c文件。非常灵活、智能。

所以这条命令还可以修改如下:

.o : .c
gcc -c $< -o $@

看到了把,原来是.c : .o,现在反过来了,变成了 .o : .c,但是对应命令也变了,展开来就是 gcc -c xx.c -o xx.o,这条命令的逻辑仍然是对的,到这里应该就能理解自动化变量的“自动”二字了,所谓自动,就是智能,管你写的什么顺序,我万变不离其宗,只按照它的理解去做,只去第一个($<)、所有依赖元素($^)、所有目标集($@),而且应用范围仅限于当前命令行对应的目标依赖关系,下一个目标会再智能转换。

————————————————
原文链接:https://blog.csdn.net/u012351051/article/details/88594616

posted @ 2022-10-21 11:16  云山漫卷  阅读(365)  评论(0编辑  收藏  举报