多目录结构的Makefile编写
目录结构
主Makefile
#主Makefile
#终极目标
TGT = target
#子目录
SUB_DIR = main test
#子目标
export SUB_TGT = built-in.o
#顶层目录
export TOP_DIR = $(shell pwd)
#头文件目录
export HEAD_DIR = $(TOP_DIR)/head
#交叉编译器
#CROSS_COMPILE = arm-linux-gnueabihf-
export CC = $(CROSS_COMPILE)gcc
#编译选项
export CFLAGS = -I$(HEAD_DIR) -Wall
#编译链接器
export LD = ld
#编译链接选项
export LDFLAGS =
#终极目标规则
$(TGT): $(SUB_DIR)
$(CC) $(CFLAGS) $(^:=/$(SUB_TGT)) -o $@
#如何进入子目录规则
$(SUB_DIR):
make -C $@
clean:
rm -vf $(TGT)
for dir in $(SUB_DIR); do \
make -C $$dir clean; \
done
.PHONY: clean $(SUB_DIR)
解析
make中有三条规则
1. $(TGT) 链接生成最终目标规则
2. $(SUB_DIR) 第一条规则的依赖,因为依赖是个目录,所以要有一个规则来说明如何进入到此目录
3. clean 清理规则
子Makefile
#子Makefile:用来告诉make如何生成当前目录下的子目标built-in.o
SRCS = test.c
SUB_DIR = foo
$(SUB_TGT): $(SRCS:.c=.o) $(SUB_DIR)
$(LD) $(LDFLAGS) $(SRCS:.c=.o) $(SUB_DIR:=/$(SUB_TGT)) -r -o $@
%.o: %.c
$(CC) $(CFLAGS) -c $<
%.d: %.c
$(CC) $(CFLAGS) $< -MM > $@
sinclude $(SRCS:.c=.d)
$(SUB_DIR):
make -C $@
clean:
rm -vf $(SUB_TGT) $(SRCS:.c=.o) $(SRCS:.c=.d)
for dir in $(SUB_DIR); do \
make -C $$dir clean; \
done
.PHONY: clean $(SUB_DIR)
分析
- 主makefile首先说明了终极目标,然后开始找终极目标的依赖,发现依赖是一个目录,那么就去找如何进入目录的指令,也就是执行了第二条指令,成功进入子目录
- 在子目录中,makefile说明了子目录的目标(这个目标是主makefile -> export下来的),依赖就是(当前目录下的.c生成的.o && 当面目录下的子目录)
2.1. 找到.o的规则开始生成.o
2.2 找到进入子目录的规则进入子目录
2.3 找到所有的依赖以后开始链接(ld) 生成子目标built-in.o - 又回到了主makefile中,将所有的built-in.o生成终极目标
图解:
├── main.c
├── Makefile
└── test.c
gcc -c main.c -o main.o
gcc -c test.c -o test.o #生成对应的.o
ld main.o test.o -r -o main #链接 -r:生成可重定位的输出(称为部分连接)
gcc main -o a.out #生成终极目标
扩展
获取上一级目录
#当前目录 的上一级目录 关键字dir
export HEAD_DIR = $(dir $(TOP_DIR))include
从make中获取参数
ifeq ("$(origin O)", "command line")
KBUILD_OUTPUT := $(O)
endif
上述代码中先使用 ifeq 来判断"$(origin O)"和"command line"是否相等。这里用到了 Makefile
中的函数 origin,origin 和其他的函数不一样,它不操作变量的值,origin 用于告诉你变量是哪
来的,语法为:
$(origin <variable>)
variable 是变量名,origin 函数的返回值就是变量来源,因此$(origin O)就是变量 O 的来源。
如果变量 O 是在命令行定义的那么它的来源就是"command line",这样"$(origin O)"和"command line"就相等了。
当这两个相等的时候变量 KBUILD_OUTPUT 就等于 O 的值,比如在命令行中输入
“make O=/home/zys/linux/uboot/out “ 的 话 那 么 KBUILD_OUTPUT =/home/zys/linux/uboot/out 。
如果没有在命令行输入 O 的 话KBUILD_OUTPUT = 空。
创建文件夹
参考来自uboot的主Makefile
ifneq ($(KBUILD_OUTPUT),)
KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT) \
&& /bin/pwd)
$(if $(KBUILD_OUTPUT),, \
$(error failed to create output directory "$(saved-output)"))
endif