• (五) openwrt打包过程


    标签(空格分隔): Makefile


    本周是成胖子每周一博第六周,更好地阅读体验,请点击这里


    前言

    前面我们已经讲了openwrt编译的大部分过程,包括大致的编译步骤,ipk的编译等.今天是我这个系列的最后一篇,我们来看看openwrt的各个部分是如何组织成为最后的bin文件的.
    最后一个步骤,我们的执行的目标是target/install.

    执行过程推导

    我们首先通过Makefile来分析下我们的依赖关系.

    通过前几篇的分析,我们应当知道此时target/install将依赖于target/linux/install.
    如下是target/linux/Makefile的节选:

    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的节选:

    include $(INCLUDE_DIR)/target.mk
    
    $(eval $(call BuildTarget))
    

    在这个Makefile文件中,我们找不到目标compile,它被文件target.mk封装.我们在ipk的编译中,已经见过类似的用法.
    如下是include/target.mk的节选:

    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的节选:

    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,

    $(LINUX_DIR)/.image的依赖和执行就在节选中,我们略过,

    同时将进入image文件夹下,执行目标compileinstall.下面我来看看进入image文件夹下,发生的故事.
    打开tareget/linux/ramips/image/Makefile,我们找了一圈,也没找到我们要的目标compileinstall;如果我们是完整编译,其实目标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格式的二进制文件,然后包括压缩内核,为内核制作头部,最后将加工过的内核部分和文件系统部分组合起来.

    尾记

    1. 在网上查找资料的时候,发现大部分资料要么雷同,要么浅尝辄止.
    2. 下面这篇文章在我深入学习的过程中,起了不小的作用.链接;就是排版太差了,我才有了自己写写的冲动.
    3. openwrt的整个编译过程是很复杂的,想想电脑一秒可以做多少次计算,而一次完整编译轻轻松松就要两三个小时.可想而知编译过程之复杂.但是我觉得我把大体的编译过程是弄明白了,也欢迎大家交流.一共用了5篇的篇幅,大体的编译过程也算是交代完了.
    4. 纸上得来终觉浅,下面我觉得我还需要更多的改改写写来加深了解.最近在瞎看看linux内核相关的东西,那本书上也说了要加深学习了解,需要更多的修修补补.与大家共勉.
  • 相关阅读:
    linux 清空文件内容命令
    优秀的java 社区
    vue强制刷新组件 ----组件重置到初始状态
    function的json对象转换字符串与字符串转换为对象的方法
    js实现深度优先遍历和广度优先遍历
    Egg.js中使用sequelize事务
    JavaScript ES6 数组新方法 学习随笔
    eggjs的参数校验模块egg-validate的使用和进一步定制化升级
    Node.js 服务端图片处理利器
    webp图片实践之路
  • 原文地址:https://www.cnblogs.com/chengyi818/p/5118999.html
Copyright © 2020-2023  润新知