《深入解析Android5.0系统》 一书笔记
Android的Build系统非常的庞大,他是基于GUN Make以及shell来构建的,我们主要的面对方向是Android.mk文件,这也是Android为我们处理好的,不用直接跟shell打交道,Build不光可以处理系统的编译打包,还能生成img镜像等,十分的强大。
从大的方面来说,Build系统分为三大块,第一块处于build/core目录下的文件,这是Build的基础框架和核心部分,第二块处于device目录下的文件,存放的是项目的一些配置信息,第三块就是各大模块的Android.mk了。
我们本篇文章依葫芦画瓢的来侃侃Build,我也是跟着书上来的,我也不是很会C/C++
在说Build前,我简单带过一下,我们之前有教过通过SecureCRT远程连接Linux,我们现在用Xshell去连接
其实Xshell连接是非常简单的,我们只需要在ubuntu的终端输入ifconfig,来查看你的ip:比如我的ip是:192.111.11.11(假设)
那么我的Xshell只需要新建一个端口
但是如果连接失败,那你就要检查一下ssh了,在ubuntu的终端输入:
ps -e |grep ssh
如果没有反应,说明你的ssh没有安装
sudo apt install openssh-server
最后就可成功连接了
我们进入build/core目录下,这个目录里有很多的mk文件以及shell脚本,perl脚本,我们跟踪一下源码的编译
source build/envsetup.sh
lunch
make
我们顺着这三条命令来查看源码
这个脚本我们先来看看他的内容是什么,我们打开build/envsetup.sh,可以看到里面的脚本中,中间部分插入了lunch选择的参数列表,在结尾处,find了device和vendor目录下的vendorsetuo.sh文件,然后运行他们,我们找一下,看下是否有vendorsetuo.sh,在 device/lge/hammerhead/目录下可以看到有一个vendorsetuo.sh脚本
我们打开它你可以发现,它里面就一行代码
add_lunch_combo aosp_hammerhead-userdebug
它依旧调用add_lunch_combo函数,这样看来,envsetup.sh除了建立shell命令外,就是执行这一句命令了,我们来看下add_lunch_combo的定义:
add_lunch_combo命令的功能,是将调用这个命令所传递的参数放在一个全局的数组变量LUNCH_MENU_CHOICES中,执行lunch就是打印出这个数组的内容了,也就是我们上篇末尾所展示的样子,还记得吗?我选了5
envsetup.sh中还定义了一些实用的shell脚本
我们来看第二行代码lunch,这里我们分析下这个函数
function lunch()
{
local answer
# 如果lunch后面没有参数,那么打印菜单,并将选择值放入answer
# 如果有参数,直接赋值
if [ "$1" ] ; then
answer=$1
else
print_lunch_menu
echo -n "Which would you like? [aosp_arm-eng] "
read answer
fi
local selection=
# 如果answer为空,则selection=aosp_arm-eng
# 如果answer为数字并且小于或等于菜单数目,则把相应的菜单栏的数字赋值给他
# 如果answer包含了一个-,则将answer赋值给selection,否则报错
if [ -z "$answer" ]
then
selection=aosp_arm-eng
elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
then
if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
then
selection=${LUNCH_MENU_CHOICES[$(($answer-1))]}
fi
elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")
then
selection=$answer
fi
if [ -z "$selection" ]
then
echo
echo "Invalid lunch combo: $answer"
return 1
fi
export TARGET_BUILD_APPS=
# 将selection中的-分割前半部分给product
# 并调用函数check_product去检查是否存在这个配置文件
local product=$(echo -n $selection | sed -e "s/-.*$//")
check_product $product
if [ $? -ne 0 ]
then
echo
echo "** Don't have a product spec for: '$product'"
echo "** Do you have the right repo manifest?"
product=
fi
# 将selection中的-分割前半部分给variant
# 并调用函数check_variant来检查这个值是否是:eng,user,debuguser
local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")
check_variant $variant
if [ $? -ne 0 ]
then
echo
echo "** Invalid variant: '$variant'"
echo "** Must be one of ${VARIANT_CHOICES[@]}"
variant=
fi
if [ -z "$product" -o -z "$variant" ]
then
echo
return 1
fi
# 将变量product赋值给TARGET_PRODUCT
# 将变量variant赋值给TARGET_BUILD_VARIANT
# 将TARGET_BUILD_TYP的环境变量设置为release
export TARGET_PRODUCT=$product
export TARGET_BUILD_VARIANT=$variant
export TARGET_BUILD_TYPE=release
echo
# 设置更多的环境变量
set_stuff_for_environment
printconfig
}
lunch命令如果没有带参数,直接菜单选择,他的格式如下:
lunch<product_name>-<build_variant>
product_name必须是你定义好的产品配置,build_variant必须是eng,user,debuguser其中一个
lunch命令主要还是根据你输入的产品配置并且设置环境变量,环境变量和产品编译相关的主要是下面这些:
最后他调用set_stuff_for_environment还会设置一些
我们注意看,执行完我们可以看到打印了很多的配置信息
这些配置将影响到我们的编译过程,我们来分析下
这些变量中第二CPU是Android5.0才加入的,因为当运行64位环境的时候还要考虑32
对于环境变量的修改,可以放到产品的定义中,也可以临时的修改,比如:
make BUILD_ID="liuguilin"
我们lunch后,就是make了,make 后面可以接参数,也是编译过程的开始,make最终生成的也是zip以及各种img,Build会收集编译模块编译,然后复制二进制文件,产品配置文件等最后打包。
一套Android的源码能编译出多个不同的产品配置,我们反过来看,实际上我们也可以猜想到,源码的各个模块都是分开的,当我们选定了一个产品配置之后,Build会把他们拼接起来,我们来看下具体的工作流程:
在执行make后,实际上执行的是根目录/Makefile文件
include build/core/main.mk
可以看到,他里面就一行代码,执行了main.mk,我们查看main.mk就可以发现,他实际上是通过include将所有的mk都包含进来,最终在内存中形成一个编译脚本的集合,这才是最终形态的Makefile
Makefile是很庞大的,但是他主要由三种内容构成:变量定义,函数定义和目标,我们如果有必要,还是要看一下他们的亲戚关系:
这些mk文件的主要作用,我们来看下:
总结:Build系统中定义了大量的编译变量,每一个都是很值得我们去了解熟悉的,虽然Google并没有给出太多的介绍。
由于太多了,我们分段来分析
# Check for broken versions of make.
# (Allow any version under Cygwin since we don't actually build the platform there.)
ifeq (,$(findstring CYGWIN,$(shell uname -sm)))
ifneq (1,$(strip $(shell expr $(MAKE_VERSION) \>= 3.81)))
$(warning ********************************************************************************)
$(warning * You are using version $(MAKE_VERSION) of make.)
$(warning * Android can only be built by versions 3.81 and higher.)
$(warning * see https://source.android.com/source/download.html)
$(warning ********************************************************************************)
$(error stopping)
endif
endif
# This is the default target. It must be the first declared target.
.PHONY: droid
DEFAULT_GOAL := droid
$(DEFAULT_GOAL):
# Targets that provide quick help on the build system.
include $(BUILD_SYSTEM)/help.mk
# Set up various standard variables based on configuration
# and host information.
include $(BUILD_SYSTEM)/config.mk
# This allows us to force a clean build - included after the config.mk
# environment setup is done, but before we generate any dependencies. This
# file does the rm -rf inline so the deps which are all done below will
# be generated correctly
include $(BUILD_SYSTEM)/cleanbuild.mk
# Include the google-specific config
-include vendor/google/build/config.mk
VERSION_CHECK_SEQUENCE_NUMBER := 5
-include $(OUT_DIR)/versions_checked.mk
# Check for the correct version of java, should be 1.7 by
# default, and 1.6 if LEGACY_USE_JAVA6 is set.
ifeq ($(LEGACY_USE_JAVA6),)
required_version := "1.7.x"
required_javac_version := "1.7"
java_version := $(shell echo '$(java_version_str)' | grep '^java .*[ "]1\.7[\. "$$]')
javac_version := $(shell echo '$(javac_version_str)' | grep '[ "]1\.7[\. "$$]')
else # if LEGACY_USE_JAVA6
required_version := "1.6.x"
required_javac_version := "1.6"
java_version := $(shell echo '$(java_version_str)' | grep '^java .*[ "]1\.6[\. "$$]')
javac_version := $(shell echo '$(javac_version_str)' | grep '[ "]1\.6[\. "$$]')
endif # if LEGACY_USE_JAVA6
ifeq ($(strip $(java_version)),)
$(info ************************************************************)
$(info You are attempting to build with the incorrect version)
$(info of java.)
$(info $(space))
$(info Your version is: $(java_version_str).)
$(info The required version is: $(required_version))
$(info $(space))
$(info Please follow the machine setup instructions at)
$(info $(space)$(space)$(space)$(space)https://source.android.com/source/initializing.html)
$(info ************************************************************)
$(error stop)
endif
# Check for the current JDK.
#
# For Java 1.7, we require OpenJDK on linux and Oracle JDK on Mac OS.
# For Java 1.6, we require Oracle for all host OSes.
requires_openjdk := false
ifeq ($(LEGACY_USE_JAVA6),)
ifeq ($(HOST_OS), linux)
requires_openjdk := true
endif
endif
# Check for the current jdk
ifeq ($(requires_openjdk), true)
# The user asked for java7 openjdk, so check that the host
# java version is really openjdk
ifeq ($(shell echo '$(java_version_str)' | grep -i openjdk),)
$(info ************************************************************)
$(info You asked for an OpenJDK 7 build but your version is)
$(info $(java_version_str).)
$(info ************************************************************)
$(error stop)
endif # java version is not OpenJdk
else # if requires_openjdk
ifneq ($(shell echo '$(java_version_str)' | grep -i openjdk),)
$(info ************************************************************)
$(info You are attempting to build with an unsupported JDK.)
$(info $(space))
$(info You use OpenJDK but only Sun/Oracle JDK is supported.)
$(info Please follow the machine setup instructions at)
$(info $(space)$(space)$(space)$(space)https://source.android.com/source/download.html)
$(info ************************************************************)
$(error stop)
endif # java version is not Sun Oracle JDK
endif # if requires_openjdk
# Check for the correct version of javac
ifeq ($(strip $(javac_version)),)
$(info ************************************************************)
$(info You are attempting to build with the incorrect version)
$(info of javac.)
$(info $(space))
$(info Your version is: $(javac_version_str).)
$(info The required version is: $(required_javac_version))
$(info $(space))
$(info Please follow the machine setup instructions at)
$(info $(space)$(space)$(space)$(space)https://source.android.com/source/download.html)
$(info ************************************************************)
$(error stop)
endif
$(shell echo 'VERSIONS_CHECKED := $(VERSION_CHECK_SEQUENCE_NUMBER)' \
> $(OUT_DIR)/versions_checked.mk)
$(shell echo 'BUILD_EMULATOR ?= $(BUILD_EMULATOR)' \
>> $(OUT_DIR)/versions_checked.mk)
# Bring in standard build system definitions.
include $(BUILD_SYSTEM)/definitions.mk
# Bring in dex_preopt.mk
include $(BUILD_SYSTEM)/dex_preopt.mk
ifneq ($(filter user userdebug eng,$(MAKECMDGOALS)),)
$(info ***************************************************************)
$(info ***************************************************************)
$(info Do not pass '$(filter user userdebug eng,$(MAKECMDGOALS))' on \
the make command line.)
$(info Set TARGET_BUILD_VARIANT in buildspec.mk, or use lunch or)
$(info choosecombo.)
$(info ***************************************************************)
$(info ***************************************************************)
$(error stopping)
endif
ifneq ($(filter-out $(INTERNAL_VALID_VARIANTS),$(TARGET_BUILD_VARIANT)),)
$(info ***************************************************************)
$(info ***************************************************************)
$(info Invalid variant: $(TARGET_BUILD_VARIANT)
$(info Valid values are: $(INTERNAL_VALID_VARIANTS)
$(info ***************************************************************)
$(info ***************************************************************)
$(error stopping)
endif
# -----------------------------------------------------------------
# Variable to check java support level inside PDK build.
# Not necessary if the components is not in PDK.
# not defined : not supported
# "sdk" : sdk API only
# "platform" : platform API supproted
TARGET_BUILD_JAVA_SUPPORT_LEVEL := platform
# -----------------------------------------------------------------
# The pdk (Platform Development Kit) build
include build/core/pdk_config.mk
ifneq ($(ONE_SHOT_MAKEFILE),)
# We've probably been invoked by the "mm" shell function
# with a subdirectory's makefile.
include $(ONE_SHOT_MAKEFILE)
# Change CUSTOM_MODULES to include only modules that were
# defined by this makefile; this will install all of those
# modules as a side-effect. Do this after including ONE_SHOT_MAKEFILE
# so that the modules will be installed in the same place they
# would have been with a normal make.
CUSTOM_MODULES := $(sort $(call get-tagged-modules,$(ALL_MODULE_TAGS)))
FULL_BUILD :=
# Stub out the notice targets, which probably aren't defined
# when using ONE_SHOT_MAKEFILE.
NOTICE-HOST-%: ;
NOTICE-TARGET-%: ;
# A helper goal printing out install paths
.PHONY: GET-INSTALL-PATH
GET-INSTALL-PATH:
@$(foreach m, $(ALL_MODULES), $(if $(ALL_MODULES.$(m).INSTALLED), \
echo 'INSTALL-PATH: $(m) $(ALL_MODULES.$(m).INSTALLED)';))
else # ONE_SHOT_MAKEFILE
ifneq ($(dont_bother),true)
#
# Include all of the makefiles in the system
#
# Can't use first-makefiles-under here because
# --mindepth=2 makes the prunes not work.
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)))
endif # dont_bother
endif # ONE_SHOT_MAKEFILE
## user/userdebug ##
user_variant := $(filter user userdebug,$(TARGET_BUILD_VARIANT))
enable_target_debugging := true
tags_to_install :=
ifneq (,$(user_variant))
# Target is secure in user builds.
ADDITIONAL_DEFAULT_PROPERTIES += ro.secure=1
ifeq ($(user_variant),userdebug)
# Pick up some extra useful tools
tags_to_install += debug
# Enable Dalvik lock contention logging for userdebug builds.
ADDITIONAL_BUILD_PROPERTIES += dalvik.vm.lockprof.threshold=500
else
# Disable debugging in plain user builds.
enable_target_debugging :=
endif
# Turn on Dalvik preoptimization for libdvm.so user builds, but only if not
# explicitly disabled and the build is running on Linux (since host
# Dalvik isn't built for non-Linux hosts).
ifeq (,$(WITH_DEXPREOPT))
ifeq ($(DALVIK_VM_LIB),libdvm.so)
ifeq ($(user_variant),user)
ifeq ($(HOST_OS),linux)
WITH_DEXPREOPT := true
endif
endif
endif
endif
# Disallow mock locations by default for user builds
ADDITIONAL_DEFAULT_PROPERTIES += ro.allow.mock.location=0
else # !user_variant
# Turn on checkjni for non-user builds.
ADDITIONAL_BUILD_PROPERTIES += ro.kernel.android.checkjni=1
# Set device insecure for non-user builds.
ADDITIONAL_DEFAULT_PROPERTIES += ro.secure=0
# Allow mock locations by default for non user builds
ADDITIONAL_DEFAULT_PROPERTIES += ro.allow.mock.location=1
endif # !user_variant
ifeq (true,$(strip $(enable_target_debugging)))
# Target is more debuggable and adbd is on by default
ADDITIONAL_DEFAULT_PROPERTIES += ro.debuggable=1
# Include the debugging/testing OTA keys in this build.
INCLUDE_TEST_OTA_KEYS := true
else # !enable_target_debugging
# Target is less debuggable and adbd is off by default
ADDITIONAL_DEFAULT_PROPERTIES += ro.debuggable=0
endif # !enable_target_debugging
## eng ##
ifeq ($(TARGET_BUILD_VARIANT),eng)
tags_to_install := debug eng
ifneq ($(filter ro.setupwizard.mode=ENABLED, $(call collapse-pairs, $(ADDITIONAL_BUILD_PROPERTIES))),)
# Don't require the setup wizard on eng builds
ADDITIONAL_BUILD_PROPERTIES := $(filter-out ro.setupwizard.mode=%,\
$(call collapse-pairs, $(ADDITIONAL_BUILD_PROPERTIES))) \
ro.setupwizard.mode=OPTIONAL
endif
ifndef is_sdk_build
# Don't even verify the image on eng builds to speed startup
ADDITIONAL_BUILD_PROPERTIES += dalvik.vm.image-dex2oat-filter=verify-none
# Don't compile apps on eng builds to speed startup
ADDITIONAL_BUILD_PROPERTIES += dalvik.vm.dex2oat-filter=interpret-only
endif
endif
# Now with all Android.mks loaded we can do post cleaning steps.
include $(BUILD_SYSTEM)/post_clean.mk
ifeq ($(stash_product_vars),true)
$(call assert-product-vars, __STASHED)
endif
include $(BUILD_SYSTEM)/legacy_prebuilts.mk
ifneq ($(filter-out $(GRANDFATHERED_ALL_PREBUILT),$(strip $(notdir $(ALL_PREBUILT)))),)
$(warning *** Some files have been added to ALL_PREBUILT.)
$(warning *)
$(warning * ALL_PREBUILT is a deprecated mechanism that)
$(warning * should not be used for new files.)
$(warning * As an alternative, use PRODUCT_COPY_FILES in)
$(warning * the appropriate product definition.)
$(warning * build/target/product/core.mk is the product)
$(warning * definition used in all products.)
$(warning *)
$(foreach bad_prebuilt,$(filter-out $(GRANDFATHERED_ALL_PREBUILT),$(strip $(notdir $(ALL_PREBUILT)))),$(warning * unexpected $(bad_prebuilt) in ALL_PREBUILT))
$(warning *)
$(error ALL_PREBUILT contains unexpected files)
endif
未完待续