查找package/compile

package/compile目标没有明确的Makefile定义。所以我们考虑通过顶层Makefile中include进来的package/Makefile找到突破口。

该文件中直接定义的只有cleanup、rootfs-prepare、index三个目标。文件最后以package作为参数调用了subdir函数。根据之前文章中关于subdir函数作用的分析,我们可以得出结论:任何非直接定义的package/xxx目标,都依赖于其特定子目录的对应目标。特定子目录的读取规则参考前文,这里直接列出结论:

  • install目标依赖于配置了CONFIG_PACKAGE_xxx为y的包目录
  • prereq目标依赖于CONFIG_PACKAGE_xxx为y或者m的包目录
  • 其他目标依赖于CONFIG_PACKAGE_xxx为y或者m的包目录

深入package/包名/

进入到具体的包,每个包的Makefile内容都各不一样。但是大体上都包含以下几个部分:

  1. include顶层的rules.mk文件
  2. 定义名称、版本、下载地址、依赖等包信息
  3. include顶层include/package.mk文件
  4. 定义该软件包的一些非通用的函数(Package/包名/操作)。
  5. 以包名为参数调用BuildPackage函数。

前面几步都没有什么特别的,最后一步的BuildPackage函数调用非常关键。该函数定义在前面include进来的顶层include/pacakge.mk文件中。

深入BuildPackage函数

定义在顶层include/package.mk文件的该函数,内容不多。主要包含:

  1. 读取顶层overlay/*/包名.mk文件里定义的特殊规则
  2. 读取默认的Package/DefaultPackage/包名函数以导入软件包的相关变量。
  3. 检查必要的变量名是否正确定义。
  4. 重要通过遍历,构建目标规则。
  5. 以$1作为参数调用Build/DefaultTargets函数设置一些默认目标规则。该函数就在本文件中定义

遍历构造规则部分,主要是遍历列表内容作为target变量的值,然后调用BuildTarget/$(target)。遍历的列表分为集中情况:

  1. 当定义了Package/包名/targets时,将其作为遍历的内容。
  2. 否则检查是否存在PKG_TARGETS变量,如果有,则将其作为遍历内容。
  3. 否则将ipkg作为遍历的内容,如果CONFIG_DEBUG_DIR变量设置的话,将debug也添加为遍历的内容。

由此可见,默认情况下就是调用的BuildTarget/ipkg函数,该函数定义在package.mk前面include进来的include/package-ipkg.mk文件中。

Build/DefaultTargets函数主要定义了prepare、configure、dist、distclean这些准备性的目标,及其关联的目标stamp文件目标。

BuildTarget/ipkg函数定义了compile、install两个重要的目标,及其关联的依赖目标。需要注意的这两个目标定义,在Package/包名/install函数有定义的前提下才有效。而这个函数必须要自己定义。

所以,执行package/compile实质上执行的就是include/package-ipkg.mk中定义的compile目标。

package/包名/compile的依赖分析

每个软件包的compile目标,都依赖于IPKG_包名$(STAGING_DIR_ROOT)/stamp/.$(1)_installed两个目标。前者主要是编译打包成ipkg。后者主要调用Package/包名/installPackage/包名/install_lib两个方法,将软件包安装到STAGING_DIR_ROOTstaging_dir/target-mips_uClibc-0.9.30.1/root-wndr4500v3/)中。

IPKG_包名这个目标的执行体主要包括三个功能:

  • 生成ipkg所需要的软件包的元数据
  • 调用Package/包名/install安装软件包到临时目录build_dir/target-mips_uClibc-0.9.30.1/包名/ipkg-wndr4500v3/包名
  • 把临时目录、元数据打包成ipkg格式的软件包,放在bin/packages/wndr4500v3_uClibc-0.9.30.1/

IPKG_包名同时依赖于$(STAMP_BUILT)目标,该目标定义在include/package.mk中。

$(STAMP_BUILT)的执行体,主要调用Build/CompileBuild/Install两个函数,以及相关的Hooks。这两个函数分别是等价于在include/pacakge-defaults.mk中定义的Build/Compile/DefaultBuild/Install/Default

$(STAMP_BUILT)依赖于$(STAMP_CONFIGURED)。类似地,这个目标调用的是Build/Configure/Default。并依赖于$(STAMP_PREPARED)目标,这个目标同样类似的调用的是Build/Prepare/Default

上面被最终调用的,定义在include/package-defaults.mk中的默认目标分别是:

  • Build/Prepare/Default:调用PKG_UNPACK解压代码并调用Build/Patch打补丁
  • Build/Configure/Default:带参数执行默认的./configure
  • Build/Compile/Default:带参数执行默认的make
  • Build/Install/Default:带参数执行默认的make install

package/包名/install

该目标依赖于IPKG_包名目标,也就是说他也需要先将软件包打包成ipkg格式。该目标的执行体最简单,直接调用ipkg命令安装打包好的ipkg文件。需要注意的是ipkg命令本身设置了几个环境变量:

  • IPKG_TMP安装所需临时目录,值为tmp/ipkg
  • IPKG_INSTROOT安装目录,值为build_dir/target-mips_uClibc-0.9.30.1/root-wndr4500v3/
  • IPKG_CONF_DIR配置目录,值为staging_dir/target-mips_uClibc-0.9.30.1/etc
  • IPKG_OFFLINE_ROOT离线安装目录,值为build_dir/target-mips_uClibc-0.9.30.1/root-wndr4500v3