Android编译系统简析

Android 编译系统 的简单理解

一,Makefile 入门  :简单来说,Makefile提供了一种机制,让使用者可以有效的组织“工作”,这里说的是“工作”,而不是“编译”,因为Makefile并一定是用来完成编译工作,事实上它本身只是一种“规则”的执行者,而使用者具体使用它来做什么没有任何限制,比如可以用它来架构编系统,也能用来生成文档,或者打印log信息等。

所以理解makefile的规则是学习的重点,mafefile是一个脚本,有make程序来解析,它是通过以下基础的规则扩展起来的。

TARGET:PREREQUISITES

COMMANDS

每个COMMANDS前都必须有个TAB制表符。

这个看起来非常简单的规则,经过一次次的扩展修饰后,便构成最终我们看到的各种庞大工程的编译系统。在这个规则中,TARGET 是需要生成的目标文件,PREREQUISITES 代表了目标所依赖的所有文件。当PREREQUISITES文件中的任何一个比TARGET新时,都会触发下面COMMAND命令的执行。

以一个简单的例子来讲解这个规则的使用方法。

示例包含的主要文件:

main.c      主函数所在文件

utility.h      提供了一个测试函数(getNumber)的声明

utility.c      提供了函数getNumber的实现

Makefile   用于编译整个工程文件

各文件的主要源码实现:

main.c 

int main(int args,char *argv[]){

printf("hello getNumber = %d \n",getNumber())

return 0;

}


utility.h

int getNumber();


utility.c

int getNumber(){

return 100;

}


Makefile

SimpleMakefile:main.o utility.o

gcc -o SimpleMakefile main.o utility.o

main.o:main.c

gcc -c main.c

utility.o:utiltiy.c

gcc -c utility.c

这里有三个TARGET ,分别是SimpleMakefile main.o utility.o,其中SimpleMakefile 依赖于后两者,main.o utility.o由对应的C文件编译生成。

当然在实际的makefile文件的编写中,会非常的简洁,因为make工具本身非常的强大,它由很多的显示规则,隐晦规则,还有自动推导功能。

二 Android的编译系统

1,makefile依赖树的概念 前面的simpleMakefil中个TARGET的依赖关系可以组成一棵树,称之为“makefile依赖树”。


这种树形结构为我们由上而下的分析Android编译系统提供了可能。

2,树根节点droid ,树根节点即可能是整个编译系统的最终目标,也可能不是。

从android源码工程的根目录分析,其下的makefile文件是编译系统的起点,他只是一个简单的文件转向,引用了另一个makefile文件

### DO NOT EDIT THIS FILE ###
include build/core/main.mk
### DO NOT EDIT THIS FILE ###

这个main.mk 文件有千行代码,其中引用了很多其他脚本文件。我们依据依赖树的概念,要去找它的树根节点。首先看一些make工作原来的知识点:

(1)编译系统中往往有不止一棵依赖树存在。在没有显示指定编译目标的情况下(执行make命令时不带任何参数来执行编译,当然make后面是可以跟参数的,这个参数指明你要编译那个TARGET),第一个符合要求的目标会被Make作为默认的依赖树根节点。

(2)make程序会对makefile中的内容按顺序进行逐条解析,一个典型的解析过程会分为三大步骤:

A ,变量赋值,环境检测等初始化

B,按规则生成所有的依赖树

C,根据用户选择的依赖树,从叶子到根逐步生成目标文件

根据这些规则,对照main.mk 可以找到make的依赖树的根节点 droid,

build/core/main.mk

# This is the default target.  It must be the first declared target.
.PHONY: droid
DEFAULT_GOAL := droid
$(DEFAULT_GOAL):

。。。。。

只不过这里的droid是一个空的“规则”,这里只是保证它是第一个出现的目标,后续内容还有很多地方对droid进行了定义。

ifneq ($(TARGET_BUILD_APPS),)

。。。  只编译app

.PHONY: apps_only
apps_only: $(unbundled_build_modules)
droid: apps_only

。。。

else # TARGET_BUILD_APPS
  $(call dist-for-goals, droidcore, \
    $(INTERNAL_UPDATE_PACKAGE_TARGET) \
    $(INTERNAL_OTA_PACKAGE_TARGET) \
    $(SYMBOLS_ZIP) \
    $(INSTALLED_FILES_FILE) \
    $(INSTALLED_BUILD_PROP_TARGET) \
    $(BUILT_TARGET_FILES_PACKAGE) \
    $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
    $(INSTALLED_RAMDISK_TARGET) \
    $(INSTALLED_FACTORY_RAMDISK_TARGET) \
    $(INSTALLED_FACTORY_BUNDLE_TARGET) \
   )

。。。 编译整个系统

# Building a full system-- the default is to build droidcore
droid: droidcore dist_files
endif # TARGET_BUILD_APPS

3,main.mk的解析 main.mk 很大一部分内容是为了完成以下工作:

a,对编译环境的检测

b,前期的一些必要处理,比如整个项目工程是不是要先进行清理工作,工具的安装

c,引用其他的makefile文件,比如config.mk cleanbuild.mk

d,设置全局变量,这些变量决定了编译的具体实现过程

e,各种函数的实现,比如my-dir可以知道当前所在的路径,

build/core/definitions.mk 

# Figure out where we are.
define my-dir
$(strip \
  $(eval LOCAL_MODULE_MAKEFILE := $$(lastword $$(MAKEFILE_LIST))) \
  $(if $(filter $(BUILD_SYSTEM)/% $(OUT_DIR)/%,$(LOCAL_MODULE_MAKEFILE)), \
    $(error my-dir must be called before including any other makefile.) \
   , \
    $(patsubst %/,%,$(dir $(LOCAL_MODULE_MAKEFILE))) \
   ) \
 )
endef

整个编译过程起主导作用的是main.mk 其中涉及的makefile文件如下:

config.mk 产品配置的主导文件

base_rules.mk 编译系统需要遵循的基础规则定义

definitions.mk 提供了大量使用函数的定义

envsetup.mk 配置编译时的环境变量

。。。

4,droidcore节点,依赖于如下几个prerequisites:

droidcore: files \ 代表其所依赖的先决条件的集合,没有实际意义
systemimage \ 将生成system.img
$(INSTALLED_BOOTIMAGE_TARGET) \ 将生成boot.img
$(INSTALLED_RECOVERYIMAGE_TARGET) \将生成recovery.img
$(INSTALLED_USERDATAIMAGE_TARGET) \将生成userdata.img
$(INSTALLED_CACHEIMAGE_TARGET) \将生成cache.img

$(INSTALLED_VENDORIMAGE_TARGET) \将生成vendor.img

$(INSTALLED_FILES_FILE)将生成installed-files.txt 用于记录当前系统中预安装的程序,库等模块。

5,android.mk 的编写规则

android系统是由非常多的子项目组成的,它有非常好的兼容性,后期的动态扩展性。Android.mk在整个源码工程中随处可见,这么多的文件,是如何整合进庞大的编译系统而保证不出错的,下面是将所有android.mk添加进编译系统的处理过程 main.mk

subdir_makefiles := \
$(shell build/tools/findleaves.py --prune=$(OUT_DIR) --prune=.repo --prune=.git $(subdirs) Android.mk)

$(foreach mk, $(subdir_makefiles), $(info including $(mk) ...)$(eval include $(mk)))

系统找到所有的Android.mk会先存入subdir_makefiles变量中,随后一次性include进整个编译文件中。

以system/core/adb/Android.mk为例子详细解释Android.mk的编写规则:

#
# Android.mk for adb
#

LOCAL_PATH:= $(call my-dir) LOCAL_PATH的位置先与CLEAR_VARS

include $(CLEAR_VARS)  CLEAR_VARS定义在build/core/clear_vars.mk中,它清楚了很多的除LOCAL_PATH变量,因而CLEAR_VARS被认为是一个编译模块的开始。

LOCAL_SRC_FILES := \ LOCAL_SRC_FILES定义了本模块编译所涉及到的所有源文件,
adb.c \
console.c \
transport.c \
transport_local.c \

LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter -Werror
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE

上面两行用于添加编译标志

LOCAL_MODULE := adb 所要生成的模块的名称

LOCAL_STATIC_LIBRARIES := libzipfile libunz libcrypto_static $(EXTRA_STATIC_LIBS)编译过程中要用到的库

include $(BUILD_HOST_EXECUTABLE) 这是整个Android.mk的重点,BUILD_HOST_EXECUTABLE表示我们希望生成一个HOST可执行文件,这里可以根据需要选择其他的BUILD***变量,每个编译模块是从CLEARS_VARS开始,到这里结束。

Android.mk中常用变量解析:

LOCAL_PATH 用于确定源码所在的目录,

CLEAR_VARS 清空很多以LOCAL_ 开头的变量

LOCAL_MODULE_PATH 模块的输出路径

LOCAL_MODULE 模块名,保证整个编译系统中唯一,而且中间不可以有空格

LOCAL_STATIC_LIBARARIES 编译所需要静态库列表

LOCAL_JAVA_LIBRARIES 编译时所需要的java库

BUILD_*** 各种形式编译模块,如生成静态库,动态链接库,可执行文件,文档等

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

新版本如果不想生成.jack,需要jar包,可以添加:

LOCAL_JACK_ENABLED := disabled

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值