文章目录
一、Makefile是啥?
在 Linux 中使用 make 命令来编译程序,特别是大程序;而 make 命令所执行的动作依赖于 Makefile 文件。最简单的 Makefile 文件如下:
1.1、了解几种文件(.o 文件和.c文件 )
.o文件是由.c文件编译得到的,一个可执行程序里面不可能只含有一个.c文件,里面肯定包含着很多其他子文件,所以我们编译得到的程序就是将很多个.c文件编译得到.o文件,然后再将很多个.o文件联合起来变成一个可执行程序!所以Makefile就是一个很好的工具!
那怎么将一个.c文件编译成一个.o文件呢?
gcc -c f1.c -o f1.o
f1.c:想要编译的.c文件
f1.o:想要编译成的.o文件
那怎么将一堆.o文件联合编译成一个可执行程序呢?
gcc f1.o f2.o main.o -o test
将f1.o,f2.o,main.o编译成一个可执行文件test
1.2、关于Makefile的写法
一个文件里面想要使用make指令来编译程序,那么里面就一定要含有一个文件(命名为Makefile或者makefile),M大小写都可以,我个人喜欢大写,看起来更好看,后面提到的关于这个名词,都会用Makefile来讲。
Makefile里面的内容格式:
注意:这里的TAB键不可以用四个空格来代替!!!
例:
f2.o:f2.c
gcc -c f2.c -o f2.o
1.3、简单使用Makefile基本指令
我们来实现一个简单的程序,关于程序内容:
f1.c:实现简单printf1打印函数
f2.c:实现简单printf2打印函数
head.h:声明printf1和printf2函数
main.c:添加head头文件,使用printf1和printf2函数
做完以上的准备工作后,我们就来编写一个简单的Makefile:
根据格式:
我们的Makefile:
test:f1.o f2.o main.o
gcc f1.o f2.o main.o -o test
f1.o:f1.c
gcc -c f1.c -o f1.o
f2.o:f2.c
gcc -c f2.c -o f2.o
main.o:main.c
gcc -c main.c -o main.o
然后使用make就可以编译得到我们的可执行程序test了
然后我们就可以运行:./test
假如我们不使用make指令,我们可以更有目的性的去编译:
例:只想要编译出f1.o文件
1.4、引入伪目标
继续在上面的测验来实现:
我们完善一下我们的Makefile,修改为:
test:f1.o f2.o main.o
gcc f1.o f2.o main.o -o test
f1.o:f1.c
gcc -c f1.c -o f1.o
f2.o:f2.c
gcc -c f2.c -o f2.o
main.o:main.c
gcc -c main.c -o main.o
clean:
rm *.o test
有前面可以知道:
make f1.o就是:
那么我们make clean不就是输入了rm *.o test指令吗?
事实确实是这样的!
我们看规律来看,都是make一个目标文件,假如我们的程序里面真的有一个clean文件呢?那会怎么样呢?
我们不就用不了这个简单的一次性删除所有.o文件和可执行程序(或者其他便捷指令)了吗?所以我们要引入伪目标:
这样就不会互相干扰了!
1.5、Makefile的优点
按照这样的编译,我们就可以写一个Makefile然后使用make指令即可编译,不用我们一条条去编译,假如我们编写了一个程序,里面有着非常复杂的代码和繁多的文件,就算用Makefile这么牛的工具也要编译个一个半个小时,编译成功之后,突然想起来一个Bug,但是只需要修改一个底层文件即可,那么修改后又得重新编译,还是要这么多时间吗?
这就要说到Makefile的优点了,Makefile会根据文件的时间戳来发现更新过的文件,减少编译工具量!
例:
第一次编译时:(会把全部文件指令都执行一遍)
假如我们的f1.c文件需要修改一下Bug,我们后来修改了一下f1.c文件(其实没有修改,这里使用了touch指令更新了一下f1.c的时间戳代替f1.c被修改):
这里只执行有关于f1.c更新的Makefile指令,可见Makefile的强大!
1.6、Makefile的使用
二、Makefile创建和使用变量
2.1、创建变量的目的
创建变量的目的就是用来代替一个文本字符串。
比如:
1、用于代替系列文件的名字
2、代替传递个编译器的参数
3、需要运行的程序。。。。。。。等等等等
这里就简单来使用一下变量。(还是基于上面的Makefile来修改)
定义一个变量,以及使用变量:|
变量定义:OBJS=man.o out.o
变量使用:$(OBJS)
test:f1.o f2.o main.o
gcc f1.o f2.o main.o -o test
f1.o:f1.c
gcc -c f1.c -o f1.o
f2.o:f2.c
gcc -c f2.c -o f2.o
main.o:main.c
gcc -c main.c -o main.o
.PHONY:clean
clean:
rm *.o test
简单使用变量优化Makefile:(这样修改工具链的时候就很方便了!)
OBJS=f1.o f2.o main.o
CC=gcc
test:$(OBJS)
$(CC) f1.o f2.o main.o -o test
f1.o:f1.c
$(CC)-c f1.c -o f1.o
f2.o:f2.c
$(CC)-c f2.c -o f2.o
main.o:main.c
$(CC)-c main.c -o main.o
.PHONY:clean
clean:
rm *.o test
2.2、自动变量的使用
$@:表示规则中的目标文件名。
$<:表示规则中的第一个依赖文件名。
$^:表示规则中的所有依赖文件名,以空格分隔。
$?:表示比目标文件更新的所有依赖文件名,以空格分隔。
$(@D):表示目标文件所在的目录名。
$(@F):表示目标文件的文件名(不包含路径)。
$+:表示规则中的所有依赖文件名,以空格分隔,并以出现的先后顺序为序。
例:(依然是在上面的Makefile修改优化)
OBJS=f1.o f2.o main.o
CC=gcc
test:$(OBJS)
$(CC) $^ -o $@
f1.o:f1.c
gcc -c $< -o $@
f2.o:f2.c
gcc -c $^ -o $@
main.o:main.c
gcc -c $^ -o $@
.PHONY:clean
clean:
rm *.o test
2.3、Makefile即时变量、延时变量
语法如下:
A = xxx // 延时变量
B ?= xxx // 延时变量,只有第一次定义时赋值才成功;如果曾定义过,此赋值无效
C := xxx // 立即变量
D += yyy // 如果 D 在前面是延时变量,那么现在它还是延时变量;
一个例子理解即时变量、延时变量:
A = $@
test:
@echo $A
上述 Makefile 中,变量 A 的值在执行时才确定,它等于 test,是延时变量。如
果使用“A :=
@
”,这是立即变量,这时
@”,这是立即变量,这时
@”,这是立即变量,这时@为空,所以 A 的值就是空。
OBJS=f1.o f2.o
OBJS+=main.o
此时OBJS=f1.o f2.o main.o
2.3、Makefile通配符
在 Makefile 中,通配符用于匹配文件名或文件路径中的多个字符,以便在规则中批量处理文件。通配符常见的有以下几种:
*:匹配零个或多个字符。
?:匹配一个任意字符。
[…]:匹配方括号内的任意一个字符。
[!..]:匹配除了方括号内的字符之外的任意一个字符。
%:%,o&%,c表示任意的.o和.c文件
在 Makefile 中,通配符通常与模式规则结合使用,以便自动化地处理一类文件。
例:
常用通配符使用:
program: *.c
gcc $^ -o $@
%.o:%.c
gcc -c $^ -o $@
在这个 Makefile 中,==*.c ==通配符匹配当前目录下所有以 .c 结尾的文件。$ ^表示所有匹配到的文件,$@ 表示目标文件。因此,program: *.c 规则会将所有的 .c 文件编译链接成一个名为 program 的可执行文件。
2.4、Makefile的常用函数
函数调用的格式如下:
$(function arguments)
1、$(foreach var, list, text)
对 list 中的每一个元素,取出来赋给 var,然后把 var 改为 text 所描述的形式。
例:
objs := a.o b.o
dep_files := $(foreach f, ( o b j s ) , . (objs), . (objs),.(f).d) // 最终 dep_files := .a.o.d .b.o.d
objs := a.o b.o
dep_files := $(foreach f, $(objs), $(f).s) // 最终 dep_files := a.o.s b.o.s
2、$(wildcard pattern)
pattern 所列出的文件是否存在,把存在的文件都列出来。
src_files := $( wildcard *.c) // 最终 src_files 中列出了当前目录下的所有.c 文件
3、$(subst from,to,text)
在文本text’中使用
to’替换每一处`from’。
比如:$(subst ee, EE, feet on the street)
结果为‘fEEt on the strEEt’。
4、$(patsubst pattern,replacement,text)
寻找text’中符合格式
pattern’的字,用replacement’替换它们。
pattern’和`replacement’中可以使用通配符。
比如:$(patsubst %.c,%.o,x.c.c bar.c)
结果为:`x.c.o bar.o’。
5、$(strip string)
去掉前导和结尾空格,并将中间的多个空格压缩为单个空格。
比如:$(strip a b c )
结果为`a b c’。
6、$(findstring find,in)
在字符串in’中搜寻
find’,如果找到,则返回值是`find’,否则返回值为空。
将分别产生值a’和
’(空字符串)。
7、$(filter pattern…,text)
返回在text’中由空格隔开且匹配格式
pattern…’的字,去除不符合格式`pattern…’的字。
比如:$(filter %.c %.s,foo.c bar.c baz.s ugh.h)
结果为`foo.c bar.c baz.s’。
8、$(filter-out pattern…,text)
返回在text’中由空格隔开且不匹配格式
pattern…’的字,去除符合格式`pattern…’的字。它是函数 filter 的反函数。
比如:$(filter %.c %.s,foo.c bar.c baz.s ugh.h)
结果为`ugh.h’。
9、$(sort list)
将‘list’中的字按字母顺序排序,并去掉重复的字。输出由单个空格隔开的字的列表。
比如:$(sort foo bar lose)
返回值是‘bar foo lose’。