用 gcc –o a a.c
可把1、2、3、4四个步骤一起完成。
代码为07.通用的Makefile_源码_文档_图片\09.Makefile\source\test
中的Makefile
。
a.c
:预处理、编译、汇编b.c
:预处理、编译、汇编优点:命令简单
缺点:如果文件多即使只修改了一个文件,但所有的文件都要重新"预处理、编译、汇编",效率低
核心:规则
目标:依赖1 依赖2
命令
命令执行的条件:
i. "依赖"文件 比 "目标"文件 新
ii.没有"目标"这个文件
第一个 Makefile :
第二个 Makefile :
这种写法,只修改了头文件 a.h
不会重新编译。
第三个 Makefile:使用通配符
$@
:表示目标;
$<
:表示第一个依赖;
$^
:表示所有的依赖;
这种写法,只修改了头文件 a.h
会重新编译;
第四个 Makefile : 使用通配符 + 自动生成依赖文件
objs := a.o b.o
test:$(objs)
gcc -o test $^
# .a.o.d .b.o.d
# foreach函数:对于变量objs里的每一个成员改为.$(f).d(加一个前缀“.”和一个后缀“.d”)
# wildcard函数:查看dep_files中是否有“.d”文件,有的话就取出来
dep_files := $(foreach f,$(objs),.$(f).d)
dep_files := $(wildcard $(dep_files))
# 如果dep_files变量不为空的话,就把dep_files包含进来
ifneq ($(dep_files),)
include $(dep_files)
endif
# -Wp,-MD,[email protected]:表示生成依赖文件a.o.d、b.o.d
%.o : %.c
gcc -Wp,-MD,.$@.d -c -o $@ $<
clean:
rm *.o test
1、执行make
命令时,使用命令gcc -o test $^
生成第一个目标test
,test
依赖于objs
,objs
为两个.o
文件;
2、.o
文件依赖于.c
文件,使用命令gcc -Wp,-MD,[email protected] -c -o $@ $<
生成。
其中 -Wp,-MD,[email protected]
:表示生成依赖文件a.o.d
、b.o.d
。
3、对于.d
文件来说:
使用dep_files := $(foreach f,$(objs),.$(f).d)
函数和dep_files := $(wildcard $(dep_files))
函数生成。
其中foreach
函数:对于变量objs里的每一个成员改为.$(f).d(加一个前缀“.”和一个后缀“.d”)
wildcard
函数:查看dep_files中是否有“.d”文件,有的话就取出来
4、再将dep_files
变量包含进来
如果dep_files
变量不为空的话,就把dep_files
包含进来
使用命令:include $(dep_files)
包含
这样编写Makefile
之后就能自动包含依赖文件了。
代码为07.通用的Makefile_源码_文档_图片\09.Makefile\source\09.show_file
下的_Makefile
。
obj-y += xxx1.o
obj-y += xxx2.o
obj-y += subdir1/ #想进入某个子目录的时候:
Makefile文件:
# 定义变量
CROSS_COMPILE = arm-linux-
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
# 导出变量,这样子目录可以使用
export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP
# Wall表示列出所有警告 -O2表示优化选项 -g表示加上调试信息
# =表示延时变量,使用时确定其值,且不能在后面追加内容
# :=表示立即变量,其值立即确定
# +=表示追加内容
# -I表示指定目录
# (shell pwd)表示获得当前目录
CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include
# -l表示指定库
# -lm表示数学库、-lfreetype表示freetype库
LDFLAGS := -lm -lfreetype
# 导出变量
export CFLAGS LDFLAGS
# 定义顶层目录为TOPDIR
TOPDIR := $(shell pwd)
export TOPDIR
# 定义目标
TARGET := show_file
# 追加子各个编译文件
obj-y += main.o
obj-y += display/
obj-y += draw/
obj-y += encoding/
obj-y += fonts/
# 这里使用-f是因为:
# 缺省情况下,当make寻找makefile文件时,它试图搜寻具有如下的名字的文件,按顺序:‘GNUmakefile’、‘makefile’和‘Makefile’。
# 通常情况下您应该把您的makefile文件命名为‘makefile’或‘Makefile’。
# (我们推荐使用‘Makefile’,因为它基本出现在目录列表的前面,后面挨着其它重要的文件如‘README’等。)。
# 虽然首先搜寻‘GNUmakefile’,但我们并不推荐使用。除非您的makefile文件是特为GNU make编写的,在其它make版本上不能执行,
# 您才应该使用‘GNUmakefile’作为您的makefile的文件名。
# 如果make不能发现具有上面所述名字的文件,它将不使用任何makefile文件。
# 这样您必须使用命令参数给定目标,make试图利用内建的隐含规则确定如何重建目标。详细内容参见使用隐含规则一节。
# 如果您使用非标准名字makefile文件,您可以使用‘-f’或‘--file’参数指定您的makefile文件。
# 参数‘-f name’或‘--file=name’能够告诉make读名字为‘name’的文件作为makefile文件。
# 如果您使用 ‘-f’或‘--file’参数多于一个,意味着您指定了多个makefile文件,所有的makefile文件按具体的顺序发生作用。
# 一旦您使用了‘-f’或‘--file’参数,将不再自动检查是否存在名为‘GNUmakefile’、‘makefile’或‘Makefile’的makefile文件。
all :
make -C ./ -f $(TOPDIR)/Makefile.build
$(CC) $(LDFLAGS) -o $(TARGET) built-in.o
# 清除,使用 make clean
clean:
rm -f $(shell find -name "*.o")
rm -f $(TARGET)
# 彻底清除,使用 make distclean
distclean:
rm -f $(shell find -name "*.o")
rm -f $(shell find -name "*.d")
rm -f $(TARGET)
# 假目标,第一个目标
PHONY := __build
__build:
# 赋一个空值
obj-y :=
subdir-y :=
# 包含当前目录下的Makefile,里面有要包含的obj-y
include Makefile
# 若:obj-y := a.o b.o c/ d/
# 那么函数:filter %/, $(obj-y 表示将不符合%/的内容去除,既保留c/、d/
# $(filter %/, $(obj-y)) : c/ d/
# 函数patsubst %/,%,$(filter %/, $(obj-y))表示在 $(filter %/, $(obj-y) 中,将 %/ 替换成 %
# 那么__subdir-y就为 : c d
# 随后子目录subdir-y就为 : c d
__subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y += $(__subdir-y)
# c/built-in.o d/built-in.o
subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)
# a.o b.o
# 那么函数:filter-out %/, $(obj-y 表示将符合%/的内容去除,既去除c/、d/
# $(foreach f,$(cur_objs),.$(f).d) 生成依赖文件
# $(wildcard $(dep_files)) 查看dep_files中是否有“.d”文件,有的话就取出来
cur_objs := $(filter-out %/, $(obj-y))
dep_files := $(foreach f,$(cur_objs),.$(f).d)
dep_files := $(wildcard $(dep_files))
# 如果dep_files变量不为空的话,就把dep_files包含进来
ifneq ($(dep_files),)
include $(dep_files)
endif
# 追加假象目标
PHONY += $(subdir-y)
# __build依赖于后面两项
__build : $(subdir-y) built-in.o
# 递归地进行处理子目录使用Makefile.build进行编译
$(subdir-y):
make -C $@ -f $(TOPDIR)/Makefile.build
# 生成built-in.o
built-in.o : $(cur_objs) $(subdir_objs)
$(LD) -r -o $@ $^
# 依赖文件,其中”=“表示用到的时候才会赋值
dep_file = .$@.d
# 生成.o文件
%.o : %.c
$(CC) $(CFLAGS) -Wp,-MD,$(dep_file) -c -o $@ $<
# 假象目标”.PHONY“依赖于”PHONY“
.PHONY : $(PHONY)
假设 obj-y := a.o b.o c/ d/
$(filter %/, $(obj-y)) 最后返回 c/ d/
所以最终上面一段的结果就是(假设 obj-y := a.o b.o c/ d/):
__subdir-y : c d
subdir-y : c d
把第二个参数的内容分别传递给第一个参数
去执行第三个语句
中间变量没什么意义,只是临时的,所以上面的f只是个临时局部变量
由于subdir-y 为 c d,分别放到第三个参数中运行,上面的最终结果为
subdir_objs := c/built-in.o d/built-in.o
这句的目的这里是由于我们要用到wildcard
这个函数,作用就是在当前目录下查找符合该函数的参数的文件,并返回。
所以我们要先将所有的文件生成依赖文件的格式,用来确定查找的范围,在用wildcard
函数判断在这个范围内的依赖文件哪些是存在的。
如果依赖文件存在,则包含依赖文件,我们知道dep_files
里面的内容就是各个.h
,也就是包含了这些.h
。
继续分析
最终我们想在顶层makefile.build
生成一个__build
文件,他依赖的目标有两个,一个是顶层目录下的所有飞文件夹的文件,之前假设当前目录下有obj-y := a.o b.o c/ d/
,则a.o b.o
最终生成built-in.o
,一个是顶层目录下的目录里面生成的subdir-y
接下来就是完成上面两个依赖的规则:
TOPDIR
是我们在顶层makefile
定义的宏,为当前目录,并且已经export
了,所以可以用之前我们假设了obj-y := a.o b.o c/ d/
,然后subdir-y : c d
。
由于subdir-y
有多个值,所以这个多目标的规则,应该如果展开这个规则呢?
继续分析第二个依赖:
展开为:
Built-in.o : a.o b.o c/built-in.o d/built-in.o
$(LD) -r -o $@ $^
其中
subdir_objs :=c/built-in.o d/built-in.o
cur_objs := a.o b.o
之前我们分析了每一层的文件最终都会生成一个built-in.o
文件,而这个built-in.o
又由两部分组成
.c
文件(不一定只有.c
,只要不再当前文件内的子目录下的都是)build-in.o
所以一层的最终目标文件的依赖为当前层的obj文件,和当前层子目录下的build-in.o
我们的 Makefile 是想生成什么样的东西?
每个子目录里面把生成的 xxx.o
打包成 built-in.o
,然后几个 built-in.o
链接到一起。
本程序的Makefile分为3类:
1. 顶层目录的Makefile
2. 顶层目录的Makefile.build
3. 各级子目录的Makefile
一、各级子目录的Makefile:
它最简单,形式如下:
obj-y += file.o
obj-y += subdir/
"obj-y += file.o"表示把当前目录下的file.c编进程序里,
"obj-y += subdir/"表示要进入subdir这个子目录下去寻找文件来编进程序里,是哪些文件由subdir目录下的Makefile决定。
注意: "subdir/"中的斜杠"/"不可省略
二、顶层目录的Makefile:
它除了定义obj-y来指定根目录下要编进程序去的文件、子目录外,主要是定义工具链、编译参数、链接参数──就是文件中用export导出的各变量。
三、顶层目录的Makefile.build:
这是最复杂的部分,它的功能就是把某个目录及它的所有子目录中、需要编进程序去的文件都编译出来,打包为built-in.o
详细的讲解请看视频。
四、怎么使用这套Makefile:
1.把顶层Makefile, Makefile.build放入程序的顶层目录
2.修改顶层Makefile
2.1 修改工具链
2.2 修改编译选项、链接选项
2.3 修改obj-y决定顶层目录下哪些文件、哪些子目录被编进程序
2.4 修改TARGET,这是用来指定编译出来的程序的名字
3. 在各一个子目录下都建一个Makefile,形式为:
obj-y += file1.o
obj-y += file2.o
obj-y += subdir1/
obj-y += subdir2/
4. 执行"make"来编译,执行"make clean"来清除,执行"make distclean"来彻底清除