注:1)make -n可列印makefile執行的指令,而不執行。
2)可以在規則的指令中增加echo跟蹤執行進度。
頂層目錄的makefile是openert的總makefile,第一個編譯目标world是make的預設編譯目标。
編譯邏輯可簡化為:
make v=s時,$openwrt_build沒有定義指派,是以總是執行“第一邏輯”,“第一邏輯”結束時再次執行make world,此時$openwrt_build=1,是以執行“第二邏輯”。
toplevel.mk中%::解釋world目标的規則。
prereq:: prepare-tmpinfo .config
@+$(no_trace_make) -r -s $@
warn_parallel_error = $(if $(build_log),,$(and $(filter
-j,$(makeflags)),$(findstring s,$(openwrt_verbose))))
ifeq ($(sdk),1)
%::
@+$(prep_mk) $(no_trace_make) -r -s prereq
@./scripts/config/conf --defconfig=.config config.in
@+$(ulimit_fix) $(submake) -r $@
else
@( \
cp .config tmp/.config; \
./scripts/config/conf --defconfig=tmp/.config -w tmp/.config
config.in > /dev/null 2>&1; \
if ./scripts/kconfig.pl '>' .config tmp/.config | grep -q
config; then \
printf "$(_r)warning: your configuration is out of
sync. please run make menuconfig, oldconfig or defconfig!$(_n)\n"
>&2; \
fi \
)
@+$(ulimit_fix) $(submake) -r $@ $(if $(warn_parallel_error), ||
{ \
printf "$(_r)build failed - please re-run with -j1 to
see the real error message$(_n)\n" >&2; \
false; \
} )
endif
執行makev=s時,上面規則簡化為:
@make v=ss -r -s prereq
@make v=s -r -s prereq
@make -w -r world
首先就引入了target,
package, tools, toolchain這四個關鍵目錄裡的makefile檔案。
include target/makefile
include package/makefile
include tools/makefile
include toolchain/makefile
這些子目錄裡的makefile使用include/subdir.mk裡定義的兩個函數來動态生成規則,這兩個函數是subdir和stampfile。
subdir指令包
# parameters: <subdir>
define subdir
$(call warn,$(1),d,d $(1))
$(foreach bd,$($(1)/builddirs),
$(call warn,$(1),d,bd $(1)/$(bd))
$(foreach target,$(subtargets),
$(foreach btype,$(buildtypes-$(bd)),
$(call
warn_eval,$(1)/$(bd),t,t,$(1)/$(bd)/$(btype)/$(target): $(if
$(quilt),,$($(1)/$(bd)/$(btype)/$(target)) $(call
$(1)//$(btype)/$(target),$(1)/$(bd)/$(btype))))
$(if $(call debug,$(1)/$(bd),v),,@)+$$(submake) -r -c
$(1)/$(bd) $(btype)-$(target) $(if $(findstring
$(bd),$($(1)/builddirs-ignore-$(btype)-$(target))), || $(call
error,$(1), error: $(1)/$(bd) [$(btype)] failed to build.))
$(if $(call diralias,$(bd)),$(call
warn_eval,$(1)/$(bd),l,t,$(1)/$(call
diralias,$(bd))/$(btype)/$(target): $(1)/$(bd)/$(btype)/$(target)))
$(call warn_eval,$(1)/$(bd),t,t,$(1)/$(bd)/$(target): $(if
$(quilt),,$($(1)/$(bd)/$(target)) $(call
$(1)//$(target),$(1)/$(bd))))
$(if $(build_log),@mkdir -p $(build_log_dir)/$(1)/$(bd))
$(foreach variant,$(if $(build_variant),$(build_variant),$(if
$(strip $($(1)/$(bd)/variants)),$($(1)/$(bd)/variants),$(if
$($(1)/$(bd)/default-variant),$($(1)/$(bd)/default-variant),__default))),
$(if $(call debug,$(1)/$(bd),v),,@)+$(if $(build_log),set
-o pipefail;) $$(submake) -r -c $(1)/$(bd) $(target)
build_variant="$(filter-out __default,$(variant))" $(if
$(build_log),silent= 2>&1 | tee
$(build_log_dir)/$(1)/$(bd)/$(target).txt) $(if $(findstring
$(bd),$($(1)/builddirs-ignore-$(target))), || $(call error,$(1),
error: $(1)/$(bd) failed to build$(if $(filter-out
__default,$(variant)), (build variant: $(variant))).))
$(if $(prereq_only)$(dump_target_db),,
# aliases
warn_eval,$(1)/$(bd),l,t,$(1)/$(call diralias,$(bd))/$(target):
$(1)/$(bd)/$(target)))
$(foreach target,$(subtargets),$(call subtarget,$(1),$(target)))
endef
subdir會周遊參數子目錄,執行make
-c操作。
stampfile指令包
# parameters: <subdir> <name>
<target> <depends> <config options> <stampfile
location>
define stampfile
$(1)/stamp-$(3):=$(if
$(6),$(6),$(staging_dir))/stamp/.$(2)_$(3)$(5)
$$($(1)/stamp-$(3)): $(tmp_dir)/.build $(4)
@+$(script_dir)/timestamp.pl -n $$($(1)/stamp-$(3)) $(1) $(4) ||
\
$(make) $(if $(quiet),--no-print-directory)
$$($(1)/flags-$(3)) $(1)/$(3)
@mkdir -p $$$$(dirname $$($(1)/stamp-$(3)))
@touch $$($(1)/stamp-$(3))
$$(if $(call debug,$(1),v),,.silent: $$($(1)/stamp-$(3)))
.precious: $$($(1)/stamp-$(3)) # work around a make bug
$(1)//clean:=$(1)/stamp-$(3)/clean
$(1)/stamp-$(3)/clean: force
@rm -f $$($(1)/stamp-$(3))
target/makefile中調用:
curdir:=target
$(curdir)/builddirs:=linux sdk imagebuilder toolchain
$(curdir)/builddirs-default:=linux
$(curdir)/builddirs-install:=linux $(if $(config_sdk),sdk) $(if
$(config_ib),imagebuilder) $(if $(config_make_toolchain),toolchain)
$(curdir)/imagebuilder/install:=$(curdir)/linux/install
$(eval $(call
stampfile,$(curdir),target,prereq,.config))
stampfile,$(curdir),target,compile,$(tmp_dir)/.build))
stampfile,$(curdir),target,install,$(tmp_dir)/.build))
$($(curdir)/stamp-install): $($(curdir)/stamp-compile)
$(eval $(call subdir,$(curdir)))
$(eval
$(call stampfile,$(curdir),target,prereq,.config))
會生成規則:
target/stamp-prereq:=$(staging_dir)/stamp/.target_prereq
$$(target/stamp-prereq): $(tmp_dir)/.build .config
@+$(script_dir)/timestamp.pl -n $$(target/stamp-prereq) target
.config || \
make $$(target/flags-prereq) target/prereq
@mkdir -p $$$$(dirname $$(target/stamp-prereq))
@touch $$(target/stamp-prereq)
$$(if $(call debug,target,v),,.silent: $$(target/stamp-prereq))
.precious: $$(target/stamp-prereq) # work around a make bug
target//clean:=target/stamp-prereq/clean
target/stamp-prereq/clean: force
@rm -f $$(target/stamp-prereq)
是以可以簡單的看作:
$(call stampfile,$(curdir),target,prereq,.config)) 生成了目标
$(target/stamp-prereq)
對于target分别生成了:$(target/stamp-prereq),
$(target/stamp-compile),
$(target/stamp-install)
toolchain
: $(toolchain/stamp-install)
package
: $(package/stamp-prereq),$(package/stamp-cleanup), $(package/stamp-compile),$(package/stamp-install)
tools
: $(tools/stamp-install)
倚賴關系如下:
$(toolchain/stamp-install):
$(tools/stamp-install)
$(target/stamp-compile):
$(toolchain/stamp-install) $(tools/stamp-install)
$(build_dir)/.prepared
$(package/stamp-compile):
$(target/stamp-compile) $(package/stamp-cleanup)
$(package/stamp-install):
$(package/stamp-compile)
$(target/stamp-install):
$(package/stamp-compile) $(package/stamp-install)
基本上就是toolchain依賴tools,target依賴toolchain,package依賴target,最後target/stamp-install倚賴于package。
kernel編譯可運作指令:maketarget/linux/{prepare,compile,install} v=s
target/linux/makefile
include $(topdir)/rules.mk
include $(include_dir)/target.mk
export target_build=1
prereq clean download prepare compile install menuconfig nconfig
oldconfig update refresh: force
@+$(no_trace_make) -c $(board) $@
target/linux/ipq806x/makefile
arch:=arm
board:=ipq806x
boardname:=qualcomm atheros ipq806x
features:=ubifs squashfs
cpu_type:=cortex-a7
maintainer:=john crispin <[email protected]>
kernelname:=zimage image dtbs
kernel_patchver:=3.14
$(eval $(call buildtarget))
buildtarget在include/target.mk中:
include $(include_dir)/kernel.mk
ifeq ($(target_build),1)
include $(include_dir)/kernel-build.mk
buildtarget?=$(buildkernel)
buildkernel在include/kernel-build.mk中:
define buildkernel
$(if $(quilt),$(build/quilt))
$(if $(linux_site),$(call download,kernel))
…
download: $(if $(linux_site),$(dl_dir)/$(linux_source))
prepare: $(stamp_configured)
compile: $(linux_dir)/.modules
$(make) -c image compile target_build=
oldconfig menuconfig nconfig: $(stamp_prepared) $(stamp_checked)
force
rm -f $(stamp_configured)
$(linux_reconf_cmd) > $(linux_dir)/.config
$(_single)$(make) -c $(linux_dir) $(kernel_makeopts) $$@
$(linux_reconf_diff) $(linux_dir)/.config >
$(linux_reconfig_target)
install: $(linux_dir)/.image
+$(make) -c image compile install target_build=
clean: force
rm -rf $(kernel_build_dir)
image-prereq:
@+$(no_trace_make) -s -c image prereq target_build=
prereq: image-prereq
至此編譯kernel時clean/prepare/compile/install目标規則出現,涉及的makefile包括:include/kernel-build.mk,include/kernel-defaults.mk.
1)觸發makevmlinux指令生成vmlinux:
install --> $(linux_dir)/.image -->
$(kernel_build_dir)/symtab.h --> `$(make) $(kernel_makeopts)
vmlinux`
2)對vmlinux做objcopy,strip操作:
$(linux_dir)/.image --> $(kernel/compileimage) --> $(call
kernel/compileimage/default) --> $(call kernel/copyimage)
define kernel/copyimage
$(kernel_cross)objcopy -o binary $(objcopy_strip) -s
$(linux_dir)/vmlinux $(linux_kernel)$(1)
$(kernel_cross)objcopy $(objcopy_strip) -s $(linux_dir)/vmlinux
$(kernel_build_dir)/vmlinux$(1).elf
$(cp) $(linux_dir)/vmlinux $(kernel_build_dir)/vmlinux$(1).debug
$(foreach k, \
$(if $(kernel_images),$(kernel_images),$(filter-out
dtbs,$(kernelname))), \
$(cp)
$(linux_dir)/arch/$(linux_karch)/boot/$(images_dir)/$(k)
$(kernel_build_dir)/$(k)$(1); \
3)核心config處理
prepare -->$(stamp_configured) --> $(kernel/configure) --> $(call
kernel/configure/default)
define kernel/configure
$(call kernel/configure/default)
define kernel/configure/default
$(linux_conf_cmd) > $(linux_dir)/.config.target
# copy config_kernel_* settings over to .config.target
awk
'/^(#[[:space:]]+)?config_kernel/{sub("config_kernel_","config_");print}'
$(topdir)/.config >> $(linux_dir)/.config.target
echo "# config_kallsyms_extra_pass is not set" >>
$(linux_dir)/.config.target
echo "# config_kallsyms_all is not set" >>
echo "# config_kallsyms_uncompressed is not set" >>
$(script_dir)/metadata.pl kconfig $(tmp_dir)/.packageinfo
$(topdir)/.config $(kernel_patchver) >
$(linux_dir)/.config.override
$(script_dir)/kconfig.pl 'm+' '+' $(linux_dir)/.config.target
/dev/null $(linux_dir)/.config.override > $(linux_dir)/.config
$(call kernel/setnoinitramfs)
rm -rf $(kernel_build_dir)/modules
$(_single) [ -d $(linux_dir)/user_headers ] || $(make)
$(kernel_makeopts) install_hdr_path=$(linux_dir)/user_headers
headers_install
$(sh_func) grep '=[ym]' $(linux_dir)/.config | lc_all=c sort |
md5s > $(linux_dir)/.vermagic
firmware由kernel和rootfs兩個部分組成,要對兩個部分先分别處理,然後再合并成一個.bin檔案。
target/linux/ipq806x/image/makefile中最後定義了生成image的規則:
$(eval $(call buildimage))
本檔案中也定義了生成image鏡像(包含ubifs鏡像)的規則。
同時注意:編譯kernel的compile或install時也會執行image下的compile和install。
include/image.mk定義buildimage指令包:
define buildimage
download:
prepare:
compile:
clean:
image_prepare:
ifeq ($(ib),)
.phony: download prepare compile clean image_prepare mkfs_prepare
kernel_prepare install
$(call build/compile)
$(call build/clean)
image_prepare: compile
mkdir -p $(kdir)/tmp
$(call image/prepare)
mkfs_prepare: image_prepare
$(call image/mkfs/prepare)
kernel_prepare: mkfs_prepare
$(call image/buildkernel)
$(if $(config_target_rootfs_initramfs),$(if $(ib),,$(call
image/buildkernel/initramfs)))
$(call image/installkernel)
$(foreach device,$(target_devices),$(call device,$(device)))
$(foreach fs,$(target_filesystems) $(fs-subtypes-y),$(call
buildimage/mkfs,$(fs)))
$$(sort $$(_kernel_images)):
@touch $$@
install: kernel_prepare
$(foreach fs,$(target_filesystems),
$(call image/build,$(fs))
$(call image/mkfs/ubifs)
$(call image/checksum,md5sum --binary,md5sums)
$(call image/checksum,openssl dgst -sha256,sha256sums)
至此編譯firmware時clean/prepare/compile/install目标規則出現,但隻有install起作用。測試中發現直接運作maketarget/linux/ipq806x/image/install v=s出錯,需要運作make
target/linux/install v=s。
install-->kernel_prepare-->mkfs_prepare -->image_prepare --> $(call image/prepare)
define image/prepare
$(cp) $(linux_dir)/vmlinux $(kdir)/$(img_prefix)-vmlinux.elf
主要處理指令包為:image/buildkernel,将裝置樹和鏡像打包成fit鏡像樹檔案:openwrt-ipq806x-fit-uimage.itb。
define image/buildkernel
$(call image/buildkernel/template,fit)
image/buildkernel/genericfit,qcom-ipq40xx,$(ipq40xx_kernel_loadaddr))
$(call image/buildkernel/multidtbfit,qcom-ipq40xx-ap.dkxx, \
$(call finddevicetrees, qcom-ipq40??-ap) $(call
finddevicetrees, qcom-ipq40??-db), \
$(ipq40xx_kernel_loadaddr))
對檔案系統處理(include/image.mk)
install-->kernel_prepare-->mkfs_prepare -->$(call image/mkfs/prepare)
define image/mkfs/prepare/default
# use symbolic permissions to avoid clobbering suid/sgid/sticky
bits
- $(find) $(target_dir) -type f -not -perm /0100 -not -name
'ssh_host*' -not -name 'shadow' -print0 | $(xargs) -0 chmod
u+rw,g+r,o+r
- $(find) $(target_dir) -type f -perm /0100 -print0 | $(xargs) -0
chmod u+rwx,g+rx,o+rx
- $(find) $(target_dir) -type d -print0 | $(xargs) -0 chmod
u+rwx,g+rx,o+rx
$(install_dir) $(target_dir)/tmp $(target_dir)/overlay
chmod 1777 $(target_dir)/tmp
chmod 700 $(target_dir)/usr/bin/scvt
test -e $(target_dir)/etc/cert && chmod 0700
$(target_dir)/etc/cert || echo ok
test -e $(target_dir)/etc/init.d/cert && chmod 0700
$(target_dir)/etc/init.d/cert || echo ok
test -e $(target_dir)/.ocloud/ && chmod 0700
$(target_dir)/.ocloud/ || echo ok
test -e $(target_dir)/.ocloud/ && chmod 0600
$(target_dir)/.ocloud/* || echo ok
test -e $(target_dir)/etc/root.secret && chmod 0600
$(target_dir)/etc/root.secret || echo ok
test -e $(target_dir)/etc/rsync.pass && chmod 0600
$(target_dir)/etc/rsync.pass || echo ok
define image/mkfs/prepare/pub_cfgs
[ -d $(target_dir)/etc/cfm/config/config-pub ] || mkdir -p
$(target_dir)/etc/cfm/config/config-pub
cp $(target_dir)/etc/config/*
- mv $(target_dir)/etc/cfm/config/config-pub/network
$(target_dir)/etc/cfm/config/config-priv/
(cd $(target_dir)/etc/cfm/config/config-pub; md5sum * >
pub-cfg-md5)
define image/mkfs/prepare
$(call image/mkfs/prepare/default)
$(call image/mkfs/prepare/pub_cfgs)
squashfs處理
install-->kernel_prepare-->$(call buildimage/mkfs,$(fs))
define buildimage/mkfs
install: mkfs-$(1)
.phony: mkfs-$(1)
mkfs-$(1): mkfs_prepare
$(image/mkfs/$(1))
$(call build/mkfs/default,$(1))
$(call build/mkfs/$(1),$(1))
$(kdir)/root.$(1): mkfs-$(1)
define image/mkfs/squashfs
$(staging_dir_host)/bin/mksquashfs4 $(target_dir)
$(kdir)/root.squashfs -nopad -noappend -root-owned -comp
$(squashfscomp) $(squashfsopt) -processors $(if
$(config_pkg_build_jobs),$(config_pkg_build_jobs),1)
squashfs打更新檔
install--> $(callimage/build,$(fs))
define image/build/squashfs
$(call prepare_generic_squashfs,$(kdir)/root.squashfs)
define image/build
$(call image/build/$(1),$(1))
dd if=$(kdir)/root$(2).$(1)
of=$(bin_dir)/$(img_prefix)$(2)-$(1)-root$(3).img bs=2k conv=sync
# pad to 4k, 8k, 16k, 64k, 128k, 256k and add jffs2 end-of-filesystem
mark
define prepare_generic_squashfs
$(staging_dir_host)/bin/padjffs2 $(1) 4 8 16 64 128 256
至此生成squashfs檔案系統鏡像openwrt-ipq806x-squashfs-root.img(root.squashfs)。
4)ubifs處理
intall --> $(callimage/mkfs/ubifs)
将檔案系統格式化為ubifs格式:openwrt-ipq806x-ubifs-root.img(root.ubifs)。
将kernel處理後的fit鏡像openwrt-ipq806x-fit-uimage.itb和rootfs處理後的root.squashfs打包成ubi格式的ubi鏡像openwrt-ipq806x-ubi-root.img。此鏡像可直接在uboot下燒寫和運作,也就是常說的factory.bin。
build_img.sh通過工具mkimage将ubi鏡像增加firmware鏡像頭後生成sysupgrade.bin以支援sysupgrade更新使用