当前位置:   article > 正文

Android build中的envsetup.sh详解_couldn't locate the top of the tree. try setting t

couldn't locate the top of the tree. try setting top.

源码基于:Android R

0. 前言

今天在编译项目的时候,想看看 envsetup.sh 中变化了些什么,才想起来编译专栏中好像没有详解该脚本,索性现在空余时间比较多,整理一下方便以后查看。

Android envsetup.sh 为编译前的准备工作,提供 lunch、m、mm等命令函数定义,是整个Android 编译系统的第一步。该文件一共1700 行左右,简单总结出两件事情:

  • 函数定义
  • 生成编译配置列表

本文会结合代码对 envsetup.sh 进行详细的剖析,由于函数比较多,本文会进行长期地补充和维护。

1. source envsetup.sh

当运行该脚本的时候终端提示:

  1. including vendor/xxx/common/vendorsetup.sh
  2. clang compdb: ln -s out/soong/development/ide/compdb/compile_commands.json $ANDROID_BUILD_TOP
  3. including vendor/qqq/opensource/core-utils/vendorsetup.sh
  4. including vendor/qqq/proprietary/common/vendorsetup.sh

那执行该脚本的时候到底做了些什么呢?

  1. build/envsetup.sh
  2. validate_current_shell
  3. source_vendorsetup
  4. addcompletions

执行该程序共运行三个函数,分别是 validate_current_shellsource_vendorsetupaddcompletions。下面来看下这三个函数做了些什么事情?

1.1 validate_current_shell

  1. function validate_current_shell() {
  2. local current_sh="$(ps -o command -p $$)"
  3. case "$current_sh" in
  4. *bash*)
  5. function check_type() { type -t "$1"; }
  6. ;;
  7. *zsh*)
  8. function check_type() { type "$1"; }
  9. enable_zsh_completion ;;
  10. *)
  11. echo -e "WARNING: Only bash and zsh are supported.\nUse of other shell would lead to erroneous results."
  12. ;;
  13. esac
  14. }

主要是测试下当前的shell 环境是否可用,对于 zsh 环境需要特殊处理,其他环境会其实warning。


1.2 source_vendorsetup

  1. function source_vendorsetup() {
  2. unset VENDOR_PYTHONPATH
  3. allowed=
  4. for f in $(find -L device vendor product -maxdepth 4 -name 'allowed-vendorsetup_sh-files' 2>/dev/null | sort); do
  5. if [ -n "$allowed" ]; then
  6. echo "More than one 'allowed_vendorsetup_sh-files' file found, not including any vendorsetup.sh files:"
  7. echo " $allowed"
  8. echo " $f"
  9. return
  10. fi
  11. allowed="$f"
  12. done
  13. allowed_files=
  14. [ -n "$allowed" ] && allowed_files=$(cat "$allowed")
  15. for dir in device vendor product; do
  16. for f in $(test -d $dir && \
  17. find -L $dir -maxdepth 4 -name 'vendorsetup.sh' 2>/dev/null | sort); do
  18. if [[ -z "$allowed" || "$allowed_files" =~ $f ]]; then
  19. echo "including $f"; . "$f"
  20. else
  21. echo "ignoring $f, not in $allowed"
  22. fi
  23. done
  24. done
  25. }
  • 首先,查找 device、vendor、product 目录下最大深度为4 的子目录中,是否存在 allowed-vendorsetup_sh-files 文件并排序,但这个文件只能1个,不然会报错退出;
  • 接着,在device、vendor、product 中同样查找最大深度为 4 的子目录中,是否存在 vendorsetup.sh,打印 includeing ... ,然后运行该 vendorsetup.sh 脚本,这些脚本大多是配置一些环境变量;

例如上面运行 envsetup.sh 时打印 including vendor/xxx/common/vendorsetup.sh,就是在 vendor/xxx/common 目录下找到了 vendorsetup.sh,运行的时候会配置一些环境变量,并打印:

clang compdb: ln -s out/soong/development/ide/compdb/compile_commands.json 


1.3 addcompletions

  1. function addcompletions()
  2. {
  3. local T dir f
  4. #避免不在 bash 或 zsh 的环境中运行
  5. #需要提前指定这两个环境变量,如果没有指定,该函数return
  6. if [ -z "$BASH_VERSION" -a -z "$ZSH_VERSION" ]; then
  7. return
  8. fi
  9. #避免运行在太久的版本中,这里是小于 3 的版本
  10. if [ -n "$BASH_VERSION" -a ${BASH_VERSINFO[0]} -lt 3 ]; then
  11. return
  12. fi
  13. local completion_files=(
  14. system/core/adb/adb.bash
  15. system/core/fastboot/fastboot.bash
  16. tools/asuite/asuite.sh
  17. )
  18. #确认上面几个脚本是否存在,通过should_add_completion确认该脚本是否在白名单中
  19. for f in ${completion_files[*]}; do
  20. if [ -f "$f" ] && should_add_completion "$f"; then
  21. . $f
  22. fi
  23. done
  24. if should_add_completion bit ; then
  25. complete -C "bit --tab" bit
  26. fi
  27. if [ -z "$ZSH_VERSION" ]; then
  28. # Doesn't work in zsh.
  29. complete -o nospace -F _croot croot
  30. fi
  31. complete -F _lunch lunch
  32. complete -F _complete_android_module_names dumpmod
  33. complete -F _complete_android_module_names pathmod
  34. complete -F _complete_android_module_names gomod
  35. complete -F _complete_android_module_names m
  36. }

详细的 complete 命令可以另外查找,最后就是把一些命令进行补齐的设置。

2. 辅助函数

2.1 hmm

hmm 函数输出 envsetupsh.sh 的帮助说明,执行 build/envsetup.sh 后可以调用的操作总结:

  1. function hmm() {
  2. cat <<EOF
  3. Run "m help" for help with the build system itself.
  4. Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
  5. - lunch: lunch <product_name>-<build_variant>
  6. Selects <product_name> as the product to build, and <build_variant> as the variant to
  7. build, and stores those selections in the environment to be read by subsequent
  8. invocations of 'm' etc.
  9. - tapas: tapas [<App1> <App2> ...] [arm|x86|mips|arm64|x86_64|mips64] [eng|userdebug|user]
  10. - croot: Changes directory to the top of the tree, or a subdirectory thereof.
  11. - m: Makes from the top of the tree.
  12. - mm: Builds and installs all of the modules in the current directory, and their
  13. dependencies.
  14. - mmm: Builds and installs all of the modules in the supplied directories, and their
  15. dependencies.
  16. To limit the modules being built use the syntax: mmm dir/:target1,target2.
  17. - mma: Same as 'mm'
  18. - mmma: Same as 'mmm'
  19. - provision: Flash device with all required partitions. Options will be passed on to fastboot.
  20. - cgrep: Greps on all local C/C++ files.
  21. - ggrep: Greps on all local Gradle files.
  22. - gogrep: Greps on all local Go files.
  23. - jgrep: Greps on all local Java files.
  24. - resgrep: Greps on all local res/*.xml files.
  25. - mangrep: Greps on all local AndroidManifest.xml files.
  26. - mgrep: Greps on all local Makefiles and *.bp files.
  27. - owngrep: Greps on all local OWNERS files.
  28. - sepgrep: Greps on all local sepolicy files.
  29. - sgrep: Greps on all local source files.
  30. - godir: Go to the directory containing a file.
  31. - allmod: List all modules. or List all modules inside certain directory.
  32. - gomod: Go to the directory containing a module.
  33. - dumpmod: Get all info of specific modules from \$ANDROID_PRODUCT_OUT/module-info.json
  34. - pathmod: Get the directory containing a module.
  35. - refreshmod: Refresh list of modules for allmod/gomod.
  36. - ninja-build: Bypass ninja generate procedure, directly use ninja to build a target or module, also build droid is support too by no param given.
  37. - ninja-query: Query input and output of a ninja target, this is the most powerful tool when digging the compile steps.
  38. - ninja-commands: Print all build commands, like a --just-print version of ninja-build, also known as the --dry-run purpose
  39. - mod: Analyze $OUT/module-info.json with parameters, see mod -h for help
  40. Environment options:
  41. - SANITIZE_HOST: Set to 'address' to use ASAN for all host modules.
  42. - ANDROID_QUIET_BUILD: set to 'true' to display only the essential messages.
  43. Look at the source to view more functions. The complete list is:
  44. EOF
  45. local T=$(gettop)
  46. local A=""
  47. local i
  48. for i in `cat $T/build/envsetup.sh | sed -n "/^[[:blank:]]*function /s/function \([a-z_]*\).*/\1/p" | sort | uniq`; do
  49. A="$A $i"
  50. done
  51. echo $A
  52. }

最后还列出了完整的函数名,通过:

sed -n "/^[[:blank:]]*function /s/function \([a-z_]*\).*/\1/p"

但其操作有一个 bug,用于匹配函数的正则表达式 function [az].* 会漏掉函数 is64bit()

将匹配模式从 function [az].*/  修改为 function [az]\w.*  就可以匹配文件中的所有函数了。

2.2 gettop

gettop 函数,从指定的 $TOP 目录或者当前目录开始查找 build/make/core/envsetup.mk,并将能找到该文件的目录返回个调用函数作为操作的根目录: 

  1. function gettop
  2. {
  3. local TOPFILE=build/make/core/envsetup.mk
  4. #如果编译环境已经设置了 $TOP,就检查 $TOP/build/make/core/envsetup.mk文件是否存在
  5. if [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then
  6. # The following circumlocution ensures we remove symlinks from TOP.
  7. #跳转到$TOP 目录,并pwd将$TOP 目录指向的真实路径存放到PWD中
  8. (cd $TOP; PWD= /bin/pwd)
  9. else
  10. #如果当前路径下能找到 build/make/core/envsetup.mk文件,则将当前目录的真实路径存放到PWD中
  11. if [ -f $TOPFILE ] ; then
  12. PWD= /bin/pwd
  13. else
  14. #如果当前目录下无法找到build/make/core/envsetup.mk文件,则不断返回到外层目录查找,
  15. #直至到根目录为止
  16. local HERE=$PWD
  17. local T=
  18. while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
  19. \cd ..
  20. T=`PWD= /bin/pwd -P`
  21. done
  22. #查找完后回到之前操作的路径
  23. \cd $HERE
  24. #如果目录$T包含了build/make/core/envsetup.mk,则说明$T是编译的根目录
  25. if [ -f "$T/$TOPFILE" ]; then
  26. echo $T
  27. fi
  28. fi
  29. fi
  30. }

2.3 croot

croot 命令用以将当前目录切换到当前编译环境的根目录。

当然croot 之后可以跟一个参数标记切到根目录之后的下一级目录,例如 croot device 命令。

详细可以查看代码:

  1. function croot()
  2. {
  3. local T=$(gettop)
  4. if [ "$T" ]; then
  5. if [ "$1" ]; then
  6. \cd $(gettop)/$1
  7. else
  8. \cd $(gettop)
  9. fi
  10. else
  11. echo "Couldn't locate the top of the tree. Try setting TOP."
  12. fi
  13. }

2.4 cproj

cproj 命令用于切换到当前模块的编译目录下(含Android.mk的目录下)

  1. function cproj()
  2. {
  3. local TOPFILE=build/make/core/envsetup.mk
  4. local HERE=$PWD #临时保存当前目录
  5. local T=
  6. #当前目录下build/make/core/envsetup.mk不存在,即当前不是编译根目录,且
  7. #当前目录不是系统根目录
  8. while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
  9. T=$PWD
  10. if [ -f "$T/Android.mk" ]; then #如果该目录下有Android.mk文件,则cd过去
  11. \cd $T
  12. return
  13. fi
  14. \cd ..
  15. done
  16. \cd $HERE #恢复之前的目录
  17. echo "can't find Android.mk"
  18. }

如上述命令,如果无法找到该模块下的 Android.mk,就会提示:

can't find Android.mk

例如在 packages/apps/Launcher3/protos/ 目录下运行 cproj 命令,则会退到 packages/apps/Launcher3/ 目录下。

2.5 getprebuilt

  1. function getprebuilt
  2. {
  3. get_abs_build_var ANDROID_PREBUILTS
  4. }
  1. function get_abs_build_var()
  2. {
  3. if [ "$BUILD_VAR_CACHE_READY" = "true" ]
  4. then
  5. eval "echo \"\${abs_var_cache_$1}\""
  6. return
  7. fi
  8. local T=$(gettop)
  9. if [ ! "$T" ]; then
  10. echo "Couldn't locate the top of the tree. Try setting TOP." >&2
  11. return
  12. fi
  13. (\cd $T; build/soong/soong_ui.bash --dumpvar-mode --abs $1)
  14. }

函数 get_abs_build_var() 用于查找编译时变量值,这里通过该函数查找变量是 ANDROID_PREBUILTS 这个绝对路径为 $TOP/prebuilt/linux-x86

2.6 setpaths

主要是配置一些环境变量,在lunch 函数或者 choosecombo 函数最后调用:

  1. function setpaths()
  2. {
  3. local T=$(gettop)
  4. if [ ! "$T" ]; then
  5. echo "Couldn't locate the top of the tree. Try setting TOP."
  6. return
  7. fi
  8. ##################################################################
  9. # #
  10. # Read me before you modify this code #
  11. # #
  12. # This function sets ANDROID_BUILD_PATHS to what it is adding #
  13. # to PATH, and the next time it is run, it removes that from #
  14. # PATH. This is required so lunch can be run more than once #
  15. # and still have working paths. #
  16. # #
  17. ##################################################################
  18. # Note: on windows/cygwin, ANDROID_BUILD_PATHS will contain spaces
  19. # due to "C:\Program Files" being in the path.
  20. # out with the old
  21. if [ -n "$ANDROID_BUILD_PATHS" ] ; then
  22. export PATH=${PATH/$ANDROID_BUILD_PATHS/}
  23. fi
  24. if [ -n "$ANDROID_PRE_BUILD_PATHS" ] ; then
  25. export PATH=${PATH/$ANDROID_PRE_BUILD_PATHS/}
  26. # strip leading ':', if any
  27. export PATH=${PATH/:%/}
  28. fi
  29. # and in with the new
  30. local prebuiltdir=$(getprebuilt)
  31. local gccprebuiltdir=$(get_abs_build_var ANDROID_GCC_PREBUILTS)
  32. # defined in core/config.mk
  33. local targetgccversion=$(get_build_var TARGET_GCC_VERSION)
  34. local targetgccversion2=$(get_build_var 2ND_TARGET_GCC_VERSION)
  35. export TARGET_GCC_VERSION=$targetgccversion
  36. # The gcc toolchain does not exists for windows/cygwin. In this case, do not reference it.
  37. export ANDROID_TOOLCHAIN=
  38. export ANDROID_TOOLCHAIN_2ND_ARCH=
  39. local ARCH=$(get_build_var TARGET_ARCH)
  40. local toolchaindir toolchaindir2=
  41. case $ARCH in
  42. x86) toolchaindir=x86/x86_64-linux-android-$targetgccversion/bin
  43. ;;
  44. x86_64) toolchaindir=x86/x86_64-linux-android-$targetgccversion/bin
  45. ;;
  46. arm) toolchaindir=arm/arm-linux-androideabi-$targetgccversion/bin
  47. ;;
  48. arm64) toolchaindir=aarch64/aarch64-linux-android-$targetgccversion/bin;
  49. toolchaindir2=arm/arm-linux-androideabi-$targetgccversion2/bin
  50. ;;
  51. mips|mips64) toolchaindir=mips/mips64el-linux-android-$targetgccversion/bin
  52. ;;
  53. *)
  54. echo "Can't find toolchain for unknown architecture: $ARCH"
  55. toolchaindir=xxxxxxxxx
  56. ;;
  57. esac
  58. if [ -d "$gccprebuiltdir/$toolchaindir" ]; then
  59. export ANDROID_TOOLCHAIN=$gccprebuiltdir/$toolchaindir
  60. fi
  61. if [ "$toolchaindir2" -a -d "$gccprebuiltdir/$toolchaindir2" ]; then
  62. export ANDROID_TOOLCHAIN_2ND_ARCH=$gccprebuiltdir/$toolchaindir2
  63. fi
  64. export ANDROID_DEV_SCRIPTS=$T/development/scripts:$T/prebuilts/devtools/tools:$T/external/selinux/prebuilts/bin
  65. # add kernel specific binaries
  66. case $(uname -s) in
  67. Linux)
  68. export ANDROID_DEV_SCRIPTS=$ANDROID_DEV_SCRIPTS:$T/prebuilts/misc/linux-x86/dtc:$T/prebuilts/misc/linux-x86/libufdt
  69. ;;
  70. *)
  71. ;;
  72. esac
  73. ANDROID_BUILD_PATHS=$(get_build_var ANDROID_BUILD_PATHS):$ANDROID_TOOLCHAIN
  74. if [ -n "$ANDROID_TOOLCHAIN_2ND_ARCH" ]; then
  75. ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_TOOLCHAIN_2ND_ARCH
  76. fi
  77. ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_DEV_SCRIPTS
  78. # Append llvm binutils prebuilts path to ANDROID_BUILD_PATHS.
  79. local ANDROID_LLVM_BINUTILS=$(get_abs_build_var ANDROID_CLANG_PREBUILTS)/llvm-binutils-stable
  80. ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_LLVM_BINUTILS
  81. # Set up ASAN_SYMBOLIZER_PATH for SANITIZE_HOST=address builds.
  82. export ASAN_SYMBOLIZER_PATH=$ANDROID_LLVM_BINUTILS/llvm-symbolizer
  83. # If prebuilts/android-emulator/<system>/ exists, prepend it to our PATH
  84. # to ensure that the corresponding 'emulator' binaries are used.
  85. case $(uname -s) in
  86. Darwin)
  87. ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/darwin-x86_64
  88. ;;
  89. Linux)
  90. ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/linux-x86_64
  91. ;;
  92. *)
  93. ANDROID_EMULATOR_PREBUILTS=
  94. ;;
  95. esac
  96. if [ -n "$ANDROID_EMULATOR_PREBUILTS" -a -d "$ANDROID_EMULATOR_PREBUILTS" ]; then
  97. ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_EMULATOR_PREBUILTS
  98. export ANDROID_EMULATOR_PREBUILTS
  99. fi
  100. # Append asuite prebuilts path to ANDROID_BUILD_PATHS.
  101. local os_arch=$(get_build_var HOST_PREBUILT_TAG)
  102. local ACLOUD_PATH="$T/prebuilts/asuite/acloud/$os_arch"
  103. local AIDEGEN_PATH="$T/prebuilts/asuite/aidegen/$os_arch"
  104. local ATEST_PATH="$T/prebuilts/asuite/atest/$os_arch"
  105. export ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ACLOUD_PATH:$AIDEGEN_PATH:$ATEST_PATH:
  106. export PATH=$ANDROID_BUILD_PATHS$PATH
  107. # out with the duplicate old
  108. if [ -n $ANDROID_PYTHONPATH ]; then
  109. export PYTHONPATH=${PYTHONPATH//$ANDROID_PYTHONPATH/}
  110. fi
  111. # and in with the new
  112. export ANDROID_PYTHONPATH=$T/development/python-packages:
  113. if [ -n $VENDOR_PYTHONPATH ]; then
  114. ANDROID_PYTHONPATH=$ANDROID_PYTHONPATH$VENDOR_PYTHONPATH
  115. fi
  116. export PYTHONPATH=$ANDROID_PYTHONPATH$PYTHONPATH
  117. export ANDROID_JAVA_HOME=$(get_abs_build_var ANDROID_JAVA_HOME)
  118. export JAVA_HOME=$ANDROID_JAVA_HOME
  119. export ANDROID_JAVA_TOOLCHAIN=$(get_abs_build_var ANDROID_JAVA_TOOLCHAIN)
  120. export ANDROID_PRE_BUILD_PATHS=$ANDROID_JAVA_TOOLCHAIN:
  121. export PATH=$ANDROID_PRE_BUILD_PATHS$PATH
  122. unset ANDROID_PRODUCT_OUT
  123. export ANDROID_PRODUCT_OUT=$(get_abs_build_var PRODUCT_OUT)
  124. export OUT=$ANDROID_PRODUCT_OUT
  125. unset ANDROID_HOST_OUT
  126. export ANDROID_HOST_OUT=$(get_abs_build_var HOST_OUT)
  127. unset ANDROID_HOST_OUT_TESTCASES
  128. export ANDROID_HOST_OUT_TESTCASES=$(get_abs_build_var HOST_OUT_TESTCASES)
  129. unset ANDROID_TARGET_OUT_TESTCASES
  130. export ANDROID_TARGET_OUT_TESTCASES=$(get_abs_build_var TARGET_OUT_TESTCASES)
  131. # needed for building linux on MacOS
  132. # TODO: fix the path
  133. #export HOST_EXTRACFLAGS="-I "$T/system/kernel_headers/host_include
  134. }

首先确认 gettop 是否能找到编译的根目录,如果找不到,则退出该函数;

接着export 一些环境变量:

  • TARGET_GCC_VERSION
  • ANDROID_DEV_SCRIPTS
  • ANDROID_TOOLCHAIN_2ND_ARCH
  • ANDROID_BUILD_PATHS
  • ASAN_SYMBOLIZER_PATH
  • ANDROID_EMULATOR_PREBUILTS
  • PYTHONPATH
  • ANDROID_PYTHONPATH
  • ANDROID_JAVA_HOME
  • JAVA_HOME
  • ANDROID_JAVA_TOOLCHAIN
  • ANDROID_PRE_BUILD_PATHS
  • ANDROID_PRODUCT_OUT
  • OUT
  • ANDROID_HOST_OUT
  • ANDROID_HOST_OUT_TESTCASES
  • ANDROID_TARGET_OUT_TESTCASES

2.7 set_sequence_number

  1. function set_sequence_number()
  2. {
  3. export BUILD_ENV_SEQUENCE_NUMBER=13
  4. }

指定环境变量 BUILD_ENV_SEQUENCE_NUMBER,后面 buildspec.mk 中会确认该环境变量的值与CORRECT_BUILD_ENV_SEQUENCE_NUMBER 相等。

2.8 set_stuff_for_environment

  1. function set_stuff_for_environment()
  2. {
  3. setpaths
  4. set_sequence_number
  5. export ANDROID_BUILD_TOP=$(gettop)
  6. # With this environment variable new GCC can apply colors to warnings/errors
  7. export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
  8. }

调用 2.6 2.7 节的函数,并设定环境变量 ANDROID_BUILD_TOP 和 GCC_COLORS。

之后调用函数 gettop 打印出来的路径,通过 ANDROID_BUILD_TOP 就可以获知。

该函数通常是在choosetype、chooseproduct、lunch 函数中调用。

2.9 printconfig

  1. function printconfig()
  2. {
  3. local T=$(gettop)
  4. if [ ! "$T" ]; then
  5. echo "Couldn't locate the top of the tree. Try setting TOP." >&2
  6. return
  7. fi
  8. get_build_var report_config
  9. }

调用get_build_var 函数将lunch 之后的编译配置信息:

  1. ============================================
  2. PLATFORM_VERSION_CODENAME=REL
  3. PLATFORM_VERSION=11
  4. TARGET_PRODUCT=shift
  5. TARGET_BUILD_VARIANT=userdebug
  6. TARGET_BUILD_TYPE=release
  7. TARGET_ARCH=arm64
  8. TARGET_ARCH_VARIANT=armv8-a
  9. TARGET_CPU_VARIANT=cortex-a55
  10. TARGET_2ND_ARCH=arm
  11. TARGET_2ND_ARCH_VARIANT=armv8-a
  12. TARGET_2ND_CPU_VARIANT=cortex-a55
  13. HOST_ARCH=x86_64
  14. HOST_2ND_ARCH=x86
  15. HOST_OS=linux
  16. HOST_OS_EXTRA=Linux-4.15.0-142-generic-x86_64-Ubuntu-16.04.5-LTS
  17. HOST_CROSS_OS=windows
  18. HOST_CROSS_ARCH=x86
  19. HOST_CROSS_2ND_ARCH=x86_64
  20. HOST_BUILD_TYPE=release
  21. BUILD_ID=RD2A.211001.002
  22. OUT_DIR=out
  23. PRODUCT_SOONG_NAMESPACES=vendor/qqq/opensource/commonsys/packages/apps/Bluetooth vendor/qqq/opensource/commonsys/system/bt/conf external/v4l2_codec2
  24. ============================================

通常在 lunch 函数设定完环境变量后会调用该函数进行 config 的打印。详细看 lunch 函数。

2.10 gettargetarch

  1. function gettargetarch
  2. {
  3. get_build_var TARGET_ARCH
  4. }

通过 get_build_var 函数获取编译目标系统的 CPU 架构,如arm64

2.11 godir

  1. function godir () {
  2. if [[ -z "$1" ]]; then
  3. echo "Usage: godir <regex>"
  4. return
  5. fi
  6. local T=$(gettop)
  7. local FILELIST
  8. if [ ! "$OUT_DIR" = "" ]; then
  9. mkdir -p $OUT_DIR
  10. FILELIST=$OUT_DIR/filelist
  11. else
  12. FILELIST=$T/filelist
  13. fi
  14. if [[ ! -f $FILELIST ]]; then
  15. echo -n "Creating index..."
  16. (\cd $T; find . -wholename ./out -prune -o -wholename ./.repo -prune -o -type f > $FILELIST)
  17. echo " Done"
  18. echo ""
  19. fi
  20. local lines
  21. lines=($(\grep "$1" $FILELIST | sed -e 's/\/[^/]*$//' | sort | uniq))
  22. if [[ ${#lines[@]} = 0 ]]; then
  23. echo "Not found"
  24. return
  25. fi
  26. local pathname
  27. local choice
  28. if [[ ${#lines[@]} > 1 ]]; then
  29. while [[ -z "$pathname" ]]; do
  30. local index=1
  31. local line
  32. for line in ${lines[@]}; do
  33. printf "%6s %s\n" "[$index]" $line
  34. index=$(($index + 1))
  35. done
  36. echo
  37. echo -n "Select one: "
  38. unset choice
  39. read choice
  40. if [[ $choice -gt ${#lines[@]} || $choice -lt 1 ]]; then
  41. echo "Invalid choice"
  42. continue
  43. fi
  44. pathname=${lines[@]:$(($choice-1)):1}
  45. done
  46. else
  47. pathname=${lines[@]:0:1}
  48. fi
  49. \cd $T/$pathname
  50. }

这是一个很使用的函数,可以快速的跳转到所指向的目标目录。该命令需要一个目标文件名作为命令行参数

  • 首先需要创建一个工程文件的filelist,该filelist 默认是放在 gettop 函数得到的工程根目录。当然如果有 OUT_DIR 指定了,那么filelist 会放在该目录下;
  • 如果 filelist 文件不存在,会创建并打印 "Creating  index...";
  • filelist 创建好后,通过 grep和sed 命令从 filelist 中查找所需要指向的目标文件所在的目录,并进行排序;
  • 如果没有找到指定的目标文件,则会打印 "Not found" 并返回;
  • 如果查找到有指定目标文件,则通过 while 循环将包含该文件的目录打印出来,并打印 "Select one: ",提醒工程师选择;
  • 选择 pathname,cd TOP/pathname;

3. 编译函数

3.1 build_build_var_cache

该函数会在 set_stuff_for_environment 函数之前调用,set_stuff_for_environment 详细看第 2.8 节。

该函数用以将 envsetup.sh 中使用函数 get_build_varget_abs_build_var 查询的变量都收集到 cached_vars cached_abs_vars 中,然后通过 soong_ui.bash 脚本创建键值对。

  1. function build_build_var_cache()
  2. {
  3. local T=$(gettop)
  4. # Grep out the variable names from the script.
  5. 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' ' '`)
  6. 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' ' '`)
  7. # Call the build system to dump the "<val>=<value>" pairs as a shell script.
  8. build_dicts_script=`\builtin cd $T; build/soong/soong_ui.bash --dumpvars-mode \
  9. --vars="${cached_vars[*]}" \
  10. --abs-vars="${cached_abs_vars[*]}" \
  11. --var-prefix=var_cache_ \
  12. --abs-var-prefix=abs_var_cache_`
  13. local ret=$?
  14. if [ $ret -ne 0 ]
  15. then
  16. unset build_dicts_script
  17. return $ret
  18. fi
  19. # Execute the script to store the "<val>=<value>" pairs as shell variables.
  20. eval "$build_dicts_script"
  21. ret=$?
  22. unset build_dicts_script
  23. if [ $ret -ne 0 ]
  24. then
  25. return $ret
  26. fi
  27. BUILD_VAR_CACHE_READY="true"
  28. }

如果在 eval 之前将 $build_dicts_script 打印出来,结果大致如下:

  1. var_cache_2ND_TARGET_GCC_VERSION='4.9'
  2. var_cache_ANDROID_BUILD_PATHS='/home/justinwei/work/myproduct/android/out/soong/host/linux-x86/bin:/home/justinwei/work/myproduct/android/out/host/linux-x86/bin'
  3. var_cache_COMMON_LUNCH_CHOICES='C3MN-userdebug aosp_arm-eng aosp_arm64-eng aosp_blueline_car-userdebug aosp_bonito_car-userdebug aosp_car_arm-userdebug aosp_car_arm64-userdebug aosp_car_x86-userdebug aosp_car_x86_64-userdebug aosp_cf_arm64_auto-userdebug aosp_cf_arm64_phone-userdebug aosp_cf_x86_64_phone-userdebug aosp_cf_x86_auto-userdebug aosp_cf_x86_phone-userdebug aosp_cf_x86_tv-userdebug aosp_coral_car-userdebug aosp_crosshatch_car-userdebug aosp_flame_car-userdebug aosp_x86-eng aosp_x86_64-eng arm_krait-eng arm_v7_v8-eng armv8-eng armv8_kryo385-eng car_x86_64-userdebug shift-userdebug shift_global-userdebug shift_in-userdebug shiftevb-userdebug qemu_trusty_arm64-userdebug silvermont-eng'
  4. var_cache_HOST_PREBUILT_TAG='linux-x86'
  5. var_cache_TARGET_ARCH='arm64'
  6. var_cache_TARGET_BUILD_VARIANT='user'
  7. var_cache_TARGET_DEVICE='shift'
  8. var_cache_TARGET_GCC_VERSION='4.9'
  9. var_cache_TARGET_PLATFORM_VERSION='RP1A'
  10. var_cache_TARGET_PRODUCT='shift'
  11. var_cache_print=''
  12. var_cache_report_config='============================================
  13. PLATFORM_VERSION_CODENAME=REL
  14. PLATFORM_VERSION=11
  15. TARGET_PRODUCT=shift
  16. TARGET_BUILD_VARIANT=user
  17. TARGET_BUILD_TYPE=release
  18. TARGET_ARCH=arm64
  19. TARGET_ARCH_VARIANT=armv8-a
  20. TARGET_CPU_VARIANT=cortex-a55
  21. TARGET_2ND_ARCH=arm
  22. TARGET_2ND_ARCH_VARIANT=armv8-a
  23. TARGET_2ND_CPU_VARIANT=cortex-a55
  24. HOST_ARCH=x86_64
  25. HOST_2ND_ARCH=x86
  26. HOST_OS=linux
  27. HOST_OS_EXTRA=Linux-5.10.102.1-microsoft-standard-WSL2-x86_64-Ubuntu-18.04.2-LTS
  28. HOST_CROSS_OS=windows
  29. HOST_CROSS_ARCH=x86
  30. HOST_CROSS_2ND_ARCH=x86_64
  31. HOST_BUILD_TYPE=release
  32. BUILD_ID=RD2A.211001.002
  33. OUT_DIR=out
  34. PRODUCT_SOONG_NAMESPACES=vendor/qcom/opensource/commonsys/packages/apps/Bluetooth vendor/qcom/opensource/commonsys/system/bt/conf
  35. ============================================'
  36. abs_var_cache_ANDROID_CLANG_PREBUILTS='/home/justinwei/work/myproduct/android/prebuilts/clang/host/linux-x86'
  37. abs_var_cache_ANDROID_GCC_PREBUILTS='/home/justinwei/work/myproduct/android/prebuilts/gcc/linux-x86'
  38. abs_var_cache_ANDROID_JAVA_HOME='/home/justinwei/work/myproduct/android/prebuilts/jdk/jdk11/linux-x86'
  39. abs_var_cache_ANDROID_JAVA_TOOLCHAIN='/home/justinwei/work/myproduct/android/prebuilts/jdk/jdk11/linux-x86/bin'
  40. abs_var_cache_ANDROID_PREBUILTS='/home/justinwei/work/myproduct/android/prebuilt/linux-x86'
  41. abs_var_cache_HOST_OUT='/home/justinwei/work/myproduct/android/out/host/linux-x86'
  42. abs_var_cache_HOST_OUT_TESTCASES='/home/justinwei/work/myproduct/android/out/host/linux-x86/testcases'

3.2 get_abs_build_var

  1. function get_abs_build_var()
  2. {
  3. if [ "$BUILD_VAR_CACHE_READY" = "true" ]
  4. then
  5. eval "echo \"\${abs_var_cache_$1}\""
  6. return
  7. fi
  8. local T=$(gettop)
  9. if [ ! "$T" ]; then
  10. echo "Couldn't locate the top of the tree. Try setting TOP." >&2
  11. return
  12. fi
  13. (\cd $T; build/soong/soong_ui.bash --dumpvar-mode --abs $1)
  14. }

通过 soog_ui.bash 解析需要查找的变量,该变量已经在之前的 build_build_var_cache 函数中创建了键值对,详细看第 3.1 节。

这些变量都是以 abs_var_cache_ 作为前缀,详细看第 3.1 节中打印的情况。

3.3 get_build_var

  1. function get_build_var()
  2. {
  3. if [ "$BUILD_VAR_CACHE_READY" = "true" ]
  4. then
  5. eval "echo \"\${var_cache_$1}\""
  6. return
  7. fi
  8. local T=$(gettop)
  9. if [ ! "$T" ]; then
  10. echo "Couldn't locate the top of the tree. Try setting TOP." >&2
  11. return
  12. fi
  13. (\cd $T; build/soong/soong_ui.bash --dumpvar-mode $1)
  14. }

该函数与上面的 get_abs_build_var 差不多的,区别在于 get_abs_build_var 指定的变量都是以 abs_var_cache_ 为前缀,而get_build_var 指定的变量都是以 var_cache_ 为前缀。

3.4 print_lunch_menu

  1. function print_lunch_menu()
  2. {
  3. local uname=$(uname)
  4. local choices=$(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES)
  5. echo
  6. echo "You're building on" $uname
  7. echo
  8. echo "Lunch menu... pick a combo:"
  9. local i=1
  10. local choice
  11. for choice in $(echo $choices)
  12. do
  13. echo " $i. $choice"
  14. i=$(($i+1))
  15. done
  16. echo
  17. }

打印 lunch 可选的 combo:

  1. You're building on Linux
  2. Lunch menu... pick a combo:
  3. 1. C3MN-userdebug
  4. 2. aosp_arm-eng
  5. 3. aosp_arm64-eng
  6. 4. aosp_blueline_car-userdebug
  7. 5. aosp_bonito_car-userdebug
  8. 6. aosp_car_arm-userdebug
  9. 7. aosp_car_arm64-userdebug
  10. 8. aosp_car_x86-userdebug
  11. 9. aosp_car_x86_64-userdebug
  12. 10. aosp_cf_arm64_auto-userdebug
  13. 11. aosp_cf_arm64_phone-userdebug
  14. 12. aosp_cf_x86_64_phone-userdebug
  15. 13. aosp_cf_x86_auto-userdebug
  16. 14. aosp_cf_x86_phone-userdebug
  17. 15. aosp_cf_x86_tv-userdebug
  18. 16. aosp_coral_car-userdebug
  19. 17. aosp_crosshatch_car-userdebug
  20. 18. aosp_flame_car-userdebug

3.5 lunch

  1. function lunch()
  2. {
  3. local answer
  4. if [ "$1" ] ; then
  5. answer=$1
  6. else
  7. print_lunch_menu
  8. echo -n "Which would you like? [aosp_arm-eng] "
  9. read answer
  10. fi
  11. local selection=
  12. if [ -z "$answer" ]
  13. then
  14. selection=aosp_arm-eng
  15. elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
  16. then
  17. local choices=($(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES))
  18. if [ $answer -le ${#choices[@]} ]
  19. then
  20. # array in zsh starts from 1 instead of 0.
  21. if [ -n "$ZSH_VERSION" ]
  22. then
  23. selection=${choices[$(($answer))]}
  24. else
  25. selection=${choices[$(($answer-1))]}
  26. fi
  27. fi
  28. else
  29. selection=$answer
  30. fi
  31. export TARGET_BUILD_APPS=
  32. local product variant_and_version variant version
  33. product=${selection%%-*} # Trim everything after first dash
  34. variant_and_version=${selection#*-} # Trim everything up to first dash
  35. if [ "$variant_and_version" != "$selection" ]; then
  36. variant=${variant_and_version%%-*}
  37. if [ "$variant" != "$variant_and_version" ]; then
  38. version=${variant_and_version#*-}
  39. fi
  40. fi
  41. if [ -z "$product" ]
  42. then
  43. echo
  44. echo "Invalid lunch combo: $selection"
  45. return 1
  46. fi
  47. TARGET_PRODUCT=$product \
  48. TARGET_BUILD_VARIANT=$variant \
  49. TARGET_PLATFORM_VERSION=$version \
  50. build_build_var_cache
  51. if [ $? -ne 0 ]
  52. then
  53. return 1
  54. fi
  55. export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT)
  56. export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT)
  57. if [ -n "$version" ]; then
  58. export TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION)
  59. else
  60. unset TARGET_PLATFORM_VERSION
  61. fi
  62. export TARGET_BUILD_TYPE=release
  63. echo
  64. set_stuff_for_environment
  65. printconfig
  66. destroy_build_var_cache
  67. }

函数是android 系统编译之前的核心操作,大致流程如下:

  • lunch 有参数否?没有的话 print_lunch_menu,工程师进行选择;
  • 根据选择,确定 build variant;
  • 根据选择,确定 build product;
  • build_build_var_cache 函数调用,配置编译所需变量,详细看第 3.1 节;
  • 设置环境变量 TARGET_PRODUCT、TARGET_BUILD_VARIANT、TARGET_PLATFORM_VERSION、TARGET_BUILD_TYPE;
  • set_stuff_for_environment 设置其他环境变量,详细看第 2.8 节;
  • printconfig 将 lunch配置信息打印出来,详细看第 2.9 节;
  • destroy_build_var_cache 销毁build_build_var_cache 创建的变量键值对;

3.6 _trigger_build

在分析 m、mm、mmm 等函数之前,需要了解下 _trigger_build 这个函数:

  1. function _trigger_build()
  2. (
  3. local -r bc="$1"; shift
  4. if T="$(gettop)"; then
  5. _wrap_build "$T/build/soong/soong_ui.bash" --build-mode --${bc} --dir="$(pwd)" "$@"
  6. else
  7. echo "Couldn't locate the top of the tree. Try setting TOP."
  8. fi
  9. )

主要调用 $TOP/build/soong/soong_ui.bash 脚本进行编译,然后通过 _wrap_build 函数对结果进行特殊显示,详细见下面。

3.6.1 _wrap_build

  1. function _wrap_build()
  2. {
  3. if [[ "${ANDROID_QUIET_BUILD:-}" == true ]]; then
  4. "$@"
  5. return $?
  6. fi
  7. local start_time=$(date +"%s")
  8. "$@"
  9. local ret=$?
  10. local end_time=$(date +"%s")
  11. local tdiff=$(($end_time-$start_time))
  12. local hours=$(($tdiff / 3600 ))
  13. local mins=$((($tdiff % 3600) / 60))
  14. local secs=$(($tdiff % 60))
  15. local ncolors=$(tput colors 2>/dev/null)
  16. if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then
  17. color_failed=$'\E'"[0;31m"
  18. color_success=$'\E'"[0;32m"
  19. color_reset=$'\E'"[00m"
  20. else
  21. color_failed=""
  22. color_success=""
  23. color_reset=""
  24. fi
  25. echo
  26. if [ $ret -eq 0 ] ; then
  27. echo -n "${color_success}#### build completed successfully "
  28. else
  29. echo -n "${color_failed}#### failed to build some targets "
  30. fi
  31. if [ $hours -gt 0 ] ; then
  32. printf "(%02g:%02g:%02g (hh:mm:ss))" $hours $mins $secs
  33. elif [ $mins -gt 0 ] ; then
  34. printf "(%02g:%02g (mm:ss))" $mins $secs
  35. elif [ $secs -gt 0 ] ; then
  36. printf "(%s seconds)" $secs
  37. fi
  38. echo " ####${color_reset}"
  39. echo
  40. return $ret
  41. }

主要处理结果,以及结果的颜色效果、编译用时。

3.7 m 和mm系列

  1. function m()
  2. (
  3. call_hook ${FUNCNAME[0]} $@
  4. if [ $? -ne 0 ]; then
  5. return 1
  6. fi
  7. _trigger_build "all-modules" "$@"
  8. )
  9. function mm()
  10. (
  11. call_hook ${FUNCNAME[0]} $@
  12. if [ $? -ne 0 ]; then
  13. return 1
  14. fi
  15. _trigger_build "modules-in-a-dir-no-deps" "$@"
  16. )
  17. function mmm()
  18. (
  19. call_hook ${FUNCNAME[0]} $@
  20. if [ $? -ne 0 ]; then
  21. return 1
  22. fi
  23. _trigger_build "modules-in-dirs-no-deps" "$@"
  24. )
  25. function mma()
  26. (
  27. call_hook ${FUNCNAME[0]} $@
  28. if [ $? -ne 0 ]; then
  29. return 1
  30. fi
  31. _trigger_build "modules-in-a-dir" "$@"
  32. )
  33. function mmma()
  34. (
  35. call_hook ${FUNCNAME[0]} $@
  36. if [ $? -ne 0 ]; then
  37. return 1
  38. fi
  39. _trigger_build "modules-in-dirs" "$@"
  40. )
  • m,从根部开始编译所有的模块,m 也可以指定module,编译直接用 m 命令即可;
  • mm,编译当前目录下所有的模块,当前目录下需要有Androd.mk,否则会往上一级找,只编译当前模块,不会编译依赖模块;
  • mmm,同mm,编译指定目录下的所有模块;
  • mma,同mm,需要编译依赖模块;
  • mmma,同mmm,需要编译依赖模块;

4. 搜索函数

  1. function ggrep()
  2. {
  3. find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.gradle" \
  4. -exec grep --color -n "$@" {} +
  5. }
  6. function gogrep()
  7. {
  8. find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.go" \
  9. -exec grep --color -n "$@" {} +
  10. }
  11. function jgrep()
  12. {
  13. find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.java" \
  14. -exec grep --color -n "$@" {} +
  15. }
  16. function cgrep()
  17. {
  18. 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' \) \
  19. -exec grep --color -n "$@" {} +
  20. }
  21. function resgrep()
  22. {
  23. local dir
  24. for dir in `find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -name res -type d`; do
  25. find $dir -type f -name '*\.xml' -exec grep --color -n "$@" {} +
  26. done
  27. }
  28. function mangrep()
  29. {
  30. find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'AndroidManifest.xml' \
  31. -exec grep --color -n "$@" {} +
  32. }
  33. function owngrep()
  34. {
  35. find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'OWNERS' \
  36. -exec grep --color -n "$@" {} +
  37. }
  38. function sepgrep()
  39. {
  40. find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -name sepolicy -type d \
  41. -exec grep --color -n -r --exclude-dir=\.git "$@" {} +
  42. }
  43. function rcgrep()
  44. {
  45. find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.rc*" \
  46. -exec grep --color -n "$@" {} +
  47. }

搜索的几个函数基本相同,都是使用的 find 命令和 grep 命令结合。

首先, find 会排除一些文件夹:

  • .repo
  • .git
  • out

接着,find 会指定文件类型:

  • ggrep 针对 *.gradle 文件
  • gogreap 针对 *.go 文件
  • jgrep 针对 *.java 文件
  • cgrep 针对 *.c  *.cc  *.cpp  *.h  *.hpp
  • resgrep 针对目录中含有 *.xml
  • mangrep 针对 AndroidManifest.xml 文件
  • owngrep 针对 OWNERS 文件
  • sepgrep 针对名为 sepolicy 目录
  • rcgrep 针对 *.rc 的文件

最后,find 通过 -exec 执行 grep 命令

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/413309
推荐阅读
相关标签
  

闽ICP备14008679号