本文主要是介绍编译环境初始化-Android10.0编译系统(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
[Android取经之路] 的源码都基于Android-Q(10.0) 进行分析
[Android取经之路] 系列文章:
《系统启动篇》
Android系统架构
Android是怎么启动的
Android 10.0系统启动之init进程
Android10.0系统启动之Zygote进程
Android 10.0 系统启动之SystemServer进程
Android 10.0 系统服务之ActivityMnagerService
Android10.0系统启动之Launcher(桌面)启动流程
Android10.0应用进程创建过程以及Zygote的fork流程
Android 10.0 PackageManagerService(一)工作原理及启动流程
Android 10.0 PackageManagerService(二)权限扫描
Android 10.0 PackageManagerService(三)APK扫描
Android 10.0 PackageManagerService(四)APK安装流程
《日志系统篇》
Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性
Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化
Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析
Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现
《Binder通信原理》:
Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
Android10.0 Binder通信原理(二)-Binder入门篇
Android10.0 Binder通信原理(三)-ServiceManager篇
Android10.0 Binder通信原理(四)-Native-C\C++实例分析
Android10.0 Binder通信原理(五)-Binder驱动分析
Android10.0 Binder通信原理(六)-Binder数据如何完成定向打击
Android10.0 Binder通信原理(七)-Framework binder示例
Android10.0 Binder通信原理(八)-Framework层分析
Android10.0 Binder通信原理(九)-AIDL Binder示例
Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub设计模式
Android10.0 Binder通信原理(十一)-Binder总结
《HwBinder通信原理》
HwBinder入门篇-Android10.0 HwBinder通信原理(一)
HIDL详解-Android10.0 HwBinder通信原理(二)
HIDL示例-C++服务创建Client验证-Android10.0 HwBinder通信原理(三)
HIDL示例-JAVA服务创建-Client验证-Android10.0 HwBinder通信原理(四)
HwServiceManager篇-Android10.0 HwBinder通信原理(五)
Native层HIDL服务的注册原理-Android10.0 HwBinder通信原理(六)
Native层HIDL服务的获取原理-Android10.0 HwBinder通信原理(七)
JAVA层HIDL服务的注册原理-Android10.0 HwBinder通信原理(八)
JAVA层HIDL服务的获取原理-Android10.0 HwBinder通信原理(九)
HwBinder驱动篇-Android10.0 HwBinder通信原理(十)
HwBinder原理总结-Android10.0 HwBinder通信原理(十一)
《编译原理》
编译系统入门篇-Android10.0编译系统(一)
编译环境初始化-Android10.0编译系统(二)
make编译过程-Android10.0编译系统(三)
Image打包流程-Android10.0编译系统(四)
Kati详解-Android10.0编译系统(五)
Blueprint简介-Android10.0编译系统(六)
Blueprint代码详细分析-Android10.0编译系统(七)
Android.bp 语法浅析-Android10.0编译系统(八)
Ninja简介-Android10.0编译系统(九)
Ninja提升编译速度的方法-Android10.0编译系统(十)
Android10.0编译系统(十一)
1 概述
上一节针对Android编译系统做了一个笼统的说明,这一节针对编译环境初始化做一下详细的展示。
2 编译环境初始化
初始化命令:
source build/envsetup.sh
envsetup.sh 主要做了下面几个事情:
envsetup.sh 构建代码:
...validate_current_shellsource_vendorsetupaddcompletions
2.1 hmm 查看支持接口
输入hmm可以看到envsetup支持的一些接口
2.2 validate_current_shell
确定当前的shell环境,建立shell命令
function validate_current_shell() {local current_sh="$(ps -o command -p $$)"case "$current_sh" in*bash*)function check_type() { type -t "$1"; };;*zsh*)function check_type() { type "$1"; }enable_zsh_completion ;;*)echo -e "WARNING: Only bash and zsh are supported.\nUse of other shell would lead to erroneous results.";;esac
}
2.3 source_vendorsetup
从device\vendor\product等目录遍历搜索vendorsetup.sh,并source进来
function source_vendorsetup() {allowed=for f in $(find -L device vendor product -maxdepth 4 -name 'allowed-vendorsetup_sh-files' 2>/dev/null | sort); doif [ -n "$allowed" ]; thenecho "More than one 'allowed_vendorsetup_sh-files' file found, not including any vendorsetup.sh files:"echo " $allowed"echo " $f"returnfiallowed="$f"doneallowed_files=[ -n "$allowed" ] && allowed_files=$(cat "$allowed")for dir in device vendor product; dofor f in $(test -d $dir && \find -L $dir -maxdepth 4 -name 'vendorsetup.sh' 2>/dev/null | sort); doif [[ -z "$allowed" || "$allowed_files" =~ $f ]]; thenecho "including $f"; . "$f"elseecho "ignoring $f, not in $allowed"fidonedone
}
例:
1.建立一个目录:/vendor/ingres/build
2.创建一个vendorsetup.sh
写一个log: echo "vendor build test."
3.执行source build/envsetup.sh
source后打印:
including vendor/ingres/build/vendorsetup.sh
vendor build test.
2.4 addcompletions
function addcompletions()
{local T dir f# Keep us from trying to run in something that's neither bash nor zsh.# 检测shell版本字符串BASH_VERSION 或ZSH_VERSION长度为0时,返回if [ -z "$BASH_VERSION" -a -z "$ZSH_VERSION" ]; thenreturnfi# Keep us from trying to run in bash that's too old.# 检测bash主版本低于3时返回if [ -n "$BASH_VERSION" -a ${BASH_VERSINFO[0]} -lt 3 ]; thenreturnfi# 指定bash文件目录并检查是否存在local completion_files=(system/core/adb/adb.bashsystem/core/fastboot/fastboot.bashtools/asuite/asuite.sh)# Completion can be disabled selectively to allow users to use non-standard completion.# e.g.# ENVSETUP_NO_COMPLETION=adb # -> disable adb completion# ENVSETUP_NO_COMPLETION=adb:bit # -> disable adb and bit completion#*.bash文件列表,并将这些*.bash文件包含进来for f in ${completion_files[*]}; doif [ -f "$f" ] && should_add_completion "$f"; then# 对*.bash文件执行'.'操作. $ffidoneif should_add_completion bit ; thencomplete -C "bit --tab" bitfiif [ -z "$ZSH_VERSION" ]; then# Doesn't work in zsh.complete -o nospace -F _croot crootficomplete -F _lunch lunch # _lunch命令提供lunch命令的补全操作complete -F _complete_android_module_names gomodcomplete -F _complete_android_module_names m
}
3. lunch aosp_arm_eng
3.1 lunch说明
环境变量初始化完成后,我们需要选择一个编译目标。lunch 主要作用是根据用户输入或者选择的产品名来设置与具体产品相关的环境变量。
如果你不知道想要编译的目标是什么,直接执行一个lunch命令,会列出所有的目标,直接回车,会默认使用aosp_arm-eng这个目标。
执行命令:lunch 1, 可以看到配置的一些环境变量
这些环境变量的含义如下:
lunch aosp_arm-eng 结束后,后创建一个out文件夹,生成一些中间文件如下图所示:
3.2 lunch()
lunch命令用来设置 TARGET_PRODUCT、TARGET_BUILD_VARIANT、TARGET_PLATFORM_VERSION、TARGET_BUILD_TYPE、TARGET_BUILD_APPS等环境变量
lunch操作流程如下:
1.获取lunch操作的参数,如果参数不为空,参数则为指定要编译的设备型号和编译类型;如果参数为空,会调用print_lunch_menu来显示Lunch菜单项,读取用户的输入,存入answer
2.如果answer为空,即之前在lunch菜单用,用户只敲了一个回车。会将默认选项改为aosp_arm-eng,结果存入selection
3.如果lunch操作得到的输入是数字,则将数字转换为LUNCH_MENU_CHOICES中的字符串,结果存入selection
4.解析selection的值,得到product = aosp_arm 和variant = eng, 把他们分别保存到TARGET_PRODUCT 和 TARGET_BUILD_VARIANT 中
5.根据前面的设置,调用build_build_var_cache 来更新编译环境相关变量
6.export 编译选项TARGET_PRODUCT, TARGET_BUILD_VARIANT和TARGET_BUILD_TYPE三元组
7.调用set_stuff_for_environment 来设置其他环境变量,如PROMPT_COMMAND,编译toolchain和tools相关的路径等
8.调用printconfig 来输出当前的设置选项
function lunch()
{local answer# 获取lunch操作的参数if [ "$1" ] ; thenanswer=$1else# lunch操作不带参数,则先显示lunch menu,然后读取用户输入print_lunch_menuecho -n "Which would you like? [aosp_arm-eng] "read answerfilocal selection=# lunch操作得到的结果为空(例如用户直接在lunch要求输入时回车的情况)# 则将选项默认为"aosp_arm-eng"if [ -z "$answer" ]thenselection=aosp_arm-eng# lunch操作得到的输入是数字,则将数字转换为LUNCH_MENU_CHOICES中的字符串elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")thenlocal choices=($(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES))if [ $answer -le ${#choices[@]} ]then# array in zsh starts from 1 instead of 0.if [ -n "$ZSH_VERSION" ]thenselection=${choices[$(($answer))]}elseselection=${choices[$(($answer-1))]}fifielseselection=$answerfiexport TARGET_BUILD_APPS=local product variant_and_version variant versionproduct=${selection%%-*} # Trim everything after first dashvariant_and_version=${selection#*-} # Trim everything up to first dashif [ "$variant_and_version" != "$selection" ]; thenvariant=${variant_and_version%%-*}if [ "$variant" != "$variant_and_version" ]; thenversion=${variant_and_version#*-}fifiif [ -z "$product" ]thenechoecho "Invalid lunch combo: $selection"return 1fi# 设置TARGET_PRODUCT和TARGET_BUILD_VARIANTTARGET_PRODUCT=$product \TARGET_BUILD_VARIANT=$variant \TARGET_PLATFORM_VERSION=$version \# 根据前面的设置,更新编译环境相关变量build_build_var_cache #参考[3.1.1]if [ $? -ne 0 ]thenreturn 1fi# export 编译选项TARGET_PRODUCT, TARGET_BUILD_VARIANT和TARGET_BUILD_TYPE三元组export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT)export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT)if [ -n "$version" ]; thenexport TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION)elseunset TARGET_PLATFORM_VERSIONfiexport TARGET_BUILD_TYPE=releaseechoset_stuff_for_environment # 设置其他环境变量,如PROMPT_COMMAND,编译toolchain和tools相关的路径等printconfig # 输出当前的设置选项destroy_build_var_cache
}
3.1.1 build_build_var_cache()
根据前面的设置,更新编译环境相关变量
主要通过执行 "build/soong/soong_ui.bash --dumpvars-mode" 完成
最终执行的是 "./out/soog_ui --dumpvars-mode"
function build_build_var_cache()
{local T=$(gettop)# Grep out the variable names from the script.cached_vars=(`cat $T/build/envsetup.sh | tr '()' ' ' | awk '{for(i=1;i<=NF;i++) if($i~/get_build_var/) print $(i+1)}' | sort -u | tr '\n' ' '`)cached_abs_vars=(`cat $T/build/envsetup.sh | tr '()' ' ' | awk '{for(i=1;i<=NF;i++) if($i~/get_abs_build_var/) print $(i+1)}' | sort -u | tr '\n' ' '`)# Call the build system to dump the "<val>=<value>" pairs as a shell script.build_dicts_script=`\builtin cd $T; build/soong/soong_ui.bash --dumpvars-mode \--vars="${cached_vars[*]}" \--abs-vars="${cached_abs_vars[*]}" \--var-prefix=var_cache_ \--abs-var-prefix=abs_var_cache_`local ret=$?if [ $ret -ne 0 ]thenunset build_dicts_scriptreturn $retfi# Execute the script to store the "<val>=<value>" pairs as shell variables.eval "$build_dicts_script"ret=$?unset build_dicts_scriptif [ $ret -ne 0 ]thenreturn $retfiBUILD_VAR_CACHE_READY="true"
}
soong_ui 由build/soong/cmd/soong_ui/main.go编译生成
[build/soong/cmd/soong_ui/main.go]
func main() {
...if os.Args[1] == "--dumpvar-mode" {dumpVar(buildCtx, config, os.Args[2:])} else if os.Args[1] == "--dumpvars-mode" {dumpVars(buildCtx, config, os.Args[2:])} else {...}
...
}
[build/soong/cmd/soong_ui/main.go]
func dumpVars(ctx build.Context, config build.Config, args []string) {varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
}
最后调用到了ckati执行-f build/make/core/config.mk
[/build/soong/ui/build/dumpvars.go]
func dumpMakeVars(ctx Context, config Config, goals, vars []string, write_soong_vars bool) (map[string]string, error) {ctx.BeginTrace(metrics.RunKati, "dumpvars")defer ctx.EndTrace()cmd := Command(ctx, config, "dumpvars",config.PrebuiltBuildTool("ckati"),"-f", "build/make/core/config.mk","--color_warnings","--kati_stats","dump-many-vars","MAKECMDGOALS="+strings.Join(goals, " "))cmd.Environment.Set("CALLED_FROM_SETUP", "true")if write_soong_vars {cmd.Environment.Set("WRITE_SOONG_VARIABLES", "true")}cmd.Environment.Set("DUMP_MANY_VARS", strings.Join(vars, " "))cmd.Sandbox = dumpvarsSandboxoutput := bytes.Buffer{}cmd.Stdout = &outputpipe, err := cmd.StderrPipe()if err != nil {ctx.Fatalln("Error getting output pipe for ckati:", err)}cmd.StartOrFatal()// TODO: error out when Stderr contains any contentstatus.KatiReader(ctx.Status.StartTool(), pipe)cmd.WaitOrFatal()ret := make(map[string]string, len(vars))...return ret, nil
}
下面我们单独研究一下config.mk。
4.config.mk
说明:config.mk首先加载了build/make/common 中的core.mk、math.mk、strings.mk、json.mk 用来配置一些shell环境、math函数、string和json的一些支持函数。最主要的操作还是加载build/make/core中的envsetup.mk和dumpvar.mk
...#配置两个目录的变量,供之后的mk使用
BUILD_SYSTEM :=$= build/make/core
BUILD_SYSTEM_COMMON :=$= build/make/common#加载core.mk, 只使用ANDROID_BUILD_SHELL来包装bash。
include $(BUILD_SYSTEM_COMMON)/core.mk#设置make中使用的有效数学函数。
include $(BUILD_SYSTEM_COMMON)/math.mkinclude $(BUILD_SYSTEM_COMMON)/strings.mkinclude $(BUILD_SYSTEM_COMMON)/json.mk# 避免硬件解码路径被覆盖的调用pathmap.mk建立硬解映射
include $(BUILD_SYSTEM)/pathmap.mk# 允许项目定义自己的全局可用变量
include $(BUILD_SYSTEM)/project_definitions.mk# ###############################################################
# Build system internal files
# ###############################################################
# 构建系统内部文件(写Android.mk时会调用include头文件,也就是这些makefile文件)BUILD_COMBOS:= $(BUILD_SYSTEM)/comboCLEAR_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_NOTICE_FILE := $(BUILD_SYSTEM)/notice_files.mk
BUILD_HOST_DALVIK_JAVA_LIBRARY := $(BUILD_SYSTEM)/host_dalvik_java_library.mk
BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY := $(BUILD_SYSTEM)/host_dalvik_static_java_library.mkBUILD_HOST_TEST_CONFIG := $(BUILD_SYSTEM)/host_test_config.mk
BUILD_TARGET_TEST_CONFIG := $(BUILD_SYSTEM)/target_test_config.mk#定义大多数全局变量。这些是特定于用户的构建配置的。
include $(BUILD_SYSTEM)/envsetup.mk#构建系统为在哪里找到内核公开了几个变量
#(1)TARGET_DEVICE_KERNEL_HEADERS是为当前正在构建的设备自动创建的。
#它被设置为$(TARGET_DEVICE_DIR)/kernel headers,
#例如DEVICE/samsung/tuna/kernel headers。此目录不是由任何人显式设置的,生成系统总是添加此子目录。
TARGET_DEVICE_KERNEL_HEADERS := $(strip $(wildcard $(TARGET_DEVICE_DIR)/kernel-headers))#(2)TARGET_BOARD_KERNEL_HEADERS由BoardConfig.mk允许包含其他目录的文件。
#如果有一些常见的地方为一组设备保留了一些报头,那么这很有用。
#例如,device/<vendor>/common/kernel头可以包含一些<vendor>设备的头。
TARGET_BOARD_KERNEL_HEADERS := $(strip $(wildcard $(TARGET_BOARD_KERNEL_HEADERS)))
TARGET_BOARD_KERNEL_HEADERS := $(patsubst %/,%,$(TARGET_BOARD_KERNEL_HEADERS))
$(call validate-kernel-headers,$(TARGET_BOARD_KERNEL_HEADERS))#(3)TARGET_PRODUCT_KERNEL_头由产品继承图生成。
#这允许体系结构产品为使用该体系结构的设备提供报头。
TARGET_PRODUCT_KERNEL_HEADERS := $(strip $(wildcard $(PRODUCT_VENDOR_KERNEL_HEADERS)))
TARGET_PRODUCT_KERNEL_HEADERS := $(patsubst %/,%,$(TARGET_PRODUCT_KERNEL_HEADERS))
$(call validate-kernel-headers,$(TARGET_PRODUCT_KERNEL_HEADERS))# 选择一个Java编译器
include $(BUILD_SYSTEM)/combo/javac.mk# A list of SEPolicy versions, besides PLATFORM_SEPOLICY_VERSION, that the framework supports.
#框架支持的SEPolicy版本列表,除了PLATFORM_SEPOLICY_VERSION
PLATFORM_SEPOLICY_COMPAT_VERSIONS := \26.0 \27.0 \28.0 \ifeq ($(CALLED_FROM_SETUP),true)
include $(BUILD_SYSTEM)/ninja_config.mk
include $(BUILD_SYSTEM)/soong_config.mk
endif#加载dumpvar.mk,用来生成make目标
include $(BUILD_SYSTEM)/dumpvar.mk
4.1 build/make/core/envsetup.mk
envsetup.mk 主要加载了product_config.mk和board_config.mk,用来得到TARGET_DEVICE和其他变量。
...
#设置host和target编译链相关的变量
include $(BUILD_SYSTEM)/combo/select.mk
#(1)阅读产品规格,这样我们就可以得到TARGET_DEVICE和其他变量,我们需要找到输出文件
include $(BUILD_SYSTEM)/product_config.mkinclude $(BUILD_SYSTEM)/board_config.mk
...
4.2 build/make/core/product_config.mk
阅读产品规格,这样我们就可以得到TARGET_DEVICE和其他变量,我们需要找到输出文件。
...
# ---------------------------------------------------------------
# Include the product definitions.
# We need to do this to translate TARGET_PRODUCT into its
# underlying TARGET_DEVICE before we start defining any rules.
#
include $(BUILD_SYSTEM)/node_fns.mk
include $(BUILD_SYSTEM)/product.mk
include $(BUILD_SYSTEM)/device.mk...#############################################################################
# Sanity check and assign default values
TARGET_DEVICE := $(PRODUCT_DEVICE)
...
4.3 build/make/core/board_config.mk
板级可以在$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)下定义,也可以在vendor/*/$(TARGET_DEVICE)下定义。
在这两个地方搜索,但要确保只存在一个。真正的板级应始终与OEM vendor相关联。
...
# Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)
# or under vendor/*/$(TARGET_DEVICE). Search in both places, but
# make sure only one exists.
# Real boards should always be associated with an OEM vendor.
ifdef TARGET_DEVICE_DIRifneq ($(origin TARGET_DEVICE_DIR),command line)$(error TARGET_DEVICE_DIR may not be set manually)endifboard_config_mk := $(TARGET_DEVICE_DIR)/BoardConfig.mk
elseboard_config_mk := \$(strip $(sort $(wildcard \$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \$(shell test -d device && find -L device -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \$(shell test -d vendor && find -L vendor -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \)))ifeq ($(board_config_mk),)$(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))endififneq ($(words $(board_config_mk)),1)$(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))endifTARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk))).KATI_READONLY := TARGET_DEVICE_DIR
endif
include $(board_config_mk)
...
5.总结
至此,envsetup.sh 和lunch()的初始化流程基本上理清了,主要就是加载了环境变量,并选择了编译目标,后面只要执行一下make就能够进行启动编译,下一节让我们一起看看敲下make后到底发生了什么。
这篇关于编译环境初始化-Android10.0编译系统(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!