makefile
2018.5.12 by jianfeng
基础
最终目标:依赖A 依赖B
最终目标命令
依赖A: 子依赖A1 子依赖A2
依赖A命令
依赖B:子依赖B1 子依赖B2
依赖B命令
......
# ------------------------------
# 执行顺序说明:
# 从底向上,从左到右。
指令
- make <target>
指定makeifle目标
- make -f <makefile>
指定makefile文件
- make -C <subdir> <target>
到指定目录下执行make
语法
赋值
- :=
直接展开,例如 a=$(b),那么变量b中的值,直接赋值给a
- =
递归展开,例如 a=$(b),那么只有在a被调用的时候才会展开b的值。
- +=
追加操作符
- ?=
# 注意:
# 1. $字符在makefile有特殊用途,如果在echo中作为普通字符,那么使用$$
# 2. $@ 在bash中也有特殊用途,如果在在echo中作为普通字符,那么使用\$$@
b=$(a)
c:=$(a)
d?=$(a)
a=123
all: A B
@echo "all ->"
@echo "----------"
@echo a=${a}
@echo b=${b}
@echo c=$(c)
@echo d=$(d)
@echo "\$$@ = "$@
@echo "$$< = "$<
@echo "$$^ = "$^
A: A1 A2
@echo "A ->"
B:
@echo "B ->"
A1:
@echo "A1 -> "
A2:
@echo "A2 -> "
# ------------------------------
# 上述makefile示例的结果:
# ------------------------------
A1 ->
A2 ->
A ->
B ->
all ->
----------
a=123
b=123
c=
d=123
$@ = all
$< = A
$^ = A B
变量引用
- $(obj)
makefile中引用变量。(在bash中,obj是等价的)
- ${CC}
makefile引用变量(常用)。但在bash中。${CC}是变量引用,$(CC)是命令
- $@
代表规则中的目标文件名
- $<
代表规则的第一个依赖的文件名
- $^
代表规则中所有依赖的列表,文件名称用空格分割
变量的替换
- ${VAR:A=B}
将变量VAR中所有的字符串A用B来替换
src := main.c test.c
obj := $(src : .c=.o)
静态规则模式
- %.o
其中%是makefile的通配符。%.o表示当前目录下的所有.o文件
$(objects) : %.o : %.c
${CC} -o $@ -c $<
伪目标
- .PHONY: <伪目标>
- 避免目标和实际文件名称冲突
当存在clean文件时,由于该规则没有依赖,因此目标被认为是最新的,从而不执行规则下的命令。例如提示"make: 'clean' is up to date."
- 提高执行make的效率。
- 多目录结构,并行处理且方便定位错误。例如示例1
- 一个makefile生成多个可执行文件
### 示例1
SUBDIRS = Source Libary Demo
subdirs:
for dir in $(SUBDIRS); do $(MAKE) -C $$dir; done
# 在子目录在执行make
# 但是上述操作方式有两点问题:
# 1. 如果make出错时,make不会停止。不好定位哪个make出的错。
# 2. 没有用到make对目录的并行处理功能
# 换成如下方式:
SUBDIRS = Source Libary Demo
.PHONY : subdirs $(SUBDIRS)
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@
### 示例2
.PHONY : all
all: Source Demo Test
Source : sm4.c
$(CC) -o $@ $^
Demo : demo.c
$(CC) -o $@ $^
Test : test.c
$(CC) -o $@ $^
示例
# 编译工具
CC := gcc
AR := ar
INCDIR := $(shell pwd)
# C预处理器使用 flag是编译器可选择的选项
# 不使用标准库和标准头文件
HEADER := -I/usr/local/include/libusb-1.0
HEADER += -I$(INCDIR)/Library
HEADER += -I$(INCDIR)/Source
LIB += -L/usr/local/lib -lusb-1.0
# C编译器的falg
# -Wall警告信息开
# -O2优化程度2
# -fno-buitin不用标准的,用自己的..
CFLAGS := -Wall -O0
#导出这些变量到全局,给其对应的子文件makefile使用
export CC AR CFLAGS HEADER LIB
#
obj := Source/main.o
obj += Source/demo.o
#
obj += Library/usbdrv.a
# -----------------------------------------
# 命令
# 最后注意:因为我封装的库usbdrv.a中调用了libusb
# 的库,因此再最终生成*.out文件时,也需要包含该库
# $(LIB),否则会出现找不到函数的问题
# undefined reference to `libusb_init'
# ... ...
# -----------------------------------------
all: $(obj)
# $(CC) -o main.out $^
$(CC) -o main.out $^ $(LIB)
%.o : %.c
$(CC) $(CFLAGS) $(HEADER) -o $@ -c $< $(LIB)
Library/usbdrv.a:
# cd Library; make; cd ..
make -C Library
clean:
cd Source; rm *.o ; cd ..
cd Library; make clean; cd ..
rm -f *.out *.bin
子目录下的驱动层,生成静态库。
objs := usbdrv.o
usbdrv.a: $(objs)
${AR} -r -o $@ $^
%.o:%.c
${CC} $(CFLAGS) $(HEADER) -o $@ -c $< $(LIB)
clean:
rm -f usbdrv.a *.o