用途
- 项目代码编译管理
- 节省编译项目的时间
- 一次编写终身受益
命名规则
- Makefile
- makefile
基本规则
规则中的三要素:目标、依赖、命令。
目标 --> 要生成的目标文件
依赖 --> 生成目标文件需要的一些文件
命令 --> 借助依赖文件生成目标文件的手段
格式
目标:依赖条件
命令
Makefile会把规则中的第一个目标作为终极目标。
工作原理
若想生成目标,检查规则中的依赖条件是否存在,如果不存在,寻找是否有规则用来生成该依赖文件
检查规则中的目标是否需要更新,必须检查它的所有依赖,依赖中有任意一个被更新,则目标必须更新。如果依赖文件比目标文件时间晚,则需要更新。
执行
make --> 通过makefile生成目标文件。
- 直接 make (使用makefile文件)
- make -f mm (指定一个名字不为makefile的文件)
make clean --> 清除编译生成的中间.o文件和最终目标文件
如果当前目录下有同名clean文件,则不能执行clean对应的命令。因为涉及到了比较文件新旧决定是否重编译。解决方案 --> 伪目标声明:.PHONY:clean
特殊符号
- :表示此条命令出错,make也会继续执行后续的命令。如:“-rm a.o b.o”
变量
普通变量
变量定义及赋值:obj = a.o b.o c.o
变量取值:foo = $(obj)
由 Makefile 维护的一些变量。通常格式都是大写。有些有默认值,有些没有。如
CC:默认值 cc(即gcc)
CPPFLAGS : 预处理器需要的选项 如:-I
CFLAGS:编译的时候使用的参数 –Wall –g -c
LDFLAGS :链接库使用的选项 –L -l
用户可以修改这些Makefile维护的变量的默认值,如
CC = gcc
自动变量
变量
$@
--> 规则中的目标
$<
--> 规则中的第一个依赖条件
$^
--> 规则中的所有依赖条件
模式规则
在规则的目标定义中使用 %
在规则的依赖条件中使用 %
示例:
%.o:%.c
$(CC) –c $< -o $@
// 解析
%.o 匹配该makefile文件上面的.o
%.c 匹配该makefile文件上面的.c
$(CC) 表示gcc
$< 表示依次取出依赖条件
$@ 表示依次取出目标值
函数
makefile中所有的函数必须都有返回值
wildcard
查找指定目录下指定类型的文件,一个参数
// 找到./src 目录下所有后缀为.c的文件,赋给变量src
src = $(wildcard ./src/*.c)
patsubst
匹配替换,从src中找到所有.c 结尾的文件,并将其替换为.o
// 把src变量中所有后缀为.c的文件替换成.o
obj = $(patsubst %.c ,%.o ,$(src))
// 指定.o 文件存放的路径 ./obj/%.o
ob = $(patsubst ./src/%.c, ./obj/%.o, $(src))
入门示例
示例一
环境
# ls
head.h main.c add.c sub.c mul.c makefile
makefile
#目标:依赖
app:main.c add.c sub.c mul.c
gcc main.c add.c sub.c mul.c -o app #gcc命令
该makefile的缺点是,每次生成目标app时,需要对main.c add.c sub.c mul.c四个文件进行编译。
示例二
环境
# ls
head.h main.c add.c sub.c mul.c makefile
makefile
#目标:依赖
#第一个目标作为终极目标
app:main.o add.o sub.o mul.o
gcc main.o add.o sub.o mul.o -o app #gcc命令
#目标:依赖
#非终极目标
main.o:main.c
gcc -c main.c #gcc命令
#目标:依赖
#非终极目标
add.o:add.c
gcc -c add.c #gcc命令
#目标:依赖
#非终极目标
mul.o:mul.c
gcc -c mul.c #gcc命令
#目标:依赖
#非终极目标
sub.o:sub.c
gcc -c sub.c #gcc命令
该makefile的相比示例一要好,因为每次生成目标app时,仅需要对main.o add.o sub.o mul.o四个文件进行链接即可。如果当前目录下已有main.o add.o sub.o mul.o并且是最新的,就不需要重新对main.c add.c sub.c mul.c四个文件进行编译了。
如果不是最新,仅需对不是最新的编译,然后链接即可。例如,假设add.o不是最新的(add.c的日期比add.o的日期新),那么该makefile会对add.c编译成add.o,然后和不需要重新编译的main.o sub.o mul.o链接生成app。节省了编译不需要更新的文件的时间。
示例三
环境
# ls
head.h main.c add.c sub.c mul.c makefile
makefile
#我们自定义的变量
obj=main.o add.o sub.o mul.o
target=app
#makefile维护的默认变量也可以修改
CC=gcc
CPPFLAGS=-I
#目标:依赖
$(target):$(obj)
$(CC) $(obj) -o $(target) #gcc命令
#%模式匹配
%.o:%c
$(CC) -c $< -o $@ #gcc命令
该makefile和示例二相比,用了变量、默认变量、自动变量、模式匹配等。节省了代码量,节省了重复的地方。
示例四
环境
//查看当前目录树状图
# tree
.
├── include
│ └── head.h
├── lib
│ ├── libMyCalc.a
│ └── libMyCalc.so
├── makefile
└── src
├── add.c
├── div.c
├── main.c
├── mul.c
└── sub.c
makefile
#自定义变量
target=app
INCLUDES = -I./include #头文件路径
LIBS = -MyCalc #库文件名字
LIB_PATH = -L./lib #库目录
#修改默认变量
CC=gcc
CPPFLAGS=-I
# 找到./src 目录下所有后缀为.c的文件,赋给变量src
src=$(wildcard ./src/*.c)
# 把src变量中所有后缀为.c的文件替换成.o 并且指定.o 文件存放的路径 ./src/%.o
obj=$(patsubst ./src/%.c, ./src/%.o , $(src))
#$(info $(src) ) 打印变量
#$(info $(obj) ) 打印变量
#目标:依赖
$(target):$(obj)
$(CC) $(obj) -o $(target) $(INCLUDES) #gcc命令
#目标:依赖 (%代表模式匹配)
%.o:%.c
$(CC) -c $< -o $@ $(INCLUDES) #gcc命令
#目标:依赖(空)
.PHONY:clean #声明为伪目标 解决如下场景: 如果当前目录下有同名clean文件,则不能执行clean对应的命令。因为涉及到了比较文件新旧决定是否重编译。
clean:
rm $(obj) $(target) #gcc命令
#目标:依赖(空)
.PHONY:hello #声明为伪目标 解决如下场景: 如果当前目录下有同名hello文件,则不能执行hello对应的命令。因为涉及到了比较文件新旧决定是否重编译。
hello:
-mkdir ./bb #命令前面加"-"的意义是: 如果该命令执行失败,不会报错终止,而是继续下面的命令执行
echo "hello, makefile" #打印字符串命令
执行效果:
// 执行makefile中的终极目标
# make
gcc -c src/mul.c -o src/mul.o -I./include #gcc命令
gcc -c src/main.c -o src/main.o -I./include #gcc命令
gcc -c src/add.c -o src/add.o -I./include #gcc命令
gcc -c src/div.c -o src/div.o -I./include #gcc命令
gcc -c src/sub.c -o src/sub.o -I./include #gcc命令
gcc ./src/mul.o ./src/main.o ./src/add.o ./src/div.o ./src/sub.o -o app -I./include #gcc命令
//查看当前目录树状图
# tree
.
├── app
├── include
│ └── head.h
├── lib
│ ├── libMyCalc.a
│ └── libMyCalc.so
├── makefile
└── src
├── add.c
├── add.o
├── div.c
├── div.o
├── main.c
├── main.o
├── mul.c
├── mul.o
├── sub.c
└── sub.o
// 执行makefile中的clean伪目标
# make clean
rm ./src/mul.o ./src/main.o ./src/add.o ./src/div.o ./src/sub.o app #gcc命令
//查看当前目录树状图
# tree
.
├── include
│ └── head.h
├── lib
│ ├── libMyCalc.a
│ └── libMyCalc.so
├── makefile
└── src
├── add.c
├── div.c
├── main.c
├── mul.c
└── sub.c
// 执行makefile中的hello伪目标
# make hello
mkdir ./bb #命令前面加"-"的意义是: 如果该命令执行失败,不会报错终止,而是继续下面的命令执行
echo "hello, makefile" #打印字符串命令
hello, makefile