标签(空格分隔): Makefilehtml
本周是成胖子每周一博第六周,更好地阅读体验,请点击这里linux
前面咱们已经讲了openwrt编译的大部分过程,包括大体的编译步骤,ipk的编译等.今天是我这个系列的最后一篇,咱们来看看openwrt的各个部分是如何组织成为最后的bin文件的.
最后一个步骤,咱们的执行的目标是target/install.函数
咱们首先经过Makefile来分析下咱们的依赖关系.学习
经过前几篇的分析,咱们应当知道此时target/install将依赖于target/linux/install.
以下是target/linux/Makefile
的节选:ui
export TARGET_BUILD=1 prereq clean download prepare compile install menuconfig nconfig oldconfig update refresh: FORCE @+$(NO_TRACE_MAKE) -C $(BOARD) $@
咱们能够看出,此时执行compile将会进入对应的目标平台执行目标compile,咱们以mt7620a为例.它属于ramips平台.
以下是target/linux/ramips/Makefile
的节选:.net
include $(INCLUDE_DIR)/target.mk $(eval $(call BuildTarget))
在这个Makefile文件中,咱们找不到目标compile,它被文件target.mk封装.咱们在ipk的编译中,已经见过相似的用法.
以下是include/target.mk
的节选:unix
ifeq ($(TARGET_BUILD),1) include $(INCLUDE_DIR)/kernel-build.mk BuildTarget?=$(BuildKernel) endif
从上面的节选咱们看到变量TARGET_BUILD为1,因此这里函数BuildTarget其实等于在kernel-build.mk
定义的变量BuildKernel.
以下是include/kernel-build.mk
的节选:code
define BuildKernel $(if $(QUILT),$(Build/Quilt)) $(if $(LINUX_SITE),$(call Download,kernel)) $(STAMP_CONFIGURED): $(STAMP_PREPARED) $(LINUX_KCONFIG_LIST) $(TOPDIR)/.config $(Kernel/Configure) touch $$@ $(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= endef
咱们绕来绕去,终于找到了咱们要的目标,install依赖于$(LINUX_DIR)/.image,htm
$(LINUX_DIR)/.image的依赖和执行就在节选中,咱们略过,blog
同时将进入image文件夹下,执行目标compile和install.下面我来看看进入image文件夹下,发生的故事.
打开tareget/linux/ramips/image/Makefile
,咱们找了一圈,也没找到咱们要的目标compile和install;若是咱们是完整编译,其实目标compile已经被执行过了.此时咱们以install为目标.玄机就在最后一句$(eval $(call BuildImage))
中,函数BuildImage被定义在include/image.mk
中,如下为节选:
define BuildImage ifeq ($(IB),) .PHONY: download prepare compile clean image_prepare mkfs_prepare kernel_prepare install compile: $(call Build/Compile) clean: $(call Build/Clean) image_prepare: compile mkdir -p $(KDIR)/tmp $(call Image/Prepare) else image_prepare: mkdir -p $(KDIR)/tmp endif 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))) 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) endef
这里咱们看到了咱们的目标install和它的依赖关系及执行语句,这里$(TARGET_FILESYSTEMS)一般为squashfs.其中大部分函数定义在咱们对应平台的image文件夹下的Makefile中.
上文中第28行需稍加注意,这里调用函数BuildImage/mkfs,定义以下:
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) endef
这里能够看到目标install依赖于mkfs-(1).
如此咱们能够获得整个依赖关系图以下:
图片不清晰能够下载下来查看.
上一节咱们分析了依赖关系,执行过程其实就是倒叙执行的过程.
主要编辑过程就是先将内核编译完成,而后将安装ipk的root-ramips文件夹制做为squash格式的二进制文件,而后包括压缩内核,为内核制做头部,最后将加工过的内核部分和文件系统部分组合起来.
openwrt
的整个编译过程是很复杂的,想一想电脑一秒能够作多少次计算,而一次完整编译轻轻松松就要两三个小时.可想而知编译过程之复杂.可是我以为我把大致的编译过程是弄明白了,也欢迎你们交流.一共用了5篇的篇幅,大致的编译过程也算是交代完了.