补充:
为什么会自动更新内容变动的?
答:因为使用make命令时,会对比目标和依赖的时间大小关系,依赖的最后更新时间比目标新则说明需要从依赖结点开始进行重新编译。
用于构建和管理项目代码的工具;可以根据文件的变动时间实现增量编译;
想要实现增量编译,用shell脚本是不好实现的,于是有了makefile这种特殊脚本专门做这个事;
可以把整个makefile构建的依赖环境想象成一个树结构,**增量编译**简单来说就是实现当某个依赖结点的内容变动时,只会重新编译从这个依赖结点到根节点(目标)这条路径上的内容,
其他路径上不重新变动,这就提高了编译的效率。
规则:
[target]:[prerequisites] // 目标是一个文件,依赖可以是多个文件
[command] // 命令前得有个tab符号
示例:
main:mian.o func.o
gcc main.o func.o -o main
main.o:main.c
gcc -c main.c -o main.o
func.o:func.c
gcc -c func.c -o func.o
伪目标就是没有依赖的目标,它每次都会被执行,并且不会生成目标文件,
所以叫伪目标;
一般用来制作清理文件、重新编译整个项目的命令,执行时需要指定名称执行make 伪目标名称
;
还可以使用.PHONY
来明确的告诉用户有哪些伪目标;
main:main.o func.o
gcc main.o func.o -o main
main.o:main.c
gcc -c main.c -o main.o
func.o:func.c
gcc -c func.c -o func.o
clean:
rm main.o func.o main
rebuild:clean main
.PHONY: clean rebuild
makefile可看作一种编程语言,它只能操作一种类型——即字符串;
1. 自定义变量:用户自己定义的变量;
规则:变量名:=内容
2. 自动变量:具有特殊含义的变量,它的含义和当前规则有关;
变量 | 说明 |
---|---|
$@ | 目标文件 |
$< | 第一个依赖文件 |
$^ | 所有依赖文件,以空格分隔 |
$? | 日期新于目标文件的所有相关文件列表,逗号分隔 |
$(@D) | 目标文件的目录名部分 |
$(@F) | 目标文件的文件名部分 |
3. 预定义变量:makefile是内部定义好的变量;
例如:CC(C编译器)、RM(删除)
前面的makefile可以优化成如下:
CC:=gcc
OBJS:= main.o func.o
main:$(OBJS)
$(CC) $^ -o $@
main.o:main.c
$(CC) -c $^ -o $@
func.o:func.c
$(CC) -c $^ -o $@
clean:
$(RM) $(OBJS) main
rebuild:clean main
.PHONY: clean rebuild
在makefile里,可以使用 %
来表示匹配任意字符;
前面的makefile可以用%通配符优化为:
CC:=gcc
OBJS:= main.o func.o
main:$(OBJS)
$(CC) $^ -o $@
%.o:%.c #先在依赖文件列表当中匹配得到后缀为.o的文件,再根据.o文件的文件名找到同名的.c文件
# 这里如果使用*.c,那么就会在当前目录所有文件里面进行匹配
$(CC) -c $^ -o $@
clean:
$(RM) $(OBJS) main
rebuild:clean main
.PHONY: clean rebuild
两个常用的内置函数
通用makefile1:打包生成一个可执行文件
OUT:=main
SRCS:=$(wildcard ./*.c)
OBJS:=$(patsubst %.c,%.o,$(SRCS))
CC:=gcc
$(OUT):$(OBJS)
$(CC) $^ -o $@
%.o:%.c
$(CC) -c $^ -o $@
clean:
$(RM) $(OBJS) main
rebuild:clean main
.PHONY: clean rebuild
通用makefile2:打包生成多个可执行文件
CC:=gcc
SRCS:=$(wildcard *.c)
EXES:=$(patsubst %.c,%,$(SRCS))
all:$(EXES)
%:%.c
$(CC) $^ -o $@ -g
clean:
$(RM) $(EXES)
rebuild: clean all
.PHONY: clean rebuild
1. 循环 for
LIST = one two three
all:
for i in $(LIST); do echo $$i; done
2. 不显示打印信息
在命令前面加上@就行了
LIST = one two three
all:
@for i in $(LIST); do echo $$i; done