Google已经给出了android的原生编译流程:source build/envsetup.sh加载命令初始化环境、lunch选择编译目标,进行产品特定配置初始化,再进行编译。
关于Android如何查找特定product配置信息,还看需要从source build/envsetup.sh看起。source build/envsetup.sh 文件是为了将相关envsetup.sh文件中的环境变量和函数导出到shell终端环境,即初始化编译环境,为了下一步编译做铺垫。
T=$(_gettop_once)
if [ ! "$T" ]; then
echo "Couldn't locate the top of the tree. Always source build/envsetup.sh from the root of the tree." >&2
return 1
fi #确认编译根目录
validate_current_shell
set_global_paths
source_vendorsetup
addcompletions
从build/envsetup.sh的执行代码段可以看出,该脚本主要执行的四个方面的事情:
function validate_current_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
}
1)如果是bash环境没有问题,
2)对于 zsh 环境需要特殊处理,通过enable_zsh_completion函数自动加载 bashcompinit 来支持 bash 风格的补全。
function enable_zsh_completion() {
# Don't override user's options if bash-style completion is already enabled.
if ! declare -f complete >/dev/null; then
autoload -U compinit && compinit
autoload -U bashcompinit && bashcompinit
fi
}
3)如果是其他环境会warning。
通过function set_global_paths() 设置,主要是一些跟lunch特定target无关的一些全局路径添加到ANDROID_GLOBAL_BUILD_PATHS 变量中,再export给环境变量PATH。下次运行时,会将ANDROID_GLOBAL_BUILD_PATHS 从 PATH 中删除。 以便可以多次获取 envsetup.sh 并且仍然具有工作路径。
而lunch特定target的特定全局路径,则配置到set_lunch_paths函数。
function set_global_paths()
{
local T=$(gettop)
if [ ! "$T" ]; then
echo "Couldn't locate the top of the tree. Try setting TOP."
return
fi
if [ -n "$ANDROID_GLOBAL_BUILD_PATHS" ] ; then
export PATH=${PATH/$ANDROID_GLOBAL_BUILD_PATHS/}
fi
# And in with the new...
ANDROID_GLOBAL_BUILD_PATHS=$T/build/bazel/bin #配置了bazel的命令路径
ANDROID_GLOBAL_BUILD_PATHS+=:$T/development/scripts # 配置了给应用开发脚本路径,如一些进程异常堆栈信息stack和gdb调试使用工具gdbclient.py的路径
# https://source.android.com/docs/core/tests/debug/gdb?hl=zh-cn
ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/devtools/tools # 配置了prebuilts目录下开发工具路径
# add kernel specific binaries
if [ $(uname -s) = Linux ] ; then #uname -s命令用于显示操作系统名称
ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/misc/linux-x86/dtc # 配置了dtc路径
ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/misc/linux-x86/libufdt # 配置了libufdt 路径
fi
# If prebuilts/android-emulator// exists, prepend it to our PATH
# to ensure that the corresponding 'emulator' binaries are used.
case $(uname -s) in #基于不同的系统设置ANDROID_EMULATOR_PREBUILTS变量
Darwin)
ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/darwin-x86_64
;;
Linux)
ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/linux-x86_64
;;
*)
ANDROID_EMULATOR_PREBUILTS=
;;
esac
if [ -n "$ANDROID_EMULATOR_PREBUILTS" -a -d "$ANDROID_EMULATOR_PREBUILTS" ]; then
ANDROID_GLOBAL_BUILD_PATHS+=:$ANDROID_EMULATOR_PREBUILTS
# 如果ANDROID_EMULATOR_PREBUILTS变量指定的目录存在,则将其添加到Android编译相关的路径变量中
# 此处配置了适合linux操作系统使用的仿真器工具linux-x86_64路径
export ANDROID_EMULATOR_PREBUILTS
fi
# Finally, set PATH
export PATH=$ANDROID_GLOBAL_BUILD_PATHS:$PATH
}
通过source相关的设置脚本。如下详解(一)
特别是一些bash命令的补全。如下详解(二)
文件包含了原始的Andorid 环境配置文件,除此之外,使用了函数source_vendorsetup同时查找vendor,device,prodcut三个目录4层范围的vendorenvsetup.sh文件,找到之后,先判断 allowed 是否为空,或者 allowed_files 和 $f 正则匹配,然后运行'. "$T/$f" '去 source $f,以包含这些文件内配置的环境变量。
2082 function source_vendorsetup() {
2083 unset VENDOR_PYTHONPATH
2084 local T="$(gettop)"
2085 allowed=
2086 for f in $(cd "$T" && find -L device vendor product -maxdepth 4 -name 'allowed-vendorsetup_sh-files' 2>/dev/null | sort); do
2087 if [ -n "$allowed" ]; then
2088 echo "More than one 'allowed_vendorsetup_sh-files' file found, not including any vendorsetup.sh files:"
2089 echo " $allowed"
2090 echo " $f"
2091 return
2092 fi
2093 allowed="$T/$f"
2094 done
2095
2096 allowed_files=
2097 [ -n "$allowed" ] && allowed_files=$(cat "$allowed")
2098 for dir in device vendor product motorola/build; do #可以添加product厂商vendorsetup.sh对应的目录
2099 for f in $(cd "$T" && test -d $dir && \
2100 find -L $dir -maxdepth 4 -name 'vendorsetup.sh' 2>/dev/null | sort); do
2101
2102 if [[ -z "$allowed" || "$allowed_files" =~ $f ]]; then
2103 echo "including $f"; . "$T/$f"
2104 else
2105 echo "ignoring $f, not in $allowed"
2106 fi
2107 done
2108 done
2109 }
此处,如果环境变量需要修改,此处可以添加vendorsetup.sh对应的目录到代码里面。可以在product后面指定了厂商(chipet或者product)环境的路径,或者在指定目录(device、product、vendor)下添加vendorsetup.sh文件,去添加厂商自己的环境。运行结果:
including device/qcom/common/cuttlestone/vendorsetup.sh
including vendor/qcom/nonhlos/build_support/vendorsetup.sh
including vendor/qcom/opensource/core-utils/vendorsetup.sh
including vendor/qcom/proprietary/common/vendorsetup.sh
including vendor/qcom/proprietary/prebuilt_grease/vendorsetup.sh
including */build/vendorsetup.sh
Initializing the Official * Linux x64 Build Toolchain Setup.
到此为止,配置的都是一些简单环境变量。
这里你可以随意添加任意厂商独有的vendorsetup.sh文件,比如说设置一些厂商专属的common 定义和变量,比如说源码路径,工具路径,脚本路径,链接地址或其他环境变量(jave_home...), for your will!
vendorsetup.sh还包含了一些命令补全的功能都在addcompletions这个函数里面。我们在Linux下都用shell的TAB自动补全功能,非常方便,有时Android开发程序,可能需要自定义实现TAB自动补全功能,这时可能就会用到complete命令来实现对命令的自定义补全,比如针对 lunch这个命令 使用 _lunch 这个函数进行补全,gomod, m 也是这样。
此处还导入了一些Android自身代码里面包含的bash补充脚本。
function addcompletions()
{
local f=
# Keep us from trying to run in something that's neither bash nor zsh.
if [ -z "$BASH_VERSION" -a -z "$ZSH_VERSION" ]; then
return #需要提前指定这两个环境变量,如果没有指定,该函数return
fi #检测 bash 或 zsh 版本以避免不在 bash 或 zsh 的环境中运行
# 避免运行在太老的版本中,这里是小于 3 的版本
if [ -n "$BASH_VERSION" -a ${BASH_VERSINFO[0]} -lt 3 ]; then
return
fi
local completion_files=(
packages/modules/adb/adb.bash
system/core/fastboot/fastboot.bash
tools/asuite/asuite.sh
prebuilts/bazel/common/bazel-complete.bash
)
# Completion can be disabled selectively to allow users to use non-standard completion.支持禁用补全命令,使用方法如下例子,配置对应的命令到变量ENVSETUP_NO_COMPLETION
# e.g.
# ENVSETUP_NO_COMPLETION=adb # -> disable adb completion
# ENVSETUP_NO_COMPLETION=adb:bit # -> disable adb and bit completion
local T=$(gettop)
# 确认上面几个脚本是否存在,通过should_add_completion确认该脚本是否在白名单中
for f in ${completion_files[*]}; do
f="$T/$f"
if [ ! -f "$f" ]; then
echo "Warning: completion file $f not found"
elif should_add_completion "$f"; then
. $f #只有在不符合ENVSETUP_NO_COMPLETION配置的禁用命令时,才运行文件
fi
done
if should_add_completion bit ; then
complete -C "bit --tab" bit
fi
if [ -z "$ZSH_VERSION" ]; then
# Doesn't work in zsh.
complete -o nospace -F _croot croot
# TODO(b/244559459): Support b autocompletion for zsh
complete -F _bazel__complete -o nospace b
fi
complete -F _lunch lunch
complete -F _complete_android_module_names pathmod
complete -F _complete_android_module_names gomod
complete -F _complete_android_module_names outmod
complete -F _complete_android_module_names installmod
complete -F _complete_android_module_names bmod
complete -F _complete_android_module_names m
}
function should_add_completion():禁用补全命令判断函数
# Takes a command name, and check if it's in ENVSETUP_NO_COMPLETION or not.
function should_add_completion() {
local cmd="$(basename $1| sed 's/_completion//' |sed 's/\.\(.*\)*sh$//')"
case :"$ENVSETUP_NO_COMPLETION": in
*:"$cmd":*)
return 1
;;
esac
return 0
}
function _complete_android_module_names() {
local word=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(allmod | grep -E "^$word") )
}
这里有个特别重要的补全命令:lunch
$ lunch
aosp_coral-userdebug aosp_flame-userdebug aosp_x86-eng
除了变量定义和一些补全函数,vendorsetup.sh还定义了几个重要的函数,用于后续编译。
hmm函数是打印envsetup.sh部分函数帮助信息的函数,除此之外外,还会打印envsetup.sh文件里面所有函数:
1 function hmm() {
2 cat <-
8 Selects as the product to build, and as the variant to build, and stores those selections in the environment to be read by subsequent
10 invocations of 'm' etc.
....
52 EOF
53 local T=$(gettop)
54 local A=""
55 local i
56 for i in `cat $T/build/envsetup.sh | sed -n "/^[[:blank:]]*function /s/function \([a-z_]*\).*/\1/p" | sort | uniq`; do
57 A="$A $i"
58 done
59 echo $A ##列出了脚本内所有的函数名
60 }
打印当前编译配置
126 function get_build_var()
127 {
128 if [ "$BUILD_VAR_CACHE_READY" = "true" ]
129 then
130 eval "echo \"\${var_cache_$1}\""
131 return 0
132 fi
133
134 local T=$(gettop)
135 if [ ! "$T" ]; then
136 echo "Couldn't locate the top of the tree. Try setting TOP." >&2
137 return 1
138 fi
139 (\cd $T; build/soong/soong_ui.bash --dumpvar-mode $1)
140 }
141
348 function printconfig()
349 {
350 local T=$(gettop)
351 if [ ! "$T" ]; then
352 echo "Couldn't locate the top of the tree. Try setting TOP." >&2
353 return
354 fi
355 get_build_var report_config
356 }
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=11
TARGET_PRODUCT=aosp_arm64
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm64
HOST_ARCH=x86
HOST_OS=Linux
HOST_BUILD_TYPE=release
BUILD_ID=OPENMASTER
OUT_DIR=out
============================================
android 系统编译之前的核心,根据输入的参数设置环境变量。将lunch函数命令关联_lunch函数,_lunch为lunch的补全函数,也就是说当关联之后,在终端输入lunch命令后,按tab键可以进行参数的补全。当执行lunch命令时,按下tab键一次,则执行_lunch函数一次,_lunch函数提供给我们可供选择的lunch选项(显示出匹配的lunch-combo)。lunch命令设置环境变量TARGET_PRODUCT,TARGET_BUILD_VARIANT,TARGET_SIMULATOR,TARGET_BUILD_TYPE,随后调用set_stuff_for_environment设置,并printconfig显示。不给参数时,将提示用户选择。
Usage: lunch [
1)判断lunch 后面是否有跟随参数?没有的话 调用print_lunch_menu,可以选择lunch的对应target。
2)根据用户选择,确定 build variant和build product,将对应的值赋值给TARGET_PRODUCT,TARGET_BUILD_VARIANT
3)调用build_build_var_cache 函数,
4)set_stuff_for_environment 设置其他环境变量
5)调用printconfig 将 lunch配置信息打印出来
6)调用destroy_build_var_cache 清除build_build_var_cache 创建的变量键值对;
lunch函数解析如下:
642 function lunch()
643 {
644 local answer
645 # 定义局部变量answer来存放`lunch`命令传进来的参数(即lunch-combo,可以是字符串或数字)
646 if [[ $# -gt 1 ]]; then
647 echo "usage: lunch [target]" >&2
648 return 1
649 fi
651 if [ "$1" ]; then
652 answer=$1
653 else
654 print_lunch_menu
655 echo -n "Which would you like? [aosp_arm-eng] "
656 read answer
657 fi # 如果用户没有没有传进来任何值,则打印出`lunch-combo`列表,提示用户输入
658
659 local selection= # 定义局部变量`selection`,用来存放`lunch-combo`字符串
661 if [ -z "$answer" ]
662 then
663 selection=aosp_arm-eng # 如果`answer`为空,则把`selection`设置为默认的`aosp_arm-eng`
664 elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
665 then
666 local choices=($(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES))
667 if [ $answer -le ${#choices[@]} ]
668 then
669 # array in zsh starts from 1 instead of 0.
670 if [ -n "$ZSH_VERSION" ]
671 then
672 selection=${choices[$(($answer))]}
673 else
674 selection=${choices[$(($answer-1))]}
675 fi
676 fi
677 else # 如果answer是数字,则把`selection`设置为对应的`lunch-combo`字符串
678 selection=$answer
679 fi #如果`answer`是字符串,则把`selection`赋值为`answer`
680
681 export TARGET_BUILD_APPS=
683 local product variant_and_version variant version
684 product=${selection%%-*} # Trim everything after first dash
685 variant_and_version=${selection#*-} # Trim everything up to first dash
686 if [ "$variant_and_version" != "$selection" ]; then
687 variant=${variant_and_version%%-*}
688 if [ "$variant" != "$variant_and_version" ]; then
689 version=${variant_and_version#*-}
690 fi
691 fi #variant获得编译变体类型:"user/userdebug/eng"
692
693 if [ -z "$product" ]
694 then
695 echo
696 echo "Invalid lunch combo: $selection"
697 return 1
698 fi #将selection去掉后缀"user/userdebug/eng"赋值给product,为空报错。
699
700 TARGET_PRODUCT=$product \
701 TARGET_BUILD_VARIANT=$variant \
702 TARGET_PLATFORM_VERSION=$version \
703 build_build_var_cache
704 if [ $? -ne 0 ]
705 then
706 return 1
707 fi
708 export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT)
709 export TARGET_BOARD_PLATFORM=$(get_build_var TARGET_BOARD_PLATFORM)
710 export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT)
711 if [ -n "$version" ]; then
712 export TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION)
713 else
714 unset TARGET_PLATFORM_VERSION
715 fi
716 export TARGET_BUILD_TYPE=release
if [ $used_lunch_menu -eq 1 ]; then
echo
echo "Hint: next time you can simply run 'lunch $selection'"
fi
717
718 [[ -n "${ANDROID_QUIET_BUILD:-}" ]] || echo
720 set_stuff_for_environment
721 [[ -n "${ANDROID_QUIET_BUILD:-}" ]] || printconfig
722 destroy_build_var_cache
if [[ -n "${CHECK_MU_CONFIG:-}" ]]; then
check_mu_config
fi
723 }
724
725 unset COMMON_LUNCH_CHOICES_CACHE
726 # Tab completion for lunch.
727 function _lunch()
728 {
729 local cur prev opts
730 COMPREPLY=()
731 cur="${COMP_WORDS[COMP_CWORD]}"
732 prev="${COMP_WORDS[COMP_CWORD-1]}"
733
734 if [ -z "$COMMON_LUNCH_CHOICES_CACHE" ]; then
735 COMMON_LUNCH_CHOICES_CACHE=$(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES)
736 fi
737
738 COMPREPLY=( $(compgen -W "${COMMON_LUNCH_CHOICES_CACHE}" -- ${cur}) )
739 return 0
740 }
COMMON_LUNCH_CHOICES参数,通过get_build_var函数来获取值。
126 function get_build_var()
127 {
128 if [ "$BUILD_VAR_CACHE_READY" = "true" ]
129 then
130 eval "echo \"\${var_cache_$1}\""
131 return 0
132 fi
133
134 local T=$(gettop)
135 if [ ! "$T" ]; then
136 echo "Couldn't locate the top of the tree. Try setting TOP." >&2
137 return 1
138 fi
139 (\cd $T; build/soong/soong_ui.bash --dumpvar-mode $1)
140 }
function add_lunch_combo()
将提供的编译选项参数添加到LUNCH_MENU_CHOICES列表中。向环境变量LUNCH_MENU_CHOICES标识的列表中添加项。envsetup.sh中默认添加了full-eng,full_x86-eng,和simulator。
读取LUNCH_MENU_CHOICES列表,打印列出可执行lunch的选项LUNCH_MENU_CHOICES。该函数调用get_build_var,并传递参数COMMON_LUNCH_CHOICES来获取当前所有平台,COMMON_LUNCH_CHOICES被定义在AndroidProducts.mk中。
607 function print_lunch_menu()
608 {
609 local uname=$(uname)
610 local choices
611 choices=$(TARGET_BUILD_APPS= TARGET_PRODUCT= TARGET_BUILD_VARIANT= get_build_var COMMON_LUNCH_CHOICES 2>/dev/null)
612 local ret=$?
613
614 echo
615 echo "You're building on" $uname
616 echo
617
618 if [ $ret -ne 0 ]
619 then
620 echo "Warning: Cannot display lunch menu."
621 echo
622 echo "Note: You can invoke lunch with an explicit target:"
623 echo
624 echo " usage: lunch [target]" >&2
625 echo
626 return
627 fi
628
629 echo "Lunch menu... pick a combo:"
630
631 local i=1
632 local choice
633 for choice in $(echo $choices)
634 do
635 echo " $i. $choice"
636 i=$(($i+1))
637 done
638
639 echo
640 }
641
在Andorid 8.0之后,编译系统对makefile做的一层封装执行make命令后,实际调用envsetup.sh脚本里面的make函数。
1720 function make()
1721 {
1722 _wrap_build $(get_make_command "$@") "$@"
1723 }
get_make_command函数会先检测 soong_ui.bash 文件是否存在,去触发make的不同调用机制,由原来的make替换为soong_ui.bash --make-mode,通过参数配置实现make的间接调用以进行编译。如果soong_ui.bash 文件不存在,直接调用系统的make命令进行编译。
目前,最新的Android代码构建,是执行 build/soong/soong_ui.bash --make-mode。
所以,get_make_command决定了make命令执行方式,函数定义如下:
1626 function get_make_command()
1627 {
1628 # If we're in the top of an Android tree, use soong_ui.bash instead of make
1629 if [ -f build/soong/soong_ui.bash ]; then
1630 # Always use the real make if -C is passed in
1631 for arg in "$@"; do
1632 if [[ $arg == -C* ]]; then
1633 echo command make
1634 return
1635 fi
1636 done
1637 echo build/soong/soong_ui.bash --make-mode
1638 else
1639 echo command make
1640 fi
1641 }
这几个函数实际上都是执行make操作,区别如下:
m:不管当前路径如何,编译所有的模块,也可以指定module,相当于在Android代码树的根目录下执行make操作
都是需要调用的_trigger_build()函数,该函数主要调用 $TOP/build/soong/soong_ui.bash 脚本进行编译,然后通过 _wrap_build 函数对结果进行特殊显示。
mm:查找当前目录下的Android.mk文件,若找到,编译当前目录下的所有模块,当前目录下需要有Androd.mk,否则会往上一级找,只编译当前模块,不会编译依赖模块;
mmm:需要指定路径,并且在指定的路径下查找Android.mk文件,找到后编译该路径下所有模块
mma:与mm类似,但会将有依赖的模块一起编译
mmma:与mmm类似,但会将有依赖的模块一起编译
例如,mmm package/apps/Calculator
function _trigger_build()
(
local -r bc="$1"; shift
local T=$(gettop)
if [ -n "$T" ]; then
_wrap_build "$T/build/soong/soong_ui.bash" --build-mode --${bc} --dir="$(pwd)" "$@"
else
>&2 echo "Couldn't locate the top of the tree. Try setting TOP."
return 1
fi
)
function m()
(
_trigger_build "all-modules" "$@"
)
function mm()
(
_trigger_build "modules-in-a-dir-no-deps" "$@"
)
function mmm()
(
_trigger_build "modules-in-dirs-no-deps" "$@"
)
function mma()
(
_trigger_build "modules-in-a-dir" "$@"
)
function mmma()
(
_trigger_build "modules-in-dirs" "$@"
)
function _wrap_build()
处理结果,以及结果的颜色效果、编译用时。
function _wrap_build()
{
if [[ "${ANDROID_QUIET_BUILD:-}" == true ]]; then
"$@"
return $?
fi
local start_time=$(date +"%s")
"$@"
local ret=$?
local end_time=$(date +"%s")
local tdiff=$(($end_time-$start_time))
local hours=$(($tdiff / 3600 ))
local mins=$((($tdiff % 3600) / 60))
local secs=$(($tdiff % 60))
local ncolors=$(tput colors 2>/dev/null)
if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then
color_failed=$'\E'"[0;31m"
color_success=$'\E'"[0;32m"
color_warning=$'\E'"[0;33m"
color_reset=$'\E'"[00m"
else
color_failed=""
color_success=""
color_reset=""
fi
echo
if [ $ret -eq 0 ] ; then
echo -n "${color_success}#### build completed successfully "
else
echo -n "${color_failed}#### failed to build some targets "
fi
if [ $hours -gt 0 ] ; then
printf "(%02g:%02g:%02g (hh:mm:ss))" $hours $mins $secs
elif [ $mins -gt 0 ] ; then
printf "(%02g:%02g (mm:ss))" $mins $secs
elif [ $secs -gt 0 ] ; then
printf "(%s seconds)" $secs
fi
echo " ####${color_reset}"
echo
return $ret
}
获取编译源码的根目录,返回当前android代码树的顶层路径,前提是当前路径位于android代码树中。
该函数配置在 $T/build/make/shell_utils.sh脚本里,通过source导入,从指定的 $TOP 目录或者当前目录开始查找 build/make/core/envsetup.mk,并将能找到该文件的目录返回个调用函数作为操作的根目录。
## $T/build/make/shell_utils.sh
49 IMPORTING_ENVSETUP=true source $T/build/make/shell_utils.sh
15 function gettop
16 {
17 local TOPFILE=build/make/core/envsetup.mk
18 # The ${TOP-} expansion allows this to work even with set -u
19 if [ -n "${TOP:-}" -a -f "${TOP:-}/$TOPFILE" ] ; then ##如果编译环境已经设置了 $TOP,就检查 $TOP/build/make/core/envsetup.mk文件是否存在
20 # The following circumlocution ensures we remove symlinks from TOP.
21 (cd "$TOP"; PWD= /bin/pwd)
#跳转到$TOP 目录,将$TOP 目录指向的真实路径存放到PWD中
22 else # TOP不存在的话
23 if [ -f $TOPFILE ] ; then #检测当前路径是否能找到 build/make/core/envsetup.mk文件
24 # The following circumlocution (repeated below as well) ensures
25 # that we record the true directory name and not one that is
26 # faked up with symlink names.
27 PWD= /bin/pwd # 如果可以找到,则将当前目录的路径存放到PWD中
28 else
29 local HERE=$PWD #找不到文件,将当前路径存入变量HERE
30 local T= # 配置变量T为空
31 while [ \( ! \( -f $TOPFILE \) \) -a \( "$PWD" != "/" \) ]; do
32 \cd .. #无法找到文件,则不断返回到外层目录查找,直至到根目录为止
33 T=`PWD= /bin/pwd -P`
34 done
35 \cd "$HERE" #返回原路径
36 if [ -f "$T/$TOPFILE" ]; then #再次确认目录$T包含了build/make/core/envsetup.mk,如果是则说明$T是编译的根目录
37 echo "$T"
38 fi
39 fi
40 fi
41 }
在编译路径下搜索匹配的路径,并且根据选择跳转。给出一个关键词,godir会输出一个路径列表供用户选择要进入的路径。路径列表包含的路径满足,路径名中包含这个词,或这路径下的文件有文件名含这个词,忽略out路径。Usage: godir
函数执行了以下操作:
1)首先创建一个工程文件的filelist,如果已经指定OUT_DIR ,否则filelist 默认是放在 gettop 函数得到的工程根目录上。
2)如果 filelist 文件不存在,就使用find 命令在项目根目录里面查找所有普通文件,重定向到文件钟;
3)filelist 创建好后,调用时,将提供的KeyKord通过 grep命令从 filelist 中查找,并进行排序
A、如果没有查找到,则打印"Not found"并直接退出。
B、如果查到,但不是唯一值,则通过 while 循环将包含该文件的目录打印出来,并打印 "Select one: ",提醒用户选择;根据用户选择对应选择编号,赋值给pathname;
C、如果查找到,且是唯一,直接把对应路径赋值给pathname;
4)跳转至 $T/$pathname。
function godir () {
if [[ -z "$1" ]]; then
echo "Usage: godir "
return
fi
local T=$(gettop)
local FILELIST
if [ ! "$OUT_DIR" = "" ]; then
mkdir -p $OUT_DIR
FILELIST=$OUT_DIR/filelist
else
FILELIST=$T/filelist
fi
if [[ ! -f $FILELIST ]]; then
echo -n "Creating index..."
(\cd $T; find . -wholename ./out -prune -o -wholename ./.repo -prune -o -type f > $FILELIST)
echo " Done"
echo ""
fi
local lines
lines=($(\grep "$1" $FILELIST | sed -e 's/\/[^/]*$//' | sort | uniq))
if [[ ${#lines[@]} = 0 ]]; then
echo "Not found"
return
fi
local pathname
local choice
if [[ ${#lines[@]} > 1 ]]; then
while [[ -z "$pathname" ]]; do
local index=1
local line
for line in ${lines[@]}; do
printf "%6s %s\n" "[$index]" $line
index=$(($index + 1))
done
echo
echo -n "Select one: "
unset choice
read choice
if [[ $choice -gt ${#lines[@]} || $choice -lt 1 ]]; then
echo "Invalid choice"
continue
fi
pathname=${lines[@]:$(($choice-1)):1}
done
else
pathname=${lines[@]:0:1}
fi
\cd $T/$pathname
}
一次性调用在构建系统的中,获取此脚本所需的所有构建变量。在lunch之后调用,输出基于特定变体的变量信息。一般在set_stuff_for_environment 之前调用。
Usage: build_build_var_cache
该函数用以将 envsetup.sh 中使用函数 get_build_var、get_abs_build_var 查询的变量都收集到 cached_vars 和 cached_abs_vars 中,然后通过 soong_ui.bash 脚本创建键值对,赋值给build_dicts_script,通过eval运行"$build_dicts_script"命令,设置的是一些var_cache_*开头的编译环境变量。该函数成功运行后,设置BUILD_VAR_CACHE_READY为true。
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 "=" 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 ]
then
unset build_dicts_script
return $ret
fi
# Execute the script to store the "=" pairs as shell variables.
eval "$build_dicts_script"
ret=$?
unset build_dicts_script
if [ $ret -ne 0 ]
then
return $ret
fi
BUILD_VAR_CACHE_READY="true"
}
make脚本中某变量的值,加上当前路径为前缀。
Usage: get_abs_build_var TARGET_PRODUCT
在build_build_var_cache函数运行后,即BUILD_VAR_CACHE_READY为true才能使用。
function get_abs_build_var()
{
if [ "$BUILD_VAR_CACHE_READY" = "true" ]
then
eval "echo \"\${abs_var_cache_$1}\""
return
fi
local T=$(gettop)
if [ ! "$T" ]; then
echo "Couldn't locate the top of the tree. Try setting TOP." >&2
return
fi
(\cd $T; build/soong/soong_ui.bash --dumpvar-mode --abs $1)
}
获取绝对变量,make脚本中某变量的值。Usage: get_build_var VAR_NAME
在build_build_var_cache函数运行后,即BUILD_VAR_CACHE_READY为true才能使用。
这个函数其实核心就是:
# build/envsetup.sh
126 function get_build_var()
127 {
...
139 (\cd $T; build/soong/soong_ui.bash --dumpvar-mode $1)
140 }
# build/soong/cmd/soong_ui/main.go
69 var commands []command = []command{
70 { ...
78 }, {
79 flag: "--dumpvar-mode",
80 description: "print the value of the legacy make variable VAR to stdout",
81 simpleOutput: true,
82 logsPrefix: "dumpvars-",
83 config: dumpVarConfig,
84 stdio: customStdio,
85 run: dumpVar,
86 }, ...
306 func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
307 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
308 flags.Usage = func() {
309 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] \n\n", os.Args[0])
310 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
311 fmt.Fprintln(ctx.Writer, "")
312
313 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
314 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
315 fmt.Fprintln(ctx.Writer, "")
316 flags.PrintDefaults()
317 }
318 abs := flags.Bool("abs", false, "Print the absolute path of the value")
319 flags.Parse(args)
320
321 if flags.NArg() != 1 {
322 flags.Usage()
323 os.Exit(1)
324 }
325
326 varName := flags.Arg(0)
327 if varName == "report_config" {
328 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
329 if err != nil {
330 ctx.Fatal(err)
331 }
332
333 fmt.Println(build.Banner(varData))
334 } else {
335 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
336 if err != nil {
337 ctx.Fatal(err)
338 }
339
340 if *abs {
341 var res []string
342 for _, path := range strings.Fields(varData[varName]) {
343 if abs, err := filepath.Abs(path); err == nil {
344 res = append(res, abs)
345 } else {
346 ctx.Fatalln("Failed to get absolute path of", path, err)
347 }
348 }
349 fmt.Println(strings.Join(res, " "))
350 } else {
351 fmt.Println(varData[varName])
352 }
353 }
354 }
39 func DumpMakeVars(ctx Context, config Config, goals, vars []string) (map[string]string, error) {
40 soongUiVars := map[string]func() string{
41 "OUT_DIR": func() string { return config.OutDir() },
42 "DIST_DIR": func() string { return config.DistDir() },
43 "TMPDIR": func() string { return absPath(ctx, config.TempDir()) },
44 }
45
46 makeVars := make([]string, 0, len(vars))
47 for _, v := range vars {
48 if _, ok := soongUiVars[v]; !ok {
49 makeVars = append(makeVars, v)
50 }
51 }
52
53 var ret map[string]string
54 if len(makeVars) > 0 {
55 var err error
56 ret, err = dumpMakeVars(ctx, config, goals, makeVars, false, "")
57 if err != nil {
58 return ret, err
59 }
60 } else {
61 ret = make(map[string]string)
62 }
63
64 for _, v := range vars {
65 if f, ok := soongUiVars[v]; ok {
66 ret[v] = f()
67 }
68 }
69
70 return ret, nil
71 }
72
function destroy_build_var_cache()
删除构建变量缓存,这样我们仍然可以调用构建系统来获取此脚本中未列出的构建变量。
function destroy_build_var_cache()
{
unset BUILD_VAR_CACHE_READY
local v
for v in $cached_vars; do
unset var_cache_$v
done
unset cached_vars
for v in $cached_abs_vars; do
unset abs_var_cache_$v
done
unset cached_abs_vars
}
envsetup.sh脚本里面还设置了一部分指定特定文件grep的简写命令, grep会默认排除一些该目录:
.repo,.git,out
ggrep 针对 *.gradle 文件
gogreap 针对 *.go 文件
jgrep 针对 *.java 文件
cgrep 针对 *.c *.cc *.cpp *.h *.hpp
resgrep 针对目录中含有 *.xml
mangrep 针对 AndroidManifest.xml 文件
owngrep 针对 OWNERS 文件
sepgrep 针对名为 sepolicy 的目录
rcgrep 针对 *.rc 的文件
1060 case `uname -s` in
1061 Darwin)
1062 function sgrep() #在所有资源文件中查找内容
1063 {
1064 find -E . -name .repo -prune -o -name .git -prune -o -type f -iregex '.*\.(c|h|cc|cpp|hpp|S|java|kt|xml|sh|mk|aidl|vts|proto)' \
1065 -exec grep --color -n "$@" {} +
1066 }
1067
1068 ;;
1069 *)
1070 function sgrep() # 在当前目录及子目录中所.c,.h,.cpp,.S,.java,.mk,.xml,.sh文件中查找。
1071 {
1072 find . -name .repo -prune -o -name .git -prune -o -type f -iregex '.*\.\(c\|h\|cc\|cpp\|hpp\|S\|java\|kt\|xml\|sh\|mk\|aidl\|vts\|proto\)' \
1073 -exec grep --color -n "$@" {} +
1074 }
1075 ;;
1076 esac
1083 function ggrep() #在当前目录以及子目录下所有.gradle文件中查找内容
1084 {
1085 find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.gradle" \
1086 -exec grep --color -n "$@" {} +
1087 }
1088
1089 function gogrep()
1090 {
1091 find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.go" \
1092 -exec grep --color -n "$@" {} +
1093 }
1094
1095 function jgrep() #在当前目录以及子目录下所有.java文件中查找内容
1096 {
1097 find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.java" \
1098 -exec grep --color -n "$@" {} +
1099 }
1100
1101 function rsgrep()
1102 {
1103 find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.rs" \
1104 -exec grep --color -n "$@" {} +
1105 }
1106
1107 function ktgrep()
1108 {
1109 find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.kt" \
1110 -exec grep --color -n "$@" {} +
1111 }
1112
1113 function cgrep() # 在当前目录以及子目录下所有.c/.cc/.cpp/.h/.hpp文件中查找内容
1114 {
1115 find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) \
1116 -exec grep --color -n "$@" {} +
1117 }
1118
1119 function resgrep() #在res相关的.xml文件中查找
1120 {
1121 local dir
1122 for dir in `find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -name res -type d`; do
1123 find $dir -type f -name '*\.xml' -exec grep --color -n "$@" {} +
1124 done
1125 }
1126
1127 function mangrep() # 在当前目录以及子目录下所有AndroidManifest.xml文件中查找内容
1128 {
1129 find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'AndroidManifest.xml' \
1130 -exec grep --color -n "$@" {} +
1131 }
1132
1133 function owngrep()
1134 {
1135 find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'OWNERS' \
1136 -exec grep --color -n "$@" {} +
1137 }
1138
1139 function sepgrep() # 在当前目录以及子目录下对名为sepolicy目录中的文件查找内容
1140 {
1141 find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -name sepolicy -type d \
1142 -exec grep --color -n -r --exclude-dir=\.git "$@" {} +
1143 }
1144
1145 function rcgrep() # 在当前目录以及子目录下所有.rc文件中查找内容
1146 {
1147 find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.rc*" \
1148 -exec grep --color -n "$@" {} +
1149 }
1151 case `uname -s` in
1152 Darwin)
1153 function mgrep() #在当前目录及子目录下所有makefile文件(包括Makefile、.mk文件等)中查找内容
1154 {
1155 find -E . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o \( -iregex '.*/(Makefile|Makefile\..*|.*\.make|.*\.mak|.*\.mk|.*\.bp)' -o -regex '(.*/)?(build|soong)/.*[^/]*\.go' \) -type f \
1156 -exec grep --color -n "$@" {} +
1157 }
1158
1159 function treegrep() #在当前目录及子目录下所有.(c|h|cpp|S|java|xml)文件中查找内容
1160 {
1161 find -E . -name .repo -prune -o -name .git -prune -o -type f -iregex '.*\.(c|h|cpp|hpp|S|java|kt|xml)' \
1162 -exec grep --color -n -i "$@" {} +
1163 }
1164
1165 ;;
1166 *)
1167 function mgrep()
1168 {
1169 find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o \( -regextype posix-egrep -iregex '(.*\/Makefile|.*\/Makefile\..*|.*\.make|.*\.mak|.*\.mk|.*\.bp)' -o -regextype posix-extended -regex '(.*/)?(build|soong)/.*[^/]*\.go' \) -type f \
1170 -exec grep --color -n "$@" {} +
1171 }
1172
1173 function treegrep()
1174 {
1175 find . -name .repo -prune -o -name .git -prune -o -regextype posix-egrep -iregex '.*\.(c|h|cpp|hpp|S|java|kt|xml)' -type f \
1176 -exec grep --color -n -i "$@" {} +
1177 }
1178
1179 ;;
1180 esac
function check_product()
检查指定的TARGET_PRODUCT是否允许,默认的有sim和generic。如果不允许,则输出错误信息,允许则无回显。Usage: check_product
function help() # 显示帮助信息
function set_java_home() # 设置JAVA_HOME环境变量为/usr/lib/jvm/java-6-sun
function check_variant()
检查variant是否支持,支持则返回0,不支持则返回1。允许的variant列表定义在envsetup.sh中的VARIANT_CHOICES中,默认是user,userdebug,eng。定制android时,可以在VARIANT_CHOICES中添加vairant。
Usage: check_variant
主要是配置lunch后或者choosecombo 函数最后调用配置的一些环境变量,将ANDROID_BUILD_PATHS和ANDROID_PRE_BUILD_PATHS路径加到PATH中。脚本会先清除这两个变量在PATH的定义,在进行重新赋值。该函数在Android U被删除,改成set_lunch_paths函数。Usage: setpaths
ANDROID_BUILD_PATHS包括android编译中要使用到的路径,例如ANDROID_BUILD_PATHS,ANDROID_TOOLCHAIN,ANDROID_TOOLCHAIN_2ND_ARCH,ANDROID_DEV_SCRIPTS,ANDROID_LLVM_BINUTILS,ANDROID_EMULATOR_PREBUILTS,ACLOUD_PATH,AIDEGEN_PATH,ATEST_PATH
ANDROID_PRE_BUILD_PATHS包含ANDROID_JAVA_TOOLCHAIN
函数还配置了ANDROID_PYTHONPATH,JAVA_HOME,ANDROID_PRODUCT_OUT,ANDROID_SOONG_HOST_OUT,ANDROID_HOST_OUT,ANDROID_TARGET_OUT_TESTCASES等等变量。
function set_lunch_paths()
{
local T=$(gettop)
if [ ! "$T" ]; then
echo "Couldn't locate the top of the tree. Try setting TOP."
return
fi
if [ -z "$ANDROID_LUNCH_BUILD_PATHS" -a -n "$ANDROID_BUILD_PATHS" ] ; then
ANDROID_LUNCH_BUILD_PATHS="$ANDROID_BUILD_PATHS"
ANDROID_BUILD_PATHS=
fi
if [ -n "$ANDROID_PRE_BUILD_PATHS" ] ; then
export PATH=${PATH/$ANDROID_PRE_BUILD_PATHS/}
# strip leading ':', if any
export PATH=${PATH/:%/}
ANDROID_PRE_BUILD_PATHS=
fi
# Out with the old...
if [ -n "$ANDROID_LUNCH_BUILD_PATHS" ] ; then
export PATH=${PATH/$ANDROID_LUNCH_BUILD_PATHS/}
fi
# And in with the new...
ANDROID_LUNCH_BUILD_PATHS=$(get_abs_build_var SOONG_HOST_OUT_EXECUTABLES)
ANDROID_LUNCH_BUILD_PATHS+=:$(get_abs_build_var HOST_OUT_EXECUTABLES)
# Append llvm binutils prebuilts path to ANDROID_LUNCH_BUILD_PATHS.
local ANDROID_LLVM_BINUTILS=$(get_abs_build_var ANDROID_CLANG_PREBUILTS)/llvm-binutils-stable
ANDROID_LUNCH_BUILD_PATHS+=:$ANDROID_LLVM_BINUTILS
# Set up ASAN_SYMBOLIZER_PATH for SANITIZE_HOST=address builds.
export ASAN_SYMBOLIZER_PATH=$ANDROID_LLVM_BINUTILS/llvm-symbolizer
# Append asuite prebuilts path to ANDROID_LUNCH_BUILD_PATHS.
local os_arch=$(get_build_var HOST_PREBUILT_TAG)
ANDROID_LUNCH_BUILD_PATHS+=:$T/prebuilts/asuite/acloud/$os_arch
ANDROID_LUNCH_BUILD_PATHS+=:$T/prebuilts/asuite/aidegen/$os_arch
ANDROID_LUNCH_BUILD_PATHS+=:$T/prebuilts/asuite/atest/$os_arch
export ANDROID_JAVA_HOME=$(get_abs_build_var ANDROID_JAVA_HOME)
export JAVA_HOME=$ANDROID_JAVA_HOME
export ANDROID_JAVA_TOOLCHAIN=$(get_abs_build_var ANDROID_JAVA_TOOLCHAIN)
ANDROID_LUNCH_BUILD_PATHS+=:$ANDROID_JAVA_TOOLCHAIN
# Fix up PYTHONPATH
if [ -n $ANDROID_PYTHONPATH ]; then
export PYTHONPATH=${PYTHONPATH//$ANDROID_PYTHONPATH/}
fi
export ANDROID_PYTHONPATH=$T/development/python-packages/adb:$T/development/python-packages/gdbrunner:$T/development/python-packages:
if [ -n $VENDOR_PYTHONPATH ]; then
ANDROID_PYTHONPATH=$ANDROID_PYTHONPATH$VENDOR_PYTHONPATH
fi
export PYTHONPATH=$ANDROID_PYTHONPATH$PYTHONPATH
unset ANDROID_PRODUCT_OUT
export ANDROID_PRODUCT_OUT=$(get_abs_build_var PRODUCT_OUT)
export OUT=$ANDROID_PRODUCT_OUT
unset ANDROID_HOST_OUT
export ANDROID_HOST_OUT=$(get_abs_build_var HOST_OUT)
unset ANDROID_SOONG_HOST_OUT
export ANDROID_SOONG_HOST_OUT=$(get_abs_build_var SOONG_HOST_OUT)
unset ANDROID_HOST_OUT_TESTCASES
export ANDROID_HOST_OUT_TESTCASES=$(get_abs_build_var HOST_OUT_TESTCASES)
unset ANDROID_TARGET_OUT_TESTCASES
export ANDROID_TARGET_OUT_TESTCASES=$(get_abs_build_var TARGET_OUT_TESTCASES)
# Finally, set PATH
export PATH=$ANDROID_LUNCH_BUILD_PATHS:$PATH
}
function set_stuff_for_environment()
依次调用set_lunch_paths,set_sequence_number。设置android编译需要的环境变量。设定环境变量 ANDROID_BUILD_TOP,该函数通常是在choosetype、chooseproduct、lunch 函数中调用。
function set_stuff_for_environment()
{
set_lunch_paths
set_sequence_number
export ANDROID_BUILD_TOP=$(gettop)
}
function set_sequence_number()
输出环境变量BUILD_ENV_SEQUENCE_NUMBER,后面 buildspec.mk 中会确认该环境变量的值与CORRECT_BUILD_ENV_SEQUENCE_NUMBER 相等。
function set_sequence_number()
{
export BUILD_ENV_SEQUENCE_NUMBER=13
}
function settitle()
设置shell的prompt提示,PROMPT_COMMAND中加入TARGET_PRODUCT,TARGET_BUILD_VARIANT,和TARGET_BUILD_APPS等信息提示。
function choosetype()
配置环境变量TARGET_SIMULATOR。linux下会提示用户选择device或simulator。然后调用set_stuff_for_environment设置
function choosetype()
配置环境变量TARGET_BUILD_TYPE_SIMULATOR。会提示用户选择release或debug。然后调用set_stuff_for_environment设置
function chooseproduct()
配置环境变量TARGET_PRODUCT。会提示用户选择release或debug。然后调用set_stuff_for_environment设置
function choosevariant()
配置环境变量TARGET_BUILD_VARIANT。会提示用户选择release或debug
function tapas() # 功能同choosecombo
用户给定variant和一个或多个app name,就是LOCAL_PACKAGE_NAME的名字。tapas设定
export TARGET_PRODUCT=generic
export TARGET_BUILD_VARIANT=
Usage: tapas
?代表可选,*代表0个,1个或多个。YourVariant 和YourAppName的次序可颠倒。
Example: tapas user Calculator Calender
function choosecombo():
设置编译参数,依次调用choosesim,choosetype,chooseproduct,choosevariant,set_stuff_for_environment配置,然后调用printconfig输出
function findmakefile()
查找makefile。查找当前或最接近自己的祖辈路径上的Android.mk,返回Android.mk的路径,假设当前路径处于android代码树中。
function gettargetarch()
获取编译目标系统的 CPU 架构,如arm64
function gettargetarch
{
get_build_var TARGET_ARCH
}
# 回到源码的根目录,改变当前路径到代码树顶层
function croot()
{
local T=$(gettop)
if [ "$T" ]; then
if [ "$1" ]; then
\cd $(gettop)/$1
else
\cd $(gettop)
fi
else
echo "Couldn't locate the top of the tree. Try setting TOP."
fi
}
#改变当前路径到最近的还有Android.mk文件的祖/父辈路径。
function cproj()
{
local TOPFILE=build/make/core/envsetup.mk
local HERE=$PWD
local T=
while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
T=$PWD
if [ -f "$T/Android.mk" ]; then # 如果该目录下有Android.mk文件,则cd过去
\cd $T
return
fi
\cd ..
done
\cd $HERE
echo "can't find Android.mk"
}
function systemstack()
使用kill -3system_server将系统进程中的线程信息写入/data/anr/traces.txt。
function gdbclient()
建立gdb调试环境,包括两步,手机上运行gdbserver,本机上运行arm-eabi-gdb。
Usage: gdbclient
EXE: AppName的执行名
PORT:gdbserver的端口,例如, 192.168.2.102:5039
AppName:手机中ps列出的app名字,据此查pid。
function getprebuilt()
输出android prebuilt的路径, 调用函数 get_abs_build_var() 查找编译时变量值,这里通过该函数查找变量是 ANDROID_PREBUILTS 这个绝对路径为 $TOP/prebuilt/linux-x86
# build/make/core/dumpvar.mk
ANDROID_PREBUILTS := prebuilt/$(HOST_PREBUILT_TAG)
function getprebuilt
{
get_abs_build_var ANDROID_PREBUILTS
}
function tracedmdump()
生成dexlist文件qtrace.dexlit,dmtrace数据文件dmtrace,和调用dmtracedump工具生成的dmtrace解析文件dmtrace.html,将生成文件放到指定路径。
Usage: tracedmdump
如果YourDirName中不含’\’,则将放置的路径是ANDROID_PRODUCT_OUT/traces/YourDirName
function runhat()
貌似使用kill -10的方法得到heap dump并取到本地。使用hat以http方式展现出来。hat可能是个lightweight http server
function getbugreports()
使用adb shell ls /sdcard/bugreports+abd pull 命令,将手机/sdcard/bugreports目录下的文件下载到本地并压缩打包
function getbugreports()
{
local reports=(`adb shell ls /sdcard/bugreports | tr -d '\r'`)
if [ ! "$reports" ]; then
echo "Could not locate any bugreports."
return
fi
local report
for report in ${reports[@]}
do
echo "/sdcard/bugreports/${report}"
adb pull /sdcard/bugreports/${report} ${report}
gunzip ${report}
done
}
function startviewserver()
用指定端口启动viewserver。Usage: startviewserver
function stopviewserver() # 关闭viewserver
function isviewserverstarted() # 检查viewserver是否可用
function smoketest() # 编译smoketest并安装手机运行
function runtest() # 运行development/testrunner/runtest.py $@
命令必须在lunch之后执行,主要执行m 去编译更新生成out/target/product/*/module-info.json文件。
function refreshmod() {
if [ ! "$ANDROID_PRODUCT_OUT" ]; then
echo "No ANDROID_PRODUCT_OUT. Try running 'lunch' first." >&2
return 1
fi
echo "Refreshing modules (building module-info.json). Log at $ANDROID_PRODUCT_OUT/module-info.json.build.log." >&2
# for the output of the next command
mkdir -p $ANDROID_PRODUCT_OUT || return 1
# Note, can't use absolute path because of the way make works.
m $(get_build_var PRODUCT_OUT)/module-info.json \
> $ANDROID_PRODUCT_OUT/module-info.json.build.log 2>&1
}
命令必须在lunch之后执行,函数会列出所有的模块名,AndroidU 该命令是由build/make/core/tasks/module-info.mk文件生成out/target/product/*/all_modules.txt ,调用的时候直接通过cat列出所有的模块名称。但是Andorid T之前,是由python直接导入json模块,去读取out/target/product/*/module-info.json所有的key值。
function allmod() {
cat $ANDROID_PRODUCT_OUT/all_modules.txt 2>/dev/null
}
#U之前
function allmod() {
verifymodinfo || return 1
python3 -c "import json; print('\n'.join(sorted(json.load(open('$ANDROID_PRODUCT_OUT/module-info.json')).keys())))"
}
命令必须在lunch之后执行,可以获得某个模块路径。从module-info.json 中获取模块的path key的值。
Usage: pathmod
function pathmod() {
if [[ $# -ne 1 ]]; then
echo "usage: pathmod " >&2
return 1
fi
verifymodinfo || return 1
local relpath=$(python3 -c "import json, os
module = '$1'
module_info = json.load(open('$ANDROID_PRODUCT_OUT/module-info.json'))
if module not in module_info:
exit(1)
print(module_info[module]['path'][0])" 2>/dev/null)
if [ -z "$relpath" ]; then
echo "Could not find module '$1' (try 'refreshmod' if there have been build changes?)." >&2
return 1
else
echo "$ANDROID_BUILD_TOP/$relpath"
fi
}
命令必须在lunch之后执行,可以获得某个路径下模块所有的名称。
通过搜索指定的路径,作为path的值去搜索,可以获得对应模块清单列表。pathmod的逆搜索过程。
Usage: dirmods
function dirmods() {
if [[ $# -ne 1 ]]; then
echo "usage: dirmods " >&2
return 1
fi
verifymodinfo || return 1
python3 -c "import json, os
dir = '$1'
while dir.endswith('/'):
dir = dir[:-1]
prefix = dir + '/'
module_info = json.load(open('$ANDROID_PRODUCT_OUT/module-info.json'))
results = set()
for m in module_info.values():
for path in m.get(u'path', []):
if path == dir or path.startswith(prefix):
name = m.get(u'module_name')
if name:
results.add(name)
for name in sorted(results):
print(name)
"
}
从module-info.json 中获取模块的path key的值,基于pathmod去执行,获得path值,则cd过去。
Usage: pathmod
function gomod() {
if [[ $# -ne 1 ]]; then
echo "usage: gomod " >&2
return 1
fi
local path="$(pathmod $@)"
if [ -z "$path" ]; then
return 1
fi
cd $path
}
使用adb install 模块的 apk,缓存在 module-info.json 中。 如果进行了任何构建更改,并且应在输出中反映出来,则应首先运行“refreshmod”。
Usage: installmod [adb install arguments]
For example: installmod -r Dialer -> adb install -r /path/to/Dialer.apk
function installmod() {
if [[ $# -eq 0 ]]; then
echo "usage: installmod [adb install arguments] " >&2
echo "" >&2
echo "Only flags to be passed after the \"install\" in adb install are supported," >&2
echo "with the exception of -s. If -s is passed it will be placed before the \"install\"." >&2
echo "-s must be the first flag passed if it exists." >&2
return 1
fi
local _path
_path=$(outmod ${@:$#:1})
if [ $? -ne 0 ]; then
return 1
fi
_path=$(echo "$_path" | grep -E \\.apk$ | head -n 1)
if [ -z "$_path" ]; then
echo "Module '$1' does not produce a file ending with .apk (try 'refreshmod' if there have been build changes?)" >&2
return 1
fi
local serial_device=""
if [[ "$1" == "-s" ]]; then
if [[ $# -le 2 ]]; then
echo "-s requires an argument" >&2
return 1
fi
serial_device="-s $2"
shift 2
fi
local length=$(( $# - 1 ))
echo adb $serial_device install ${@:1:$length} $_path
adb $serial_device install ${@:1:$length} $_path
}
function verifymodinfo()
验证module-info.txt文件是否存在
function verifymodinfo() {
if [ ! "$ANDROID_PRODUCT_OUT" ]; then
if [ "$QUIET_VERIFYMODINFO" != "true" ] ; then
echo "No ANDROID_PRODUCT_OUT. Try running 'lunch' first." >&2
fi
return 1
fi
if [ ! -f "$ANDROID_PRODUCT_OUT/module-info.json" ]; then
if [ "$QUIET_VERIFYMODINFO" != "true" ] ; then
echo "Could not find module-info.json. Please run 'refreshmod' first." >&2
fi
return 1
fi
}
usage: bmod
如果使用 bp2build 转换,则返回 Soong 模块的 Bazel 标签。
function bmod()
(
if [ $# -ne 1 ]; then
echo "usage: bmod " >&2
return 1
fi
# We could run bp2build here, but it might trigger bp2build invalidation
# when used with `b` (e.g. --run_soong_tests) and/or add unnecessary waiting
# time overhead.
#
# For a snappy result, use the latest generated version in soong_injection,
# and ask users to run m bp2build if it doesn't exist.
converted_json="$(get_abs_build_var OUT_DIR)/soong/soong_injection/metrics/converted_modules_path_map.json"
if [ ! -f ${converted_json} ]; then
echo "bp2build files not found. Have you ran 'm bp2build'?" >&2
return 1
fi
local target_label=$(python3 -c "import json
module = '$1'
converted_json='$converted_json'
bp2build_converted_map = json.load(open(converted_json))
if module not in bp2build_converted_map:
exit(1)
print(bp2build_converted_map[module] + ':' + module)")
if [ -z "${target_label}" ]; then
echo "$1 is not converted to Bazel." >&2
return 1
else
echo "${target_label}"
fi
)
获取模块已安装输出的列表,也就是安装路径,缓存在 module-info.json 中。
usage: outmod
从module-info.json 中获取模块的installed key的值,获得所有模块安装路径。
function outmod() {
if [[ $# -ne 1 ]]; then
echo "usage: outmod " >&2
return 1
fi
verifymodinfo || return 1
local relpath
relpath=$(python3 -c "import json, os
module = '$1'
module_info = json.load(open('$ANDROID_PRODUCT_OUT/module-info.json'))
if module not in module_info:
exit(1)
for output in module_info[module]['installed']:
print(os.path.join('$ANDROID_BUILD_TOP', output))" 2>/dev/null)
if [ $? -ne 0 ]; then
echo "Could not find module '$1' (try 'refreshmod' if there have been build changes?)" >&2
return 1
elif [ ! -z "$relpath" ]; then
echo "$relpath"
fi
}
https://blog.csdn.net/Eliot_Haber/article/details/106687337
https://www.cnblogs.com/sjjg/p/6137364.html
https://blog.csdn.net/shift_wwx/article/details/130932382