查找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
变量的不同,compile
和install
目标的定义也不同。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/Prepare
和Image/BuildKernel
两个目标和具体的平台相关,所以就在target/linux/wndr4500v3/image/Makefile中定义。。其他的都定义在顶层目录下的include/image.mk中
总结
target/xxx 目标,通过层层嵌套,最终执行的是target/linux/wndr4500v3/中的xxx*目标。
target/compile 的执行流程就是:
- 通过
Kernel/Configure
配置内核,依赖FORCE每次都执行 - 通过
Kernel/CompileModules
编译内核模块,依赖FORCE每次都执行 - 进入到images目录执行compile,本质上没做什么。
target/install 的执行流程就是:
- 通过
Kernel/Configure
配置内核,依赖FORCE每次都执行 - 通过
Kernel/CompileImage
编译内核,依赖FORCE每次都执行 - 进入到images目录执行install和compile(compile本质上没做什么)。
其中,Kernel/CompileModules
和Kernel/CompileImage
定义在顶层目录下的include/kernel-defaults.mk中。