您的位置:首页 > 产品设计 > UI/UE

剖析安卓build系统的设计思想以及案例模拟

2015-08-30 12:05 441 查看
1.引言

安卓系统在根目录中只有一个Makefile文件,每个模块只有一个android.mk文件,这是Makefile文件的一个片段,为什么要这么设计呢?这种设计方式解决了什么问题呢?相信读完本文将会找到这些问题的答案。

2.问题的提出

对于一个小菜鸟来说,最先想到的方案是为每个模块写一个makefile,示例工程如下:

Project
  ----Makefile
  ----ant
       ----Makefile
       ----main.c
  ----bee
       ----Makefile
       ----parse.c
       ----parse.h


顶级目录的makefile内容如下:

MODULES = ant bee
all:
    for dir in $(MODULES); do \
        (cd $$dir; $(MAKE) all); \
    done


ant目录下的makefile内容如下:

all: main.o
main.o: main.c ../bee/parse.h
    $(CC) -I../bee -c main.c


bee目录下的makefile内容如下:

OBJ = ../ant/main.o parse.o
all: prog
prog: $(OBJ)
    $(CC) -o $@ $(OBJ)
parse.o: parser.c parse.h
    $(CC) -c parse.c


我们使用DAG来描述ant模块文件依赖关系:



同样的,bee模块的文件依赖关系如下:



这样我们就构建好2个模块的编译系统,只要make一下就能够得到我们想要的结果,不知道细心的读者是否发现了一个问题:main.o是依赖parse.h的,但是main.o并不清楚parse.h是如何诞生的,毕竟他们是处于两个模块的,两个makefile就像两个房间一样,他们各自身处于自己的空间当中,所以为了暴露这个问题,我们增加如下情景:

bee目录的parse.h和parse.c文件是通过yacc工具自动生成的,也就是我们在bee/Makefile文件增加以下内容来生成parse.h和parse.c文件。依赖关系现在如下:



假如我们修改了parse.y文件,然后在工程的根目录下执行make指令,得到的结果是否是我们想要的呢?答案是否定的,且听我细细道来。

make指定一旦执行,就会分别进入每个模块分别进行编译,即执行里面的makefile,那么这里首先进入ant模块,由于仅仅修改parse.y文件,所以main.o不会更新,完蛋啦,因为实际上parse.y更新,parse.h就要更新,进而main.o也是需要更新的,但这里却没有更新,也就是说,prog使用了陈旧的main.o。

问题出在哪里?还是每个模块一个makefile的方案是不对的。

每个模块一个makefile就需要依次编译每个模块,但是模块间又太过于松散,也就是一个模块中的目标可能无法感知依赖的变化,比如ant模块无法感知其依赖的变化(依赖由bee模块生成),一旦先编译了ant模块,那么就会出现我们不想要的结果。

3.改进实现

找到原因之后,那么如何解决呢?其实就是增加模块之间的耦合度,本质上就是让模块之间能够互相感知变化,下面将改进上面的设计,从而帮助我们理解安卓编译系统的设计思想。

改进以后的工程结构如下:




示例代码地址如下:

http://download.csdn.net/detail/getnextwindow/9063217

根目录下面只有一个Makefile文件,ant和bee模块下只有一个makefile片段,depend.sh自动生成头文件依赖的脚本。

Makefile实现如下:

MODULES :=ant bee
CFLAGS +=$(patsubst %,-I %,$(MODULES))
LIBS :=
SRC :=
include $(patsubst %,%/module.mk,$(MODULES))
OBJ:=$(patsubst %.c,%.o,$(filter %.c,$(SRC)))
prog:$(OBJ)
        $(CC) -o $@ $(OBJ) $(LIBS)
include $(OBJ:.o=.d)
%.d: %.c
        depend.sh `dirname $*.c` $(CFLAGS) $*.c >$@
.PHONY :clean
clean:
        rm -f prog $(OBJ) $(OBJ:.o=.d)


bee模块的module.mk文件如下:

SRC += bee/parse.y
LIBS += -ly
%.c %.h: %.y
$(YACC) -d $*.y
mv y.tab.c $*.c
mv y.tab.h $*.h


现在来分析新设计是如何解决前面的问题的,假如我们现在仅仅修改了parse.y文件。

一旦开始执行make命令,会将所有include指令中的文件加载进来,然后才开始根据所有规则产生目标文件。加载进来bee模块的module.mk以后,就会更新parse.c和parse.h,接着就会更新依赖parse.c和parse.h的目标文件,即parse.o、parse.d和main.o,所以这里main.o感知到了变化。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: