Android编译系统分析
概要
由于Android编译系统的复杂和使用了不熟悉的Python,所以对其一直望而却步;工作中使用Android.mk也仅仅是拷贝修改。最近由于工作需要解决一个编译方面的问题1,浏览了一下编译系统;另外,项目上的编译控制是使用在Android编译系统基础之上的一个sh脚本,由于脚本维护滞后和Android源码目录增加删除修改的原因,该sh已经不能用来编译SDK。在解决 问题1之后,一股冲动,要编译出SDK供调试程序使用,就学习研究了一下Android编译系统,根据product makefile修改sdk.mk,终于编译出了SDK。
下面是学习研究过程中,参考的一些资料汇总。针对工作的使用,加了体会和注释。
编译脚本及系统变量
转自 http://blog.csdn.net/yili_xie/archive/2009/11/30/4906865.aspx
本文档主要描述envsetup.sh和Android.mk使用的LOCAL_XX和BUILD_XX变量。
build/envsetup.sh脚本分析
在编译源代码之前通常需要在android源代码顶层目录执行 . ./build/envsetup.sh目的是为了使用脚本 envsetup.sh里面定义了一些函数:
function help()
function get_abs_build_var()
function get_build_var()
function check_product()
function check_variant()
function setpaths()
function printconfig()
function set_stuff_for_environment()
function set_sequence_number()
function settitle()
function choosetype()
function chooseproduct()
function choosevariant()
function tapas()
function choosecombo()
function print_lunch_menu()
function lunch()
function gettop
function m()
function findmakefile()
function mm()
function mmm()
function croot()
function pid()
function gdbclient()
function jgrep()
function cgrep()
function resgrep()
function getprebuilt
function tracedmdump()
function runhat()
function getbugreports()
function startviewserver()
function stopviewserver()
function isviewserverstarted()
function smoketest()
function runtest()
function runtest_py()
function godir ()
choosecombo 命令分析:
function choosecombo()
{
choosesim $1
echo
echo
choosetype $2
echo
echo
chooseproduct $3
echo
echo
choosevariant $4
echo
set_stuff_for_environment
printconfig
}
会依次进行如下选择:
Build for the simulator or the device?
1. Device
2. Simulator ----- Emulator is not Simulator! Select 1.
Which would you like? [1]
Build type choices are:
1. release
2. debug
Which would you like? [1]
Product choices are:
1. emulator
2. generic
3. sim
4. littleton
You can also type the name of a product if you know it.
Which would you like? [littleton]
Variant choices are:
1. user
2. userdebug
3. eng
Which would you like? [eng] user
默认选择以后会出现:
TARGET_PRODUCT=littleton
TARGET_BUILD_VARIANT=user
TARGET_SIMULATOR=false
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID= 2.3.A.8.9
==========
function chooseproduct()函数分析:
choices=(`/bin/ls build/target/board/*/BoardConfig.mk vendor/*/*/BoardConfig.mk 2> /dev/null`)
读取 build/target/board/* 目录下的板配置文件:BoardConfig.mk
读取 vendor/*/*/目录下的板配置文件:BoardConfig.mk
choices 的值为:
build/target/board/emulator/BoardConfig.mk
build/target/board/generic/BoardConfig.mk
build/target/board/sim/BoardConfig.mk
vendor/marvell/littleton/BoardConfig.mk
经过:
for choice in ${choices[@]}
do
# The product name is the name of the directory containing
# the makefile we found, above.
prodlist=(${prodlist[@]} `dirname ${choice} | xargs basename`)
done
的处理,prodlist的值为:
emulator generic sim littleton
所以选择菜单为:
Product choices are:
1. emulator
2. generic
3. sim
4. littleton
如果选择 4,那么 TARGET_PRODUCT被赋值为: littleton。
board_config_mk := /
$(strip $(wildcard /
$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk /
vendor/*/$(TARGET_DEVICE)/BoardConfig.mk /
))
Note:
ICS是读取AndroidProduct.mk,而不是BoardConfig.mk,但功能是一样的,即搜索平台名称,列表,供选择,赋值TARGET_PRODUCT,输出变量。
268#
269# This function chooses a TARGET_PRODUCT by picking a product by name.
270# It finds the list of products by finding all theAndroidProducts.mk
271# files and looking for the product specific filenames in them.
272#
273functionchooseproduct()
274{
275# Find the list of all products by looking for allAndroidProducts.mk files under the
276# device/, vendor/ andbuild/target/product/ directories and look for the format
277# LOCAL_DIR/<ProductSpecificFile.mk> and extract the name ProductSpecificFile from it.
278# This will give the list of all products that can be built using choosecombo
279
280 local -aprodlist
281
282# Find allAndroidProducts.mk files under the dirs device/,build/target/ and vendor/
283# Extract lines containing .mk from them
284# Extract lines containing LOCAL_DIR
285# Extract the name of the product specific file
286
287 prodlist=(`/usr/bin/finddevice/build/target/vendor/ -nameAndroidProducts.mk2>/dev/null|
288 xargsgrep -h \.mk|
289 grepLOCAL_DIR|
290 cut -d'/' -f2|cut -d' ' -f1|sort|uniq|cut -d'.' -f1`)
291
292 localindex=1
293 local p
294 echo"Product choices are:"
295 for pin ${prodlist[@]}
296 do
297 echo" $index. $p"
298 let"index = $index + 1"
299 done
300
301 if ["x$TARGET_PRODUCT" != x ] ;then
302 default_value=$TARGET_PRODUCT
303 else
304 default_value=full
305 fi
306
307 exportTARGET_PRODUCT=
308 localANSWER
309 while [ -z"$TARGET_PRODUCT" ]
310 do
311 echo"You can also type the name of a product if you know it."
312 echo -n"Which product would you like? [$default_value] "
313 if [ -z"$1" ] ;then
314 readANSWER
315 else
316 echo $1
317 ANSWER=$1
318 fi
319
320 if [ -z"$ANSWER" ] ;then
321 exportTARGET_PRODUCT=$default_value
322 elif (echo -n $ANSWER | grep -q -e "^[0-9][0-9]*$") ;then
323 localpoo=`echo -n $ANSWER`
324 if [$poo -le ${#prodlist[@]} ] ; then
325 exportTARGET_PRODUCT=${prodlist[$(($ANSWER-1))]}
326 else
327 echo"** Bad product selection:$ANSWER"
328 fi
329 else
330 ifcheck_product$ANSWER
331 then
332 exportTARGET_PRODUCT=$ANSWER
333 else
334 echo"** Not a valid product: $ANSWER"
335 fi
336 fi
337 if [ -n"$1" ] ;then
338 break
339 fi
340 done
341
342 set_stuff_for_environment
343}
怎样添加一个模块
LOCAL_PATH:= $(call my-dir)
#编译静态库
include $(CLEAR_VARS)
LOCAL_MODULE = libhellos
LOCAL_CFLAGS = $(L_CFLAGS)
LOCAL_SRC_FILES = hellos.c
LOCAL_C_INCLUDES = $(INCLUDES)
LOCAL_SHARED_LIBRARIES := libcutils
LOCAL_COPY_HEADERS_TO := libhellos
LOCAL_COPY_HEADERS := hellos.h
include $(BUILD_STATIC_LIBRARY)
#编译动态库
include $(CLEAR_VARS)
LOCAL_MODULE = libhellod
LOCAL_CFLAGS = $(L_CFLAGS)
LOCAL_SRC_FILES = hellod.c
LOCAL_C_INCLUDES = $(INCLUDES)
LOCAL_SHARED_LIBRARIES := libcutils
LOCAL_COPY_HEADERS_TO := libhellod
LOCAL_COPY_HEADERS := hellod.h
include $(BUILD_SHARED_LIBRARY)
#使用静态库
include $(CLEAR_VARS)
LOCAL_MODULE := hellos
LOCAL_STATIC_LIBRARIES := libhellos
LOCAL_SHARED_LIBRARIES :=
LOCAL_LDLIBS += -ldl
LOCAL_CFLAGS := $(L_CFLAGS)
LOCAL_SRC_FILES := mains.c
LOCAL_C_INCLUDES := $(INCLUDES)
include $(BUILD_EXECUTABLE)
#使用动态库
include $(CLEAR_VARS)
LOCAL_MODULE := hellod
LOCAL_MODULE_TAGS := debug
LOCAL_SHARED_LIBRARIES := libc libcutils libhellod
LOCAL_LDLIBS += -ldl
LOCAL_CFLAGS := $(L_CFLAGS)
LOCAL_SRC_FILES := maind.c
LOCAL_C_INCLUDES := $(INCLUDES)
include $(BUILD_EXECUTABLE)
Note:
CLEAR_VARS用来将这些Shell变量的值清空,达到LOCAL的目的。
系统变量解析
LOCAL_MODULE -编译的目标对象
LOCAL_SRC_FILES -编译的源文件
LOCAL_C_INCLUDES -需要包含的头文件目录
LOCAL_SHARED_LIBRARIES - 链接时需要的共享库
LOCAL_PREBUILT_LIBS - 预编译好的静态库或动态库,可用于集成第三方库
LOCAL_LDLIBS - 链接时需要的外部库
LOCAL_PRELINK_MODULE - 是否需要prelink处理
LOCAL_JAVA_LIBRARIES - 编译JAVA程序时需要的jar包
LOCAL_PACKAGE_NAME-编译JAVA APK放入目标PACKAGE名字
可以看出这些变量就是Make系统的参数,完全就是编译器和链接器和其他二进制工具的控制参数。
BUILD_STATIC_LIBRARY-编译成静态库的控制脚本
BUILD_SHARED_LIBRARY -编译成动态库的控制脚本
BUILD_EXECUTABLE -编译成可执行文件的控制脚本
BUILD_PACKAGE -指明编译成Java APK包
另外的,
LOCAL_MODULE_TAGS - 是控制编译出的该模块加入到特定编译Varient(user/ud/eng)中。
下面是函数或变量的详细内容。
LOCAL_PATH - 编译时的目录
$(call 目录,目录….) 目录引入操作符
如该目录下有个文件夹名称 src,则可以这样写 $(call src),那么就会得到 src目录的完整路径
include $(CLEAR_VARS) -清除之前的一些系统变量
CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
在 build/core/config.mk 定义 CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
通过include 包含自定义的.mk文件(即是自定义编译规则)或是引用系统其他的.mk文件(系统定义的编译规则)。
LOCAL_SRC_FILES - 编译的源文件
可以是.c, .cpp, .java, .S(汇编文件)或是.aidl等格式
不同的文件用空格隔开。如果编译目录子目录,采用相对路径,如子目录/文件名。也可以通过$(call目录),指明编译某目录下所有.c/.cpp/.java/.S/ .aidl文件.追加文件 LOCAL_SRC_FILES +=文件
LOCAL_C_INCLUDES - 需要包含的头文件目录
可以是系统定义路径,也可以是相对路径. 如该编译目录下有个include目录,写法是include/*.h
LOCAL_MODULE - 编译的目标对象
module 是指系统的 native code,通常针对c,c++代码
./system/core/sh/Android.mk:32:LOCAL_MODULE:= sh
./system/core/libcutils/Android.mk:71:LOCAL_MODULE := libcutils
./system/core/cpio/Android.mk:9:LOCAL_MODULE := mkbootfs
./system/core/mkbootimg/Android.mk:8:LOCAL_MODULE := mkbootimg
./system/core/toolbox/Android.mk:61:LOCAL_MODULE:= toolbox
./system/core/logcat/Android.mk:10:LOCAL_MODULE:= logcat
./system/core/adb/Android.mk:65:LOCAL_MODULE := adb
./system/core/adb/Android.mk:125:LOCAL_MODULE := adbd
./system/core/init/Android.mk:20:LOCAL_MODULE:= init
./system/core/vold/Android.mk:24:LOCAL_MODULE:= vold
./system/core/mountd/Android.mk:13:LOCAL_MODULE:= mountd
LOCAL_PACKAGE_NAME
Java 应用程序的名字用该变量定义,如
./packages/apps/Music/Android.mk:9:LOCAL_PACKAGE_NAME := Music
./packages/apps/Browser/Android.mk:14:LOCAL_PACKAGE_NAME := Browser
./packages/apps/Settings/Android.mk:8:LOCAL_PACKAGE_NAME := Settings
./packages/apps/Stk/Android.mk:10:LOCAL_PACKAGE_NAME := Stk
./packages/apps/Contacts/Android.mk:10:LOCAL_PACKAGE_NAME := Contacts
./packages/apps/Mms/Android.mk:8:LOCAL_PACKAGE_NAME := Mms
./packages/apps/Camera/Android.mk:8:LOCAL_PACKAGE_NAME := Camera
./packages/apps/Phone/Android.mk:11:LOCAL_PACKAGE_NAME := Phone
./packages/apps/VoiceDialer/Android.mk:8:LOCAL_PACKAGE_NAME := VoiceDialer
BUILD_XXXX_YYYY_ZZZZ宏在build/core/config.mk中定义。
BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk
BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk
BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
BUILD_RAW_STATIC_LIBRARY := $(BUILD_SYSTEM)/raw_static_library.mk
BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
BUILD_RAW_EXECUTABLE:= $(BUILD_SYSTEM)/raw_executable.mk
BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk
BUILD_HOST_PREBUILT:= $(BUILD_SYSTEM)/host_prebuilt.mk
BUILD_PREBUILT:= $(BUILD_SYSTEM)/prebuilt.mk
BUILD_MULTI_PREBUILT:= $(BUILD_SYSTEM)/multi_prebuilt.mk
BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk
BUILD_STATIC_JAVA_LIBRARY:= $(BUILD_SYSTEM)/static_java_library.mk
BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
BUILD_DROIDDOC:= $(BUILD_SYSTEM)/droiddoc.mk
BUILD_COPY_HEADERS := $(BUILD_SYSTEM)/copy_headers.mk
BUILD_KEY_CHAR_MAP := $(BUILD_SYSTEM)/key_char_map.mk
使用时,只需要在Android.mk中include $(BUILD_XXXX_YYYY_ZZZZ)即可。
作用从名称可以看出,包括HOST和TARGET的可执行程序或库或二进制工具链。
============
LOCAL_PRELINK_MODULE
Prelink利用事先链接代替运行时链接的方法来加速共享库的加载,它不仅可以加快起动速度,还可以减少部分内存开销,是各种Linux架构上用于减少程序加载时间、缩短系统启动时间和加快应用程序启动的很受欢迎的一个工具。程序运行时的动态链接尤其是重定位(relocation)的开销对于大型系统来说是很大的。
动态链接和加载的过程开销很大,并且在大多数的系统上,函数库并不会常常被更动,每次程序被执行时所进行的链接动作都是完全相同的,对于嵌入式系统来说尤其如此。因此,这一过程可以改在运行时之前就可以预先处理好,即花一些时间利用Prelink工具对动态共享库和可执行文件进行处理,修改这些二进制文件并加入相应的重定位等信息,节约了本来在程序启动时的比较耗时的查询函数地址等工作,这样可以减少程序启动的时间,同时也减少了内存的耗用。
Prelink的这种做法当然也有代价:每次更新动态共享库时,相关的可执行文件都需要重新执行一遍Prelink才能保证有效,因为新的共享库中的符号信息、地址等很可能与原来的已经不同了,这就是为什么 android framework代码一改动,这时候就会导致相关的应用程序重新被编译。
这种代价对于嵌入式系统的开发者来说可能稍微带来一些复杂度,不过好在对用户来说几乎是可以忽略的。
--------------------
变量设置为false那么将不做prelink操作
LOCAL_PRELINK_MODULE := false
默认是需要prlink的,同时需要在 build/core/prelink-linux-arm.map中加入
libhellod.so 0x96000000
这个map文件好像是制定动态库的地址的,在前面注释上面有一些地址范围的信息,注意库与库之间的间隔数,如果指定不好的话编译的时候会提示说地址空间冲突的问题。另外,注意排序,这里要把数大的放到前面去,按照大小降序排序。
解析 LOCAL_PRELINK_MODULE 变量
build/core/dynamic_binary.mk:94:ifeq ($(LOCAL_PRELINK_MODULE),true)
ifeq ($(LOCAL_PRELINK_MODULE),true)
$(prelink_output): $(prelink_input) $(TARGET_PRELINKER_MAP) $(APRIORI)
$(transform-to-prelinked)
transform-to-prelinked定义:
./build/core/definitions.mk:1002:define transform-to-prelinked
define transform-to-prelinked
@mkdir -p $(dir $@)
@echo "target Prelink: $(PRIVATE_MODULE) ($@)"
$(hide) $(APRIORI) /
--prelinkmap $(TARGET_PRELINKER_MAP) /
--locals-only /
--quiet /
$< /
--output $@
endef
./build/core/config.mk:183:
APRIORI := $(HOST_OUT_EXECUTABLES)/apriori$(HOST_EXECUTABLE_SUFFIX)
prelink工具不是常用的prelink而是apriori,其源代码位于”
参考文档:
动态库优化——Prelink(预连接)技术
http://www.eefocus.com/article/09-04/71629s.html
===============
LOCAL_ARM_MODE := arm
目前Android大部分都是基于Arm处理器的,Arm指令用两种模式Thumb(每条指令两个字节)和arm指令(每条指令四个字节)
LOCAL_CFLAGS += -O3 -fstrict-aliasing -fprefetch-loop-arrays
通过设定编译器操作,优化级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
LOCAL_CFLAGS += -W -Wall
LOCAL_CFLAGS += -fPIC -DPIC
LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -DSH_HISTORY
LOCAL_CFLAGS += -DUSEOVERLAY2
根据条件选择相应的编译参数
ifeq ($(TARGET_ARCH),arm)
LOCAL_CFLAGS += -DANDROID_GADGET=1
LOCAL_CFLAGS := $(PV_CFLAGS)
endif
ifeq ($(TARGET_BUILD_TYPE),release)
LOCAL_CFLAGS += -O2
endif
LOCAL_LDLIBS := -lpthread
LOCAL_LDLIBS += -ldl
ifdef USE_MARVELL_MVED
LOCAL_WHOLE_STATIC_LIBRARIES += lib_il_mpeg4aspdecmved_wmmx2lnx \ lib_il_h264decmved_wmmx2lnx
LOCAL_SHARED_LIBRARIES += libMrvlMVED
else
LOCAL_WHOLE_STATIC_LIBRARIES += lib_il_h264dec_wmmx2lnx \ lib_il_mpeg4aspdec_wmmx2lnx
endif
PACKAGE OVERLAYS
PRODUCT_COPY_FILES
OVERRIDEN_PACKAGES
本小段是工作中用到的点的记录.
PRODUCT_PACKAGES
Lists the APKs to install. Such as Calendar Contacts Phone.
PRODUCT_COPY_FILES
List of words like source_path:destination_path.
The file at the source path should be copied to the destination path whenbuilding this product. It can be used to copy third party apk to /system/app/ to deploy the apps.
The rules for the copy steps are defined in config/Makefile.
PRODUCT_PROPERTY_OVERRIDES
List of property assignments in the format "key=value"
PRODUCT_PACKAGE_OVERLAYS, DEVICE_PACKAGE_OVERLAYS
Indicate whether to use default resources or add any product specific overlays;
The two overlays are really doing the same thing but at different priorities.
The product overlays take priority over device overlays.
PRODUCT_BUILD_PROP_OVERRIDES := PRODUCT_NAME=mysidspr BUILD_ID=ICL53F
LOCAL_OVERRIDES_PACKAGES one package can override multi packages.
Use strip function to strip them, which means make only one space between the package names.
OVERRIDE_BUILT_MODULE_PATH
Use strip function to strip multi paths, which means make only one space between the many path names.
Snippets from main.mk
# Some packages may override others using LOCAL_OVERRIDES_PACKAGES.
# Filter out (do not install) any overridden packages.
overridden_packages := $(call get-package-overrides,$(modules_to_install))
ifdef overridden_packages
# old_modules_to_install := $(modules_to_install)
modules_to_install := /
$(filter-out $(foreach p,$(overridden_packages),$(p) %/$(p).apk), /$(modules_to_install))
endif
> In cupcake you can definePRODUCT_PACKAGE_OVERLAYS in your product makefile
> to specify one or more directories that will be merged in withthebase platform directories.
For example:
> PRODUCT_PACKAGE_OVERLAYS := vendor/mycompany/prod_overlay \
> vendor/mycompany/base_overlay
> Now you can replace or add framework resources by putting them in either of these:
> vendor/mycompany/base_overlay/frameworks/base/core/res/res/
> vendor/mycompany/prod_overlay/frameworks/base/core/res/res/
> You can use this to replace any resource in the tree, both in the framework and in specific packages, by just putting them in a directory corresponding to the same path where you find them in the platform tree.
> Also whenadding new resources to the frameworks that you want to use in your own apps that are built into the system image, you can use the magic syntax "@*android:type/name" to reference them without having to make them public. You can likewise find private resources in Java at com.android.internal.R.
> Obviously, changing a source file will force the rebuild of the specific part affected, but what about changing device overlays, initrc files or resources for example ?
For overlays and resources, have a look at build/core/package.mk. More specifically, here are the relevant snippets:
LOCAL_RESOURCE_DIR := \
$(wildcard $(foreach dir, $(PRODUCT_PACKAGE_OVERLAYS), \
$(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR)))) \
$(wildcard $(foreach dir, $(DEVICE_PACKAGE_OVERLAYS), \
$(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR)))) \
$(LOCAL_RESOURCE_DIR)
...
all_resources := $(strip \
$(foreach dir, $(LOCAL_RESOURCE_DIR), \
$(addprefix $(dir)/, \
$(patsubst res/%,%, \
$(call find-subdir-assets,$(dir)) \
) \
) \
))
...
all_res_assets := $(strip $(all_assets) $(all_resources))
...
$(R_file_stamp): $(all_res_assets) $(full_android_manifest) $(AAPT) | $(ACP)
So, basically, if you change a resource, whether it be an overlay or not, it should trigger the rebuilding of the encompassing package. For initrc files, it depends how it's getting included I guess. For the main init.rc file in system/core/rootdir/init.rc, the corresponding mk (system/core/rootdir/Android.mk) has this snippet:
ifneq ($(TARGET_PROVIDES_INIT_RC),true)
file := $(TARGET_ROOT_OUT)/init.rc
$(file) : $(LOCAL_PATH)/init.rc | $(ACP)
$(transform-prebuilt-to-target)
ALL_PREBUILT += $(file)
$(INSTALLED_RAMDISK_TARGET): $(file)
endif
INSTALLED_RAMDISK_TARGET is what generates the RAM disk image, and it's made here to depend on init.rc. So if you touch this file, it will cause the RAM disk image to be regenerated. As you can see, though, the dependencies aren't centralized.
> Do we need to do a whole "make clean" or is a more specific target
> availaible ?
There's a "make installclean" which will wipe out just the stuff that changes from one make target to another (i.e. if you switch from "make droid" (a.k.a. plain "make") to "make sdk".) Here's what it actually does (from build/core/cleanbuild.mk):
installclean_files := \
$(HOST_OUT)/obj/NOTICE_FILES \
$(HOST_OUT)/sdk \
$(PRODUCT_OUT)/*.img \
$(PRODUCT_OUT)/*.txt \
$(PRODUCT_OUT)/*.xlb \
$(PRODUCT_OUT)/*.zip \
$(PRODUCT_OUT)/data \
$(PRODUCT_OUT)/obj/APPS \
$(PRODUCT_OUT)/obj/NOTICE_FILES \
$(PRODUCT_OUT)/obj/PACKAGING \
$(PRODUCT_OUT)/recovery \
$(PRODUCT_OUT)/root \
$(PRODUCT_OUT)/system \
$(PRODUCT_OUT)/dex_bootjars \
$(PRODUCT_OUT)/obj/JAVA_LIBRARIES
...
installclean: FILES := $(installclean_files)
installclean: dataclean
$(hide) rm -rf $(FILES)
@echo "Deleted images and staging directories."
In other words, that list up there gets nucked every time you switch make targets. My only regret is that there isn't an "imagesclean" target which just wipes out $(PRODUCT_OUT)/*.img and corresponding source dirs
($(PRODUCT_OUT)/{root,system,data}).
Android 的编译系统
转自:http://www.360doc.com/content/11/0609/14/474846_122680003.shtml
本文主要从编译全局控制的角度描述。
以下主要流程都在build/core/main.mk里安排。
初始化相关的参数设置(buildspec.mk、envsetup.mk、config.mk)
检测编译环境和目标环境
读取product的配置信息及目标平台信息
清除输出目录
检查版本号
读取Board的配置
读取所有Module的配置
根据配置产生必要的规则(build/core/Makefile)
生成image
主要配置和实现文件:
build/core/config.mk summary of config
build/core/envsetup.mk generate dir config and so on
build/target/product product config
build/target/board board config
build/core/combo build flags config
这里解释下这里的board和product目录。
board目录主要是设计到硬件芯片的配置,比如是否提供硬件的某些功能,比如说GPU等等,或者芯片支持浮点运算等等。
product目录是指针对当前的芯片配置定义你将要生产产品的个性配置,主要是指APK方面的配置,哪些APK会包含在哪个product中,哪些APK在当前product中是不提供的。
config.mk是一个总括性的东西,它里面定义了各种module编译所需要使用的HOST工具以及如何来编译各种模块,比如说 BUILT_PREBUILT就定义了如何来编译预编译模块。envsetup.mk主要会读取由envsetup.sh写入环境变量中的一些变量来配置编译过程中的输出目录,combo里面主要定义了各种Host和Target结合的编译器和编译选项。
配置部分主要完成以下几个工作:
a) 基于Android 产品的配置(product config):选择构建安装的运行程序(user package)
b) 设置 target 等相关变量TARGET_ARCH, TARGET_OS,TARGET_BUILD_TYPE,
TARGET_PREBUILT_TAG
c) 根据编译环境设置 host等相关变量HOST_OS, HOST_ARCH,HOST_BUILD_TYPE,
HOST_PREBUILT_TAG
d) 编译 target上运行程序所需的工具链及编译参数设置,如linux-arm-cc,cflag,include目录等。
e) 编译 host上运行程序所需的工具链及编译参数设置。
下图简要介绍了Android build system的配置部分的主要构成及相互关系。
在main.mk里,简单设置几个主要编译路径的变量后,来到config.mk:
——————————————config.mk——————————————
其中设置了源文件的一系列路径,包括头文件、库文件、服务、API已经编译工具的路径(前36行)。
从40行开始,定义一些编译模块的生成规则:
除了CLEAR_VARS是清楚本地变量之外,其他所有的都对应了一种模块的生成规则,每一个本地模块最后都会include其中的一种来生成目标模块。
回到config.mk,接着会尝试读取buildspec.mk的设置:
如同注释所说,会尝试查找buildspec.mk,如果文件不存在会自动使用环境变量的设置,如果仍然未定义,会按arm默认的设置去build。
这里的buildspec.mk可以自己创建,也可以将原先build/下的buildspec.mk.default直接命名为buildspec.mk并移到根目录。
实际上,buildspec.mk配置都被屏蔽了,我们可以根据需要直接打开和修改一些变量。在这里我们可以加入自己的目标产品信息:
ifndef TARGET_PRODUCT
TARGET_PRODUCT:=generic_x86
endif
以及输出目录设置:
OUT_DIR:=$(TOPDIR)generic_x86
回到config.mk,接着进行全局变量设置,进入envsetup.mk:
——————————————envsetup.mk——————————————
里面的大部分函数都在build/envsetup.sh中定义。
首先,设置版本信息,(11行)在build/core/version_defaults.mk中具体定义平台版本、SDK版本、Product版本,我们可以将BUILD_NUMBER作为我们产品generic_x86的version信息,当然,也可以自定义一个版本变量。
回到envsetup.mk,接着设置默认目标产品(generic),这里由于我们在buildspec.mk里设置过TARGET_PRODUCT,事实上这个变量值为generic_x86。
然后读取product的设置(41行),具体实现在build/core/product_config.mk中,进而进入product.mk,从build/target/product/AndroidProducts.mk中读出PRODUCT_MAKEFILES,这些makefile各自独立定义product,而我们的产品generic_x86也应添加一个makefile文件generic_x86.mk。在generic_x86.mk中我们可以加入所需编译的PRODUCT_PACKAGES。
下面为generic_x86.mk:
接着回到config.mk,(114行)这里会搜索所有的BoardConfig.mk,主要有以下几个地方:
这里的TARGET_DEVICE就是generic_x86,就是说为了定义我们自己的产品generic_x86,我们要在build/target/board下添加一个自己的目录generic_x86用来加载自己的board配置。
在BoardConfig.mk中会决定是否编译bootloader、kernel等信息。
结束全局变量配置后,回到main.mk,对编译工具及版本进行检查,错误便中断编译。
line142,包含文件definitions.mk,这里面定义了许多变量和函数供main.mk使用。
line 446,这里会去读取所有的Android.mk文件:
其中include $(ONE_SHOT_MAKEFILE)
这个ONE_SHOT_MAKEFILE是在前面提到的mm(envsetup.mk)函数中赋值的:
ONE_SHOT_MAKEFILE=$M make -C $T files $@
回到main.mk,最终将遍历查找到的所有子目录下的Android.mk的路径保存到subdir_makefiles变量里(main.mk里的470行):
我们在package/apps下每个模块根目录都能看到Android.mk,里面会去定义当前本地模块的Tag:LOCAL_MODULE_TAGS,Android会通过这个Tag来决定哪些本地模块会编译进系统,通过PRODUCT和LOCAL_MODULE_TAGS来决定哪些应用包会编译进系统。(前面说过,你也能通过buildspec.mk来制定你要编译进系统的模块)
这个过程在mian.mk的445行开始,最后需要编译的模块路径打包到ALL_DEFAULT_INSTALLED_MODULES(602行):
关于Main.mk更多的话
user_PACKAGES := PRODUCT_PACKAGES PRODUCT_PACKAGES在core.mk, generic_no_telephony.mk和sdk.mk中定义,是APK名称列表。
[user/eng/debug/tests]_MODULES += user_PACKAGES
modules_to_install是根据规则从[user/eng/debug/tests]_MODULES检出,然后除去overriden_packages,然后除去target_gnu_MODULES,对于剩下的modules_to_install,不是检查modules_to_install的存在性,而是检查所有PRODUCT_PACKAGES的存在性。
envsetup.sh通过遍历特定的目录下的AndroidProduct.mk文件,这个文件中的会有如下语句:
envsetup.sh会去掉.mk用msm7627a作为平台名称,供配置选择,而该平台对应的编译文件就是msm7627a.mk,其中会根据情况包含一些其他的.mk文件,如../
Prebuild的模块可以直接拷贝到相关输出目录,也可以使用BUILD_PREBUILT或BUILD_MULTI_PREBUILT进行拷贝,模块可以是app,lib等。
Resources overlay可以使用PRODUCT_PACKAGE_OVERLAYS由编译系统进行目录遍历的归并。
一些要需要编译的模块可以直接加到PRODUCT_PACKAGES中进行编译。
如果想用Vendor自定义的不同名Prebuild包替换掉原生的apk,可以直接将原生apk编译模块名从PRODUCT_PACKAGES中去掉,添加上自己的apk名。当然最好是在定制包目录下使用Android.mk的LOCAL_OVERRIDES_PACKAGES BUILT_PREBUILD更正规一些。
ALL_MODULES 是用来根据MODULE_TAG控制拷贝(安装)。所以Prebuild的包可以根据其输出方式决定是否加入到ALL_MODULES和PRODUCT_PACKAGES中。
ALL_MODULES.$(m).INSTALLED := $path/$package_name.apk
ALL_MODULES和PRODUCT_PACKAGES的关系?
ALL_MODULES在base_rules.mk中定义,是所有LOCAL_MODULE的集合;PRODUCT_PACKAGES在product.mk中定义,会以$PRODUCT.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES体现在main,mk中使用,在common.mk/sdk.mk中初始化赋值。
编译系统会扫描源码目录中所有的Android.mk,形成subdir_makefiles,解析所有Android.mk,每个Android.mk会包含base_rules.mk中从而引入ALL_MODULES和其代码,从而会将LOCAL_MODULE添加到ALL_MODULES中,LOCAL_MODULE的详细信息会添加到ALL_MODULES.$(LOCAL_MODULE)的各信息字段中,如INSTALLED字段就包含完整的模块安装路径名, dir/file_name.ext。这用于控制特定产品Product(user,debug,eng,tests)的模块的安装。
大致是PRODUCT_PACKAGES中的模块会被编译,而ALL_MODULES用来控制输出安装。
在main.mk中,编译sdk时有如下代码片段用于模块检查,
# Ensure every module listed in PRODUCT_PACKAGES gets something installed
$(foreach m, $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES), \
$(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\
$(error Module '$(m)' in PRODUCT_PACKAGES has nothing to install!)))
编译时可能会报某些模块无从安装。
这主要是因为不知道模块如何输出安装。
说明模块名在PRODUCT_PACKAGES中,但是ALL_MODULES中对应模块的INSTALLED目录为空,或者ALL_MODULES中不存在该项。
这通常是自己在PRODUCT_PACKAGES中直接添加需要编译的模块,然后自己拷贝;
或者其INSTALLED因为没有默认输出目录从而为空,或者没有ALL_MODULES += $(LOCAL_MODULE)将其添加到ALL_MODULES中,或者根本就不从在对应MODULE目标的Android.mk。
这可以自己根据情况要么filter_out,要么添加ALL_MODULES.$(m)各子字段即可。
如下为编译SDK时,Filter out掉的包,因为这些源码目录已经被删掉(用Vendor Prebuild的VendorNamePackageName.apk模块代替),从而Android.mk也不复存在,但是MOUDLE Name没有PRODUCT_PACKAGES中去掉。
ifdef is_sdk_build
# same name packages in core.mk replaced by vendor prebuild.
CORE_PACKAGES_REPLACED_BY_PREBUILD := \
Contacts \
ContactsProvider \
Home \
TelephonyProvider \
libdrmframeworkcommon
# same name packages in sdk.mk replaced by vendor prebuild.
SDK_PACKAGES_REPLACED_BY_PREBUILD := \
Calculator \
Camera \
DeskClock \
Email \
Exchange \
Gallery \
Music \
Mms \
OpenWnn \
libWnnEngDic \
libWnnJpnDic \
libwnndict \
Phone \
PinyinIME \
Launcher2 \
SdkSetup \
LatinIME \
CalendarProvider \
Calendar
PACKAGES_REPLACED_BY_PREBUILD := \
$(CORE_PACKAGES_REPLACED_BY_PREBUILD) \
$(SDK_PACKAGES_REPLACED_BY_PREBUILD)
# filter out deleted packages, these modules will be copy to destination dirs directly.
PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES := \
$(filter-out $(PACKAGES_REPLACED_BY_PREBUILD),$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES))
endif
所有需要配置的准备工作都已完成,下面该决定如何生成image输出文件了,这一过程实际上在build/core/Makefile中处理的。
这里定义各种img的生成方式,包括ramdisk.img、userdata.img、system.img、update.zip、recover.img等。
当Make include所有的文件,完成对所有make文件的解析以后就会寻找生成对应目标的规则,依次生成它的依赖,直到所有满足的模块被编译好,然后使用相应的工具打包成相应的img。
具体make操作:
完整编译
我们在根目录下输入make命令即可开始完全编译。这个命令实际编译生成的默认目标是droid。
也就是说,大家敲入make实际上执行的make droid。而接下来大家看看main.mk文件里最后面的部分,会有很多伪目标,如sdk、clean、clobber等,这些在默认的make droid的命令下是不会执行的。我们可以在make后加上这些标签来单独实现一些操作。如:输入make sdk将会生成该版本对应的SDK,输入make clean会清除上次编译的输出。
模块编译
有时候我们只修改了某一个模块,希望能单独编译这个模块而不是重新完整编译一次,这时候我们要用到build/envsetup.sh中提供的几个bash的帮助函数。
在源代码根目录下执行:
. build/envsetup.sh(.后面有空格)
这样大家相当于多了几个可用的命令。
这时可以用help命令查看帮助信息:
其中对模块编译有帮助的是tapas、m、mm、mmm这几个命令。
1、tapas——以交互方式设置build环境变量。
输入:tapas
第一步,选择目标设备:
第二步,选择代码格式:
第三步,选择产品平台:
注意:这里,Google源代码里默认是generic,而我们针对自己的产品应修改成generic_x86具体在build/envsetup.sh里的函数chooseproduct()中对相应代码进行修改。
2、m、mm、mmm使用独立模块的make命令。
几个命令的功能使用help命令查看。
举个例子,我们修改了Camera模块的代码,现在需要重新单独编译这一块,这时可以使用mmm命令,后面跟指定模块的路径(注意是模块的根目录)。
具体如下:
mmm packages/apps/Camera/
为了可以直接测试改动,编译好后需要重新生成system.img可以执行:make snod
单独编译image文件
一般我们完整编译后,会生成三个重要的image文件:ramdisk.img、system.img和userdata.img。当然我们可以分开单独去编译这三个目标:
make ramdisk —— ramdisk.img
make userdataimage —— userdata.img
make systemimage —— system.img
Android Make脚本的简记(1-5)
转自 http://blog.chinaunix.net/u/8866/
本文详细解析了编译系统的mk脚本文件,特别用示例的方式描述了编译的过程。
Android Make脚本的简记(1)
Build Layers描述的是产品的硬件配置情况,据此make时选择不同的配置和模块。按照从上到下的顺序,Build Layer分成4层。
Layer sample Note
Arch arm, x86 处理器的种类
Board - 板子类型的代号
Device - device配置的类型代号
Product - 具体产品的代号
以calculator为例,app代码可以放到packages/apps/目录下边,一个app对应一个目录,此例,pakcages/apps/Calculator/。创建Android.mk,已去除多余的注释行。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := libarity
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := Calculator
include $(BUILD_PACKAGE)
include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := libarity:arity-2.1.2.jar
include $(BUILD_MULTI_PREBUILT)
# Use the following include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))
至少有一个子目录,src下放源码。
Android.mk中需要赋值的几个LOCAL_XXX变量,
LOCAL_PATH,调用my-dir(在defination.mk中定义),得到当前路径,即,
LOCAL_MODULE_TAGS,取值范围
debug eng tests optional samples shell_ash shell_mksh。注意不能取值user,如果要预装,则应定义core.mk。
LOCAL_SRC_FILES,app的所有源码,可以调用all-java-files-under得到,如果是java源码的话。
LOCAL_PACKAGE_NAME,package的名字,这个名字在脚本中将标识这个app或package。
$(CLEAR_VARS)指的是clear_vars.mk,脚本会清空所有LOCAL_xxx的变量,不影响后面这些变量的使用。
$(BUILD_PACKAGE)指的是package.mk生成规则
最后一句all-makefiles-under将会包含当前目录下所有的mk脚本文件。
Include $(CLEAR_VARS)后,可以做为局部宏定义使用.
说明:
必须定义, 在app或package的Android.mk中必须给定值。
可选定义,在app或package的Android.mk中可以也可以不给定值。
不用定义,在app或package的Android.mk中不要给定值,脚本自动指定值。
LOCAL_PATH, 当前路径,必须定义。
LOCAL_PACKAGE_NAME, 必须定义,package的名字,这个名字在脚本中将标识app或package。
LOCAL_MODULE_SUFFIX, 不用定义,module的后缀,=.apk。
LOCAL_MODULE, 不用定义,=$(LOCAL_PACKAGE_NAME)。
LOCAL_JAVA_RESOURCE_DIRS, 不用定义。
LOCAL_JAVA_RESOURCE_FILES, 不用定义。
LOCAL_MODULE_CLASS, APPS/ETC(for firmware)/EXECUTABLES/STATIC_LIBRARYS/SHARED_LIBRARYS/JAVA_LIBRARYS
LOCAL_MODULE_TAGS, 可选定义。默认optional。取值范围user debug eng tests optional samples shell_ash shell_mksh。
LOCAL_ASSET_DIR, 可选定义,推荐不定义。默认$(LOCAL_PATH)/assets
LOCAL_RESOURCE_DIR, 可选定义,推荐不定义。默认product package和device package相应的res路径和$(LOCAL_PATH)/res。
LOCAL_EXPORT_PACKAGE_RESOURCES, 可选定义,默认null。如果允许app的资源被其它模块使用,则设置true。
LOCAL_PROGUARD_ENABLED, 可选定义,默认为full,如果是user或userdebug。取值full, disabled, custom。
LOCAL_MANIFEST_FILE AndroidManifest.xml
full_android_manifest, 不用定义,=$(LOCAL_PATH)/AndroidManifest.xml。
LOCAL_CERTIFICATE, 可选定义,默认为testkey。最终
private_key := $(LOCAL_CERTIFICATE).pk8
certificate := $(LOCAL_CERTIFICATE).x509.pem
以Calculator为例,
设置中间生成目录路径,中间路径将放置R.stamp文件。
由LOCAL_PATH 导出LOCAL_ASSET_DIR all_assets,LOCAL_RESOURCE_DIR all_resources,由LOCAL_PACKAGE_NAME导出变量LOCAL_MODULE。
设置LOCAL_MODULE_CLASS=APPS,此值local-intermediates-dir会用到。
package_expected_intermediates_COMMON := $(call local-intermediates-dir,COMMON)
这里COMMON是null,而LOCAL_MODULE_CLASS=APPS,所以
package_expected_intermediates_COMMON=
out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates
即
package_expected_intermediates_COMMON=
out/target/common/obj/APPS/Calculator_intermediates
设置
LOCAL_BUILT_MODULE_STEM := package.apk
而
LOCAL_BUILT_MODULE :=$(built_module_path)/$(LOCAL_BUILT_MODULE_STEM) @base_rules.mk
built_module_path := $(intermediates) @base_rules.mk
intermediates := $(call local-intermediates-dir) @java.mk
最终
LOCAL_BUILT_MODULE=
out/target/product/
$(LOCAL_MODULE)_intermediates/$(LOCAL_BUILT_MODULE_STEM)
即
LOCAL_BUILT_MODULE=
out/target/product/generic/obj/APPS/Calculator_intermediates/package.apk
由LOCAL_CERTIFICATE导出
private_key := $(SRC_TARGET_DIR)/product/security/$(LOCAL_CERTIFICATE).pk8
certificate :=$(SRC_TARGET_DIR)/product/security/$(LOCAL_CERTIFICATE).x509.pem
LOCAL_CERTIFICATE默认为testkey。
PACKAGES.$(LOCAL_PACKAGE_NAME).PRIVATE_KEY := $(private_key)
PACKAGES.$(LOCAL_PACKAGE_NAME).CERTIFICATE := $(certificate)
PACKAGES.$(LOCAL_PACKAGE_NAME).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES))
PACKAGES.$(LOCAL_PACKAGE_NAME).RESOURCE_FILES := $(all_resources)
PACKAGES := $(PACKAGES) $(LOCAL_PACKAGE_NAME)
全编译时,PACKAGES变量将会记录遍历到的packages。
Android Make脚本的简记(2)
选取APPS场景,以Calculator为例说明。
LOCAL_JAVA_LIBRARIES=true时,Android.mk中不能定义LOCAL_SDK_VERSION。
当LOCAL_SDK_VERSION=current,LOCAL_JAVA_LIBRARIES=android_stubs_current。
package.mk中定义LOCAL_BUILT_MODULE_STEM=package.apk。
两个中间目录的路径,即对应的obj目录下APPS/
intermediates=out/target/product/generic/obj/APPS/Calculator_intermediates
intermediates.COMMON=out/target/common/obj/APPS/Calculator_intermediates
LOCAL_INTERMEDIATE_TARGETS先前package.mk中已经定义了R.stamp,
java.mk又增添了7个。
LOCAL_INTERMEDIATE_TARGETS += /
$(full_classes_jar) /
$(full_classes_compiled_jar) /
$(full_classes_emma_jar) /
$(full_classes_full_names_jar) /
$(full_classes_stubs_jar) /
$(full_classes_jarjar_jar) /
$(built_dex)
此例中,具体值是
LOCAL_INTERMEDIATE_TARGETS=
out/target/common/obj/APPS/Calculator_intermediates/src/R.stamp @ package.mk
out/target/common/obj/APPS/Calculator_intermediates/classes.jar @full_classes_jar
out/target/common/obj/APPS/Calculator_intermediates/classes-full-debug.jar @full_classes_compiled_jar
out/target/common/obj/APPS/Calculator_intermediates/emma_out/lib/classes-full-debug.jar @full_classes_emma_jar
out/target/common/obj/APPS/Calculator_intermediates/classes-full-names.jar @full_classes_full_names_jar
out/target/common/obj/APPS/Calculator_intermediates/stubs.jar @full_classes_stubs_jar
out/target/common/obj/APPS/Calculator_intermediates/classes-jarjar.jar @full_classes_jarjar_jar
out/target/common/obj/APPS/Calculator_intermediates/classes.dex @built_dex
java.mk随后include base_rules.mk
后面处理了EMMA,PROGUARD在enable/disable情况下的动作,最后定义的target, $(LOCAL_MODULE)-findbugs因为prebuilt/common下还没有findbugs,目前不可用。
java.mk还定义了几个特别的变量,
ALL_MODULES.$(LOCAL_MODULE).PROGUARD_ENABLED:=$(LOCAL_PROGUARD_ENABLED)
ALL_MODULES.$(LOCAL_MODULE).CHECKED := $(full_classes_compiled_jar)
ALL_MODULES.$(LOCAL_MODULE).STUBS := $(full_classes_stubs_jar)
续1的场景。
提取变量my_prefix:=TARGET_
LOCAL_MODULE_TAGS在Android.mk或package.mk中已经设定,默认是optional。
确认LOCAL_MODULE_PATH,默认$($(my_prefix)OUT$(use_data)_$(LOCAL_MODULE_CLASS)),此例中是out/target/product/generic/system/app
设定
module_id :=MODULE.$(TARGET).$(LOCAL_MODULE_CLASS).$(LOCAL_MODULE),此例MODULE.TARGET.APPS.Calculator。
设定中间目录路径intermediates,intermediates.COMMON,参见1.
设定LOCAL_MODULE_STEM=$(LOCAL_MODULE),此例,Calculator。LOCAL_INSTALLED_MODULE_STEM=Calculator.apk。
LOCAL_INTERMEDIATE_TARGETS追加上package.apk,参见1.
处理aidl,转为java,放在intermediates.COMMON下的目录中。
处理logtag,转为java,放在intermediates.COMMON下的目录中。
确定java_sources,这包括android.mk中包含的,aidl和logtag生成的。
处理java_resource_files
处理java lib相关
定义clean-$(LOCAL_MODULE) target, 可以删除app/package的生成文件,包括$(PRIVATE_CLEAN_FILES),$(LOCAL_BUILT_MODULE),$(LOCAL_INSTALLED_MODULE),$(intermediates),$(intermediates.COMMON)
还定义了$(LOCAL_MODULE) target, 几个变量的值
LOCAL_MODULE=Calculator
LOCAL_BUILT_MODULE=out/target/product/generic/obj/APPS/Calculator_intermediates/package.apk
LOCAL_INSTALLED_MODULE=out/target/product/generic/system/app/Calculator.apk
最后定义了几个ALL_MODULES变量。
ALL_MODULES.$(LOCAL_MODULE).CLASS
ALL_MODULES.$(LOCAL_MODULE).PATH
ALL_MODULES.$(LOCAL_MODULE).TAGS
ALL_MODULES.$(LOCAL_MODULE).CHECKED
ALL_MODULES.$(LOCAL_MODULE).BUILT
ALL_MODULES.$(LOCAL_MODULE).INSTALLED
ALL_MODULES.$(LOCAL_MODULE).REQUIRED
ALL_MODULES.$(LOCAL_MODULE).EVENT_LOG_TAGS
续1的场景。
mulit_prebuilt.mk顾名思义就是多次调用prebuilt.mk,对几种明确的prebuilt library完成需要的copy操作。
multi_prebuilt.mk定义了命令auto-prebuilt-boilerplate。入口有6个参数
# $(1): file list, "
# $(2): IS_HOST_MODULE
# $(3): MODULE_CLASS
# $(4): OVERRIDE_BUILT_MODULE_PATH
# $(5): UNINSTALLABLE_MODULE
# $(6): BUILT_MODULE_STEM
根据这6个参数,命令确定
LOCAL_IS_HOST_MODULE
LOCAL_MODULE_CLASS
OVERRIDE_BUILT_MODULE_PATH
LOCAL_UNINSTALLABLE_MODULE
LOCAL_MODULE
LOCAL_SRC_FILES
LOCAL_BUILT_MODULE_STEM
LOCAL_MODULE_SUFFIX
并调用prebuilt.mk
multi_prebuilt.mk中分别对下面5中lib调用了auto-prebuilt-boilerplate。
prebuilt_static_libs := $(filter %.a,$(LOCAL_PREBUILT_LIBS))
prebuilt_shared_libs := $(filter-out %.a,$(LOCAL_PREBUILT_LIBS))
prebuilt_executables := $(LOCAL_PREBUILT_EXECUTABLES)
prebuilt_java_libraries := $(LOCAL_PREBUILT_JAVA_LIBRARIES)
prebuilt_static_java_libraries := $(LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES)
续1的场景。
首先,include base_rules.mk
定义
PACKAGES.$(LOCAL_MODULE).OVERRIDES
第二步,如果是APPS类型,则zipalign,并拷贝到中间路径$(intermediates)。不是APPS,则不做zipalign。
本例是JAVA_LIBRARY类型,目的路径out/target/common/obj/JAVA_LIBRARIES/libarity_intermediates/javalib.jar,注意其中的libarity和javalib.jar。
最后检查 signed情况。
Android Make脚本的简记(3)
main.mk中调用了findleaves.py,得到所有子目录下Android.mk文件的路径。
subdir_makefiles := /
$(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git $(subdirs) Android.mk)
$(subdirs)一般编译中取值$(TOP)。
使用方法,
Usage: %(progName)s [
Options:
--mindepth=
Both behave in the same way as their find(1) equivalents.
--prune=
Avoids returning results from inside any directory called
(e.g., "*/out/*"). May be used multiple times.
程序首先依次选取dirlist中的目录,然后遍历所有子目录查找Android.mk文件,如果有,则加入到返回列表中。
pathmap.mk 中定义了一个列表pathmap_INCL,列表中每项是"短名:路径"对。命令include-path-for使用这个pathmap_INCL列表,输入短名,得到路径。你可以在这个列表中添加自己的对。使用$(call include-path-for, <短名>)就可以得到路径。
另外,定义了FRAMEWORKS_BASE_JAVA_SRC_DIRS,含有frameworks/base目录下含java文件的所有目录。
其次,定义了一些变量,例如通用的编译参数,package的后缀名等。
随后包含buildspec.mk。
接着包含envsetup.mk。
然后包含$(board_config_mk)。$(board_config_mk)是位于build/target/board /$(TARGET_DEVICE)/,device/*/$(TARGET_DEVICE)/,或vendor/*/$(TARGET_DEVICE) /目录下的BoardConfig.mk文件。
4. buildspec.mk的分析 buildspec.mk是用户应当配置的脚本文件,模板可以使用buildspec.mk.default,放到$(TOP)下。 在 buildspec.mk中,用户应该配置好主要的参数,例如 TARGET_PRODUCT, TARGET_BUILD_VARIANT, CUSTOM_MODULES, TARGET_SIMULATOR, TARGET_BUILD_TYPE, CUSTOM_LOCALES, 和BUILD_ENV_SEQUENCE_NUMBER等。 如果不使用buildspec.mk配置参数,也可以使用环境变量的形式。若不配置参数,那么android会使用默认的参数。
首先包含进version_defaults.mk,定义好一些版本相关的变量。参见version_defaults.mk。
定义CORRECT_BUILD_ENV_SEQUENCE_NUMBER,这个数字用于buildspec.mk更新时的提醒,应该同buildspec.mk中的或环境变量中的BUILD_ENV_SEQUENCE_NUMBER相等。一般不用关注。
随后检查TARGET_PRODUCT,若为空,则置generic。TARGET_PRODUCT应当在buildspec.mk或环境变量中已经定义好。
再检查TARGET_BUILD_VARIANT,若为空,则置eng。TARGET_BUILD_VARIANT应当在buildspec.mk或环境变量中已经定义好。
然后包含进product_config.mk。
接着,检查$(TARGET_BUILD_VARIANT),取值范围是eng user userdebug tests。
随后判定HOST_OS(linux),HOST_ARCH(x86)
接着,确定TARGET_ARCH和TARGET_OS,若没有定义,则取默认值。
TARGET_ARCH := arm
TARGET_OS := linux
接着,确定TARGET_BUILD_TYPE,若没有定义,则取默认值。
TARGET_BUILD_TYPE := release
接着,确定OUT_DIR。OUT_DIR是存放中间文件和最终结果的地方。若没有定义,则取默认值。
OUT_DIR := $(TOPDIR)out
随后,定义了一些列的路径变量
DEBUG_OUT_DIR,TARGET_OUT_ROOT_release,TARGET_OUT_ROOT_debug,TARGET_OUT_ROOT,BUILD_OUT,PRODUCT_OUT,TARGET_COMMON_OUT_ROOT,等等。
version_defaults.mk是检查一些跟版本相关的变量是否定义,如果未定义,则使用默认值。这些变量包括
PLATFORM_VERSION,默认AOSP
PLATFORM_SDK_VERSION,默认8
PLATFORM_VERSION_CODENAME,默认AOSP
DEFAULT_APP_TARGET_SDK,默认AOSP
BUILD_ID,默认UNKNOWN
BUILD_NUMBER,默认eng.$(USER).$(shell date +%Y%m%d.%H%M%S)的形式。
version_defaults.mk首先包含进build_id.mk。用户应当配置build_id.mk,而不应该改动version_defaults.mk文件。
然后检查上述变量,如未定义则赋值默认值。
用户可以在build_id.mk中定义这样几个参数,
PLATFORM_VERSION
PLATFORM_SDK_VERSION
PLATFORM_VERSION_CODENAME
DEFAULT_APP_TARGET_SDK
BUILD_ID
BUILD_NUMBER
这些参数最终将出现build.prop中。
Froyo的build_id.mk中定义了2个变量,
BUILD_ID,通常用于说明分支branch的,默认的是OPENMASTER,用户应该配置这个参数。
DISPLAY_BUILD_NUMBER,在TARGET_BUILD_VARIANT=user的版本中,build.prop中是ro.build.id是显示成$(BUILD_ID).$(BUILD_NUMBER),还是显示成$(BUILD_ID)形式。设成true,则显示前者。
make PRODUCT-
如果使用上述形式的make命令,那么将等同于
TARGET_PRODUCT:=
TARGET_BUILD_VARIANT:=
goal_name:=PRODUCT-
MAKECMDGOALS:=droid
.PHONY: $(goal_name)
$(goal_name): $(MAKECMDGOALS)
endif
注意,goal的取值范围是user userdebug eng tests,如果不属于上述范围,则将算入MAKECMDGOALS中,此时, TARGET_BUILD_VARIANT := eng。例如
make PRODUCT-dream-installclean
等同于
TARGET_PRODUCT=dream make installclean
使用make PRODUCT-
make APP-
如果使用上述形式的make命令,那么将等同于
TARGET_BUILD_APPS:=
unbundled_goals:=APP-
MAKECMDGOALS:=droid
.PHONY: $(unbundled_goals)
$(unbundled_goals): $(MAKECMDGOALS)
使用make APP-
注意,PRODUCT-
处理完PRODUCT-
node_fns.mk
product.mk
device.mk
上面的3个mk文件定义了一些命令,用于搜寻product, device对应的目录,生成相应的PRODUCT.XXX,和DEVICE.XXX变量。
接着,使用$(call import-products, $(get-all-product-makefiles))遍历Prodcut相关的AndroidProducts.mk文件,读入PRODCUTS.xxx变量。可以去掉文件中下面两句话的注释符,查看。
#$(dump-products)
#$(error done)
随后,使用PRODCUT.xxx和TARGET_PRODUCT,得到INTERNAL_PRODUCT信息,即指定product的路径。
再由INTERNAL_PRODUCT得到TARGET_DEVICE, PRODUCT_LOCALES,PRODUCT_BRAND, PRODUCT_MODEL, PRODUCT_MANUFACTURER, PRODUCT_DEFAULT_WIFI_CHANNELS, PRODUCT_POLICY, PRODUCT_COPY_FILES, PRODUCT_PROPERTY_OVERRIDES, PRODUCT_PACKAGE_OVERLAYS, DEVICE_PACKAGE_OVERLAYS, PRODUCT_TAGS, PRODUCT_OTA_PUBLIC_KEYS。
由PRODUCT_LOCALES导出PRODUCT_AAPT_CONFIG。
ADDITIONAL_BUILD_PROPERTIES中追加PRODUCT_PROPERTY_OVERRIDES中的值。
上面所说的这些值,实际上都是在product的mk文件中定义。
定义了一些命令。这些命令在product.mk,device.mk,和product_config.mk中会使用。这里重点说明import-nodes。
import-nodes需要3个入口参数:
$(1)是一个字串,是输出变量的主干名。例如”PRODUCTS"和”DEVICES“。
$(2)是一个makefile文件列表,这些文件中应该含有对$(3)中变量的定义。
$(3)是一个变量列表。
import- nodes会创建这样形式的变量,以$(1)="PRODUCTS", $(2)中含有"build/target/product/core.mk", $(3)中含有"PRODUCT_NAME",而且core.mk中定义了PRODUCT_NAME:=core为例,
PRODUCT.build/target/product/core.mk.PRODUCT_NAME:=core
import- nodes中还考虑了inherit的问题,如果某个PRODUCTS.XXX变量的值中有‘@inherit:
在product_config.mk中会说明$(2)中的mk文件列表是AndroidProducts.mk中的PRODUCT_MAKEFILES定义的。
node_fns.mk的代码真的很杀伤脑细胞...
product.mk构造了一些命令,供product_config.mk中使用。
_find-android-products-files这个命令会得到device/和vendor/,包括子目录,以及build/target/product/下的AndroidProducts.mk文件列表。
get-all-product-makefiles这个命令会得到所有$(_find-android-products-files)的AndroidProducts.mk文件中PRODUCT_MAKEFILES变量定义的mk文件。
_product_var_list对应的是import-nodes命令的$(3),定义了会生成那些PRODUCT属性名的变量。这些变量实际也是在product的mk文件中要考虑定义的。
_product_var_list := /
PRODUCT_NAME /
PRODUCT_MODEL /
PRODUCT_LOCALES /
PRODUCT_PACKAGES /
PRODUCT_DEVICE /
PRODUCT_MANUFACTURER /
PRODUCT_BRAND /
PRODUCT_PROPERTY_OVERRIDES /
PRODUCT_COPY_FILES /
PRODUCT_OTA_PUBLIC_KEYS /
PRODUCT_POLICY /
PRODUCT_PACKAGE_OVERLAYS /
DEVICE_PACKAGE_OVERLAYS /
PRODUCT_CONTRIBUTORS_FILE /
PRODUCT_TAGS /
PRODUCT_SDK_ADDON_NAME /
PRODUCT_SDK_ADDON_COPY_FILES /
PRODUCT_SDK_ADDON_COPY_MODULES /
PRODUCT_SDK_ADDON_DOC_MODULE /
PRODUCT_DEFAULT_WIFI_CHANNELS
import-products会调用import-nodes。product_config.mk中用到。
define import-products
$(call import-nodes,PRODUCTS,$(1),$(_product_var_list))
endef
inherit-product命令则将在所有的PRODUCT.xxx变量值中后缀上'@inherit:
check-all-products命令借助$(PRODUCTS)诸变量,会对product进行唯一性检查和PRODUCT_NAME,PRODUCT_BRAND,PRODCUT_COPY_FILES的简单检查。
resolve-short-product-name命令,给定Product的短名,返回对应mk的路径。
同product.mk类似,device.mk构造了一些命令。有resolve-short-device-name,和import-devices。
Android Make脚本的简记(4)
首先,包含pathmap.mk,其次,定义了一些变量,例如通用的编译参数,package的后缀名等。
随后包含buildspec.mk。
接着包含envsetup.mk。envsetup.mk中会遍历所有product相关的路径,载入所有支持的product的信息到变量集 PRODUCT.
然后包含$(board_config_mk)。$(board_config_mk)是位于 build/target/board/$(TARGET_DEVICE)/,device/*/$(TARGET_DEVICE)/,或vendor /*/$(TARGET_DEVICE)/目录下的BoardConfig.mk文件。 $(TARGET_DEVICE)已经在product_config.mk中定义了。在包含$(board_config_mk)之前,会做检查,多个$(board_config_mk)存在则报错。
定义TARGET_DEVICE_DIR,TARGET_BOOTLOADER_BOARD_NAME,TARGET_CPU_ABI等跟board相关的变量。
接着,依次以HOST_和TARGET_条件包含select.mk。这里说明TARGET_的select.mk。先定义combo_os_arch,通常是linux-arm,然后定义各种跟编译链接相关的一些变量,最后再包含进build/core/combo/TARGET_linux- arm.mk。
再包含javac.mk,定义javac的命令和通用参数。
随后,定义一些变量,指向通用工具,其中一些是os提供的,例如YACC;一些是froyo编译生成,放在out/host/linux-x86/bin/下,一些是预定义的脚本和工具,例如MKTARBALL。
最后定义了一些编译链接变量,这里专门列出,
HOST_GLOBAL_CFLAGS += $(COMMON_GLOBAL_CFLAGS)
HOST_RELEASE_CFLAGS += $(COMMON_RELEASE_CFLAGS)
HOST_GLOBAL_CPPFLAGS += $(COMMON_GLOBAL_CPPFLAGS)
HOST_RELEASE_CPPFLAGS += $(COMMON_RELEASE_CPPFLAGS)
TARGET_GLOBAL_CFLAGS += $(COMMON_GLOBAL_CFLAGS)
TARGET_RELEASE_CFLAGS += $(COMMON_RELEASE_CFLAGS)
TARGET_GLOBAL_CPPFLAGS += $(COMMON_GLOBAL_CPPFLAGS)
TARGET_RELEASE_CPPFLAGS += $(COMMON_RELEASE_CPPFLAGS)
HOST_GLOBAL_LD_DIRS += -L$(HOST_OUT_INTERMEDIATE_LIBRARIES)
TARGET_GLOBAL_LD_DIRS += -L$(TARGET_OUT_INTERMEDIATE_LIBRARIES)
HOST_PROJECT_INCLUDES:= $(SRC_HEADERS) $(SRC_HOST_HEADERS) $(HOST_OUT_HEADERS)
TARGET_PROJECT_INCLUDES:= $(SRC_HEADERS) $(TARGET_OUT_HEADERS)
ifneq ($(TARGET_SIMULATOR),true)
TARGET_GLOBAL_CFLAGS += $(TARGET_ERROR_FLAGS)
TARGET_GLOBAL_CPPFLAGS += $(TARGET_ERROR_FLAGS)
endif
HOST_GLOBAL_CFLAGS += $(HOST_RELEASE_CFLAGS)
HOST_GLOBAL_CPPFLAGS += $(HOST_RELEASE_CPPFLAGS)
TARGET_GLOBAL_CFLAGS += $(TARGET_RELEASE_CFLAGS)
TARGET_GLOBAL_CPPFLAGS += $(TARGET_RELEASE_CPPFLAGS)
其中的TARGET_PROJECT_INCLUDES包含了SRC_HEADERS,添加头文件路径的话,可以改动SRC_HEADERS。
最后包含进dumpvar.mk
javac.mk中会定义javac的编译命令和通用参数。
CUSTOM_JAVA_COMPILER做为javac.mk的入口参数,可以考虑openjdk,eclipse。不定义时则使用默认的javac。另外定义为openjdk时,因为prebuilt/对应目录下没有相应的工具,所以还不可用。
依次一般忽略定义CUSTOM_JAVA_COMPILER,只要直接配置自己编译环境的path,指向使用的javac就可以了。
javac在linux平台的定义是
javac -J-Xmx512M -target 1.5 -Xmaxerrs 9999999
-J-Xmx512M,传递给vm launcher参数-Xmx512M,告知起始空间设定为512M。
-target 1.5,编译的结果适用1.5版本。
-Xmaxerrs 9999999,最大输出的错误数是9999999。
dumpvar.mk 支持两种target: dumpvar-
使用方法:假设位于$(TOPDIR)路径,
CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core make -f build/core/config.mk dumpvar-
或
CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core make -f build/core/config.mk dumpvar-abs-
第一种形式,返回varName的值。第二种形式,返回varName的值,前缀上路径。考虑到android脚本中广泛使用':=’的变量定义方法,因此,基本上只能显示dumpvar.mk之前定义的变量值。LOCAL_xxxx的变量也不适用。
main.mk在包含了config.mk后,会包含进cleanbuild.mk。
定义了add-clean-step命令。有一个入口参数
$(1),执行删除操作的具体shell命令。
一般add-clean-step应当在%/cleanspec.mk脚本中使用,命令会为$(1)定义一个变量保存,变量的名字是 INTERNAL_STEP.$(_acs_id),所有的$(_acs_id)保存在INTERNAL_STEPS中。$(_acs_id)的值分成3个部分构造
第一部分是有cleanspec.mk的路径转化而来,用'_'替代'/','-'替代'.',后缀_acs。第二部分是$(INTERNAL_CLEAN_BUILD_VERSION),默认是4,第三部分是有'@'组成,cleanspec.mk中的第几个add- clean-step就用几个@。
例如,packages/apps/Camera/cleanspec.mk中定义了两个删除动作
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/Camera*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Camera*)
那么,对应的有
INTERNAL_STEP.packages_apps_Camera_CleanSpec-mk_acs4@ := rm -rf $(PRODUCT_OUT)/obj/APPS/Camera*
INTERNAL_STEP.packages_apps_Camera_CleanSpec-mk_acs4@@ := rm -rf $(OUT_DIR)/target/common/obj/APPS/Camera*
接着,包扩进cleanspec.mk
包含进$(PRODUCT_OUT)/clean_steps.mk,
接下来,检查CURRENT_CLEAN_BUILD_VERSION是否与INTERNAL_CLEAN_BUILD_VERSION相同,默认是4
如果相同,
执行所有在INTERNAL_STEPS中登记的删除操作。
否则,
删除 $(OUT_DIR)
然后,重新生成$(PRODUCT_OUT)/clean_steps.mk,写入"CURRENT_CLEAN_BUILD_VERSION := $(INTERNAL_CLEAN_BUILD_VERSION)"和"CURRENT_CLEAN_STEPS := $(INTERNAL_CLEAN_STEPS)"。
随后,读入$(PRODUCT_OUT)/previous_build_config.mk,看是否与当前的编译选项一致,不一致则标明上次的中间文件不可用,则删除相应的中间目录,或提示用户。接着重新将当前的信息写入$(PRODUCT_OUT)/previous_build_config.mk,格式是,
current_build_config := /
$(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT)$(building_sdk)-{$(locale_list)}
echo "PREVIOUS_BUILD_CONFIG := $(current_build_config)" > /
$(previous_build_config_file)
最后,定义了两个target, installclean和dataclean。
dataclean删除的主要是./$(PRODUCT_OUT)/data/*,
installclean的删除包括dataclean。installclean的本意是用于不同build_type编译时删除前次的中间文件。
总结cleanbuild.mk的内容,就3件事,一是载入所有的CleanSpec.mk,二是检查更新clean_steps.mk和 previous_build_config.mk,避免不同编译间的互相干扰。最后是,定义installclean和dataclean。
首先定义
INTERNAL_CLEAN_BUILD_VERSION := 4
接着使用findleaves.py遍历所有子目录,找到CleanSpec.mk,并包含进。用户可以在CleanSpec.mk中定义自己需要的删除操作。实际上还可以包含不仅仅是删除的操作。
至此,INTERNAL_STEP.XXXX包含了所有CleanSpec.mk定义的clean动作。
main.mk 在cleanbuild.mk后,会借助$(OUT_DIR)/version_checked.mk检查版本,如果版本不一致,则重新检查系统文件系统大小写敏感问题,路径上是否含有空格,java和javac的版本,没有问题,则更新version_checked.mk。
version_checked.mk中就定义了
VERSIONS_CHECKED := $(VERSION_CHECK_SEQUENCE_NUMBER)
checkbuild貌似并未使用。
showcommands必须同其它target一同使用,showcommands会详细打印出执行的具体命令内容。
definations.mk中定义了大量的命令,其它的mk文件将使用。这其中包括执行编译链接的命令,通常是transform-XXX-to-XXX的形式,例如,transform-cpp-to-o。
其中的inherit-package命令有待研究...
Android Make脚本的简记(5)
首先定义target,用于生成$(OUT_DOCS)/index.html
再定义target,用于生成$(TARGET_ROOT_OUT)/default.prop
再定义target,用于生成$(TARGET_OUT)/build.prop。build.prop文件记录了一系列属性值。它的内容分成两部分,第一部分是一些关于 product,device,build的一般性属性值,第二部分的属性值源自ADDITIONAL_BUILD_PROPERTIES。 product配置mk文件中定义的PRODUCT_PROPERTY_OVERRIDES会加入到 ADDITIONAL_BUILD_PROPERTIES,建议增加property时,直接修改 PRODUCT_PROPERTY_OVERRIDES。
再定义target,用于生成$(PRODUCT_OUT)/sdk/sdk-build.prop
再定义target,package-stats,用于生成$(PRODUCT_OUT)/package-stats.txt,这个文件包含了.jar,.apk后缀文件的信息。
再定义target,apkcerts-list,用于生成$(name)-apkcerts-$(FILE_NAME_TAG),描述各module的certificate和private_key文件信息。
接着,如果定义了CREATE_MODULE_INFO_FILE,则生成$(PRODUCT_OUT)/module-info.txt,其中包含了描述所有module的信息。
再定义target,event-log-tags。
接着,处理ramdisk.img
再处理boot.img,如果TARGET_NO_KERNEL不是true,则将kernel和ramdisk.img组装成boot.img。
接着,定影命令combine-notice-files,用于生成target,notice_files。notice_files会抽取生成相应的声明文件。
随后,建立target,otacert,用于将.x509.pem后缀的认证文件打包存放到$(TARGET_OUT_ETC)/security/otacerts.zip。
接着,建立target,recoveryimage,处理recovery img
还有下面的target,
systemimage-nodeps, snod
systemtarball-nodeps,stnod
boottarball-nodeps,btnod
userdataimage-nodeps
userdatatarball-nodeps
otatools
target-files-package
otapackage
installed-file-list
tests-zip-package
dalvikfiles
updatepackage
最后包含进 build/core/task/下的mk文件。