openwrt目录结构

上图是openwrt目录结构,其中第一行是原始目录,第二行是编译过程中生成的目录。各目录的作用是:

  • 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会放到此目录下。

  • main Makefile

    openwrt根目录下的Makefile是执行make命令时的入口。从这里开始分析。

    world:
    
    ifndef ($(OPENWRT_BUILD),1) # 第一个逻辑 ... else # 第二个逻辑 ... endif

    上面这段是主Makefile的结构,可以得知:

    1. 执行make时,若无任何目标指定,则默认目标是world
    2. 执行make时,无参数指定,则会进入第一个逻辑。如果执行命令make OPENWRT_BUILD=1,则直接进入第二个逻辑。

    编译时一般直接使用make V=s -j5这样的命令,不会指定OPENWRT_BUILD变量

    第一个逻辑

     override OPENWRT_BUILD=1 export OPENWRT_BUILD

    更改了OPENWRT_BUILD变量的值。这里起到的作用是下次执行make时,会进入到第二逻辑中。

    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)\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目标,这两个目标都会进入到第二逻辑中。

    第二逻辑

    首先就引入了target, package, tools, toolchain这四个关键目录里的Makefile文件

     include target/Makefile include package/Makefile include tools/Makefile include toolchain/Makefile

    这些子目录里的Makefile使用include/subdir.mk里定义的两个函数来动态生成规则,这两个函数是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两个部分组成,要对两个部分先分别处理,然后再合并成一个.bin文件。先看一下这个流程。

    "target/linux/ramips/image/Makefile" 文件中的最后一句:$(eval $(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_mt7620a/ 目录中:

    lzma e vmlinux -lc1 -lp2 -pb2 vmlinux.bin.lzma

    MkImage

    build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/ 目录中:

    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

    把uImage.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-mt7620a
    • 生成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-mt7620a,$(KDIR)/vmlinux-mt7620a.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 等过程生成一个uImage,再与mksquashfs工具制作的文件系统rootfs.squashfs合并。

openwrt: Makefile 框架分析[转载]的更多相关文章

  1. openwrt: Makefile 框架分析

    openwrt: Makefile 框架分析 原文链接:blog.chinaunix.net/uid-26675482-id-4704952.html 本篇的主要目的是想通过分析Makefile,了解 ...

  2. contiki makefile框架分析 < contiki学习之一 >

    在linux下的工程编译,基本都可以使用makefile这个工具来完成.Contiki OS亦如此,下面分析contiki整个Makefile的框架,对makefile的具体内容暂不做分析.本文依赖于 ...

  3. openwrt<转载--openwrt框架分析 >

    这次讲讲openwrt的结构. 1. 代码上来看有几个重要目录package, target, build_root, bin, dl.... ---build_dir/host目录是建立工具链时的临 ...

  4. 【转载】openwrt框架分析

    文章出处:http://blog.csdn.net/kingvenll/article/details/27545221 这次讲讲openwrt的结构. 1. 代码上来看有几个重要目录package, ...

  5. openwrt luci web分析

    openwrt luci web分析 来源 https://www.jianshu.com/p/596485f95cf2 www/cbi-bin/luci #!/usr/bin/lua --cgi的执 ...

  6. VS2010/MFC编程入门之四(MFC应用程序框架分析)

    VS2010/MFC编程入门之四(MFC应用程序框架分析)-软件开发-鸡啄米 http://www.jizhuomi.com/software/145.html   上一讲鸡啄米讲的是VS2010应用 ...

  7. cc2530 makefile简略分析 <contiki学习之三>

    前面将contiki的makefile框架都理了下,这篇就以cc2530为收篇吧,也即makefile分析就该到此为止了. contiki/examples/cc2530dk 打开Makefile如下 ...

  8. (七) UVC框架分析

    title: UVC框架分析 date: 2019/4/23 19:50:00 toc: true --- UVC框架分析 源码的位置在drivers\media\video\uvc,查看下Makef ...

  9. SSH框架总结(环境搭建+框架分析+实例源码下载)

    一.SSH框架简介 SSH是struts+spring+hibernate集成的web应用程序开源框架. Struts:用来控制的,核心控制器是Controller. Spring:对Struts和H ...

随机推荐

  1. git上面创建个人简历-链接

    github创建个人在线简历: https://segmentfault.com/a/1190000006820290

  2. INSPIRED启示录 读书笔记 - 第18章 重新定义产品说明文档

    理想的产品说明文档 1.产品说明文档应该完整地描述用户体验——不只是用户需求,还包括交互设计和视觉设计.用户需求和用户体验是密不可分的 2.产品说明文档必须准确地描述软件的行为 3.产品说明文档必须以 ...

  3. jenkins tomcat

    tomcat增加用户配置: <role rolename="tomcat"/> <role rolename="role1"/> < ...

  4. 【P2325】王室联邦(树的遍历+贪心)

    在肖明 #神#的推荐下,我尝试了这个题,一开始想的是暴力枚举所有的点,然后bfs层数,试着和肖明 #神#说了这种方法之后, #神#轻蔑的一笑,说这不就是一个贪心么,你只需要先建树,然后从底下向上遍历, ...

  5. nginx 反向代理配置之---可配置多域名请求

    配置文件如下: server { listen 80; server_name ngin服务器所对应的的域名; error_log /data/logs/nginx/mainsite.error.lo ...

  6. 简单的文件上传的下载(动态web项目)

    1.在页面中定义一个form表单,如下: <!-- 文件上传 --> <form action="${pageContext.request.contextPath}/Fi ...

  7. 泛型学习第二天——C#中的List<string>泛型类示例

    在C#代码中使用一系列字符串(strings)并需要为其创建一个列表时,List<string>泛型类是一个用于存储一系列字符串(strings)的极其优秀的解决办法.下面一起有一些Lis ...

  8. How to use Jenkins

    一.关键点 1.how to start the build server? do i need to start some app to do this? I don't believe so... ...

  9. Kafka详解三:开发Kafka应用

    问题导读 1.Kafka系统由什么组成?2.Kafka中和producer相关的API是什么? 一.整体看一下Kafka        我们知道,Kafka系统有三大组件:Producer.Consu ...

  10. 爬虫之MongoDB的图片

    聚合: