Android编译之android.mk
1. android系统源码的编译流程
来回顾⼀下常见的编译步骤:
source build/envtup.sh
lunch xxx
make -j8 2>&1 | tee build.log
这三步究竟做了什么呢?我们来逐步分析⼀下。
1.1 source build/envtup.sh
build/envtup.sh这个⽂件中定义了⼀些变量和函数,执⾏source build/envtup.sh之后,envtup.sh中的变量成了全局变量,⽽其中的函数也可以直接在当前终端命令⾏中使⽤了。这些函数可以帮助我们切换⽬录,查找⽂件,可以使我们在编译源码时更⽅便。这些函数可通过hmm函数来查看,下⾯列出了⼀些常⽤的函数:
lunch: lunch <product_name>-<build_variant> (选择要编译的⽬标产品和版本)
tapas: tapas [<App1> <App2> ...] [arm|x86|mips|armv5] [eng|urdebug|ur]
croot: Changes directory to the top of the tree.(切换到源码的顶层⽬录)
m: Makes from the top of the tree.(从顶层⽬录build整个系统)
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.(构建指定⽬录下所有的模块,但不包括它们的依赖)
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 pasd on to fastboot.(将设备所有需要的分区刷⼊,选项将传递给fastboot)
cgrep: Greps on all local C/C++ files.(在C,C++⽂件中搜索指定关键字)
ggrep: Greps on all local Gradle files.(在gradle⽂件中搜索指定关键字)
jgrep: Greps on all local Java files.(在java⽂件中搜索指定关键字)
resgrep: Greps on all local res/*.xml files.(在资源xml⽂件中搜索指定关键字)
mangrep: Greps on all l files.(在l⽂件中搜索指定关键字)
mgrep: Greps on all local Makefiles files.(在Makefiles和android.mk⽂件中搜索指定关键字)
pgrep: Greps on all local policy files.(在policy⽂件中搜索指定关键字)
sgrep: Greps on all local source files.(在所有本地⽂件中搜索指定关键字)
godir: Go to the directory containing a file.(切换到包含某个⽂件的⽬录下)
除了hmm提⽰的这些,还有⼀些⽅法,具体可以直接查看build/envtup.sh⽂件:
cproj: 向上切换到最近包含Android.mk的⽬录下
findmakefile: 打印当前⽬录所在⼯程的Android.mk的⽂件路径
getsdcardpath: 获取Sd卡路径
getscreenshotpath: 获取屏幕截图的路径
getlastscreenshot: 获取最后⼀张截图,导出到当前⽬录下
getbugreports: 将bug报告从设备上导出到本地,bug报告存放于⽬录/sdcard/bugreports
gettop: 获取Android源码根⽬录
pid: pid processname 查看某个可执⾏程序对应的进程id
key_back: 模拟按返回键
key_home: 模拟按Home键
key_menu: 模拟按菜单键
envtup.sh中还定义了add_lunch_combo函数,并且多次执⾏了add_lunch_combo函数,将⾃⾝定义的所有product添加到LUNCH_MENU_CHOICES中:
# Clear this variable. It will be built up again when the vendortup.sh
# files are included at the end of this file.
unt LUNCH_MENU_CHOICES
function add_lunch_combo()
{
local new_combo=$1
local c
for c in ${LUNCH_MENU_CHOICES[@]} ; do
if [ "$new_combo" = "$c" ] ; then
return
fi
done
LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
}
# add the default one here
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
最后,envtup.sh中还遍历并执⾏vendor和device⽬录下的所有vendortup.sh⽂件:
# Execute the contents of any vendortup.sh files we can find.
for f in `test -d device && find -L device -maxdepth 4 -name 'vendortup.sh' 2> /dev/null | sort` \
`test -d vendor && find -L vendor -maxdepth 4 -name 'vendortup.sh' 2> /dev/null | sort` \
`test -d product && find -L product -maxdepth 4 -name 'vendortup.sh' 2> /dev/null | sort`
do
echo "including $f"
. $f
done
unt f
这些vendortup.sh中也定义了的⼀些product,执⾏这些vendortup.sh后,也会将它们当中定义的product添加到LUNCH_MENU_CHOICES中:
add_lunch_combo full_k63v2_64_bsp-eng
add_lunch_combo full_k63v2_64_bsp-ur
add_lunch_combo full_k63v2_64_bsp-urdebug
在此之后,我们就可以在命令⾏⽤lunch函数从LUNCH_MENU_CHOICES中选择需要编译的product。
1.2 lunch
执⾏lunch函数时,如果⽤户指定了product,获取指定的product。如果⽤户未指定product,调⽤print_lunch_menu函数输出上⼀步⽣成的lunch menu choices让⽤户选择。如⽤户指定了product或者提⽰后选择了product,会获取⽤户指定的product,并提取$product 和$variant。然后检查是否⽀持product,如不⽀持,提⽰不⽀持并退出。如⽀持,接着会调⽤t_stuff_for_environment函数设置⼀系列环境变量,还会调⽤printconfig输出相关变量和配置信息。
function lunch()
{
local answer
if [ "$1" ] ; then
answer=$1
el
print_lunch_menu
echo -n "Which would you like? [aosp_arm-eng] "
read answer
fi
local lection=
if [ -z "$answer" ]
then
lection=aosp_arm-eng
elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
then
if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
then
lection=${LUNCH_MENU_CHOICES[$(($answer-1))]}
fi
el
lection=$answer
fi
export TARGET_BUILD_APPS=
local product variant_and_version variant version
product=${lection%%-*} # Trim everything after first dash
variant_and_version=${lection#*-} # Trim everything up to first dash
if [ "$variant_and_version" != "$lection" ]; then
variant=${variant_and_version%%-*}
if [ "$variant" != "$variant_and_version" ]; then
version=${variant_and_version#*-}
fi
fi
if [ -z "$product" ]
then
echo
echo "Invalid lunch combo: $lection"
return 1
fi
TARGET_PRODUCT=$product \
TARGET_BUILD_VARIANT=$variant \
TARGET_PLATFORM_VERSION=$version \
build_build_var_cache
if [ $? -ne 0 ]
then
return 1
fi
export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT)
export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT)
if [ -n "$version" ]; then
export TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION)
el
unt TARGET_PLATFORM_VERSION
fi
export TARGET_BUILD_TYPE=relea
echo
t_stuff_for_environment
printconfig
destroy_build_var_cache
}
1.3 make -j8 2>&1 | tee build.log
-j8是⽤于指定编译的cpu核⼼,如果编译服务器配置好,可以略为改⼤⼀些。
2>&1 | tee build.log则是同时输出编译的提⽰、异常信息到终端和build.log⽂件中。
make则是执⾏make命令,寻找源码根⽬录下的Makefile,解析Makefile并开始整个源码的编译。
接下来我们就从源码根⽬录下的Makefile开始逐步解析。
2. android的Makefile和android.mk
执⾏make之后,从源码根⽬录下的Makefile开始逐步解析。该Makefile的内容很少,只是导⼊了build/make/core/main.mk。Makefile:
### DO NOT EDIT THIS FILE ###
include build/make/core/main.mk
### DO NOT EDIT THIS FILE ###
main.mk中有两个最重要的部分,⼀个是导⼊build/make/core/config.mk和build/make/core/definitions.mk。
main.mk:
......
BUILD_SYSTEM := $(TOPDIR)build/make/core
......
# Set up various standard variables bad on configuration
# and host information.
include $(BUILD_SYSTEM)/config.mk
......
# Bring in standard build system definitions.
include $(BUILD_SYSTEM)/definitions.mk
......
config.mk中定义了⼀系列编译需要⽤到的变量,⽐如常⽤的CLEAR_VARS、BUILD_PACKAGE。这些变量实际上是每⼀个变量导⼊另⼀个.mk⽂件。每⼀个被导⼊的.mk完成⼀个基本功能,⽐如,CLEAR_VARS对应的build/make/core/clear_vars.mk是清除编译的临时变量,BUILD_PACKAGE对应的build/make/core/package.mk是编译APK。
config.mk:
# Set up efficient math functions which are ud in make.
# Here since this file is included by envtup as well as during build.
include $(BUILD_SYSTEM)/math.mk
# Various mappings to avoid hard-coding paths all over the place
include $(BUILD_SYSTEM)/pathmap.mk
# Allow projects to define their own globally-available variables
include $(BUILD_SYSTEM)/project_definitions.mk
# ###############################################################
# Build system internal files
# ###############################################################
BUILD_COMBOS:= $(BUILD_SYSTEM)/combo
CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk
BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk
BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
BUILD_HEADER_LIBRARY:= $(BUILD_SYSTEM)/header_library.mk
BUILD_AUX_STATIC_LIBRARY:= $(BUILD_SYSTEM)/aux_static_library.mk
BUILD_AUX_EXECUTABLE:= $(BUILD_SYSTEM)/aux_executable.mk
BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk
BUILD_PHONY_PACKAGE:= $(BUILD_SYSTEM)/phony_package.mk
BUILD_RRO_PACKAGE:= $(BUILD_SYSTEM)/build_rro_package.mk
BUILD_HOST_PREBUILT:= $(BUILD_SYSTEM)/host_prebuilt.mk
BUILD_PREBUILT:= $(BUILD_SYSTEM)/prebuilt.mk
BUILD_MULTI_PREBUILT:= $(BUILD_SYSTEM)/multi_prebuilt.mk
BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk
BUILD_STATIC_JAVA_LIBRARY:= $(BUILD_SYSTEM)/static_java_library.mk
BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
BUILD_DROIDDOC:= $(BUILD_SYSTEM)/droiddoc.mk
BUILD_APIDIFF:= $(BUILD_SYSTEM)/apidiff.mk
BUILD_COPY_HEADERS := $(BUILD_SYSTEM)/copy_headers.mk
BUILD_NATIVE_TEST := $(BUILD_SYSTEM)/native_test.mk
BUILD_NATIVE_BENCHMARK := $(BUILD_SYSTEM)/native_benchmark.mk
BUILD_HOST_NATIVE_TEST := $(BUILD_SYSTEM)/host_native_test.mk
BUILD_FUZZ_TEST := $(BUILD_SYSTEM)/fuzz_test.mk
BUILD_HOST_FUZZ_TEST := $(BUILD_SYSTEM)/host_fuzz_test.mk
BUILD_SHARED_TEST_LIBRARY := $(BUILD_SYSTEM)/shared_test_lib.mk
BUILD_HOST_SHARED_TEST_LIBRARY := $(BUILD_SYSTEM)/host_shared_test_lib.mk
BUILD_STATIC_TEST_LIBRARY := $(BUILD_SYSTEM)/static_test_lib.mk
BUILD_HOST_STATIC_TEST_LIBRARY := $(BUILD_SYSTEM)/host_static_test_lib.mk
BUILD_NOTICE_FILE := $(BUILD_SYSTEM)/notice_files.mk
BUILD_HOST_DALVIK_JAVA_LIBRARY := $(BUILD_SYSTEM)/host_dalvik_java_library.mk
BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY := $(BUILD_SYSTEM)/host_dalvik_static_java_library.mk
BUILD_HOST_TEST_CONFIG := $(BUILD_SYSTEM)/host_test_config.mk
BUILD_TARGET_TEST_CONFIG := $(BUILD_SYSTEM)/target_test_config.mk
⽽definitions.mk中⽤define也定义了⼀系列变量,⽐如常⽤的my-dir、all-subdir-makefiles、all-subdir-java-files等等。这些编译主要⽤于处理⽬录相关的功能,⽐如my-dir是获取当前⽬录路径,all-subdir-makefiles调⽤所有⼦⽬录的android.mk。definitions.mk中有很多变量,列举如下:
definitions.mk: