文章目录

  • 1、envsetup.sh
  • 2、lunch
  • 3、make

在Android开发环境中编译一个目标时,一般要执行下面三行命令:

$ . build/envsetup.sh
$ lunch <product_name>-<build_variant>
$ make [module]

这三行命令是什么意思呢?下面逐一介绍。

1、envsetup.sh

envsetup.sh是个shell脚本,位于build目录下,第一行命令便是执行这个脚本。执行脚本有多种方式,那为什么要使用句点来执行呢?传统的执行脚本方式有下面两种:

$ bash envsetup.sh
$ chmod +x envsetup.sh; ./envsetup.sh

上面两种执行方式的效果一样,都是在当前shell的子shell中进行的,类似于一个子进程,其结果是在脚本执行结束之后,脚本中的全局变量、函数等在当前shell是访问不到的,脚本的执行结果也不会影响当前shell,但是,这个脚本从名字上来看是做一些环境配置相关的事情,事实上也确实如此,这样的话脚本执行结果应该对当前shell有效,而句点命令就满足这个要求,另外,“source”命令也有同样的效果,可以替代句点命令。

以Android 6.0 marshmallow版本为例,这个脚本有1560行代码,里面都有些什么东西呢?这个脚本的一大特点就是定义了一系列函数,几乎占据了脚本的全部内容,这些函数可以在脚本内部调用,使用句点命令执行脚本之后,这些函数也可以在当前shell调用。下面先罗列一下这些函数的名字,多达70个,它们以空格分隔。

addcompletions add_lunch_combo cgrep check_product check_variant choosecombo chooseproduct choosetype choosevariant core coredump_enable coredump_setup cproj croot findmakefile get_abs_build_var getbugreports get_build_var getdriver getlastscreenshot get_make_command getprebuilt getscreenshotpath getsdcardpath gettargetarch gettop ggrep godir hmm is isviewserverstarted jgrep key_back key_home key_menu lunch _lunch m make mangrep mgrep mm mma mmm mmma pez pid printconfig print_lunch_menu provision qpid rcgrep resgrep runhat runtest sepgrep set_java_home setpaths set_sequence_number set_stuff_for_environment settitle sgrep smoketest stacks startviewserver stopviewserver systemstack tapas tracedmdump treegrep

有这么多的函数,在shell中常用的也就几个,这些函数在脚本的第一个函数hmm中作了具体说明如下:

Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
- lunch:     lunch <product_name>-<build_variant>
- tapas:     tapas [<App1> <App2> ...] [arm|x86|mips|armv5|arm64|x86_64|mips64] [eng|userdebug|user]
- croot:     Changes directory to the top of the tree.
- m:         Makes from the top of the tree.
- mm:        Builds all of the modules in the current directory, but not their dependencies.
- mmm:       Builds all of the modules in the supplied directories, but not their dependencies.To limit the modules being built use the syntax: mmm dir/:target1,target2.
- mma:       Builds all of the modules in the current directory, and their dependencies.
- mmma:      Builds all of the modules in the supplied directories, and their dependencies.
- provision: Flash device with all required partitions. Options will be passed on to fastboot.
- cgrep:     Greps on all local C/C++ files.
- ggrep:     Greps on all local Gradle files.
- jgrep:     Greps on all local Java files.
- resgrep:   Greps on all local res/*.xml files.
- mangrep:   Greps on all local AndroidManifest.xml files.
- mgrep:     Greps on all local Makefiles files.
- sepgrep:   Greps on all local sepolicy files.
- sgrep:     Greps on all local source files.
- godir:     Go to the directory containing a file.

当我们执行这个脚本的时候,与这些函数都没什么关系,能够执行到的代码有下面几行。

首先,是一个if语句,判断当前shell即SHELL这个系统变量是否为bash,不是bash时给出警告:

if [ "x$SHELL" != "x/bin/bash" ]; thencase `ps -o command -p $$` in*bash*);;*)echo "WARNING: Only bash is supported, use of other shell would lead to erroneous results";;esac
fi

然后,通过一个for循环查找device、vendor、product这几个目录下名为vendorsetup.sh的脚本,test命令测试被查找对象是否为一个目录,为目录时执行find命令开始查找,包括链接文件,目录查找深度为4,标准错误输出重定向到/dev/null,sort排序增加了下面echo命令的可读性,文件找到后立刻通过句点命令执行这个文件,for循环结束后unset变量f以防止全局变量的不良影响。

for f in `test -d device && find -L device -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \`test -d vendor && find -L vendor -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \`test -d product && find -L product -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort`
doecho "including $f". $f
done
unset f

最后,执行addcompletions函数,如果shell不是bash或者bash的版本小于3的话将直接return,该函数的真正目的是执行sdk/adb_completion目录下所有以.bash结尾的脚本:

addcompletions
addcompletions函数的定义如下:
function addcompletions()
{local T dir fif [ -z "${BASH_VERSION}" ]; thenreturnfiif [ ${BASH_VERSINFO[0]} -lt 3 ]; thenreturnfidir="sdk/bash_completion"if [ -d ${dir} ]; thenfor f in `/bin/ls ${dir}/[a-z]*.bash 2> /dev/null`; doecho "including $f". $fdonefi
}

下面是envsetup.sh这个脚本执行的输出,显示了执行的外部脚本的路径:

including device/asus/deb/vendorsetup.sh
including device/asus/flo/vendorsetup.sh
including device/asus/fugu/vendorsetup.sh
including device/generic/mini-emulator-arm64/vendorsetup.sh
including device/generic/mini-emulator-armv7-a-neon/vendorsetup.sh
including device/generic/mini-emulator-mips/vendorsetup.sh
including device/generic/mini-emulator-x86_64/vendorsetup.sh
including device/generic/mini-emulator-x86/vendorsetup.sh
including device/htc/flounder/vendorsetup.sh
including device/huawei/angler/vendorsetup.sh
including device/lge/bullhead/vendorsetup.sh
including device/lge/hammerhead/vendorsetup.sh
including device/moto/shamu/vendorsetup.sh
including sdk/bash_completion/adb.bash

以device/asus/deb/vendorsetup.sh为例,里面有一行内容如下:

add_lunch_combo aosp_deb-userdebug

上面的代码调用了add_lunch_combo函数,参数为aosp_deb-userdebug,意思是添加一个产品,产品名为aosp_deb,编译条件为userdebug,其实在envsetup.sh脚本内部也添加了几个默认的产品:

add_lunch_combo aosp_arm-eng
add_lunch_combo aosp_arm64-eng
add_lunch_combo aosp_mips-eng
add_lunch_combo aosp_mips64-eng
add_lunch_combo aosp_x86-eng
add_lunch_combo aosp_x86_64-eng

sdk/bash_completion/adb.bash定义了许多adb相关的函数,它们都以_adb开头,实现Tab键自动补全命令的功能,关键代码如下:

if [[ $(type -t compopt) = "builtin" ]]; thencomplete -F _adb adb
elsecomplete -o nospace -F _adb adb
fi

complete是bash的内建命令,-F表示执行adb命令时的自动补全功能由_adb函数提供,nospace表示自动补全命令的最后没有空格,默认是有空格的。

2、lunch

lunch是envsheup.sh中定义的一个函数,参数是前面介绍的通过add_lunch_combo函数添加的某个值,由两部分组成,产品名和编译条件,后者包括eng、userdebug、user,这三个选项在envsetup.sh中保存在了一个数组(或列表)中:

VARIANT_CHOICES=(user userdebug eng)

下面来看一下lunch函数的具体实现,它由两部分组成,第一部分是lunch函数本身,它的主要任务就是检查函数参数的正确性,即产品名是否正确、编译条件是否正确。首先,如果我们给lunch命令传入了参数它就开始检查这个参数,否则调用print_lunch_menu函数告诉我们lunch命令的参数有哪些候选项,通过read命令读入,这时我们可以输入某个具体的参数或者参数前面的数字序列号,两者都可以被正确处理。然后调用check_product检查产品名是否正确,调用check_variant检查编译条件是否正确。最后就是一些系统环境变量的设置,供makefile使用。

function lunch()
{local answerif [ "$1" ] ; thenanswer=$1elseprint_lunch_menuecho -n "Which would you like? [aosp_arm-eng] "read answerfilocal selection=if [ -z "$answer" ]thenselection=aosp_arm-engelif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")thenif [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]thenselection=${LUNCH_MENU_CHOICES[$(($answer-1))]}fielif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")thenselection=$answerfiif [ -z "$selection" ]thenechoecho "Invalid lunch combo: $answer"return 1fiexport TARGET_BUILD_APPS=local product=$(echo -n $selection | sed -e "s/-.*$//")check_product $productif [ $? -ne 0 ]thenechoecho "** Don't have a product spec for: '$product'"echo "** Do you have the right repo manifest?"product=filocal variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")check_variant $variantif [ $? -ne 0 ]thenechoecho "** Invalid variant: '$variant'"echo "** Must be one of ${VARIANT_CHOICES[@]}"variant=fiif [ -z "$product" -o -z "$variant" ]thenechoreturn 1fiexport TARGET_PRODUCT=$productexport TARGET_BUILD_VARIANT=$variantexport TARGET_BUILD_TYPE=releaseechoset_stuff_for_environmentprintconfig
}

第二部分是_lunch函数,它的作用是实现lunch命令的候选参数的自动补全功能,通过Tab键完成,熟悉shell的话都知道通过Tab键实现命令自动补全,这个函数使用了一些相关的系统变量,如COMPREPLY、COMP_WORDS、COMP_CWORD,以及bash内建的命令completion和compgen,它们有其固定的用法。

function _lunch()
{local cur prev optsCOMPREPLY=()cur="${COMP_WORDS[COMP_CWORD]}"prev="${COMP_WORDS[COMP_CWORD-1]}"COMPREPLY=( $(compgen -W "${LUNCH_MENU_CHOICES[*]}" -- ${cur}) )return 0
}
complete -F _lunch lunch

下面是lunch命令后跟着回车键的结果:

You're building on LinuxLunch menu... pick a combo:1. aosp_arm-eng2. aosp_arm64-eng3. aosp_mips-eng4. aosp_mips64-eng5. aosp_x86-eng6. aosp_x86_64-eng7. aosp_deb-userdebug8. aosp_flo-userdebug9. full_fugu-userdebug10. aosp_fugu-userdebug11. mini_emulator_arm64-userdebug12. m_e_arm-userdebug13. mini_emulator_mips-userdebug14. mini_emulator_x86_64-userdebug15. mini_emulator_x86-userdebug16. aosp_flounder-userdebug17. aosp_angler-userdebug18. aosp_bullhead-userdebug19. aosp_hammerhead-userdebug20. aosp_hammerhead_fp-userdebug21. aosp_shamu-userdebugWhich would you like? [aosp_arm-eng]

然后选择对应的combo就可以了,比如说选择“5”或者“aosp_x86-eng”,两者的结果都是一样的,如下:

============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=6.0.1
TARGET_PRODUCT=aosp_x86
TARGET_BUILD_VARIANT=eng
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=x86
TARGET_ARCH_VARIANT=x86
TARGET_CPU_VARIANT=
TARGET_2ND_ARCH=
TARGET_2ND_ARCH_VARIANT=
TARGET_2ND_CPU_VARIANT=
HOST_ARCH=x86_64
HOST_OS=linux
HOST_OS_EXTRA=Linux-3.13.0-74-generic-x86_64-with-Ubuntu-14.04-trusty
HOST_CROSS_OS=windows
HOST_BUILD_TYPE=release
BUILD_ID=MASTER
OUT_DIR=out
============================================

上面的结果与选择的“aosp_x86-eng”相匹配。

3、make

make很简单了,就是执行makefile,Android源码根目录下只有一个简单的Makefile,只读文件:

### DO NOT EDIT THIS FILE ###a
include build/core/main.mk
### DO NOT EDIT THIS FILE ###

这个文件是必须的,一般不要修改,真正的东西在build/core/main.mk中。

有一点很重要,执行make命令时,真正的编译过程开始于envsetup.sh中的make函数,源码如下:

function make()
{local start_time=$(date +"%s")$(get_make_command) "$@"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 ]; thencolor_failed=$'\E'"[0;31m"color_success=$'\E'"[0;32m"color_reset=$'\E'"[00m"elsecolor_failed=""color_success=""color_reset=""fiechoif [ $ret -eq 0 ] ; thenecho -n "${color_success}#### make completed successfully "elseecho -n "${color_failed}#### make failed to build some targets "fiif [ $hours -gt 0 ] ; thenprintf "(%02g:%02g:%02g (hh:mm:ss))" $hours $mins $secselif [ $mins -gt 0 ] ; thenprintf "(%02g:%02g (mm:ss))" $mins $secselif [ $secs -gt 0 ] ; thenprintf "(%s seconds)" $secsfiecho " ####${color_reset}"echoreturn $ret
}

关键代码在于这一行“$(get_make_command) "$@"”“$(get_make_command)”的返回值为“command make”,“$@”的意思是make命令后面的参数。make函数其它代码的作用就是根据编译结果计算整个编译过程所花的时间,编译结果的输出还有个简单的配色方案。

【Bash百宝箱】Android envsetup.sh及lunch相关推荐

  1. android8.1 source build/envsetup.sh分析 增加删除lunch

    https://blog.csdn.net/weixin_39694445/article/details/84753142 Android 编译过程 1.  初始化参数设置  环境变量 2.  检查 ...

  2. 【Bash百宝箱】Makefile快速入门

    一.读懂Makefile 1.Makefile简介 简单来说,Makefile就是帮助我们编译工程(文件)并生成可执行文件(目标文件).现在的IDE以及一些自动化编译工具基本上都做了Makefile的 ...

  3. 编译Android源码(2) ---- envsetup.sh文件分析

    在Android源码下载完成后,只需要简单的三个步骤就能把Android编译完成( http://source.android.com/source/building.html): 1.当前目录切换到 ...

  4. 【转】Android source build/envsetup.sh学习笔记

    原文网址:http://blog.csdn.net/mliubing2532/article/details/7567164 如果你只需要修改某一个模块的内容,但是却每次都要执行make, 最后等待很 ...

  5. android 编译步骤分析之envsetup.sh

    1. cd AP      进入AP目录,如果已经进入到了A目录,可以忽略 2. source build/envsetup.sh        初始编译环境,envsetup.sh里面是一些编译命令 ...

  6. 【Bash百宝箱】Android源码下载及编译

    文章目录 0.版本管理 1.环境准备 2.源码下载 3.源码编译 4.产品定制 5.系统image tip:下文基于Android 6.0 Marshmallow,后续版本可能稍有不同. 0.版本管理 ...

  7. Android 编译系统分析之lunch分析

    已开通新的博客,后续文字都会发到新博客 http://www.0xfree.top Android 编译系统解析系列文档 编译系统入口envsetup.sh解析 source build/envset ...

  8. build/envsetup.sh脚本分析

    1. Android编译系统分析 编译脚本及系统变量 build/envsetup.sh脚本分析 在编译源代码之前通常需要在android源代码顶层目录执行 . ./build/envsetup.sh ...

  9. Android 4.03 编译系统------lunch

    http://www.2cto.com/kf/201208/150878.html 前面分析了Android 4.03编译系统-------envsetup.sh,今天来说lunch命令. 在Andr ...

最新文章

  1. 226. Invert Binary Tree 翻转二叉树
  2. 如何用两个开关控制同一盏灯
  3. 紧急预警:wls9_async_response.war组件漏洞的延续
  4. Soul网关源码解析目录
  5. springboot 不使用 thymeleaf_springboot 使用swagger 不显示basic-error-controller解决
  6. 20muduo_base库源码分析(十一)
  7. flutter 进度条_OkHttp 在 Flutter中的运用场景,Flutter语言连接网络的方法介绍
  8. Mysql官网下载教程
  9. Symbian面试题
  10. java calendar格式化_java日期处理专题:java 日期格式化
  11. 【个人网站】零基础个人网站搭建完整教程(附免费源码)
  12. 处理猪舌须知小窍门-滋阴润燥好良方-菜椒滑猪舌
  13. Android CoordinatorLayout之自定义Behavior
  14. dataV实现扇形图轮播
  15. Python开发【十一章】:RabbitMQ队列
  16. yxc_第二章 数据结构(一)_栈和队列
  17. 【C语言】实现两个字符串的连接(不使用库函数),即把字符串b连接到字符串a后面。
  18. 京东7天无理由退货换货运费应该谁来出是商家支付还是买家支付?
  19. 主攻文推荐攻守都有系统_【图片】【推文】一些喜欢的主攻文_主攻文吧_百度贴吧...
  20. 【英语学习】经济学人 (Intelligence Unit) 2020年12月版

热门文章

  1. FS78P372N单片机替代义隆EM78P372N
  2. 【学习笔记】Argus--H5游戏性能测试工具
  3. Rstudio中文乱码,UTF-8之后仍乱码,文本打开仍乱码
  4. 云米涉嫌专利侵权是小米生态链的整体隐患
  5. 百度之星(2015)
  6. 界面适配华为手机的虚拟按键的解决方案
  7. 「科普」如何评价供应商的MES系统
  8. 关于达内培训的名企定制班
  9. 达特茅斯学院计算机phd,2020年达特茅斯学院博士申请时间
  10. 什么是WiFi 7?| 奇怪的知识又增加了……