make,Makefile简易教程

一、概述

make是一个类UNIX系统下的编译命令,也可以理解为一个项目管理工具,通过make可以按照自己指定的编译命令编译整个项目,相当于将在命令行的编译命令按序执行,省去了反复键入编译命令的麻烦。除此之外,如果手动执行编译命令,不仅费时难以记忆,最重要的是每执行一次编译命令,项目中的整个文件都要重新编译,即使是未修改过的文件,这在大型项目中是难以忍受的。而make就提供了一种完美的解决方案,它将要执行的编译命令通过特定的语法组织到Makefile文件中,每次只要执行make命令,就可以完成整个项目的构建。

make执行时是根据文件的时间戳来选择要编译的文件。比如某个项目中你只是修改了其中一个文件,这个文件的时间戳就会晚于你所要生成的目标文件的时间戳,因为最终的目标文件肯定是最后生成的。如此,便不回去重新编译那些未修改的文件,省去了大量不必要的编译时间。所谓Makefile,就是将make要执行的编译命令写入到此文件中,这个文件也可以是其它名字,make时可以使用make -f filename来进行构建,当然,减少不必要的麻烦,还是统一命名为Makefile或者makefile为好。Makefile中指定了要执行的编译命令以及依赖的相关文件,执行make时,会在命令执行目录下寻找Makefile文件,根据该文件来构建整个项目。在类UNIX系统下的C/C++项目,make都是不可或缺的。

二、Makefile基础

一条规则的基本要素:

  • 目标:执行make要生成的目标文件
  • 依赖:用来生成目标文件的依赖文件
  • 命令:即如何用依赖文件生成目标文件的规则
# 一条简单的规则
# test为目标,test.c为依赖
# gcc一行即是命令,须以TAB键开头
test: test.c
	gcc test.c -o test 

在一条规则中,目标必须存在,依赖和命令两个中至少要存在一个。在具体的Makefile编写之前,先讲一下Makefile文件中的几个基础知识,后续编写实例会用到

一条规则:模式规则

模式规则:模式规则类似于普通规则。只是在模式规则中,目标名中需要包含有模式字符“%”,包含有模式字符“%”的目标被用来匹配一个文件名,“%”可以匹配任何非空字符串。规则的依赖文件中同样可以使用“%”,依赖文件中模式字符“%”的取值情况由目标中的“%”来决定。例如:对于模式规则“%.o : %.c”,它表示的含义是:所有的.o文件依赖于对应的.c文件。我们可以使用模式规则来定义隐含规则。

两个函数

通配符函数:$(wildcard <pattern>)

功能:返回pattern指定的文件

用法:

# 获取当前目录下的.c文件,并存储到变量SRC中
SRC = $(wildcard *.c)

字符串替换函数:$(patsubst <pattern> <replacement> <text>)

功能:匹配text中符合pattern的单词(单词以“空格”、“Tab”或“回车”“换行”分隔),匹配成功用replacement进行替换,返回被替换过后的字符串。这里,pattern可以包括通配符%,表示任意长度的字符串,如果replacement也包含%,那么replacement中的%将是pattern中那个%所代表的字符串

用法:

# 将SRC中的.c文件替换为同名的.o文件然后返回
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
# 或者 OBJ= $($(SRC):%.c=%.o)
三个自动变量
$@ # 规则中的目标 
$< # 所有依赖
$^ # 第一个依赖
伪目标

除了以上几点,Makefile中还有一个比较重要的概念就是伪目标,我们通常再重新执行make之前, 会执行make clean命令,这个命令的作用就是删除一些规则指定的文件,如生成的中间文件以及可执行文件等,但是,如果Makefile所在文件下恰好存在一名为clean文件时,则不会执行相应的删除文件命令,解决办法就是将clean声明为伪目标,如此,不管当前文件夹是是否存在clean文件,执行make clean时都会执行相应的删除命令,其声明格式为:

.PHONY: clean

三、Makefile实例编写

有了以上基础知识,便可以编写一些基础的Makefile文件了,下面通过一个具体的实例来演示:

创建Makefile

所需文件:add.c,min.c,main.c

# 依赖.o文件生成可执行文件app
app: main.o add.o min.o
	gcc main.o add.o min.o -o app
# 依赖.c文件生成.o文件
main.o: main.c
	gcc -c main.c -o main.o
add.o: add.c
	gcc -c add.c -o add.o
min.o: min.c
	gcc -c min.c -o min.o
# clean目标用于删除中间文件
clean:
	rm -rf main.o add.o min.o

注意:Makefile文件按照从上到下的规则依次执行的,并且将文件的第一个目标作为终极目标,执行结果:

image-20210627003431690

使用自定义变量
# 声明自定义变量 obj
obj = main.o add.o min.o
# 使用 $ 取出自定义变量的值
app: $(obj)
	gcc main.o add.o min.o -o app
# 依赖.c文件生成.o文件
main.o: main.c
	gcc -c main.c -o main.o
add.o: add.c
	gcc -c add.c -o add.o
min.o: min.c
	gcc -c min.c -o min.o
# clean目标用于删除中间文件
clean:
	rm -rf $(obj)
使用自动变量
target = app
obj = main.o add.o min.o

$(target): $(obj)
	gcc $^ -o $@
# 依赖.c文件生成.o文件
%.o: %.c
	gcc -c $< -o $@
# clean目标用于删除中间文件
clean:
	rm -rf $(obj) $(target)
使用Makefile变量
target = app
obj = main.o add.o min.o

CC = gcc
# 编译时使用的参数,如 -c, -g, -Wall, -I 等
CPPFLAGS = 
CFLAGS = -c
# 链接库使用的选项 -L, -l
LDFLAGS = 

$(target): $(obj)
	$(CC) $^ -o $@

%.o: %.c
	$(CC) $(CFLAGS) $< -o $@

clean:
	rm -rf $(obj) $(target)
使用函数
src = $(wildcard ./*.c)
obj = $(patsubst ./%.c, ./%.o, $(src))
target = app

ALL:$(target)

CC = gcc 
CFLAGS = -c

$(target): $(obj)
	$(CC) $^ -o $@

%.o: %.c
	$(CC) $(CFLAGS) $< -o $@

clean:
	# 命令前加'-',执行出错时会继续执行
	-rm -rf $(obj) $(target)
# 声明伪目标
.PHONY: clean ALL
编译不同目录下文件

重新组织文件结构,源文件存放在src目录下,头文件在inc目录下,中间的.o文件存放在obj目录下,可执行文件放在bin目录下,如下图所示:

image-20210627014946600

src = $(wildcard ./src/*.c)
obj = $(patsubst ./src/%.c, ./obj/%.o, $(src))
inc_path = ./inc/
target = ./bin/app

ALL:$(target)

CC = gcc 
CFLAGS = -c 

$(target): $(obj)
	$(CC) $^ -o $@

./obj/%.o: ./src/%.c
	$(CC) $(CFLAGS) -I $(inc_path) $< -o $@

clean:
	-rm -rf $(obj) $(target)

.PHONY: clean ALL

执行结果如下:

image-20210627015913432

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>