12 15

OpenWRT编译系统分析之target/install

查找target/install

该目标主要用于安装target系统。但是搜索代码可以发现,该目标没有任何Makefile直接、明确的定义过。那么该目标是怎么执行的呢?

从顶层Makefile我们可以看到有include target/Makefile指令。所以多半这个目标应该和target/Makefile有关系了。而打开target/Makefile你会发现,除了一堆变量定义,最后就是将target目录名作为$1参数调用了之前文章中提到的subdir函数。

按照之前文章中的解析,subdir函数肯定定义了$1/install也就是target/instal目标,该目标没有执行体,只有依赖目标。由于target/Makefile中存在target/builddirs-install变量,根据前面的解析,其变量值就是target/install的依赖目标。

target/builddirs-install的内容第一个就是写死的linux。其次,如果定义了CONFIG_SDK那么还包含sdk,如果定义了CONFIG_IB,还包含imagebuilder。

所以综合起来,执行target/install,实质就是执行其依赖target/linux/install。而根据前面的解析,target/linux/install的执行体,就是进入到target下的linux/子目录,执行install目标。

深入target/linux子目录

该目录下的Makefile非常简单,直接定义了install这个目标。依赖FORCE这个伪目标,表示每次都必须执行。执行体也只有一行,就是进入到BOARD这个变量定义的子目录下,执行install目标。

那么BOARD在哪儿定义的呢?跟踪代码容易发现,该变量定义在顶层目录的rules.mk文件,值大概就是CONFIG_TARGET_BOARD变量。这个变量就不用说了,定义在配置文件.config中。针对本文所用的WNDR4300v2而言,该变量的值就是wndr4500v3(擦,竟然不是wndr4300v2,Netgear也是够懒的)。

OK,那么接下来make就会进入到target/linux目录的wndr4500v3子目录了。

深入target/linux/wndr4500v3

和target/Makefile的尿性一样,wndr4500v3目录中的Makefile,特么也没有定义任何目标,只是在文件末尾调用了BuildGitTarget函数。该函数定义在哪儿呢?答案是顶层目录下include/target.mk文件。

这个文件更奇葩了。的确是有BuildGitTarget的定义。一个是当DUMP变量为1时,该函数等价于BuildTargets/DumpCurrent。一个是当TARGET_BUILD变量为1时,该函数就是直接等价于BuildGitKernel

由于调用我们的targe/linux/Makefile文件中,明确通过export定义了TARGET_BUILD为1。加上根据经验猜测,BuildTargets/DumpCurrent并不会真正Build,只是将当前已经Built的显示出来而已。所以我们可以直接认为BuildGitTarget就等价于BuildGitKernel

那么BuildGitKernel在哪儿定义的呢?答案就是顶层目录下的include/kernel-build.mk文件中。该文件就是在BuildGitKernel赋值给BuildGitTarget前,通过include指令明确引用的。

从include/kernel-build.mk文件中,很容易看到,BuildGitTarget函数中,定义了一个install目标,由于是target/linux/wndr4500v3/Makefile中调用的,所以target/linux/wndr4500v3/Makefile中install目标,实际上也就是该目标。那么该目标的具体内容是什么呢?

首先,该目标依赖于*$(LINUX_DIR)/.image*目标。其执行体也很简单,把TARGET_BUILD变量的值清空后,进入到image子目录,执行compile、install两个目标。

先说依赖,该依赖目标同样也是在BuildGitKernel函数中定义的,主要调用了Kernel/CompileImage函数。该函数也在该文件中定义,直接调用Kernel/CompileImage/Default。该函数定义在顶层目录的include/kernel-default.mk文件中。主要干了两件事,一是编译内核。一是将内核通过objcopy打包成vmlinux。

再说执行体,执行体就是进入image子目录,执行compile和install两个目标。

深入target/linux/wndr4500v3/image

在目录下执行compile和install两个目标,就要先看看该目录下的Makefile了。不错,又是一个更坑爹的货。照样没有直接定义目标,照样在最后调用了一个BuildImage的函数。是的,通过搜索代码很容易发现,这个函数定义在顶层目录下的include/image.mk中。

打开该文件发现,根据IB变量的不同,compileinstall目标的定义也不同。IB变量主要当使用ImageBuilder制作镜像时定义(别问我为什么知道,我真的是凭感觉猜的),其他情况未定义。所以:

  • 对于compile目标,其依赖是compile-targets,执行体调用Build/Compile函数。这两个函数实际上都没有定义。所以该目标什么都没做。(注意include/package.mk中定义了Build/Compile函数,但那是package的默认编译函数,和这里的无关)
  • 对于install目标,其以来是compile、install-targets(该目标依赖和执行体没有,可以忽略),执行体依次调用
    • Image/Prepare
    • Image/mkfs/prepare
    • Image/BuildKernel
    • Image/mkfs/jffs2
    • Image/mkfs/squashfs
    • Image/mkfs/tgz
    • Image/mkfs/cpiogz
    • Image/mkfs/ext2
    • Image/mkfs/iso
    • Image/Checksum (定义在include/image.mk) 这一系列目标中,除了Image/PrepareImage/BuildKernel两个目标和具体的平台相关,所以就在target/linux/wndr4500v3/image/Makefile中定义。。其他的都定义在顶层目录下的include/image.mk中

总结

target/xxx 目标,通过层层嵌套,最终执行的是*target/linux/wndr4500v3/中的*xxx*目标。

target/compile 的执行流程就是:

  1. 通过Kernel/Configure配置内核,依赖FORCE每次都执行
  2. 通过Kernel/CompileModules编译内核模块,依赖FORCE每次都执行
  3. 进入到images目录执行compile,本质上没做什么。

target/install 的执行流程就是:

  1. 通过Kernel/Configure配置内核,依赖FORCE每次都执行
  2. 通过Kernel/CompileImage编译内核,依赖FORCE每次都执行
  3. 进入到images目录执行install和compile(compile本质上没做什么)。

其中,Kernel/CompileModulesKernel/CompileImage定义在顶层目录下的include/kernel-defaults.mk中。