头图

前言

搞了openwrt很长一段时间了,一直都是基于它新加一下功能,还没有从系统整体层面深入解剖过它的流程,今儿就整理一下。

结构

openwrt build dir

  • 编译前原有目录:

    • config : 用于构建系统的配置和选项
    • include :包含一些Makefile文件,定义构建系统的一些基本的规则和函数。会被其他Makefile文件引用
    • package :包含一些软件包定义,定义在固件中包含的应用程序和库,每个软件包都有一个对应的子目录。
    • scripts :一些脚本文件,执行各种任务,如下载源代码、解压缩文件、编译软件包等
    • target : 包含针对不同硬件目标的配置和规则,可以定义目标设备的特定配置的和功能选项
    • toochain :包含交叉编译的配置,可以配置 工具链的版本、架构等选项
    • tools : 包含构建系统所需的工具和脚本,如打包固件、生成镜像等, 工具如:automake、make、sed 等
  • 编译后新增目录:

    • bin : 编译完成后生成的固件文件,如根文件系统镜像、内核镜像以及其它引导加载程序文件等
    • build_dir :编译过程中生成的临时构建文件,是编译过程的工作目录
    • dl :存放了已经下载的软件包源代码压缩包,重新编译时,系统可以使用已下载的源码,不需要重新下载
    • feeds :软件包源的定义,软件包源用于指定从哪里获取软件包的源代码以及软件包的配置信息。
    • staging_dir :含了构建过程中生成的临时文件,如编译后的头文件、共享库和工具, 用于交叉编译软件包和构建最终的固件
    • tmp :临时目录,用于存放构建过程中的各种临时文件和日志

Makefile过程

编译时是根据根目录下的Makefile开始的。我编译一般用的是make V=s。主makefile的逻辑如下:

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

编译流程

根据make进入第一个逻辑,通过OPENWRT_BUILD=1可以在第二次make时进入第二个逻辑,那么第二个make是怎么来的? 知识点来了:

执行 make 指令后,可以根据 Makefile 中某些目标指令执行新的 make 指令,也就是说,make 是可以嵌套的, Makefile是可以重入的

如何开始的第二次make

上述的第一个逻辑,引入了一下的mk文件:

 include $(TOPDIR)/include/debug.mk
 include $(TOPDIR)/include/depends.mk
 include $(TOPDIR)/include/toplevel.mk

其中include/toplevel.mk文件中有对world目标作解释:

%::
    @+$(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 $@ $(if $(WARN_PARALLEL_ERROR), || { \
        printf "$(_R)Build failed - please re-run with -j1 to see the real error message$(_N)\n" >&2; \
        false; \
    } )

include/toplevel.mk 是 OpenWrt 构建系统中的一个重要的 Makefile,它包含了一些全局配置和规则,用于整体的构建过程。在 include/toplevel.mk 中定义的 %:: 规则是一个通用规则,用于处理未匹配到明确规则的目标。这意味着,如果你运行 make world,构建系统会尝试匹配 world 这个目标,但如果没有找到明确的规则,它将回退到 %:: 规则。
$@ 对应的就是 world, 结合结合 verbose.mk,可以得出$(SUBMAKE) 就是 umask 022; make -w,所以指令翻译为:

_limit=`ulimit -n`; [ "$_limit" = "unlimited" -o "$_limit" -ge 1024 ] || ulimit -n 1024; umask 022; make -w -r world

总结:make V=s, 进入world编译目标,进入第一个逻辑,赋值OPENWRT_BUILD=1,进行初始化,通过include/toplevel.mk又调用一遍make,在主makefile中进入第二个逻辑。

第二个逻辑流程

第二个逻辑首先引入了一些makefile:

  include $(INCLUDE_DIR)/subdir.mk
  include target/Makefile
  include package/Makefile
  include tools/Makefile
  include toolchain/Makefile

include $(INCLUDE_DIR)/subdir.mk :这个命令的目的是引入 subdir.mk 文件,以便处理 OpenWrt 项目中的子目录,其中包括各种软件包、工具和配置文件。
下边的四个makefile都是根据subdir.mk中的两个函数subdir、stampfile编译.
1.subdir 函数:

  • 作用:subdir 函数用于进入子目录,并执行该子目录的 Makefile 文件中的构建规则。它允许在 Makefile 中进行递归构建,以便处理项目的不同子目录。
  • 语法:$(call subdir,directory),其中 directory 是要进入的子目录的名称。
  • 示例:

    $(call subdir,package/foo)
  • 详细说明:这个函数将进入指定的子目录 directory,然后执行子目录中的 Makefile 文件,以执行与该子目录相关的构建任务。这对于管理多个子目录的构建过程非常有用。

2.stampfile 函数:

  • 作用:stampfile 函数用于创建一个称为 "stamp file" 的标记文件,以跟踪某个构建步骤是否已经完成。通常情况下,一个构建步骤只有在标记文件不存在或时间戳比相关的依赖文件更新时才会执行。
  • 语法:$(call stampfile,stampname,target,dependencies),其中:

    • stampname 是标记文件的名称。
    • target 是与标记文件关联的目标。
    • dependencies 是目标的依赖文件。
  • 示例:

    $(call stampfile,myscript,$(CURDIR)/myscript.sh,$(CURDIR)/myinputfile)
  • 详细说明:这个函数用于创建一个标记文件 stampname,如果 target 的依赖文件 dependencies 更新了,标记文件将被更新,否则它将保持不变。这有助于确保只有在依赖文件发生变化时才执行特定的构建步骤,从而提高构建效率。

NULL
30 声望0 粉丝