转载请注明出处:http://blog.csdn.net/droyon/article/details/8639773
最近在读android内核剖析,将一些心得记录下来,方便自己查阅。
在android源代码文件夹下存在build文件夹,,他下面的文件构成了编译系统,该目录下的make脚本和shell脚本共同组成了Androd的编译环境。
在我们编译系统时,我们一般先执行:
. build/envsetup.sh
make
make后面可以跟上参数,参数包括:产品名,模块名,sdk。
make 后面无论跟的是产品名还是模块名,那么首先编译系统要认识。产品和模块名如何要让android系统认识,是在android编译系统定义的,以及输出路径out(out下有两个目录,target是最终的输出目标,host是在编译sdk,或者cts时,生成的供pc使用的一些工具)目录的指定。android编译系统实现在build文件夹下,那里存放了一系列的*.mk文件。每个子项目目录下都有Android.mk,在文件中定义了子项目要生成的目标文件,是apk还是jar包。
1、关于Android.mk的语法:
LOCAL_PATH:指定了子项目的绝对路径,一般通过编译系统中定义的call-dir获取。
LOCAL_PATH:= $(call my-dir)
LOCAL_MODULE_TAGS:指定子项目的标签,标签包括:user、eng、userdebug、optional。
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES:指定子项目所包含的所有源文件。可以通过函数获得,如下所示。
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
src/com/android/music/IMediaPlaybackService.aidl
LOCAL_PACKAGE_NAME:指定项目名称,这个名称就是子项目名。对于c++项目使用LOCAL_MODULE。
LOCAL_PACKAGE_NAME := Music
LOCAL_MODULE:= reference-ril
BUILE_PACKAGE:代表编译生成apk,还有BUILE_HOST_EXECUTABLE,BUILE_HOST_STATIC_LIBRARY
2、build编译系统文件加载顺序
执行make命令,会加载根目录中的Makefile,在这个文件中,会加载buile/core/main.mk,在这个文件中定义了,make命令不跟参数,所执行的情况。
.PHONY: droid
DEFAULT_GOAL := droid
$(DEFAULT_GOAL):
在执行main.mk过程中,会包含build/core/config.mk,在这个文件中,定义了一系列路径变量:
SRC_HEADERS := \
$(TOPDIR)system/core/include \
$(TOPDIR)hardware/libhardware/include \
$(TOPDIR)hardware/libhardware_legacy/include \
$(TOPDIR)hardware/ril/include \
$(TOPDIR)dalvik/libnativehelper/include \
$(TOPDIR)frameworks/base/include \
$(TOPDIR)frameworks/base/opengl/include \
$(TOPDIR)frameworks/base/native/include \
$(TOPDIR)external/skia/include
SRC_HOST_HEADERS:=$(TOPDIR)tools/include
SRC_LIBRARIES:= $(TOPDIR)libs
SRC_SERVERS:= $(TOPDIR)servers
SRC_TARGET_DIR := $(TOPDIR)build/target
SRC_API_DIR := $(TOPDIR)frameworks/base/api
在config.mk文件中会包含pathmap.mk文件,在这个文件中也会定义一系列的framework层常量,很重要,在后面会引用
FRAMEWORKS_BASE_SUBDIRS := \
$(addsuffix /java, \
core \
graphics \
location \
media \
drm \
opengl \
sax \
telephony \
wifi \
keystore \
icu4j \
voip \
)
FRAMEWORKS_BASE_JAVA_SRC_DIRS := \
$(addprefix frameworks/base/,$(FRAMEWORKS_BASE_SUBDIRS))
等,这个常量定义了编译到framework.jar的源文件目录,如果你修改了android framework层的代码,新建了目录,如果目录不再第一段代码中,那么可能编译不到framework.jar中。
然后继续config.mk的执行。然后config.mk会定义一系列的编译宏,在Android.mk中最后使用的那个命令include $(BUILD_PACKAGE)
BUILD_COMBOS:= $(BUILD_SYSTEM)/combo
CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.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_PHONY_PACKAGE:= $(BUILD_SYSTEM)/phony_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_PACKAGE,代表编译源文件成为apk。
再往下config.mk文件包含buildspec.mk在这个文件中会定义我们要编译的产品的名字。(如有错误请指正)文件路径为:根目录/buildspec.mk
再往下cofig.mk文件会包含envsetup.mk,主要是环境的配置,例如apk安装路径常量,设置目标输出路径。
OUT_DIR := $(TOPDIR)out
TARGET_SYSLOADER_SYSTEM_OUT := $(TARGET_SYSLOADER_OUT)/root/system
TARGET_INSTALLER_OUT := $(PRODUCT_OUT)/installer
TARGET_INSTALLER_DATA_OUT := $(TARGET_INSTALLER_OUT)/data
其中会包含product_config.mk文件,在这个文件中,它会包含product.mk文件,在product.mk文件中,会检测device 、verndor、build/target/product目录下所有的AndroidProduct.mk文件。
define _find-android-products-files
$(shell test -d device && find device -maxdepth 6 -name AndroidProducts.mk) \
$(shell test -d vendor && find vendor -maxdepth 6 -name AndroidProducts.mk) \
$(SRC_TARGET_DIR)/product/AndroidProducts.mk
endef
文件目录深度为6级。
然后在product_config.mk文件中,调用自身函数将product加入进来。
ifneq ($(strip $(TARGET_BUILD_APPS)),)
# An unbundled app build needs only the core product makefiles.
$(call import-products,$(call get-product-makefiles,\
$(SRC_TARGET_DIR)/product/AndroidProducts.mk))
else
# Read in all of the product definitions specified by the AndroidProducts.mk
# files in the tree.
#
#TODO: when we start allowing direct pointers to product files,
# guarantee that they're in this list.
$(call import-products, $(get-all-product-makefiles))
endif # TARGET_BUILD_APPS
到这里product_config.mk结束,继续config.mk文件的执行,也就是上面所的设置环境变量,设置OUT_DIR := $(TOPDIR)out,等。
这样envsetup.mk文件就执行结束了。继续执行config.mk文件,再往下config.mk文件会加载每一个product下的BoardConfig.mk文件。
board_config_mk := \
$(strip $(wildcard \
$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
device/*/$(TARGET_DEVICE)/BoardConfig.mk \
vendor/*/$(TARGET_DEVICE)/BoardConfig.mk \
))
ifeq ($(board_config_mk),)
$(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))
endif
ifneq ($(words $(board_config_mk)),1)
$(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))
endif
include $(board_config_mk
BoardCOnfig.mk会加载一些属性,这些属性会加入到系统属性中。
最后config.mk文件会设置工具命令宏。
AAPT := $(HOST_OUT_EXECUTABLES)/aapt$(HOST_EXECUTABLE_SUFFIX)
config.mk文件就执行结束了。
继续main.mk文件的执行。接下来检查编译系统,是linux还是window以及苹果 Darwin等,以及检查openJDK的版本等。
在然后,加载definations.mk加载各种函数,包括我们在Android.mk中使用的my-dir。
define my-dir