Android build编译过程

Android build编译过程及Android.mk与Android.bp分析

Android的官方网站:http://source.android.com/source/building.html

按照google给出的编译步骤如下:
   1> source build/envsetup.sh:加载命令
   2> lunch:选择平台编译选项
   3> make:执行编译
如V3的编译命令为
source build/envsetup.sh
lunch gwmv3-userdebug
make -j6 2>&1 | tee build_$(date +%Y%m%d_%H%M%S).log

1. source build/envsetup.sh
这个命令是用来将envsetup.sh里的所有用到的命令加载到环境变量里去,我们来分析下它。

envsetup.sh里的主要命令如下:
function help()                  # 显示帮助信息
function get_abs_build_var()           # 获取绝对变量
function get_build_var()             # 获取绝对变量
function check_product()             # 检查product
function check_variant()             # 检查变量
function setpaths()                # 设置文件路径
function printconfig()              # 打印配置
function set_stuff_for_environment()        # 设置环境变量
function set_sequence_number()            # 设置序号
function settitle()                # 设置标题
function choosetype()               # 设置type
function chooseproduct()              # 设置product
function choosevariant()              # 设置variant
function tapas()                  # 功能同choosecombo
function choosecombo()               # 设置编译参数
function add_lunch_combo()             # 添加lunch项目
function print_lunch_menu()            # 打印lunch列表
function lunch()                 # 配置lunch
function m()                   # make from top
function findmakefile()              # 查找makefile
function mm()                   # make from current directory
function mmm()                   # make the supplied directories
function croot()                 # 回到根目录
function cproj()
function pid()
function systemstack()
function gdbclient()
function jgrep()                 # 查找java文件
function cgrep()                  # 查找c/cpp文件
function resgrep()
function tracedmdump()
function runhat()
function getbugreports()
function startviewserver()
function stopviewserver()
function isviewserverstarted()
function smoketest()
function runtest()
function godir ()                 # 跳到指定目录

# add_lunch_combo 函数被多次调用,就是它来添加Android编译选项

envsetup.sh其主要作用如下:
1. 加载了编译时使用到的函数命令,如:help,lunch,m,mm,mmm等
2. 添加了编译选项:generic-eng和simulator,这两个选项是系统默认选项
3. 查找vendor/<-厂商目录>/和vendor/<厂商目录>/build/目录下的vendorsetup.sh,如果存在的话,加载执行它,添加厂商自己定义产品的编译选项
其实,上述第3条是向编译系统添加了厂商自己定义产品的编译选项,里面的代码就是:add_lunch_combo xxx-xxx。
根据上面的内容,可以推测出,如果要想定义自己的产品编译项,简单的办法是直接在envsetup.sh最后,添加上add_lunch_combo myProduct-eng;但不建议这么做;
建议在vendor目录下创建自己公司名字,然后在公司目录下创建一个新的vendorsetup.sh,在里面添加上自己的产品编译项
当我们在终端执行命令source build/envsetup.sh时,其实是完整的加载了脚本envsetup.sh中的变量和方法,其中最重要的函数比如lunch就是在这一步加载到shell环境变量中去的;

除了加载上述函数,还执行了以下初始化和动作:
1> 提前定义3种编译模式,供后面使用:
VARIANT_CHOICES=(user userdebug eng)
2> 使用变量LUNCH_MENU_CHOICES之前,先将它清空:
unset LUNCH_MENU_CHOICES
3> 加载一些默认的板类型选项,后续的lunch菜单中就可以看到这几个板类型: 
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
调用 add_lunch_combo 函数实际是向全局数组变量 LUNCH_MENU_CHOICES 中追加选项,LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo);
add_lunch_combo 的处理逻辑就是先从 LUNCH_MENU_CHOICES 中查找,看存不存在要添加的板型,如果存在就直接返回,如果不存在就追加到LUNCH_MENU_CHOICES中;
4> 当敲入lunch命令后用Tab键进行补全时,会执行_lunch()函数
complete -F _lunch lunch
函数_lunch的实现:
function _lunch()
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    COMPREPLY=( $(compgen -W "${LUNCH_MENU_CHOICES[*]}" -- ${cur}) )
    return 0
}
数组LUNCH_MENU_CHOICES的内容会在补全时打印出来,而数组其实就是调用函数add_lunch_combo增加进来的;
5> shell检查和警告,这里只支持bash,如果是其他的shell会发出这个WARNING:
echo "WARNING: Only bash is supported, use of other shell would lead to erroneous results"
6> 整个source过程中最重要的步骤:
1665 # Execute the contents of any vendorsetup.sh files we can find.
1666 for f in `test -d device && find -L device -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \
1667          `test -d vendor && find -L vendor -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \
1668          `test -d product && find -L product -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort`
1669 do
1670     echo "including $f"
1671     . $f
1672 done
1673 unset f
主要执行以下3个动作
1.搜索所有vendorsetup.sh脚本;
2.打印所有找到的vendorsetup.sh文件路径;
3.加载所有找到的vendorsetup.sh脚本
其中-maxdepth 4表示查找的最大目录深度为4层,如果添加的vendorsetup.sh脚本的目录层级太深,会发生找不到的情况。


2. lunch操作
在source之后,紧接着就是执行lunch操作,其实就是build/envsetup.sh脚本中的lunch函数
1> 获取用户编译目标到answer变量:
576     local answer
577 
578     if [ "$1" ] ; then
579         answer=$1
580     else
581         print_lunch_menu
582         echo -n "Which would you like? [aosp_arm-eng] "
583         read answer
584     fi
如果lunch命令后跟有参数,则直接赋给answer变量;
如果lunch命令后没有参数,则调用函数print_lunch_menu打印出板型列表供用户选择,并将用户的选择幅值给answer变量;
print_lunch_menu就是打印数组LUNCH_MENU_CHOICES的内容;
2> 从answer变量到selection变量
如果answer为空,则selection默认赋值为aosp_arm-eng;
如果answer是纯数字,则将answer作为数组下标从LUNCH_MENU_CHOICES数组中取出板型名称;
如果anwser是字符串,并且字符串使用”-”连接,而且”-”连接的前后两个子串中都没有”-”,则认为是板型名称字符串,直接赋给selection。
经过上述3步,如果发现selection仍然为空,则直接报错并退出
3> 从selection变量到variant变量
从selection中取出“-”后面的字符串到variant,类似从字符串"aosp_arm-eng"中取出"eng",然后调用函数check_variant判断variant是否符合要求。
check_variant函数主要就是用到前文source流程中初始化为"user userdebug eng"的VARIANT_CHOICES数组,判断variant是否是数组成员之一,是则返回0,不是则返回1。
4> 从selection变量到product变量
从selection中取出“-”前面的字符串到product,类似从字符串"aosp_arm-eng"中取出"aosp_arm",
然后将product赋值给TARGET_PRODUCT,将variant赋值给TARGET_BUILD_VARIANT,
然后再调用函数build_build_var_cache对编译时所必需的环境变量进行赋值和处理,并根据函数返回结果做对应处理。
5> 导出参数到环境变量
export TARGET_PRODUCT=$product
export TARGET_BUILD_VARIANT=$variant
export TARGET_BUILD_TYPE=release
6> 最后三个函数调用:
641     set_stuff_for_environment
642     printconfig
643     destroy_build_var_cache
函数set_stuff_for_environment主要就是设置PROMPT_COMMAND,ANDROID_BUILD_PATHS,JAVA_HOME和BUILD_ENV_SEQUENCE_NUMBER等环境变量;
函数printconfig用来打印最终准备好的环境变量
函数destroy_build_var_cache用来清除不再需要的中间环节产生的变量的值。
看一下环境变量,会看到增加了很多Android相关的环境变量

3. make执行编译
3.1 编译入口
当我们在Android源码根目录下执行make的时候,会查找当前目录下的Makefie文件或者makefile文件并且执行,在android/Makefile文件中,它只有一行有用的内容
include build/core/main.mk
因此,执行make时真正的入口是android/build/core/main.mk文件。

3.2 整体依赖
执行make命令的时候,并没有传入目标,那么就会执行默认的目标。那默认的目标是什么呢?在android/build/core/main.mk中有这样几行
39 # This is the default target.  It must be the first declared target.
40 .PHONY: droid
41 DEFAULT_GOAL := droid
42 $(DEFAULT_GOAL): droid_targets
可知默认编译的就是droid这个伪目标,make工具遇到伪目标以后,会检查解析伪目标的依赖,如果伪目标存在依赖,就会检查这些依赖,
如果这些依赖是伪目标,就继续检查这个伪目标的依赖,如果不是伪目标,就会生成这个目标,如此一层层递归下去。
1168:droid_targets: apps_only
1216:droid_targets: droidcore dist_files
droid这个伪目标依赖droidcore和dist_files两大部分(整体编译时TARGET_BUILD_APPS为空),然后再将这两个依赖逐步解析下去,可以得到编译droid的整体依赖关系
请参考 https://blog.csdn.net/lizekun2010/article/details/52598105

1109 droidcore: files \
1110     systemimage \
1111     $(INSTALLED_BOOTIMAGE_TARGET) \
1112     $(INSTALLED_RECOVERYIMAGE_TARGET) \
1113     $(INSTALLED_VBMETAIMAGE_TARGET) \
1114     $(INSTALLED_USERDATAIMAGE_TARGET) \
1115     $(INSTALLED_CACHEIMAGE_TARGET) \
1116     $(INSTALLED_BPTIMAGE_TARGET) \
1117     $(INSTALLED_VENDORIMAGE_TARGET) \
1118     $(INSTALLED_PRODUCTIMAGE_TARGET) \
1119     $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) \
1120     $(INSTALLED_FILES_FILE) \
1121     $(INSTALLED_FILES_FILE_VENDOR) \
1122     $(INSTALLED_FILES_FILE_PRODUCT) \
1123     $(INSTALLED_FILES_FILE_SYSTEMOTHER) \
1124     soong_docs

droid 所依赖的其他 Make 目标的说明
名称    说明
apps_only    该目标将编译出当前配置下不包含 user,userdebug,eng 标签(关于标签,请参见后文“添加新的模块”)的应用程序。
droidcore    该目标仅仅是所依赖的几个目标的组合,其本身不做更多的处理。
dist_files    该目标用来拷贝文件到 /out/dist 目录。
files    该目标仅仅是所依赖的几个目标的组合,其本身不做更多的处理。
prebuilt    该目标依赖于 $(ALL_PREBUILT),$(ALL_PREBUILT)的作用就是处理所有已编译好的文件。
$(modules_to_install)    modules_to_install 变量包含了当前配置下所有会被安装的模块(一个模块是否会被安装依赖于该产品的配置文件,模块的标签等信息),因此该目标将导致所有会被安装的模块的编译。
$(modules_to_check)    该目标用来确保我们定义的构建模块是没有冗余的。
$(INSTALLED_ANDROID_INFO_TXT_TARGET)    该目标会生成一个关于当前 Build 配置的设备信息的文件,该文件的生成路径是:out/target/product/<product_name>/android-info.txt
systemimage    生成 system.img。
$(INSTALLED_BOOTIMAGE_TARGET)    生成 boot.img。
$(INSTALLED_RECOVERYIMAGE_TARGET)    生成 recovery.img。
$(INSTALLED_USERDATAIMAGE_TARGET)    生成 userdata.img。
$(INSTALLED_CACHEIMAGE_TARGET)    生成 cache.img。
$(INSTALLED_FILES_FILE)    该目标会生成 out/target/product/<product_name>/ installed-files.txt 文件,该文件中内容是当前系统镜像中已经安装的文件列表。

其他主要 Make 目标
Make 目标    说明
make clean    执行清理,等同于:rm -rf out/。
make sdk    编译出 Android 的 SDK。
make clean-sdk    清理 SDK 的编译产物。
make update-api    更新 API。在 framework API 改动之后,需要首先执行该命令来更新 API,公开的 API 记录在 frameworks/base/api 目录下。
make dist    执行 Build,并将 MAKECMDGOALS 变量定义的输出文件拷贝到 /out/dist 目录。
make all    编译所有内容,不管当前产品的定义中是否会包含。
make help    帮助信息,显示主要的 make 目标。
make snod    从已经编译出的包快速重建系统镜像。
make libandroid_runtime    编译所有 JNI framework 内容。
makeframework    编译所有 Java framework 内容。
makeservices    编译系统服务和相关内容。
make <local_target>    编译一个指定的模块,local_target 为模块的名称。
make clean-<local_target>    清理一个指定模块的编译结果。
makedump-products    显示所有产品的编译配置信息,例如:产品名,产品支持的地区语言,产品中会包含的模块等信息。
makePRODUCT-xxx-yyy    编译某个指定的产品。
makebootimage    生成 boot.img
makerecoveryimage    生成 recovery.img
makeuserdataimage    生成 userdata.img
makecacheimage    生成 cache.img

补充说明
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文件调用关系如下
main.mk -> config.mk -> envsetup.mk -> product_config.mk
其中product_config.mk文件,首先调用方法get-all-product-makefiles找出所有的AndroidProducts.mk文件:
178 ifneq ($(strip $(TARGET_BUILD_APPS)),)
179 # An unbundled app build needs only the core product makefiles.
180 all_product_configs := $(call get-product-makefiles,\
181     $(SRC_TARGET_DIR)/product/AndroidProducts.mk)
182 else
183 # Read in all of the product definitions specified by the AndroidProducts.mk
184 # files in the tree.
185 all_product_configs := $(get-all-product-makefiles)
186 endif
方法get-all-product-makefiles的定义在文件product.mk中

然后从all_product_configs中找出我们当前产品的 AndroidProducts.mk 文件
190 # Find the product config makefile for the current product.
191 # all_product_configs consists items like:
192 # <product_name>:<path_to_the_product_makefile>
193 # or just <path_to_the_product_makefile> in case the product name is the                                  
194 # same as the base filename of the product config makefile.
195 current_product_makefile :=
196 all_product_makefiles :=
197 $(foreach f, $(all_product_configs),\
198     $(eval _cpm_words := $(subst :,$(space),$(f)))\
199     $(eval _cpm_word1 := $(word 1,$(_cpm_words)))\
200     $(eval _cpm_word2 := $(word 2,$(_cpm_words)))\
201     $(if $(_cpm_word2),\
202         $(eval all_product_makefiles += $(_cpm_word2))\
203         $(eval all_named_products += $(_cpm_word1))\
204         $(if $(filter $(TARGET_PRODUCT),$(_cpm_word1)),\
205             $(eval current_product_makefile += $(_cpm_word2)),),\
206         $(eval all_product_makefiles += $(f))\
207         $(eval all_named_products += $(basename $(notdir $(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时选择的板型会保存到 TARGET_PRODUCT , 此处会从所有的 AndroidProducts.mk 文件路径中过滤出包含 TARGET_PRODUCT 路径的那一个;
比如 ./device/generic/arm64/AndroidProducts.mk
而这个AndroidProducts.mk的内容其实就是指定板型配置信息文件;

接着调用import-products导入产品配置信息:
228 ifeq ($(load_all_product_makefiles),true)
229 # Import all product makefiles.
230 $(call import-products, $(all_product_makefiles))
231 else
232 # Import just the current product.
233 ifndef current_product_makefile
234 $(error Can not locate config makefile for product "$(TARGET_PRODUCT)")
235 endif
236 ifneq (1,$(words $(current_product_makefile)))
237 $(error Product "$(TARGET_PRODUCT)" ambiguous: matches $(current_product_makefile))
238 endif
239 $(call import-products, $(current_product_makefile))
240 endif  # Import all or just the current product makefile
方法import-products的定义也在文件android/build/core/product.mk中

接着设置TARGET_DEVICE的值,
274 # Find the device that this product maps to.
275 TARGET_DEVICE := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE)

接着设置PRODUCT_COPY_FILES,这个变量指定了需要拷贝的文件
337 # A list of words like <source path>:<destination path>[:<owner>].
338 # The file at the source path should be copied to the destination path
339 # when building  this product.  <destination path> is relative to
340 # $(PRODUCT_OUT), so it should look like, e.g., "system/etc/file.xml".
341 # The rules for these copy steps are defined in build/make/core/Makefile.
342 # The optional :<owner> is used to indicate the owner of a vendor file.
343 PRODUCT_COPY_FILES := \
344     $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_COPY_FILES))

接着设置PRODUCT_PROPERTY_OVERRIDES属性:
346 # A list of property assignments, like "key = value", with zero or more
347 # whitespace characters on either side of the '='.
348 PRODUCT_PROPERTY_OVERRIDES := \
349     $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PROPERTY_OVERRIDES))

接着回到 build/core/envsetup.mk 中,include了板型配置文件BoardConfig.mk
240 # Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)
241 # or under vendor/*/$(TARGET_DEVICE).  Search in both places, but
242 # make sure only one exists.
243 # Real boards should always be associated with an OEM vendor.
244 board_config_mk := \
245     $(strip $(sort $(wildcard \
246         $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
247         $(shell test -d device && find -L device -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \
248         $(shell test -d vendor && find -L vendor -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \
249     )))
250 ifeq ($(board_config_mk),)
251   $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))
252 endif
253 ifneq ($(words $(board_config_mk)),1)
254   $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))
255 endif
256 include $(board_config_mk)
至此,板型配置基本加载完毕。

3.3.2 加载所有模块
加载完板型配置信息后,回到main.mk文件中,如果 ONE_SHOT_MAKEFILE 这个变量被定义了,那么就是编译一个模块,如果没有被定义,就说明是编译整个系统。
MAKECMDGOALS 是make的一个环境变量,当我们执行make的时候并没有设置它,因此它为空。所以 dont_bother 不等于true,因此会加载所有的 Android.mk,
查找系统中所有的 Android.mk, 然后循环include进来;
448 #
449 # Include all of the makefiles in the system
450 #
451 
452 subdir_makefiles := $(SOONG_ANDROID_MK) $(file <$(OUT_DIR)/.module_paths/Android.mk.list)
453 subdir_makefiles_total := $(words $(subdir_makefiles))
454 .KATI_READONLY := subdir_makefiles_total
455 
456 $(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_to     tal)] including $(mk) ...)$(eval include $(mk)))

core/soong_config.mk:7:SOONG_ANDROID_MK := $(SOONG_OUT_DIR)/Android-$(TARGET_PRODUCT).mk
subdir_makefiles 包含 SOONG_ANDROID_MK 和 $(OUT_DIR)/.module_paths/Android.mk.list 中的所有makefile 列表;

执行m命令相当于是在执行make命令。对整个Android源码进行编译。m命令其实定义在 build/make/envsetup.sh;
_wrap_build $T/build/soong/soong_ui.bash --make-mode $@
55 soong_build_go soong_ui android/soong/cmd/soong_ui
56 
57 cd ${TOP}
58 exec "$(getoutdir)/soong_ui" "$@"

build/soong/ui/build/finder.go 
78     androidMks := f.FindFirstNamedAt(".", "Android.mk")
79     err := dumpListToFile(androidMks, filepath.Join(dumpDir, "Android.mk.list"))

在definitions.mk文件中还有很多这样的定义
981:define transform-l-to-c-or-cpp
992:define transform-y-to-c-or-cpp
1025:define transform-renderscripts-to-java-and-bc
1044:define transform-bc-to-so
1064:define transform-renderscripts-to-cpp-and-bc
1087:define transform-aidl-to-java
1094:define transform-aidl-to-cpp
1132:define transform-vts-to-cpp
1157:define transform-logtags-to-java
1169:define transform-proto-to-java
1190:define transform-proto-to-cc
1223:define transform-cpp-to-o-compiler-args
1246:define transform-cpp-to-o
1252:define transform-cpp-to-o
1268:define transform-c-or-s-to-o-compiler-args
1279:define transform-c-to-o-compiler-args
1294:define transform-c-to-o
1300:define transform-c-to-o
1310:define transform-s-to-o
1319:define transform-asm-to-o
1334:define transform-m-to-o
1343:define transform-host-cpp-to-o-compiler-args
1364:define transform-host-cpp-to-o
1370:define transform-host-cpp-to-o
1385:define transform-host-c-or-s-to-o-common-args
1395:define transform-host-c-or-s-to-o
1403:define transform-host-c-to-o-compiler-args
1416:define transform-host-c-to-o
1422:define transform-host-c-to-o
1432:define transform-host-s-to-o
1441:define transform-host-m-to-o
1450:define transform-host-mm-to-o
1579:define transform-o-to-static-lib
1625:define transform-o-to-aux-static-lib
1635:define transform-o-to-aux-executable-inner
1648:define transform-o-to-aux-executable
1654:define transform-o-to-aux-static-executable-inner
1668:define transform-o-to-aux-static-executable
1734:define transform-host-o-to-static-lib
1755:define transform-host-o-to-shared-lib-inner
1780:define transform-host-o-to-shared-lib
1786:define transform-host-o-to-package
1797:define transform-o-to-shared-lib-inner
1821:define transform-o-to-shared-lib
1836:define transform-to-stripped
1843:define transform-to-stripped-keep-mini-debug-info
1864:define transform-to-stripped-keep-symbols
1886:define transform-o-to-executable-inner
1912:define transform-o-to-executable
1929:define transform-o-to-static-executable-inner
1954:define transform-o-to-static-executable
1975:define transform-host-o-to-executable-inner
1999:define transform-host-o-to-executable
2289:define transform-java-to-header.jar
2382:define transform-classes.jar-to-dex
2400:define transform-classes-d8.jar-to-dex
2607:define transform-host-java-to-package
2614:define transform-host-java-to-dalvik-package
2724:define transform-prebuilt-to-target
2730:define transform-prebuilt-to-target-strip-comments
2823:define transform-jar-to-proguard
2828:define transform-jar-to-proguard
2839:define transform-jar-to-dex-r8
2853:define transform-generated-source

加载完所有的makefile之后会对 droidcore 目标的所有依赖逐个编译;
1109 droidcore: files \
1110     systemimage \
1111     $(INSTALLED_BOOTIMAGE_TARGET) \
1112     $(INSTALLED_RECOVERYIMAGE_TARGET) \
1113     $(INSTALLED_VBMETAIMAGE_TARGET) \
1114     $(INSTALLED_USERDATAIMAGE_TARGET) \
1115     $(INSTALLED_CACHEIMAGE_TARGET) \
1116     $(INSTALLED_BPTIMAGE_TARGET) \
1117     $(INSTALLED_VENDORIMAGE_TARGET) \
1118     $(INSTALLED_PRODUCTIMAGE_TARGET) \
1119     $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) \
1120     $(INSTALLED_FILES_FILE) \
1121     $(INSTALLED_FILES_FILE_VENDOR) \
1122     $(INSTALLED_FILES_FILE_PRODUCT) \
1123     $(INSTALLED_FILES_FILE_SYSTEMOTHER) \
1124     soong_docs

m
  相当于是在执行make命令。对整个Android源码进行编译。
mm
  如果是在Android源码根目录下执行,那么就相当于是执行make命令对整个源码进行编译。
  如果是在Android源码根目录下的某一个子目录执行,那么就会从该子目录开始,一直往上一个目录直至到根目录,
  寻找是否存在一个Android.mk文件。如果存在的话,就通过make命令对该Android.mk文件描述的模块进行编译。
mmm
  后面可以跟一个或者若干个目录。如果指定了多个目录,那么目录之间以空格分隔,并且每一个目录下都必须存在一个Android,mk文件。
  如果没有在目录后面通过冒号指定模块名称,那么在Android.mk文件中描述的所有模块都会被编译,否则只有指定的模块会被编译。
  如果需要同时指定多个模块,那么这些模块名称必须以逗号分隔。

如果用户想个性定制自己的产品,应该有以下流程:
1. 创建公司目录
    #mkdir vendor/farsight
2. 创建一个vendorsetup.sh文件,将当前产品编译项添加到lunch里,让lunch能找到用户个性定制编译项
    #echo "add_lunch_combo fs100-eng" > vendor/farsight/vendorsetup.sh
3. 仿着Android示例代码,在公司目录下创建products目录
    #mkdir -p vendor/farsight/products
4. 仿着Android示例代码,在products目录下创建两个mk文件
    #touch vendor/farsight/products/AndroidProduct.mk vendor/farsight/products/fs100.mk
build/core/main.mk       包含了config.mk,它主要定义了编译全部代码的依赖关系
build/core/config.mk     定义了大量的编译脚本命令,编译时用到的环境变量,引入了 envsetup.mk 文件,加载board相关配置文件。
build/core/envsetup.mk   定义了编译时用到的大量OUT输出目录,加载product_config.mk文件
build/core/product_config.mk 定义了Vendor目录下Product相关配置文件解析脚本,读取AndrodProducts.mk生成TARGET_DEVICE变量

Build 结果的目录结构
所有的编译产物都将位于 out 目录下,该目录下主要有以下几个子目录:
out/host/:该目录下包含了针对主机的 Android 开发工具的产物。即 SDK 中的各种工具,例如:emulator,adb,aapt 等。
out/target/common/:该目录下包含了针对设备的共通的编译产物,主要是 Java 应用代码和 Java 库。
out/target/product/<product_name>/:包含了针对特定设备的编译结果以及平台相关的 C/C++ 库和二进制文件。其中,<product_name>是具体目标设备的名称。
out/dist/:包含了为多种分发而准备的包,通过“make disttarget”将文件拷贝到该目录,默认的编译目标不会产生该目录。

Build 生成的镜像文件
Build 的产物中最重要的是三个镜像文件,它们都位于 /out/target/product/<product_name>/ 目录下。
这三个文件是:
system.img:包含了 Android OS 的系统文件,库,可执行文件以及预置的应用程序,将被挂载为根分区。
ramdisk.img:在启动时将被 Linux 内核挂载为只读分区,它包含了 /init 文件和一些配置文件。它用来挂载其他系统镜像并启动 init 进程。
userdata.img:将被挂载为 /data,包含了应用程序相关的数据以及和用户相关的数据。

各种模块的编译方式的定义文件
文件名    说明
host_static_library.mk    定义了如何编译主机上的静态库。
host_shared_library.mk    定义了如何编译主机上的共享库。
static_library.mk    定义了如何编译设备上的静态库。
shared_library.mk    定义了如何编译设备上的共享库。
executable.mk    定义了如何编译设备上的可执行文件。
host_executable.mk    定义了如何编译主机上的可执行文件。
package.mk    定义了如何编译 APK 文件。
prebuilt.mk    定义了如何处理一个已经编译好的文件 ( 例如 Jar 包 )。
multi_prebuilt.mk    定义了如何处理一个或多个已编译文件,该文件的实现依赖 prebuilt.mk。
host_prebuilt.mk    处理一个或多个主机上使用的已编译文件,该文件的实现依赖 multi_prebuilt.mk。
java_library.mk    定义了如何编译设备上的共享 Java 库。
static_java_library.mk    定义了如何编译设备上的静态 Java 库。
host_java_library.mk    定义了如何编译主机上的共享 Java 库。
为了减少代码冗余,需要将共同的代码复用起来,复用的方式是将共同代码放到专门的文件中,然后在其他文件中包含这些文件的方式来实现的。


Android.mk 专题

Android.mk 编写
LOCAL_PATH := $(call my-dir) # 定义了当前模块的相对路径,必须在include $(CLEAR_VARS)之前;
include $(CLEAR_VARS)        # 清空当前环境变量,除了 LOCAL_PATH;
LOCAL_MODULE := test         # 编译生成的目标名称
LOCAL_SRC_FILES := test.c    # 编译该模块需要的源文件
include $(BUILD_EXECUTABLE)  # 编译所生成的目标文件格式


多源码文件编译
将工程下的所有源码文件添加到变量中的方式有以下两种
1. 将每个文件添加到 Android.mk 中;
2. 使用系统提供的函数处理; 定义在 build/core/definitions.mk all-c-files-under;
LOCAL_C_ALL_FILES := $(call all-c-files-under,.)
LOCAL_SRC_FILES := $(LOCAL_C_ALL_FILES)
类似的还有
all-cpp-files-under
all-java-files-under
如:
###############################################################
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := test
LOCAL_C_ALL_FILES := $(call all-c-files-under,src)
LOCAL_SRC_FILES := $(LOCAL_C_ALL_FILES)
#LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_EXECUTABLE)
###############################################################


定义目标生成路径,默认在out的对应目录
LOCAL_MODULE_RELATIVE_PATH := $(LOCAL_PATH)/lib
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin


编译生成动态库
设置编译生成的目标文件类型即可
include $(BUILD_SHARED_LIBRARY)

编译生成静态库
设置编译生成的目标文件类型即可
include $(BUILD_STATIC_LIBRARY)

如何引入系统库
LOCAL_SHARED_LIBRARIES += liblog

如何引入第三方库
LOCAL_LDFLAGS += -L Path -lxxx

如何引入静态库
LOCAL_LDFLAGS += $(LOCAL_PATH)/lib/libxxx.so

如何引入头文件
LOCAL_C_INCLUDES += $(LOCAL_PATH)/inc/test.h


如何编译 jar 包
LOCAL_MODULE              # 编译的 jar 包的名字;
BUILD_STATIC_JAVA_LIBRARY # 编译生成静态 jar 包;使用.class文件打包而成,可以在任何java虚拟机上运行;
BUILD_JAVA_LIBRARY        # 编译生成共享 jar 包;在静态jar包基础上使用.dex打包而成的,.dex是android系统使用的文件格式;
###############################################################
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := com.demomk.myjar
LOCAL_SRC_FILES := $(call all-subdir-java-files)

include $(BUILD_STATIC_JAVA_LIBRARY)
#include $(BUILD_JAVA_LIBRARY)
###############################################################

如何编译 apk
BUILD_PACKAGE      # 编译生成 apk
LOCAL_PACKAGE_NAME # 编译生成的 apk 名字
###############################################################
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := LocalPackage
include $(BUILD_PACKAGE)
###############################################################

apk 中如何导入 jar 包和库文件
LOCAL_STATIC_JAVA_LIBRARY # 引用静态 jar 包;
LOCAL_JAVA_LIBRARY        # 应用动态 jar 包;
###############################################################
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_STATIC_JAVA_LIBRARY := static-lib 
#LOCAL_JAVA_LIBRARY := shared-lib

LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := LocalPackage
include $(BUILD_PACKAGE)
###############################################################

如何预编译 jar 包
BUILD_PREBUILT     # 预编译
LOCAL_MODULE_CLASS # 指定编译生成的文件类型,包括
  JAVA_LIBRARIES: dex归档文件
  APPS: apk文件
  SHARED_LIBRARIES: 动态库文件
  EXECUTABLES: 二进制文件
  ETC: 其他文件格式
LOCAL_MODULE       # 预编译出的包文件名
###############################################################
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_MODULE := com.demomk.share
LOCAL_SRC_FILES := com.demomk.static # 源文件可以指定原有的一个jar包

include $(BUILD_PREBUILT)
###############################################################

Android.mk 中如何加判断语句
ifeq($(VALUE),x) #ifneq
  do_yes
else
  do_no
endif
ifeq/ifneq 根据判断条件,执行相关编译指令;


Android.bp
Android.bp是用来替换Android.mk的配置文件,它使用Blueprint框架来解析。Blueprint是生成、解析Android.bp的工具,是Soong的一部分。
Soong则是专为Android编译而设计的工具,Blueprint只是解析文件的形式,而Soong则解释内容的含义,最终转换成Ninja文件。

Ninja 是Google的一名程序员推出的小而快的构建工具,一般在Unix/Linux上的程序通过make/makefile来构建编译,而Ninja通过将编译任务并行组织,大大提高了构建速度。
# 安装
sudo apt install ninja-build
一般是通过cmake来生成ninja的配置,进而进行编译。
# 示例
cmake -G "Ninja" 
ninja

从android N之后,我们发现好多模块下面没有了android.mk文件,多了一个android.bp文件。这个是google在android N之后新的编译配置文件;
在实际项目中,我们需要把android.mk转换为android.bp,sdk中提供了androidmk工具,可以直接把android.mk转换为android.bp.
源码在:build/soong/androidmk
在out下面找下androidmk工具,如果没有可以执行命令:m -j blueprint_tools
然后在 out/soong/host/linux-x86/bin/androidmk 生成工具

$ androidmk  android.mk > android.bp
这样即可把android.mk转换为android.bp文件了

Android.bp 语法
Android.bp文件用类似JSON的简洁声明来描述需要构建的模块。
1、模块
定义一个模块从模块的类型开始,模块有不同的类型,如下例子中的“cc_binary”,
模块包含一些属性格式为 “property-name: property-value”,其中name属性必须指定,其属性值必须是全局唯一的。
cc_binary{
    name: "gzip",
    srcs: ["src/test/minigzip.c"],
    shared_libs: ["libz"],
    stl: "none",
}
默认模块“cc_defaults”的用法如下。
cc_defaults{
    name: "gzip_defaults",
    shared_libs: ["libz"],
    stl: "none",
}
cc_binary{
    name: "gzip",
    defaults: ["gzip_defaults"],
    srcs: ["src/test/minigzip.c"],
}

2、变量
变量赋值可以通过“=”号赋值。
变量是不可变的,但有一个例外,可以附上 += 赋值,但仅在变量被引用之前。
gzip_srcs = ["src/test/minigzip.c"],
cc_binary {
    name: "gzip",
    srcs: gzip_srcs,
    shared_libs: ["libz"],
    stl: "none",
}

3、注释
注释包括单行注释//和多行注释/* */。

4、类型
具体支持以下几种类型:
Bool            ('true' or 'false')
Integers        ('int')
Strings         ('"string"')
Listsof strings ('["string1", "string2"]')
Maps            ('{key1: "value1", key2: ["value2"]}')

5、操作符
除了 = 赋值以外;
String类型、字符串列表类型和Map类型支持操作符“+”。

6、格式控制工具
bpfmt是一个bp文件的格式控制工具,包括4个空格的缩进、列表有多个元素时每个元素一行、列表和map的最后一个元素多一个冗余的逗号等等。
工具目录为:build/blueprint/bpfmt/
在当前目录下执行以下命令:
$ bpfmt-w .

7、Android.mk文件转为Android.bp
androidmk 工具可以把mk文件转换为bp文件,但一些复杂的用法和自定义的规则需要手动转换。
Android.mk --Soong--Blueprint--> Android.bp
Android.bp --Blueprint--Soong--> Ninja
工具源码目录为 build/soong/androidmk/
编译后会生成到 out/soong/host/linux-x86/bin/androidmk
$ androidmk Android.mk > Android.bp

8、支持的模块类型
Android.bp可以支持android_app、cc_binary、cc_binary_host等多种类型,具体如下

732 var moduleTypes = map[string]string{
733     "BUILD_SHARED_LIBRARY":        "cc_library_shared",
734     "BUILD_STATIC_LIBRARY":        "cc_library_static",
735     "BUILD_HOST_SHARED_LIBRARY":   "cc_library_host_shared",
736     "BUILD_HOST_STATIC_LIBRARY":   "cc_library_host_static",
737     "BUILD_HEADER_LIBRARY":        "cc_library_headers",                                                                      
738     "BUILD_EXECUTABLE":            "cc_binary",
739     "BUILD_HOST_EXECUTABLE":       "cc_binary_host",
740     "BUILD_NATIVE_TEST":           "cc_test",
741     "BUILD_HOST_NATIVE_TEST":      "cc_test_host",
742     "BUILD_NATIVE_BENCHMARK":      "cc_benchmark",
743     "BUILD_HOST_NATIVE_BENCHMARK": "cc_benchmark_host",
744 
745     "BUILD_JAVA_LIBRARY":             "java_library",
746     "BUILD_STATIC_JAVA_LIBRARY":      "java_library_static",
747     "BUILD_HOST_JAVA_LIBRARY":        "java_library_host",
748     "BUILD_HOST_DALVIK_JAVA_LIBRARY": "java_library_host_dalvik",
749     "BUILD_PACKAGE":                  "android_app",
750 }
源码位于 build/soong/androidmk/cmd/androidmk/android.go

9、支持预编译类型
Android.bp可以支持4种预编译类型,如下
752 var prebuiltTypes = map[string]string{
753     "SHARED_LIBRARIES": "cc_prebuilt_library_shared",
754     "STATIC_LIBRARIES": "cc_prebuilt_library_static",
755     "EXECUTABLES":      "cc_prebuilt_binary",
756     "JAVA_LIBRARIES":   "java_import",
757 }

10、模块名称解析
soong提供了可以在不同目录中配置相同的模块名称,只要每个模块的名称在不同的命名空间中声明。
soong_namespace{
    imports: ["path/to/otherNamespace1", "path/to/otherNamespace2"],
}

11、条件编译
如下面的arch属性,会根据具体的值是arm还是x86来设置不同的srcs属性值;
cc_library{
    ...
    srcs: ["generic.cpp"],
    arch: {
        arm: {
            srcs: ["arm.cpp"],
        },
        x86: {
            srcs: ["x86.cpp"],
        },
    },
}

Android 利用Blueprint和Soong 来解析bp文件,经过最终转换为ninja files。
Blueprint和Soong都是由Golang写的项目。


参考资料
https://blog.csdn.net/wang92453/article/details/21964491
https://blog.csdn.net/lizekun2010/article/details/52598105
https://www.cnblogs.com/wangzhe1635/articles/8674914.html
https://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/index.html
https://www.jianshu.com/p/7fe1beb38817
https://blog.csdn.net/csdn66_2016/article/details/80258627

猜你喜欢

转载自blog.csdn.net/u010783226/article/details/109044738
今日推荐