原网址:http://blog.csdn.net/lizekun2010/article/details/52598105
本文按照Android编译三部曲(source,lunch和make)的步骤来分析查看每个环节的主要流程,由于编译系统太过庞大,这里只是从关键的主干流程上做一个分析,不可能做到每个细节都剖析清楚,由于水平有限,如果有描述不够正确的地方,欢迎大家毫无保留的指正错误,在此先谢过。
1,source流程
当我们在终端执行命令source build/envsetup.sh时,其实是完整的加载了脚本envsetup.sh中的变量和方法,其中最重要的函数比如lunch就是在这一步加载到shell环境变量中去的,比较常用的函数有:
croot |
切换到源码树的根目录 |
lunch |
选择编译板型 |
m |
在源码树的根目录执行 make |
mm |
build 当前目录下的模块 |
mmm |
build 指定目录下的模块 |
cgrep |
在所有 C/C++ 文件上执行 grep |
jgrep |
在所有 Java 文件上执行 grep |
resgrep |
在所有 res/*.xml 文件上执行 grep |
godir |
转到包含某个文件的目录路径 |
printconfig |
显示当前 Build 的配置信息 |
add_lunch_combo |
在 lunch 函数的菜单中添加一个条目 |
除了加载上述函数,还执行了以下初始化和动作:
1> 提前定义3种编译模式,供后面使用:
- 139 VARIANT_CHOICES=(user userdebug eng)
说明:数字139是这行代码在文件中的行号,下面的也是如此。
2> 使用变量LUNCH_MENU_CHOICES之前,先将它清空:
- 503 unset LUNCH_MENU_CHOICES
3> 默认会加载如下6个板型选项,这样后续的lunch菜单中就可以看到这几个板型:
- 517 add_lunch_combo aosp_arm-eng
- 518 add_lunch_combo aosp_arm64-eng
- 519 add_lunch_combo aosp_mips-eng
- 520 add_lunch_combo aosp_mips64-eng
- 521 add_lunch_combo aosp_x86-eng
- 522 add_lunch_combo aosp_x86_64-eng
下面就看看函数add_lunch_combo的实现:
- 504 function add_lunch_combo()
- 505 {
- 506 local new_combo=$1
- 507 local c
- 508 for c in ${LUNCH_MENU_CHOICES[@]} ; do
- 509 if [ "$new_combo" = "$c" ] ; then
- 510 return
- 511 fi
- 512 done
- 513 LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo) //这里把板型添加到数组中去了
- 514 }
处理逻辑就是先从LUNCH_MENU_CHOICES中循环查找,看存不存在要添加的板型,如果存在就直接返回,如果不存在就添加到LUNCH_MENU_CHOICES中;除了上述添加的aosp的6个板型外,在device/actions/目录中还有大量的add_lunch_combo的调用,部分摘录如下:
- ./device/actions/s900_evb/vendorsetup.sh:add_lunch_combo s900_evb-eng
- ./device/actions/s900_evb/vendorsetup.sh:add_lunch_combo s900_evb-userdebug
- ./device/actions/s900_evb/vendorsetup.sh:add_lunch_combo s900_evb-user
- ./device/actions/v700_gb7_nibiru/vendorsetup.sh:add_lunch_combo v700_gb7_nibiru-eng
- ./device/actions/v700_gb7_nibiru/vendorsetup.sh:add_lunch_combo v700_gb7_nibiru-userdebug
- ./device/actions/v700_gb7_nibiru/vendorsetup.sh:add_lunch_combo v700_gb7_nibiru-user
- ./device/actions/v700_cxvr/vendorsetup.sh:add_lunch_combo v700_cxvr-eng
- ./device/actions/v700_cxvr/vendorsetup.sh:add_lunch_combo v700_cxvr-userdebug
- ./device/actions/v700_cxvr/vendorsetup.sh:add_lunch_combo v700_cxvr-user
- ./device/actions/s900_96board/vendorsetup.sh:add_lunch_combo s900_96board-eng
- ./device/actions/s900_96board/vendorsetup.sh:add_lunch_combo s900_96board-userdebug
- ./device/actions/s900_96board/vendorsetup.sh:add_lunch_combo s900_96board-user
这些全部都是在板型目录中的vendorsetup.sh脚本中写明的,脚本何时被调用的呢?【留个问题在此】
4> 下面这行的意思是,当敲入lunch命令后用Tab键进行补全时,会执行_lunch()函数:
- 630 complete -F _lunch lunch
马上实践一下,发现敲完lunch后按Tab键补全果然会有东西打印出来:
- aosp_angler-userdebug gt9_ebox-user s900_96board_sd0_boot-eng s900_evb_sd0_boot-eng s900vr_ys_2k-eng
- aosp_arm-eng gt9_ebox-userdebug s900_96board_sd0_boot-user s900_evb_sd0_boot-user s900vr_ys_2k-user
- aosp_arm64-eng hikey-userdebug s900_96board_sd0_boot-userdebug s900_evb_sd0_boot-userdebug s900vr_ys_2k-userdebug
- aosp_bullhead-userdebug m_e_arm-userdebug s900_RY_VR-eng s900_qcb-eng v700_cxvr-eng
- aosp_dragon-eng m_e_mips-userdebug s900_RY_VR-user s900_qcb-user v700_cxvr-user
- aosp_dragon-userdebug m_e_mips64-eng s900_RY_VR-userdebug s900_qcb-userdebug v700_cxvr-userdebug
- aosp_flounder-userdebug mini_emulator_arm64-userdebug s900_evb-eng s900_tpe-eng v700_gb7_nibiru-eng
那就看看函数_lunch的实现:
- 620 function _lunch()
- 621 {
- 622 local cur prev opts
- 623 COMPREPLY=()
- 624 cur="${COMP_WORDS[COMP_CWORD]}"
- 625 prev="${COMP_WORDS[COMP_CWORD-1]}"
- 627 COMPREPLY=( $(compgen -W "${LUNCH_MENU_CHOICES[*]}" -- ${cur}) )
- 628 return 0
- 629 }
这个函数的代码几乎是固定的,除了627行中的${LUNCH_MENU_CHOICES[*]}是可变的,其他部分都必须这么写,否则无法实现补全。所以数组LUNCH_MENU_CHOICES的内容就是补全时打印出来的内容,而数组的内容其实就是上面提到的调用函数add_lunch_combo增加进来的。
5> shell检查和警告,这里只支持bash,如果是其他的shell会发出这个WARNING:
- 1612 if [ "x$SHELL" != "x/bin/bash" ]; then
- 1613 case `ps -o command -p $$` in
- 1614 *bash*)
- 1615 ;;
- 1616 *)
- 1617 echo "WARNING: Only bash is supported, use of other shell would lead to erroneous results"
- 1618 ;;
- 1619 esac
- 1620 fi
6> 这一步是整个source过程中最重要的步骤:
- 1622 # Execute the contents of any vendorsetup.sh files we can find.
- 1623 for f in `test -d device && find -L device -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \
- 1624 `test -d vendor && find -L vendor -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \
- 1625 `test -d product && find -L product -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort`
- 1626 do
- 1627 echo "including $f"
- 1628 . $f
- 1629 done
- 1630 unset f
这里可以分解为3个动作:
第一, 搜索所有vendorsetup.sh脚本;
第二, 打印所有找到的vendorsetup.sh文件路径;
第三, 加载所有找到的vendorsetup.sh脚本。【这里回答了上面留的问题】
那就看看vendorsetup.sh的内容,以板型v700_cxvr为例:
- 17 add_lunch_combo v700_cxvr-eng
- 18 add_lunch_combo v700_cxvr-userdebug
- 19 add_lunch_combo v700_cxvr-user
其实就是调用函数add_lunch_combo把板型v700_cxvr增加到LUNCH_MENU_CHOICES数组中去。
注意:查找的目录层级为4层,如果添加的vendorsetup.sh脚本的目录层级太深,会发生找不到的情况。
7> 看看source的最后一步:
- 1632 addcompletions
直接看函数addcompletions的实现:
- 311 function addcompletions()
- 312 {
- 313 local T dir f
- 314
- 315 # Keep us from trying to run in something that isn't bash.
- 316 if [ -z "${BASH_VERSION}" ]; then
- 317 return
- 318 fi
- 319
- 320 # Keep us from trying to run in bash that's too old.
- 321 if [ ${BASH_VERSINFO[0]} -lt 3 ]; then
- 322 return
- 323 fi
- 324
- 325 dir="sdk/bash_completion"
- 326 if [ -d ${dir} ]; then
- 327 for f in `/bin/ls ${dir}/[a-z]*.bash 2> /dev/null`; do
- 328 echo "including $f"
- 329 . $f
- 330 done
- 331 fi
- 332 }
如果BASH的版本为空或者小于3都直接返回,否则打印android/sdk/bash_completion/目录下的以.bash结尾的所有文件,目前看来只有这一个:
including sdk/bash_completion/adb.bash
至此, source build/envsetup.sh的过程就分析完了。
2,lunch流程
在source流程之后,紧接着就是执行lunch操作,lunch操作执行的其实就是build/envsetup.sh脚本中的lunch函数,下面看看lunch函数都做了哪些事情。
1> 获取用户编译目标到answer变量:
- 545 local answer
- 546
- 547 if [ "$1" ] ; then
- 548 answer=$1
- 549 else
- 550 print_lunch_menu
- 551 echo -n "Which would you like? [aosp_arm-eng] "
- 552 read answer
- 553 fi
如果lunch命令后跟有参数,则直接赋给answer变量;
如果lunch命令后没有参数,则调用函数print_lunch_menu打印出板型列表供用户选择,并将用户的选择存储在answer变量中。
看看print_lunch_menu函数的实现:
- 524 function print_lunch_menu()
- 525 {
- 526 local uname=$(uname)
- 527 echo
- 528 echo "You're building on" $uname
- 529 echo
- 530 echo "Lunch menu... pick a combo:"
- 531
- 532 local i=1
- 533 local choice
- 534 for choice in ${LUNCH_MENU_CHOICES[@]}
- 535 do
- 536 echo " $i. $choice"
- 537 i=$(($i+1))
- 538 done
- 539
- 540 echo
- 541 }
函数的作用就是打印数组LUNCH_MENU_CHOICES的内容。
2> 从answer变量到selection变量:
- 555 local selection=
- 556
- 557 if [ -z "$answer" ]
- 558 then
- 559 selection=aosp_arm-eng
- 560 elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
- 561 then
- 562 if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
- 563 then
- 564 selection=${LUNCH_MENU_CHOICES[$(($answer-1))]}
- 565 fi
- 566 elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")
- 567 then
- 568 selection=$answer
- 569 fi
- 570
- 571 if [ -z "$selection" ]
- 572 then
- 573 echo
- 574 echo "Invalid lunch combo: $answer"
- 575 return 1
- 576 fi
如果answer为空,则selection默认赋值为aosp_arm-eng;
如果answer是纯数字,则将answer作为数组下标从LUNCH_MENU_CHOICES数组中取出板型名称;
如果anwser是字符串,并且字符串使用”-”连接,而且”-”连接的前后两个子串中都没有”-”,则认为是板型名称字符串,直接赋给selection。
经过上述3步,如果发现selection仍然为空,则直接报错并退出。
3> 从selection变量到variant变量
- 580 local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")
- 581 check_variant $variant
- 582 if [ $? -ne 0 ]
- 583 then
- 584 echo
- 585 echo "** Invalid variant: '$variant'"
- 586 echo "** Must be one of ${VARIANT_CHOICES[@]}"
- 587 variant=
- 588 fi
从selection中取出“-”后面的字符串到variant,类似从字符串"aosp_arm-eng"中取出"eng",然后调用函数check_variant判断variant是否符合要求。check_variant函数很简单,主要就是用到前文source流程中初始化为"user userdebug eng"的VARIANT_CHOICES数组,判断variant是否是数组成员之一,是则返回0,不是则返回1。
4> 从selection变量到product变量
- 590 local product=$(echo -n $selection | sed -e "s/-.*$//")
- 591 TARGET_PRODUCT=$product \
- 592 TARGET_BUILD_VARIANT=$variant \
- 593 build_build_var_cache
- 594 if [ $? -ne 0 ]
- 595 then
- 596 echo
- 597 echo "** Don't have a product spec for: '$product'"
- 598 echo "** Do you have the right repo manifest?"
- 599 product=
- 600 fi
再从selection中取出“-”前面的字符串到product,类似从字符串"aosp_arm-eng"中取出"aosp_arm",然后将product赋值给TARGET_PRODUCT,将variant赋值给TARGET_BUILD_VARIANT,然后再调用函数build_build_var_cache对编译时所必需的环境变量进行赋值和处理,并根据函数返回结果做对应处理。
5> 对关键变量做最终处理:
- 602 if [ -z "$product" -o -z "$variant" ]
- 603 then
- 604 echo
- 605 return 1
- 606 fi
- 607
- 608 export TARGET_PRODUCT=$product
- 609 export TARGET_BUILD_VARIANT=$variant
- 610 export TARGET_BUILD_TYPE=release
如果到这里仍然发现product或者variant为空,那肯定是出错了,直接退出;否则导出以下准备好的宏变量供整个shell环境使用:
TARGET_PRODUCT
TARGET_BUILD_VARIANT
TARGET_BUILD_TYPE
6> 最后三个函数调用:
- 614 set_stuff_for_environment
- 615 printconfig
- 616 destroy_build_var_cache
函数set_stuff_for_environment主要就是设置PROMPT_COMMAND,ANDROID_BUILD_PATHS,JAVA_HOME和BUILD_ENV_SEQUENCE_NUMBER等等环境变量;
函数printconfig用来打印最终准备好的环境变量,通常如下:
- ============================================
- PLATFORM_VERSION_CODENAME=REL
- PLATFORM_VERSION=7.0
- TARGET_PRODUCT=s900_RY_VR
- TARGET_BUILD_VARIANT=userdebug
- TARGET_BUILD_TYPE=release
- TARGET_BUILD_APPS=
- TARGET_ARCH=arm
- TARGET_ARCH_VARIANT=armv7-a-neon
- TARGET_CPU_VARIANT=cortex-a53
- TARGET_2ND_ARCH=
- TARGET_2ND_ARCH_VARIANT=
- TARGET_2ND_CPU_VARIANT=
- HOST_ARCH=x86_64
- HOST_2ND_ARCH=x86
- HOST_OS=linux
- HOST_OS_EXTRA=Linux-3.2.0-29-generic-x86_64-with-Ubuntu-12.04-precise
- HOST_CROSS_OS=windows
- HOST_CROSS_ARCH=x86
- HOST_CROSS_2ND_ARCH=x86_64
- HOST_BUILD_TYPE=release
- BUILD_ID=NRD90M
- OUT_DIR=out
- ============================================
函数destroy_build_var_cache用来清除不再需要的中间环节产生的变量的值。
至此,lunch流程就分析完了。
3,make流程
3.1 编译入口
当我们在Android源码根目录下执行make的时候,会查找当前目录下的Makefie文件或者makefile文件并且执行,在android/Makefile文件中,它只有一行有用的内容:
- 1 ### DO NOT EDIT THIS FILE ###
- 2 include build/core/main.mk
- 3 ### DO NOT EDIT THIS FILE ###
因此,执行make时真正的入口是android/build/core/main.mk文件。
3.2 整体依赖
我们在Android源码根目录下执行make命令的时候,并没有传入目标,那么就会执行默认的目标。那默认的目标是什么呢?在android/build/core/main.mk中有这样几行:
- 63 # This is the default target. It must be the first declared target.
- 64 .PHONY: droid
- 65 DEFAULT_GOAL := droid
- 66 $(DEFAULT_GOAL): droid_targets
从63行注释可以看出,默认编译的就是droid这个伪目标,make工具遇到伪目标以后,会检查解析伪目标的依赖,如果伪目标存在依赖,就会检查这些依赖,如果这些依赖是伪目标,就继续检查这个伪目标的依赖,如果不是伪目标,就会生成这个目标,如此一层一层递归下去。
另外,在android/build/core/main.mk中还有这样几行:
- 1046 # Building a full system-- the default is to build droidcore
- 1047 droid_targets: droidcore dist_files
这就说明droid这个伪目标依赖droidcore和dist_files两大部分(整体编译时TARGET_BUILD_APPS为空),然后再将这两个依赖逐步解析下去,可以得到编译droid的整体依赖关系如下图:
有必要说明两点:
1)有些依赖(比如INSTALLED_BOOTIMAGE_TARGET)在android/build/core/main.mk中没有定义,而是在android/build/core/Makefile中定义的;
2)上面dist_files也是个伪目标,并且它没有任何依赖,利用dist-for-goals方法来拷贝库文件,可忽略。
3.3 编译主流程
3.3.1 加载板型配置
首先各mk文件调用关系如下:
下面就来看看product_config.mk文件,首先调用方法get-all-product-makefiles找出所有的AndoridProducts.mk文件:
- 182 ifneq ($(strip $(TARGET_BUILD_APPS)),)
- 183 # An unbundled app build needs only the core product makefiles.
- 184 all_product_configs := $(call get-product-makefiles,\
- 185 $(SRC_TARGET_DIR)/product/AndroidProducts.mk)
- 186 else
- 187 # Read in all of the product definitions specified by the AndroidProducts.mk
- 188 # files in the tree.
- 189 all_product_configs := $(get-all-product-makefiles)
- 190 endif
(方法get-all-product-makefiles的定义在文件product.mk中,实现细节可自行研究)
然后从all_product_configs中找出我们当前产品的AndoridProducts.mk文件:
- 192 # Find the product config makefile for the current product.
- 193 # all_product_configs consists items like:
- 194 # <product_name>:<path_to_the_product_makefile>
- 195 # or just <path_to_the_product_makefile> in case the product name is the
- 196 # same as the base filename of the product config makefile.
- 197 current_product_makefile :=
- 198 all_product_makefiles :=
- 199 $(foreach f, $(all_product_configs),\
- 200 $(eval _cpm_words := $(subst :,$(space),$(f)))\
- 201 $(eval _cpm_word1 := $(word 1,$(_cpm_words)))\
- 202 $(eval _cpm_word2 := $(word 2,$(_cpm_words)))\
- 203 $(if $(_cpm_word2),\
- 204 $(eval all_product_makefiles += $(_cpm_word2))\
- 205 $(if $(filter $(TARGET_PRODUCT),$(_cpm_word1)),\
- 206 $(eval current_product_makefile += $(_cpm_word2)),),\
- 207 $(eval all_product_makefiles += $(f))\
- 208 $(if $(filter $(TARGET_PRODUCT),$(basename $(notdir $(f)))),\
- 209 $(eval current_product_makefile += $(f)),)))
- 210 _cpm_words :=
- 211 _cpm_word1 :=
- 212 _cpm_word2 :=
- 213 current_product_makefile := $(strip $(current_product_makefile))
比如我们在执行lunch时选择的板型是v700_cxvr,则current_product_makefile的值就是:android/device/actions/v700_cxvr/AndroidProducts.mk,而这个AndroidProducts.mk的内容其实就是指定板型配置信息文件android/device/actions/v700_cxvr/v700_cxvr.mk
接着调用import-products导入产品配置信息:
- 239 $(call import-products, $(current_product_makefile))
(方法import-products的定义也在文件android/build/core/product.mk中,实现细节可自行研究)
接着设置TARGET_DEVICE的值,其实就是v700_cxvr:
- 268 # Find the device that this product maps to.
- 269 TARGET_DEVICE := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE)
接着设置PRODUCT_COPY_FILES,这个变量指定了需要拷贝的文件:
- 346 PRODUCT_COPY_FILES := \
- 347 $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_COPY_FILES))
接着设置PRODUCT_PROPERTY_OVERRIDES属性:
- 351 PRODUCT_PROPERTY_OVERRIDES := \
- 352 $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PROPERTY_OVERRIDES))
接着回到android/build/core/envsetup.mk中,include了板型配置文件BoardConfig.mk
- 144 board_config_mk := \
- 145 $(strip $(sort $(wildcard \
- 146 $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
- 147 $(shell test -d device && find -L device -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \
- 148 $(shell test -d vendor && find -L vendor -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \
- 149 )))
- 150 ifeq ($(board_config_mk),)
- 151 $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))
- 152 endif
- 153 ifneq ($(words $(board_config_mk)),1)
- 154 $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))
- 155 endif
- 156 include $(board_config_mk)
至此,板型配置基本加载完毕。
3.3.2 加载所有模块
加载完板型配置信息后,回到main.mk文件中,很快发现了ONE_SHOT_MAKEFILE的使用。如果这个变量被定义了,那么就是编译一个模块,如果没有被定义,就说明是编译整个系统。MAKECMDGOALS是make的一个环境变量,当我们执行make的时候并没有设置它,因此它为空。所以dont_bother不等于true,因此会加载所有的Android.mk,这里是调用一个python脚本android/build/tools/findleaves.py来查找系统中所有的Android.mk,然后循环include进来:
- 542 # Include all of the makefiles in the system
- 543 #
- 544
- 545 # Can't use first-makefiles-under here because
- 546 # --mindepth=2 makes the prunes not work.
- 547 subdir_makefiles := \
- 548 $(shell build/tools/findleaves.py $(FIND_LEAVES_EXCLUDES) $(subdirs) Android.mk)
- 549
- 550 ifeq ($(USE_SOONG),true)
- 551 subdir_makefiles := $(SOONG_ANDROID_MK) $(call filter-soong-makefiles,$(subdir_makefiles))
- 552 endif
- 553
- 554 $(foreach mk, $(subdir_makefiles),$(info including $(mk) ...)$(eval include $(mk))) //这里循环include进来
3.4 JACK调用
JACK是Android7.0默认使用的编译器,在config.mk中,有JACK的定义,其实JACK的值为:out/host/linux-x86/bin/jack
- 499 # Generic tools.
- 500 JACK := $(HOST_OUT_EXECUTABLES)/jack
在definitions.mk中,有call-jack的定义:
- 2015 # Call jack
- 2016 #
- 2017 define call-jack
- 2018 JACK_VERSION=$(PRIVATE_JACK_VERSION) $(JACK) $(DEFAULT_JACK_EXTRA_ARGS)
- 2019 endef
在definitions.mk中,也有jack-java-to-dex的定义,在jack-java-to-dex中会调用call-jack去编译java文件:
- 2092 define jack-java-to-dex
- 2093 $(hide) rm -f [email protected]
- 2094 $(hide) rm -f $(PRIVATE_CLASSES_JACK)
- 2095 $(hide) rm -rf $(PRIVATE_JACK_INTERMEDIATES_DIR)
- 2096 $(hide) mkdir -p $(dir [email protected])
- 2097 $(hide) mkdir -p $(dir $(PRIVATE_CLASSES_JACK))
- 2098 $(hide) mkdir -p $(PRIVATE_JACK_INTERMEDIATES_DIR)
- 2099 $(if $(PRIVATE_JACK_INCREMENTAL_DIR),$(hide) mkdir -p $(PRIVATE_JACK_INCREMENTAL_DIR))
- 2100 $(call dump-words-to-file,$(PRIVATE_JAVA_SOURCES),$(PRIVATE_JACK_INTERMEDIATES_DIR)/java-source-list)
- 2101 $(hide) if [ -d "$(PRIVATE_SOURCE_INTERMEDIATES_DIR)" ]; then \
- 2102 find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' >> $(PRIVATE_JACK_INTERMEDIATES_DIR)/java-source-list; \
- 2103 fi
- 2104 $(hide) tr ' ' '\n' < $(PRIVATE_JACK_INTERMEDIATES_DIR)/java-source-list \
- 2105 | $(NORMALIZE_PATH) | sort -u > $(PRIVATE_JACK_INTERMEDIATES_DIR)/java-source-list-uniq
- 2106 $(if $(PRIVATE_JACK_PROGUARD_FLAGS), \
- 2107 $(hide) echo -basedirectory $(CURDIR) > [email protected]; \
- 2108 echo $(PRIVATE_JACK_PROGUARD_FLAGS) >> [email protected]; \
- 2109 )
- 2110 $(if $(PRIVATE_EXTRA_JAR_ARGS),
- 2111 $(hide) mkdir -p [email protected]
- 2112 $(hide) $(call create-empty-package-at,[email protected])
- 2113 $(hide) $(call add-java-resources-to,[email protected])
- 2114 $(hide) unzip -qo [email protected] -d [email protected]
- 2115 $(hide) rm [email protected])
- 2116 $(hide) if [ -s $(PRIVATE_JACK_INTERMEDIATES_DIR)/java-source-list-uniq ] ; then \
- 2117 export tmpEcjArg="@$(PRIVATE_JACK_INTERMEDIATES_DIR)/java-source-list-uniq"; \
- 2118 else \
- 2119 export tmpEcjArg=""; \
- 2120 fi; \
- 2121 $(call call-jack) \ //这里调用的call-jack
- 2122 $(strip $(PRIVATE_JACK_FLAGS)) \
- 2123 $(strip $(PRIVATE_JACK_COVERAGE_OPTIONS)) \
- 2124 $(if $(NO_OPTIMIZE_DX), \
- 2125 -D jack.dex.optimize="false") \
- 2126 $(if $(PRIVATE_RMTYPEDEFS), \
- 2127 -D jack.android.remove-typedef="true") \
- 2128 $(addprefix --classpath ,$(strip \
- 2129 $(call normalize-path-list,$(PRIVATE_BOOTCLASSPATH_JAVA_LIBRARIES) $(PRIVATE_ALL_JACK_LIBRARIES)))) \
- 2130 $(addprefix --import ,$(call reverse-list,$(PRIVATE_STATIC_JACK_LIBRARIES))) \
- 2131 $(if $(PRIVATE_EXTRA_JAR_ARGS),--import-resource [email protected]) \
- 2132 -D jack.android.min-api-level=$(PRIVATE_JACK_MIN_SDK_VERSION) \
- 2133 -D jack.import.resource.policy=keep-first \
- 2134 -D jack.import.type.policy=keep-first \
- 2135 --output-jack $(PRIVATE_CLASSES_JACK) \
- 2136 $(if $(PRIVATE_JACK_INCREMENTAL_DIR),--incremental-folder $(PRIVATE_JACK_INCREMENTAL_DIR)) \
- 2137 --output-dex $(PRIVATE_JACK_INTERMEDIATES_DIR) \
- 2138 $(addprefix --config-jarjar ,$(strip $(PRIVATE_JARJAR_RULES))) \
- 2139 $(if $(PRIVATE_JACK_PROGUARD_FLAGS),--config-proguard [email protected]) \
- 2140 $$tmpEcjArg \
- 2141 || ( rm -rf $(PRIVATE_CLASSES_JACK); exit 41 )
- 2142 $(hide) mv $(PRIVATE_JACK_INTERMEDIATES_DIR)/classes*.dex $(dir [email protected])
- 2143 $(hide) rm -f $(PRIVATE_JACK_INTERMEDIATES_DIR)/java-source-list
- 2144 $(if $(PRIVATE_EXTRA_JAR_ARGS),$(hide) rm -rf [email protected])
- 2145 $(hide) mv $(PRIVATE_JACK_INTERMEDIATES_DIR)/java-source-list-uniq $(PRIVATE_JACK_INTERMEDIATES_DIR).java-source-list
- 2146 $(if $(PRIVATE_JAR_PACKAGES), $(hide) echo unsupported options PRIVATE_JAR_PACKAGES in [email protected]; exit 53)
- 2147 $(if $(PRIVATE_JAR_EXCLUDE_PACKAGES), $(hide) echo unsupported options JAR_EXCLUDE_PACKAGES in [email protected]; exit 53)
- 2148 $(if $(PRIVATE_JAR_MANIFEST), $(hide) echo unsupported options JAR_MANIFEST in [email protected]; exit 53)
- 2149 endef
在java.mk和host_dalvik_java_library.mk中,都有调用jack-java-to-dex来编译java:
上图中,左边浅红色部分这些宏变量在 config.mk文件中有定义,定义如下:
BUILD_STATIC_JAVA_LIBRARY=static_java_library.mk
BUILD_JAVA_LIBRARY=java_library.mk
BUILD_PACKAGE=package.mk
BUILD_HOST_DALVIK_JAVA_LIBRARY=host_dalvik_java_library.mk
这些宏变量将会大量出现在各个模块的Android.mk文件中,而Android.mk文件又被编译系统全部找出并include进来(上面3.3.2有提到),这样编译系统就等于间接调用了jack来编译java文件。