本周成胖子每周一博到了第四周^_^javascript
前一篇,咱们大概描述了整个镜像文件的生成过程.本周咱们来解析主Makefile,看看主要编译过程是怎么产生的.java
咱们以chaos calmer
的代码为例,整个编译的入口是在源码根目录下的Makefile.编译的各类命令都应该在源码根目录下键入.
整个主Makefile的结构以下:linux
world:
ifneq ($(OPENWRT_BUILD),1)
顶层
else
第二层
endif
开始部分是一些注释和变量定义及路径检查.
根据Makefile的规则,在没有指定编译目标的时候,Makefile中的第一个目标将做为默认目标.
换句话说,当咱们执行make V=s
时,这个时候编译的目标就是world
.和咱们执行make world V=s
效果是同样的.ruby
一般在编译时,咱们不会定义变量OPENWRT_BUILD
的值,因此一般咱们是会走到顶层的.
顶层代码以下:bash
_SINGLE=export MAKEFLAGS=$(space);
override OPENWRT_BUILD=1
export OPENWRT_BUILD
GREP_OPTIONS=
export GREP_OPTIONS
include $(TOPDIR)/include/debug.mk
include $(TOPDIR)/include/depends.mk
include $(TOPDIR)/include/toplevel.mk
这里咱们看到变量OPENWRT_BUILD
被置为1.而后包含了3个.mk
文件.
这里稍微解释下.mk
文件.它们通常没有什么执行动做,都是一些变量的定义还有依赖关系的说明.能够类比于C语言的头文件来理解.markdown
debug.mk:ide
能够经过定义DEBUG的值来控制编译过程函数
depends.mk工具
主要定义了rdep这个变量ui
toplevel.mk
这个是咱们跟踪编译过程的重要的文件.这个文件在源码根目录下的
include
文件夹下.
核心代码以下:
%::
@+$(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; \
} )
除了少数在toplevel中被定义的目标外,其余编译目标都会走到这里.将之简化后:
%::
make prereq
make $@
首先执行prereq
,而后再执行咱们指定的目标或者默认目标world
.
prereq整理后的依赖关系以下:
其中
staging_dir/host/.prereq-build:
将会执行一系列主机检查,是否安装了必要的软件.
prepare-tmpinfo:
根据scan.mk,扫描
target/linux
和package
目录,生成packageinfo和targetinfo.
总之,顶层完成一系列必要的准备工做.对于绝大多数的目标而言,顶层是必经之路.固然,在toplevel.mk
中,咱们也能够看到目标menuconfig
.也就是说对于目标menuconfig
而言,将不会执行到第二层的逻辑.
在上面执行完make prereq
以后,将执行make world
.
还记得咱们进入顶层后修改了变量OPENWRT_BUILD
么?当再次执行make world
的时候,因为条件不知足,咱们将直接进入第二层来执行.
include rules.mk
include $(INCLUDE_DIR)/depends.mk
include $(INCLUDE_DIR)/subdir.mk
include target/Makefile
include package/Makefile
include tools/Makefile
include toolchain/Makefile
rules.mk:
很重要的一个mk文件,其中规定了不少有用的变量,包括各类目录路径的定义,交叉编译器等等.其中
ifeq ($(DUMP),) -include $(TOPDIR)/.config endif
就是包含了咱们的配置文件.对于
Makefile
而言,.config
文件就是一大串变量的定义.Makefile能够直接读取这些定义,从而控制编译过程.
subdir.mk:
这个是读懂咱们整个编译过程的关键所在,其中主要定义了两个函数:subdir和stampfile,咱们稍后加以解释.
接下来,包含了4个Makefile文件.咱们以target/Makefile
为例.该文件位于target
目录下.
核心部分为:
$(eval $(call stampfile,$(curdir),target,prereq,.config))
$(eval $(call stampfile,$(curdir),target,compile,$(TMP_DIR)/.build))
$(eval $(call stampfile,$(curdir),target,install,$(TMP_DIR)/.build))
$(eval $(call subdir,$(curdir)))
这里调用了subdir.mk
中定义的stampfile
函数.将会生成target/stamp-prereq
,target/stamp-compile
,target/stamp-install
三个变量.
以target/stamp-prereq
为例,执行部分为make target/prereq
.同理target/stamp-compile
,执行部分为make target/compile
.
最后又调用了sbudir
函数,这个函数规定了目标和各子文件夹之间的依赖关系.若是有必定的Makefile基础能够去读读subdir.mk
文件.
举例而言就是:
当执行目标为
target/compile
,这个目标将依赖于target/linux/compile
.
当执行目标为package/compile
,这个目标将依赖于package
目录下,各子文件夹的compile
.
下面就是规定了一系列的依赖关系,我已经将他们梳理了出来,以下图:
这里就是第二层解析后的依赖关系.当依赖关系生成后,将会从最早被依赖的目标开始执行.
好比咱们能够看到进入第二层后,tools/stamp-install
将会最早被执行,也就是主机工具将会最早被编译,安装.咱们上一篇提升的整个编译过程能从上图中得出.
make -C
package/stamp-compile
和target/stamp-install
.下周再会^_^