Linux应用开发基础知识——Makefile初级菜鸟教程(九)

一、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’。

Makefile 是一种用来自动化编译程序的工具,使用它可以简化编译过程,提高编译效率。下面是一个简单的 Makefile 菜鸟教程。 假设我们的工程目录结构如下: ``` project ├── src │ ├── main.c │ ├── func1.c │ └── func2.c ├── include │ ├── func1.h │ └── func2.h └── bin ``` 其中,src 目录包含了源代码,include 目录包含了头文件,bin 目录用于存放编译后的可执行文件。 我们的目标是编译出一个名为 project 的可执行文件。下面是一个简单的 Makefile: ```makefile CC = gcc CFLAGS = -Wall -I./include LDFLAGS = -lm SRCS := $(wildcard src/*.c) OBJS := $(patsubst %.c, %.o, $(SRCS)) TARGET = bin/project all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ clean: rm -rf $(OBJS) $(TARGET) ``` 接下来逐行解释每个部分的作用: - `CC` 定义编译器的路径和名称。 - `CFLAGS` 定义编译器的编译选项。这里使用了 `-Wall` 表示开启所有警告信息,`-I./include` 表示头文件的路径为 `./include`。 - `LDFLAGS` 定义链接器的链接选项。这里使用了 `-lm` 表示链接 `libm` 库。 - `SRCS` 使用 `wildcard` 函数获取 `src` 目录下的所有 `.c` 文件。 - `OBJS` 使用 `patsubst` 函数将 `.c` 文件名替换成 `.o` 文件名。 - `TARGET` 定义目标可执行文件的路径和名称。 - `all` 是一个伪目标,表示默认编译所有目标。 - `$(TARGET)` 是一个依赖目标,表示编译目标可执行文件。它依赖于所有的 `.o` 文件。 - `$(OBJS)` 是一个中间变量,表示所有的 `.o` 文件。 - `$@` 表示目标文件名,`$^` 表示所有依赖文件名。 - `%.o: %.c` 是一个模式规则,表示将所有 `.c` 文件编译成 `.o` 文件。 - `clean` 是一个伪目标,表示清除所有中间文件和目标文件。 使用方法: 1. 将上述代码保存为 `Makefile` 文件。 2. 在终端中进入工程目录。 3. 执行命令 `make`,即可自动编译出目标可执行文件。 4. 执行命令 `make clean`,即可清除所有中间文件和目标文件。 以上就是一个简单的 Makefile 菜鸟教程,希望能对你有所帮助。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值