makefile基础知识
1、赋值符号的区别:
= 是最基本的赋值,用到了之后才赋值,不能在变量后追加内容
:= 是覆盖之前的值,立即赋值,可以在变量后追加内容
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值
2、自动变量:
$< 第一个依赖文件的名称
$? 所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚
$@ 目标的完整名称
$^ 所有的依赖文件,以空格分开,不包含重复的依赖文件
3、常用的函数:
1. $(patsubst %.c,%.o,x.c.cbar.c)
把字串“x.c.cbar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o”
2.$(filter <pattern...>,<text> )
以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式。
3.$(filter-out <pattern...>,<text> )
4.$(foreach <var>,<list>,<text> )
把参数<list>中的单词逐一取出放到参数<var>所指定的变量中, 然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环这个过程。
5.shell函数,例如files := $(shell echo *.c)
通用的makefile文件的编译过程:
从顶层开始递归进入子目录,当进入到一个目录的最底层时,开始使用编译器编译,再将该层的所有.o文件打包成build-in.o,返回它的上一层目录再递归进入子目录,当编译完所有的子目录后,就开始编译顶层的.c文件,最后将顶层的.o文件和顶层每个子目录的build-in.o链接成我们的目标文件。
实战:
假如一个目录结构如下:
a
----d
----a.c
b
----b.c
c
----c.c
main.c
makefile
makefile.built
顶层的Makefile
#定义各个编译用到的工具
CROSS_COMPILE = #交叉编译器 arm-none-Linux-gnueabi- AS = $(CROSS_COMPILE)as #汇编器 LD = $(CROSS_COMPILE)ld #连接器 CC = $(CROSS_COMPILE)gcc #编译器 CPP = $(CC) -E #预处理 AR = $(CROSS_COMPILE)ar #归档文件 NM = $(CROSS_COMPILE)nm #列出object文件中的符号 STRIP = $(CROSS_COMPILE)strip #丢弃目标文件中的符号 OBJCOPY = $(CROSS_COMPILE)objcopy #转换目标文件格式 OBJDUMP = $(CROSS_COMPILE)objdump #反汇编 export AS LD CC CPP AR NM #将变量导出供下一个makefile使用 export STRIP OBJCOPY OBJDUMP CFLAGS := -Wall -O2 -g #CFLAGS 指定编译参数
#CFLAGS += -I $(shell pwd)/include
LDFLAGS := export CFLAGS LDFLAGS TOPDIR := $(shell pwd) export TOPDIR TARGET := test obj-y += main.o #obj-y main.o 编译链接进项目 obj-y += a/ obj-y += b/ obj-y += c/ all :
# make的递归执行,make的“-C”选项,是首先进入子目录而后再执行make。
#当需要将一个普通命名的文件作为makefile文件时,需要使用make的“-f”、“--file”或者“--makefile”选项来指定。 make -C ./ -f $(TOPDIR)/Makefile.build $(CC) $(LDFLAGS) -o $(TARGET) built-in.o clean: rm -f $(shell find -name "*.o") #删除命令,默认是 rm -f rm -f $(shell find -name "*.d") rm -f $(TARGET)
#执行make时,目标“all”被作为终极目标。
#伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它 是否要执行。
#我们只有通过显示地指明这个“目标”才能让其生效。
#为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,
#不管是否有这个文件,这个目标就是“伪目标”。 .PHONY:all clean
Makefile.build
PHONY := build #定义一个PHONY变量 build : #开头说明build伪目标,使其成为Makefile.build的第一个目标 obj-y := subdir-y := include Makefile __subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y))) #筛选出当前目录的目标变量中的子目录,并且去掉/,在这里我们获得了子目录的名字 subdir-y += $(__subdir-y) subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o) #对于subdir-y里面的每一个值(目录),增加一个相应的目录/built-in.o的变量值 cur_objs := $(filter-out %/, $(obj-y)) # 得到obj-y中的.o文件 dep_files := $(foreach f,$(cur_objs),.$(f).d) # .$(f).d 第一个.表示隐藏的 后面的.表示后缀 dep_files := $(wildcard $(dep_files))
#ifneq ($(dep_files),) # include $(dep_files) #endif PHONY += $(subdir-y) build : $(subdir-y) built-in.o $(subdir-y): make -C $@ -f $(TOPDIR)/Makefile.build built-in.o : $(cur_objs) $(subdir_objs) $(LD) -r -o $@ $^ dep_file = .$@.d %.o : %.c $(CC) $(CFLAGS) -Wp,-MD,$(dep_file) -c -o $@ $< .PHONY : $(PHONY)
子目录下的Makefile
obj-y += a.o
obj-y += d/
就是添加该目录下的文件和子目录,子目录要以 / 结尾。