使用lab1_result目录
执行的命令为
make mytest
代码均添加在如下代码后面一行。
include tools/function.mk
mytest:
echo $(call listf,./libs,c)
结果
./libs/string.c ./libs/printfmt.c
toobj
mytest:
@echo $(call toobj,a.c,hello)
输出
obj/hello/a.o
todep
跟toobj相似,只是借用了toobj来实现取了下巧
mytest:
@echo $(call todep,a.c,hello)
输出
obj/hello/a.d
totarget
mytest:
@echo $(call totarget,a.c)
结果
bin/a.c
packetname
mytest:
@echo $(call packetname,hello)
输出
__objs_hello
总结
listf
用来获取c文件,
toobj
指定obj/boot/*.o obj/kern/*.o等
todep
指定的东西与.o
在同一目录下
totarget
在bin/
下面
以前函数均是返回一个字符串,.d文件指明其依赖的其它文件。
SLASH := /
OBJDIR := obj
BINDIR := bin
listf = $(filter $(if $(2),$(addprefix %.,$(2)),%),\
$(wildcard $(addsuffix $(SLASH)*,$(1))))
# get .o obj files: (#files[, packet])
toobj = $(addprefix $(OBJDIR)$(SLASH)$(if $(2),$(2)$(SLASH)),\
$(addsuffix .o,$(basename $(1))))
# get .d dependency files: (#files[, packet])
todep = $(patsubst %.o,%.d,$(call toobj,$(1),$(2)))
totarget = $(addprefix $(BINDIR)$(SLASH),$(1))
# change $(name) to $(OBJPREFIX)$(name): (#names)
packetname = $(if $(1),$(addprefix $(OBJPREFIX),$(1)),$(OBJPREFIX))
# cc compile template, generate rule for dep, obj: (file, cc[, flags, dir])
define cc_template
$$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$@)
@$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$@) $$@"> $$@
$$(call toobj,$(1),$(4)): $(1) | $$$$(dir $$$$@)
@echo + cc $$<
$(V)$(2) -I$$(dir $(1)) $(3) -c $$< -o $$@
ALLOBJS += $$(call toobj,$(1),$(4))
endef
$(info $(call cc_template,a.c ,gcc ,-g,))
test:
echo hello
OBJDIR := obj
BINDIR := bin
SLASH := /
.SECONDEXPANSION:
# -------------------- function begin --------------------
# list all files in some directories: (#directories, #types)
listf = $(filter $(if $(2),$(addprefix %.,$(2)),%),\
$(wildcard $(addsuffix $(SLASH)*,$(1))))
# get .o obj files: (#files[, packet])
toobj = $(addprefix $(OBJDIR)$(SLASH)$(if $(2),$(2)$(SLASH)),\
$(addsuffix .o,$(basename $(1))))
# get .d dependency files: (#files[, packet])
todep = $(patsubst %.o,%.d,$(call toobj,$(1),$(2)))
totarget = $(addprefix $(BINDIR)$(SLASH),$(1))
# change $(name) to $(OBJPREFIX)$(name): (#names)
packetname = $(if $(1),$(addprefix $(OBJPREFIX),$(1)),$(OBJPREFIX))
# cc compile template, generate rule for dep, obj: (file, cc[, flags, dir])
define cc_template
$$(call toobj,$(1),$(4)): $(1) | $$$$(dir $$$$@)
@echo + cc $$<
$(V)$(2) -I$$(dir $(1)) $(3) -c $$< -o $$@
$$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$@)
@$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$@) $$@"> $$@
ALLOBJS += $$(call toobj,$(1),$(4))
endef
all: obj/test.o obj/test.d
$(eval $(call cc_template,test.c,gcc,-g,))
其中,第一行
@gcc -I$(dir a.c ) -g -MM $< -MT "$(patsubst %.d,%.o,$@) $@"> $@
-I$(dir a.c)
,用于取.c所有的目录为include的查找目录,即include<...>
的文件的查找目录被显示申明,不会再去查找系统默认库目录。
随便写一个test.c,可以对比如下两个的输出,这个gcc指令用来生成.d
文件的。
gcc -MM test.c -MT "test.d"
,
gcc -MM test.c
下面一个gcc命令用于生成.o文件
注意到$$$$(dir $$$$@)
行为比较怪异,这是二次展开,用法特殊
当eval执行完后
代码大概如下,
all: obj/test.d obj/test.o
obj/test.d: test.c | obj
gcc -I. -g -MM test.c -MT "test.o test.d" > obj/test.d
obj/test.o: test.c | obj
@echo + cc test.c
@gcc -I . -g -c test.c -o obj/test.o
ALLOBJS+=obj/test.o
OBJDIR := obj
BINDIR := bin
SLASH := /
OBJPREFIX := __objs_
.SECONDEXPANSION:
# -------------------- function begin --------------------
# list all files in some directories: (#directories, #types)
listf = $(filter $(if $(2),$(addprefix %.,$(2)),%),\
$(wildcard $(addsuffix $(SLASH)*,$(1))))
# get .o obj files: (#files[, packet])
toobj = $(addprefix $(OBJDIR)$(SLASH)$(if $(2),$(2)$(SLASH)),\
$(addsuffix .o,$(basename $(1))))
# get .d dependency files: (#files[, packet])
todep = $(patsubst %.o,%.d,$(call toobj,$(1),$(2)))
totarget = $(addprefix $(BINDIR)$(SLASH),$(1))
# change $(name) to $(OBJPREFIX)$(name): (#names)
packetname = $(if $(1),$(addprefix $(OBJPREFIX),$(1)),$(OBJPREFIX))
# cc compile template, generate rule for dep, obj: (file, cc[, flags, dir])
define cc_template
$$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$@)
@$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$@) $$@"> $$@
$$(call toobj,$(1),$(4)): $(1) | $$$$(dir $$$$@)
@echo + cc $$<
$(V)$(2) -I$$(dir $(1)) $(3) -c $$< -o $$@
ALLOBJS += $$(call toobj,$(1),$(4))
endef
# add files to packet: (#files, cc[, flags, packet, dir])
define do_add_files_to_packet
__temp_packet__ := $(call packetname,$(4))
ifeq ($$(origin $$(__temp_packet__)),undefined)
$$(__temp_packet__) :=
endif
__temp_objs__ := $(call toobj,$(1),$(5))
$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(5))))
$$(__temp_packet__) += $$(__temp_objs__)
endef
$(eval $(call do_add_files_to_packet,test.c,gcc,-g,hello,))
$(info $(call do_add_files_to_packet,test.c,gcc,-g,hello,))
all:obj/test.o obj/test.d
echo ${__objs_hello}
关于do_add_files_to_packet干了什么,生成target obj/.o,obj/.d,并且将所有的.o文件收集到变量packet里面带上一个__objs_前缀。
将一些.o
文件收集到变量__objs_packet中。
# add objs to packet: (#objs, packet)
define do_add_objs_to_packet
__temp_packet__ := $(call packetname,$(2))
ifeq ($$(origin $$(__temp_packet__)),undefined)
$$(__temp_packet__) :=
endif
$$(__temp_packet__) += $(1)
endef
__temp_target__存放你要生成的可执行目标,
__temp_target__将__objs_packt里的.o文件和objs里的.o文件接在一起
TARGETS加上当前目标
编译当前目标
# add packets and objs to target (target, #packes, #objs[, cc, flags])
define do_create_target
__temp_target__ = $(call totarget,$(1))
__temp_objs__ = $$(foreach p,$(call packetname,$(2)),$$($$(p))) $(3)
TARGETS += $$(__temp_target__)
ifneq ($(4),)
$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@)
$(V)$(4) $(5) $$^ -o $$@
else
$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@)
endif
endef
之个finish是帮助生成文件夹的
# finish all
define do_finish_all
ALLDEPS = $$(ALLOBJS:.o=.d)
$$(sort $$(dir $$(ALLOBJS)) $(BINDIR)$(SLASH) $(OBJDIR)$(SLASH)):
@$(MKDIR) $$@
endef
总结:
do_add_files_to_packet:调用了cc_template,需要一个obj目录并生成.o和.d文件
do_add_objs_to_packet:在__objs_(
2
)
变
量
里
加
上
2)变量里加上
2)变量里加上(1)变量里的.o文件
do_create_target:产生相应的target,形如bin/boot,将packes是一堆变量,每个变量里面的.o文件取出,并加上objs,得到一大堆.o文件,利用.o生成当前目标,并将目标存于TARGETS