一般初始化android的编译环境,最简单的方法是这样的:
1. step 1: . build/envsetup.sh
2. step 2: lunch
下面我们看看这两个命令的底层细节是怎样的。
step 1 source了 build/envsetup.sh这文件,这个文件里面有什么内容呢? 看了一下全部是各种函数
1 function hmm() {
2 cat <<EOF
3 Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
4 - lunch: lunch <product_name>-<build_variant>
5 - tapas: tapas [<App1> <App2> ...] [arm|x86|mips|armv5] [eng|userdebug|user]
6 - croot: Changes directory to the top of the tree.
7 - m: Makes from the top of the tree.
8 - mm: Builds all of the modules in the current directory, but not their dependencies.
9 - mmm: Builds all of the modules in the supplied directories, but not their dependencies.
10 - mma: Builds all of the modules in the current directory, and their dependencies.
11 - mmma: Builds all of the modules in the supplied directories, and their dependencies.
12 - cgrep: Greps on all local C/C++ files.
13 - jgrep: Greps on all local Java files.
14 - resgrep: Greps on all local res/*.xml files.
15 - godir: Go to the directory containing a file.
16
17 Look at the source to view more functions. The complete list is:
18 EOF
19 T=$(gettop)
20 local A
21 A=""
22 for i in `cat $T/build/envsetup.sh | sed -n "/^function /s/function \([a-z_]*\).*/\1/p" | sort`; do
23 A="$A $i"
24 done
25 echo $A
26 }
上面是一个例子,也是一个帮助信息,可以列出了当前文件中所有的函数。然后看文件的最后实际执行了一小段脚本:
1328 if [ "x$SHELL" != "x/bin/bash" ]; then
1329 case `ps -o command -p $$` in
1330 *bash*)
1331 ;;
1332 *)
1333 echo "WARNING: Only bash is supported, use of other shell would lead to erroneous results"
1334 ;;
1335 esac
1336 fi
1337
1338 # Execute the contents of any vendorsetup.sh files we can find.
1339 for f in `test -d device && find device -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null` \
1340 `test -d vendor && find vendor -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null`
1341 do
1342 echo "including $f"
1343 . $f
1344 done
1345 unset f
1346
1347 addcompletions
做了两件重要的事情 : (1)source device,vendor目录下面的vendorsetup.sh文件 (2)添加命令adb 补全功能(无关紧要)
vendorsetup.sh中都只有一行,举个例子 device/asus/flo/vendorsetup.sh有这么一行:
17 add_lunch_combo aosp_flo-userdebug
那么add_lunch_combo做了什么呢?他吧aosp_flo-userdebug 这些变量放到一个叫做LUNCH_MENU_CHOICES的数组里面。看看代码吧:
413 # Clear this variable. It will be built up again when the vendorsetup.sh
414 # files are included at the end of this file.
415 unset LUNCH_MENU_CHOICES
416 function add_lunch_combo()
417 {
418 local new_combo=$1
419 local c
420 for c in ${LUNCH_MENU_CHOICES[@]} ; do
421 if [ "$new_combo" = "$c" ] ; then
422 return
423 fi
424 done
425 LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
426 }
如果这个变量没有重复的话,就放到数组里面吧。
综上所述,. build/envsetup.sh 的执行就是添加了各种函数,而且建立起了一个数组,这个数组是所有(厂商——设备)的组合,以供你下面选择你需要编译那个设备的代码。
step 2: 下面是step 2 : lunch 的执行细节。
lunch这个函数有两个用法,一个是有参数的用法,另一个是没有参数的用法。 有参数的那么参数就是你要选择的那个设备。没参数的时候会把所有设备列出来,供你选择。
当然,这个列出的菜单,有名字也有前面的数字标号,你可以直接填数字,这样会快,不用敲一大堆字母了。
之后会对你的输入(这个输入包括参数和选择)做sanity check。最后通过你的选择得到两个很重要的变量,并且export粗来:
515
516 export TARGET_PRODUCT=$product
517 export TARGET_BUILD_VARIANT=$variant
518 export TARGET_BUILD_TYPE=release
最后把搜集到的信息打印粗来。整个lunch过程到此结束。多说无意,上代码吧:
453 function lunch()
454 {
455 local answer
456
457 if [ "$1" ] ; then
458 answer=$1
459 else
460 print_lunch_menu
461 echo -n "Which would you like? [aosp_arm-eng] "
462 read answer
463 fi
464
465 local selection=
466
467 if [ -z "$answer" ]
468 then
469 selection=aosp_arm-eng
470 elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
471 then
472 if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
473 then
474 selection=${LUNCH_MENU_CHOICES[$(($answer-1))]}
475 fi
476 elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")
477 then
478 selection=$answer
479 fi
480
481 if [ -z "$selection" ]
482 then
483 echo
484 echo "Invalid lunch combo: $answer"
485 return 1
486 fi
487
488 export TARGET_BUILD_APPS=
489
490 local product=$(echo -n $selection | sed -e "s/-.*$//")
491 check_product $product
492 if [ $? -ne 0 ]
493 then
494 echo
495 echo "** Don't have a product spec for: '$product'"
496 echo "** Do you have the right repo manifest?"
497 product=
498 fi
499
500 local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")
501 check_variant $variant
502 if [ $? -ne 0 ]
503 then
504 echo
505 echo "** Invalid variant: '$variant'"
506 echo "** Must be one of ${VARIANT_CHOICES[@]}"
507 variant=
508 fi
509
510 if [ -z "$product" -o -z "$variant" ]
511 then
512 echo
513 return 1
514 fi
515
516 export TARGET_PRODUCT=$product
517 export TARGET_BUILD_VARIANT=$variant
518 export TARGET_BUILD_TYPE=release
519
520 echo
521
522 set_stuff_for_environment
523 printconfig
524 }
这里面对参数用sed进行拆分,得到prduct和variant .然后调用 check_product 和 check_variant 对两个变量进行sanity check。下面看下这两个函数,都比较短。
52 # check to see if the supplied product is one we can build
53 function check_product()
54 {
55 T=$(gettop)
56 if [ ! "$T" ]; then
57 echo "Couldn't locate the top of the tree. Try setting TOP." >&2
58 return
59 fi
60 CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
61 TARGET_PRODUCT=$1 \
62 TARGET_BUILD_VARIANT= \
63 TARGET_BUILD_TYPE= \
64 TARGET_BUILD_APPS= \
65 get_build_var TARGET_DEVICE > /dev/null
66 # hide successful answers, but allow the errors to show
67 }
这个函数由调用了get_build_var,也很短:
40 # Get the exact value of a build variable.
41 function get_build_var()
42 {
43 T=$(gettop)
44 if [ ! "$T" ]; then
45 echo "Couldn't locate the top of the tree. Try setting TOP." >&2
46 return
47 fi
48 CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
49 make --no-print-directory -C "$T" -f build/core/config.mk dumpvar-$1
50 }
这里执行了一个make命令,命令执行的文件是 build/core/config.mk 目标为dumpvar-$1(dumpvar-TARGET_DEVICE).很显然,代码引导我们查看文件
build/core/config.mk 并且搜索目标dumpvar-TARGET_DEVICE.
通过查看build/core/config.mk 我们知道了,这个文件并没有dumpvar-TARGET_DEVICE.但是他包含了其他几个重要文件,这些文件里面有我们要找的目标。
buid/core/envsetup.mk BoardConfig.mk dumpvar.mk.包含关系相关的代码如下:
134 # ---------------------------------------------------------------
135 # Define most of the global variables. These are the ones that
136 # are specific to the user's build configuration.
137 include $(BUILD_SYSTEM)/envsetup.mk
138
139 # Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)
140 # or under vendor/*/$(TARGET_DEVICE). Search in both places, but
141 # make sure only one exists.
142 # Real boards should always be associated with an OEM vendor.
143 board_config_mk := \
144 $(strip $(wildcard \
145 $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
146 $(shell test -d device && find device -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \
147 $(shell test -d vendor && find vendor -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \
148 ))
149 ifeq ($(board_config_mk),)
150 $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))
151 endif
152 ifneq ($(words $(board_config_mk)),1)
153 $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))
154 endif
155 include $(board_config_mk)
所以我们下面重点查看这三个文件,并搜索目标dumpva-TARGET_DEVICE.
第一个文件:build/core/envsetup.mk 的代码并不多,其中有这么一行:
115 include $(BUILD_SYSTEM)/product_config.mk
这个文件又包含了build/core/product_config.mk,查看这个文件,这个文件解密了TARGET_DEVICE这个变量
275 TARGET_DEVICE := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE)
可以看出TARGET_DEVICE是根据PRODUCT来取得的,根据PRODUCT变量取得该目录下面的AndroidProduct.mk,进而取得 full_xx.mk这个是当前product的配置文件。
这里面有个变量叫做PRODUCT_DEVICE经过重命名之后得到的 $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE) 。
第二个文件:BoardConfig.mk这个是在具体设备的目录下面的,内容不多,涉及到CPU的体系结构等。
第三个文件:dumpvar.mk 内容比较多。 但是这里面有我们要找的目标dumpvar-TARGET_DEVICE。
53 dumpvar_goals := \
54 $(strip $(patsubst dumpvar-%,%,$(filter dumpvar-%,$(MAKECMDGOALS))))
55 ifdef dumpvar_goals
56
57 ifneq ($(words $(dumpvar_goals)),1)
58 $(error Only one "dumpvar-" goal allowed. Saw "$(MAKECMDGOALS)")
59 endif
60
61 # If the goal is of the form "dumpvar-abs-VARNAME", then
62 # treat VARNAME as a path and return the absolute path to it.
63 absolute_dumpvar := $(strip $(filter abs-%,$(dumpvar_goals)))
64 ifdef absolute_dumpvar
65 dumpvar_goals := $(patsubst abs-%,%,$(dumpvar_goals))
66 ifneq ($(filter /%,$($(dumpvar_goals))),)
67 DUMPVAR_VALUE := $($(dumpvar_goals))
68 else
69 DUMPVAR_VALUE := $(PWD)/$($(dumpvar_goals))
70 endif
71 dumpvar_target := dumpvar-abs-$(dumpvar_goals)
72 else
73 DUMPVAR_VALUE := $($(dumpvar_goals))
74 dumpvar_target := dumpvar-$(dumpvar_goals)
75 endif
76
77 .PHONY: $(dumpvar_target)
78 $(dumpvar_target):
79 @echo $(DUMPVAR_VALUE)
可以看到这个目标,并没有干什么事,就是吧TARGET_DEVICE打印出来而已。