一文掌握Makefile技能,实用为王

本文从实践中讲解Makefile,通过实例逐步解析Makefile的语法和使用,包括hello world示例、自动化变量、模式规则等。旨在帮助读者掌握编写Makefile的技巧,从简单到复杂,实现项目的有效编译和管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

从自己学习makefile的体验来讲,当我百度输入"makefile XXX"时,想要获取的一般有如下几点:

  • 在阅读项目中的makefile文件时,不知道某一句是什么意思。例如:"$@","@<"是什么?语句看着没多少内容,可是啥意思呢?一脸懵逼!!!
  • 在构建项目中,按照自己想法写出适配的makefile。看着demo中的makefile,写的好工整啊,自己写的是坨翔么!
  • 想要系统学习makefile技能,在网络上找资源。尝试看了一些,奈何这个学习makefile的系统工程看了后面忘了前面。练习不多,时间一长,它认识我,我忘了它!

因此,本文期望从实践出发,通过例子学习语法。而不是先介绍语法规则,然后再用例子。例子从简单到复杂,最终可以应用在项目中。水平有限,欢迎讨论。 本文更新中!

第一节 Makefile 版hello world
1.1 hello world

“hello world!”一般是程序员遇到的第一个程序。那如何编译输出呢?

当前目录下有文件:“main.c”。文件内容如下:

#include <stdio.h>
int main(char argc, char **argv[])
{
   printf("Hello world! ");
   return 0;
}

在终端,输入:

gcc -o hello main.c

此时,当前目录生成了相应的可执行文件:”hello“。
如果用Makefile是如何来写呢?先放Makefile文件:

hello:
   gcc -o hello main.c

对比通过命令来生成hello,只是多了一行"hello:"。makefile是通过make执行的规则脚本。
上例中通过makefile来告诉make,需要生成目标“hello”,以及如何生成-通过执行”gcc -o hello main.c“ 。
来解析下上面最简单的例子:
“hello:” makefile的目标,它存在的意义!官方说法:规则的目标
“gcc -o hello main.c”:告诉make为了目标,要干什么活。官方说法:规则的命令(注意此行gcc前面必须是一个Tab,是必须!)
这里涉及gcc的语法(gcc --help即可查看):gcc [options] file… 其中[options]:" -o Place the output into "

gcc中“-o”选项是表示生成可执行文件,但上例中还隐含了部分内容。main.c 到hello之间并不是一步到位。
还有一些中间文件,其中之一是目标文件“main.o”。
如果我们想要获取中间文件main.o,应该把命令改为:

gcc -c main.c
gcc -o hello main.o

对应makefile文件需要修改为:

hello:
	gcc -c main.c
	gcc -o hello main.o

此时,在执行命令make后,当前目录不仅仅有可执行文件hello,还有存在目标文件:main.o

现在将makefile文件换一种等价写法:

hello:main.o
	gcc -o hello main.o
main.o:
	gcc -c main.c

此时“hello:”后面多了"main.o"。此处的的"main.o",官方称为:规则的依赖
而main.o 是规则的依赖,还是新的规则的目标

以上例为基础,我们再添加一些内容,修改makefile如下:

.PHONY:clean
hello:main.o
	gcc -o hello main.o
main.o:
	gcc -c main.c
clean:
	rm *.o

上例中,增加了一个目标clean。并且clean是“.PHONY”的依赖。

".PHONY"是makefile的特殊的目标,其后的依赖被均被当伪目标。

make clean是一般用来清理生成目标的中间文件。那为何要将clean作为伪目标呢?

第一,clean是执行删除任务,并未生成目标“clean”。
第二,假设clean不是伪目标,若目录中存在命名为“clean”的文件,而clean并没有依赖。此时,make会认为已有目标文件,不会执行清理操作。

1.2 自动化变量

在稍微复杂点的makefile,经常能见到$@, $<, $^。这些称为自动化变量,上面只是部分的自动化变量。

先来认识一下:
$@:规则的目标文件名
$<:规则的第一个依赖文件名,那规则多个依赖呢?
$^:规则依赖文件列表
利用自动变量改写Makefile如下:

.PHONY:clean
hello:main.o
	gcc -o $@ $<
main.o:
	gcc -c $<
clean:
	rm *.o

例如:
hello:main.o
 gcc -o $@ < 其 中 < 其中 <@代表目标hello, $<代表第一个依赖项 main.o
假设增加另外一个源文件display.c,该文件内容如下:

#include <stdio.h>
void display(void)
{printf("[%s]: Now, this is another source file ", __FUNCTION__);return}

并在修改main.c 如下:

#include <stdio.h>

extern void display(void);
int main(char argc, char **argv[])
{
   printf("Hello world ");
   display();        
   return 0;
}

此时,main中调用函数"display"。生成目标hello因此需要增加另外一个依赖。看到这里,你可以尝试自己修改makefile来生成相应的可执行程序–hello。
makefile修改如下:

.PHONY: clean
hello: main.o display.o
	gcc -o $@ $^
main.o:main.c
	gcc -c $<
display.o:display.c
	gcc -c $<
clean:    
	rm *.o 

可以看到在hello的命令中,将原始的“ < ” , 替 换 为 了 “ <”,替换为了“ <^”。这是因为hello现在具有多个依赖项了。

1.3 模式规则

通过自动变量,将原始的makefile简化了一些。现在利用模式规则,再进一步简化makefile!如果一个目标中带有“%”,那么这个目标通过模式规则进行匹配。利用%.o:%.c 来替换“main.o:main.c”, “display.o:display.c”。
上例中的makefile可改写为:

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

观察上面这个makefile,自动化变量是不是很好的和模式规则结合起来?特别是工程较大,源文件较多的情况下,特别适合利用上例简化!

第二节 多个源文件编译
第三节 递归编译
第四节 Makefile的小九九
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值