官网:https://www.gnu.org/software/make/
文档:https://www.gnu.org/software/make/manual/
参考:
- https://seisman.github.io/how-to-write-makefile/overview.html
- https://makefiletutorial.com/
1. 基本使用
# the first is the final target
main: main.c foo.o bar.o
gcc main.c foo.o bar.o -o main
foo.o: foo.c
gcc -c foo.c
bar.o: bar.c
gcc -c bar.c
解释:最终目标文件main依赖于main.c和func.o,由gcc生成。func.c一样。
phony
phony即伪目标。Makefile的target默认是文件,而phony的目标是执行程序。
下面的demo支持make clearall
和make clear
指令。
CC = gcc
main: main.o
$(CC) main.o -o main
main.o: main.s
$(CC) -c main.s -o main.o
main.s: main.i
$(CC) -S main.i -o main.s
main.i: main.c
$(CC) -E main.c -o main.i
.phony: clean cleanall
clean:
rm main.i main.s main.o
cleanall:
rm main.i main.s main.o main
利用伪目标也可以生成多个文件:
all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
默认目标的特性是,总是被执行的,但由于“all”又是一个伪目标,伪目标只是一个标签不会生成文件,所以不会有“all”文件产生
规则
2. 命令
常用的echo命令,需要加@,这样就不会显示echo xxx
了。
异常处理
如果有命令出错,make就会停止。但像mkdir这种命令,出错也应该继续,这种情况可以讲个横杠前缀:
-mkdir FOO
或者make -i
。
分号
场景:第二条命令在第一条指令的基础上运行
cd /home/hchen; pwd
3. 变量
指定编译器
如果某天不用gcc了,上面的脚本就得手动一个个改。所以可以把编译器保存为变量。
赋值:
- := 普通赋值;
- = 递归赋值;
- += 追加赋值;
- ?= 条件赋值,左边没定义才会赋值;
CC := g++
main: main.c foo.o
$(CC) main.c foo.o -o main
foo.o: foo.c
$(CC) -c foo.c
其它参数,如参数,目标,也常常作为变量。
CC := g++
TARGET := main
CFLAGS = -lm -Wall -g
$(TARGET): main.c
$(CC) $(CFLAGS) main.c -o main
内置变量
Makefile有一些非常有用的变量:
- $@, 目标文件;
- $^, 所有的依赖文件
- $<, 第一个依赖文件。
- $*, 无扩展名的目标文件
- $?, 时间戳比目标文件晚的依赖文件
CC := gcc
main: main.o
$(CC) $^ -o $@
%.o: %.c
$(CC) -c $^ -o $@
通配符
# 所有.c
objects := $(wildcard *.c)
目标变量
可以理解为局部变量,只在目标对应的规则里生效。
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o
prog.o : prog.c
$(CC) $(CFLAGS) prog.c
foo.o : foo.c
$(CC) $(CFLAGS) foo.c
bar.o : bar.c
$(CC) $(CFLAGS) bar.c
4. 语法
条件语句
ifeq, ifneq
ifeq ($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
ifdef
bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif
函数
demo:
$(subst ee,EE,feet on the street)
5. 其它
一些参数
-f指定makefile
编译子目录Makefile
subsystem1:
cd subdir && $(MAKE)
subsystem2:
$(MAKE) -C subdir
$(MAKE)是带了参数的命令行
冲突情况
如果有main1.c和main2.c各有一个main,就只编译第一个。
# 执行
main1: main1.c func.o
gcc main1.c foo.o -o main1
# 不执行
main2: main2.c func.o
gcc main2.c foo.o -o main2
# 执行
func.o: func.c
gcc -c func.c
clean:
rm *.o main1 main2
使用all就可以生成两个了.
all: main1 main2
main1: main1.c func.o
gcc main1.c foo.o -o main1
main2: main2.c func.o
gcc main2.c foo.o -o main2
func.o: func.c
gcc -c func.c
clean:
rm *.o main1 main2