Openwrt:Makefileフレームワーク解析

24882 ワード

http://blog.chinaunix.net/uid-26675482-id-4704952.html
本編の主な目的は,Makefileを解析することによってopenwrtコンパイルプロセスを理解することである.以下の点に注目してください.
Openwrtディレクトリ構造メインMakefileの解析プロセスは,各サブディレクトリのターゲット生成である.
kernelコンパイルプロセスfirmwareの生成プロセスパッケージのコンパイルプロセスOpenwrtディレクトリ構造
公式ソースのダウンロード速度が速すぎて、githubからopenwrtのコード倉庫をcloneしました.
git clone https://github.com/openwrt-mirror/openwrt.git

openwrt: Makefile 框架分析_第1张图片
上図はopenwrtディレクトリ構造で、最初の行は元のディレクトリで、2番目の行はコンパイル中に生成されたディレクトリです.各ディレクトリの役割は次のとおりです.
tools-コンパイルにはいくつかのツールが必要です.toolsには、これらのツールを取得およびコンパイルするコマンドが含まれています.中にはMakefileが入っていて、patchもあるかもしれません.各Makefileには$(eval$(call HostBuild))があり、このツールをコンパイルするのはホスト上で使用するためであることを示しています.
toolchain-kernel headers,C library,bin-utils,compiler,debugger を取得するコマンドが含まれています.
target-各プラットフォームはこのディレクトリでfirmwareとkernelのコンパイルプロセスを定義します.
Package-パッケージごとのMakefileが含まれます.OpenwrtはMakefileテンプレートを定義し、各ソフトウェアはこのテンプレートを参照してパッケージのバージョン、ダウンロードアドレス、コンパイル方法、インストールアドレスなど、独自の情報を定義します.
include-openwrtのMakefileはすべてここに保存されています.
scripts-パッケージ管理に使用されるperlスクリプトがあります.
dl-パッケージをダウンロードしたら、このディレクトリにを入れます.
build_dir-パッケージはbuildに解凍されます.dir/で、をコンパイルします.
staging_dir-最終インストールディレクトリ.tools,toolchainはここにインストールされ,rootfsもここに置かれる.
feeds -
bin-コンパイルが完了すると、firmwareと各ipkがこのディレクトリの下に配置されます.
OpenWrt Development Guide
main Makefile
Openwrtルートディレクトリの下にあるMakefileは、makeコマンドを実行するときのエントリです.ここから分析を始めます.
world:

ifndef ($(OPENWRT_BUILD),1) #       ... else #       ... endif

上の部分はメインMakefileの構造で、次のことがわかります.
makeを実行する場合、ターゲット指定がない場合、デフォルトのターゲットはworld です.
makeを実行すると、パラメータ指定がなく、最初の論理に入ります.コマンドmake OPENWRT_を実行する場合BUILD=1は、2番目の論理に直接入ります.
コンパイル時にmake V=s-j 5のようなコマンドをそのまま使用するのが一般的で、OPENWRT_は指定されませんBUILD変数
最初の論理
 override OPENWRT_BUILD=1 export OPENWRT_BUILD

OPENWRT_を変更しましたBUILD変数の値.ここで果たす役割は,次回makeを実行すると,第2の論理に入ることである.
toplevel.mkの%::worldターゲットのルールを説明します.
prereq:: prepare-tmpinfo .config @+$(MAKE) -r -s tmp/.prereq-build $(PREP_MK) @+$(NO_TRACE_MAKE) -r -s $@ %:: @+$(PREP_MK) $(NO_TRACE_MAKE) -r -s prereq @( \
		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)
"
>&2; \ fi \ ) @+$(ULIMIT_FIX) $(SUBMAKE) -r $@

make V=sを実行すると、上記のルールは以下のように簡略化されます.
prereq:: prepare-tmpinfo .config @make -r -s tmp/.prereq-build @make V=ss -r -s prereq %:: @make V=s -r -s prereq @make -w -r world

最終的にprereqとworldターゲットが実行され、両方のターゲットが第2の論理に入ることがわかります.
だいにろんり
まずtarget,package,tools,toolchainの4つのキーディレクトリのMakefileファイルを導入しました
 include target/Makefile include package/Makefile include tools/Makefile include toolchain/Makefile

これらのサブディレクトリのMakefileはinclude/subdirを使用します.mkで定義された2つの関数は規則を動的に生成します.この2つの関数はsubdirとstampfileです.
stampfile
target/Makefileの例:
(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) 

したがって、(eval(call stampfile、(curdir)、target、prereq,.config))ターゲット(target/stamp-prereq)を生成
targetに対してそれぞれ生成された:(target/stamp?preq)、(target/stamp-copile)、$(target/stamp-install)toolchain : $(toolchain/stamp-install)
package : (package/stamp?preq),(package/stamp-cleanup), (package/stamp?compile),(package/stamp-install)
tools : $(tools/stamp-install)
OpenWrtのメインMakefile作業手順
subdir
subdirという関数はたくさんのものを書いていて、複雑に見えます.
$(call subdir,target)は、下のサブディレクトリを巡回し、make-C操作を実行します.これでサブディレクトリに切り込みます.
ディレクトリ変数
いくつかの重要なディレクトリパス:
KERNEL_BUILD_DIR
build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/linux-3.14.18

LINUX_DIR
build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/linux-3.14.18

KDIR
build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a

BIN_DIR
bin/ramips Makefileにはrulesが含まれています.mk, target.mk等mkファイル、これらのファイルには多くの変数が定義されており、パスに関連するものもあれば、ソフトウェアに関連するものもあります.これらの変数はMakefileプロジェクト全体でよく使われています.

TARGET_ROOTFS_DIR
build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2

BUILD_DIR
build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2

STAGING_DIR_HOST
staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2

TARGET_DIR
build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/root-ramips

kernelコンパイル:
target/linux/ramips/Makefile: $(eval $(call BuildTarget)) target/linux/Makefile : export TARGET_BUILD=1 include/target.mk:
ifeq ($(TARGET_BUILD),1)
  include $(INCLUDE_DIR)/kernel-build.mk
  BuildTarget?=$(BuildKernel)
endif

BuildKernelはinclude/kernel-buildです.mkは、カーネルをコンパイルする方法を説明する複数行変数を定義し、主にinstallルールの依存チェーンに注目します.
 $(KERNEL_BUILD_DIR)/symtab.h: FORCE
	rm -f $(KERNEL_BUILD_DIR)/symtab.h
	touch $(KERNEL_BUILD_DIR)/symtab.h
	+$(MAKE) $(KERNEL_MAKEOPTS) vmlinux
	... $(LINUX_DIR)/.image: $(STAMP_CONFIGURED) $(if $(CONFIG_STRIP_KERNEL_EXPORTS),$(KERNEL_BUILD_DIR)/symtab.h) FORCE $(Kernel/CompileImage) $(Kernel/CollectDebug)
	touch $$@


  install: $(LINUX_DIR)/.image +$(MAKE) -C image compile install TARGET_BUILD= 
1.   make vmlinux    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/CompileImage/Default) $(KERNEL_CROSS)objcopy -O binary $(OBJCOPY_STRIP) -S $(LINUX_DIR)/vmlinux $(LINUX_KERNEL)$(1)
        --> build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/vmlinux $(KERNEL_CROSS)objcopy $(OBJCOPY_STRIP) -S $(LINUX_DIR)/vmlinux $(KERNEL_BUILD_DIR)/vmlinux$(1).elf
        --> build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/vmlinux.elf $(CP) $(LINUX_DIR)/vmlinux $(KERNEL_BUILD_DIR)/vmlinux.debug
        --> build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/vmlinux.debug

firmwareの生成
firmwareはkernelとrootfsの2つの部分からなり、2つの部分をそれぞれ処理してから1つにマージする.binファイル.まずこの流れを見てみましょう.
「target/linux/ramips/image/Makefile」ファイルの最後の文:$(call BuildImage))、BuildImageをここに展開します.BuildImageはinclude/imageで定義する.mkファイルでは、複数のターゲットのルールが定義されています.
define BuildImage

    compile: compile-targets FORCE
		**$(call Build/Compile)**

    install: compile install-targets FORCE ... $(call Image/BuildKernel) ##   vmlinux ... $(call Image/mkfs/squashfs) ##   squashfs,  vmlinux     .bin   ... endef

処理vmlinux:Image/BuildKernel
target/linux/ramips/image/Makefile:
define Image/BuildKernel
	cp $(KDIR)/vmlinux.elf $(BIN_DIR)/$(VMLINUX).elf
	cp $(KDIR)/vmlinux $(BIN_DIR)/$(VMLINUX).bin $(call CompressLzma,$(KDIR)/vmlinux,$(KDIR)/vmlinux.bin.lzma) $(call MkImage,lzma,$(KDIR)/vmlinux.bin.lzma,$(KDIR)/uImage.lzma)
	cp $(KDIR)/uImage.lzma $(BIN_DIR)/$(UIMAGE).bin
ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),)
	cp $(KDIR)/vmlinux-initramfs.elf $(BIN_DIR)/$(VMLINUX)-initramfs.elf
	cp $(KDIR)/vmlinux-initramfs $(BIN_DIR)/$(VMLINUX)-initramfs.bin $(call CompressLzma,$(KDIR)/vmlinux-initramfs,$(KDIR)/vmlinux-initramfs.bin.lzma) $(call MkImage,lzma,$(KDIR)/vmlinux-initramfs.bin.lzma,$(KDIR)/uImage-initramfs.lzma)
	cp $(KDIR)/uImage-initramfs.lzma $(BIN_DIR)/$(UIMAGE)-initramfs.bin
endif $(call Image/Build/Initramfs) endef

lzma圧縮コア
build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt 7620 a/ディレクトリ:
lzma e vmlinux -lc1 -lp2 -pb2 vmlinux.bin.lzma

MkImage
build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt 7620 a/ディレクトリ:
mkimage -A mips -O linux -T  kernel -C lzma -a 0x80000000 -e 0x80000000 -n "MIPS OpenWrt Linux-3.14.18" -d vmlinux.bin.lzma uImage.lzma

copy
VMLINUX:=$(IMG_PREFIX)-vmlinux --> openwrt-ramips-mt7620a-vmlinux UIMAGE:=$(IMG_PREFIX)-uImage --> openwrt-ramips-mt7620a-uImage
cp $(KDIR)/uImage.lzma $(BIN_DIR)/$(UIMAGE).bin

それをlzmaはbin/ramips/ディレクトリの下にコピーします:cp$(KDIR)/uImage.lzma bin/ramips/openwrt-ramips-mt7620a-uImage
squashfsを作成し、生成する.bin: $(call Image/mkfs/squashfs)
 define Image/mkfs/squashfs @mkdir -p $(TARGET_DIR)/overlay $(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) $(call Image/Build,squashfs)
endif

mkdir -p $(TARGET_DIR)/overlay
mkdir -p build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/root-ramips/overlay
mksquashfs4
$(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ファイルシステムを作成しrootを生成する.squashfs:
mksquashfs4 root-ramips root.squashfs -nopad -noappend -root-owned -comp gzip -b 256k -p '/dev d 755 0 0' -p '/dev/console c 600 0 0 5 1' -processors 1

$(call Image/Build,squashfs)
target/linux/ramips/image/Makefileでは、次の操作を行います.
define Image/Build $(call Image/Build/$(1))
	dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/$(IMG_PREFIX)-root.$(1) bs=128k conv=sync $(call Image/Build/Profile/$(PROFILE),$(1))
endef

dd if=(KDIR)/root.squashfsof=(BIN_DIR)/$(IMG_PREFIX)-root.squashfs bs=128k conv=sync
dd if=build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/root.squashfs of=bin/ramips/openwrt-ramips-mt7620-root.squashfs bs=128k conv=sync
(callImage/Build/Profile/(PROFILE),squashfs)
target/linux/ramips/mt7620a/profiles/00-default.mk,でProfile関数を呼び出す:$(eval$(call Profile,Default))
include/target.mkではProfile関数が定義されており、PROFILE=Default
define Image/Build/Profile/Default
	$(call Image/Build/Profile/MT7620a,$(1)) ... endef

ルール依存シーケンスは次のとおりです.
$(call Image/Build/Profile/$(PROFILE),squashfs)
  --> $(call BuildFirmware/Default8M/squashfs,squashfs,mt7620a,MT7620a)  --> $(call BuildFirmware/OF,squashfs,mt7620a,MT7620a,8060928)  --> $(call MkImageLzmaDtb,mt7620a,MT7620a)  --> $(call PatchKernelLzmaDtb,mt7620a,MT7620a)  --> $(call MkImage,lzma,$(KDIR)/vmlinux-mt7620a.bin.lzma,$(KDIR)/vmlinux-mt7620a.uImage)  --> $(call MkImageSysupgrade/squashfs,squashfs,mt7620a,8060928)

主な手順:
コピー:cp(KDIR)/vmlinux(KDIR)/vmlinux-mt 7620 a dtbファイルの生成:(LINUXDIR)/scripts/dtc/dtc?Odtb?o(KDIR)/MT7620a.dtb ../dts/MT7620a.dts
カーネルとdtbファイルのマージ:(STAGINGDIRHOST)/bin/patch?dtb(KDIR)/vmlinux-mt7620a $(KDIR)/MT7620a.dtb
使用lzma圧縮:(callCompressLzma、(KDIR)/vmlinux-mt 7620 a,$(KDIR)/vmlinux-mt 7620 a.bin.lzma)
lzmaを圧縮したファイルはmkimageツールで処理され、ubootで識別可能な情報をヘッダに追加します.
次に、firmwareファームウェアを統合して生成します.
MkImageSysupgrade/squashfs, squashfs, mt7620a,8060928
cat vmlinux-mt7620a.uImage root.squashfs > openwrt-ramips-mt7620-mt7620a-squashfs-sysupgrade.bin-->squashfs binドキュメントを作成し、そのサイズ<8060928が有効であることを確認します.そうしないと、エラーが発生します.
まとめ:全体の流れの下で、実は最も煩わしいのはやはりカーネルに対してファイルvmlinuxの操作を生成して、objcopy、patch-dtb、lzma、mkimageなどの過程を経て1つのuImageを生成して、更にmksquashfsツールと作成するファイルシステムrootfs.squashfsマージ.