Android源码解析之——Build系统

一、概述

一般Android App Developer,是无需关心Build系统是如何运作的,因为Android StudioGradle工具已经帮我们简化了这些操作。但如果你想成为一个Android Framework Engineer,就必须对此有所了解,因为无论是你想在Aosp源码中添加自己的设备,还是想新增系统应用,这些知识都是必要的。

可是对于新手,往往对不知道Build系统如何入门,遇到问题也只能Google和Baidu,但能否解决问题,这通常跟系统版本密切相关。所以:当遇到编译的问题时,阅读Build系统源码才是王道。

Android的Build系统是基于GNU Make和Shell构建的一套编译环境。Android的Build系统可分为3大模块:

  • 1、位于build/core目录下的文件,这是Android Build系统的框架和核心。
  • 2、位于device目录下的文件,存放的是具体产品的配置文件。
  • 3、模块的编译文件:Android.mk,位于模块的源文件目录下。

我们在上一章已经知道编译系统镜像的步骤:

  • 1、建立Android编译环境 $ source ./build/envsetup.sh

  • 2、根据用户输入或选择的产品名来设置与具体产品相关的环境变量 $ lunch <product_name>-<build_variant> 或者 $ lunch <序号> 或者 $ lunch #打印出产品菜单项 $ 输入序号

  • 3、开始编译 $ make update-api -j4 $ make -j4

我们可以通过上述步骤,去逐步分析Build系统是如何去编译这个系统源码的。

这里,我们采用android-5.0.2_r1的分支源码,其余分支源码也类似。

二、系统镜像编译流程分析

首先,我们执行./build/envsetup.sh脚本文件,该脚本中定义了一些有用的shell命令,可通过hmm命令进行查看:

sh 复制代码
$ source ./build/envsetup.sh
$ 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.
- 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.
- sgrep:   Greps on all local source files.
- godir:   Go to the directory containing a file.

Look at the source to view more functions. The complete list is:
addcompletions add_lunch_combo cgrep check_product check_variant choosecombo chooseproduct  choosetype choosevariant cproj croot findmakefile gdbclient gdbwrapper get_abs_build_var getbugreports get_build_var getdriver getlastscreenshot get_make_command getprebuilt getscreenshotpath getsdcardpath get_symbols_directory 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 qpid 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

其中常用的命令有lunchmmmmammmmmmacroot,其中lunch命令是我们接下来分析的重点,而后5个命令在hmm中已给出了简单清晰的解释,故不赘述。

当执行source ./build/envsetup.sh时,会自动执行以下代码:

sh 复制代码
......省略部分代码......
# 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

......省略部分代码......
# Execute the contents of any vendorsetup.sh files we can find.
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`
do
    echo "including $f"
    . $f
done
unset f

可以清楚发现,其主要功能包括:

  • 1、利用add_lunch_combo函数添加aosp设备默认编译选项。
  • 2、去device、vendor、product三个目录下搜寻vendorsetup.sh文件,并include进来。

vendorsetup.sh文件通常内容也只是add_lunch_combo xxxxx,用来添加自定义设备编译选项。

那我们接下来分析add_lunch_combo命令:

sh 复制代码
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_lunch_combo命令会将所传递的参数存放到一个全局的数组变量LUNCH_MENU_CHOICES中。执行lunch命令时,打印的菜单项正是该数组的内容。

当所有设备编译选项都被add_lunch_combo添加后,就可以执行lunch命令对编译环境进行正式的配置。

sh 复制代码
function lunch()
{
    local answer

    // 如果lunch命令参数非空,则给变量answer赋值,否则打印LUNCH_MENU_CHOICES数组
    if [ "$1" ] ; then
        answer=$1
    else
        print_lunch_menu
        echo -n "Which would you like? [aosp_arm-eng] "
        read answer
    fi

    local selection=

    // 用户依旧不选择lunch项,则将selection设置为aosp_arm-eng
    if [ -z "$answer" ]
    then
        selection=aosp_arm-eng
    elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
    then
        // 如果变量answer是数字,则将selection设置为LUNCH_MENU_CHOICES数组中对应项
        if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
        then
            selection=${LUNCH_MENU_CHOICES[$(($answer-1))]}
        fi
    // 如果变量answer非数字,判断其格式是否符合规范
    elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")
    then
        selection=$answer
    fi

    if [ -z "$selection" ]
    then
        echo
        echo "Invalid lunch combo: $answer"
        return 1
    fi

    export TARGET_BUILD_APPS=

    // 把变量selection中的字符串用"-"分成两部分,前部分赋值给变量product,并调用函数check_product去检查是否存变量product所对应的产品配置文件
    local product=$(echo -n $selection | sed -e "s/-.*$//")
    check_product $product
    if [ $? -ne 0 ]
    then
        echo
        echo "** Don't have a product spec for: '$product'"
        echo "** Do you have the right repo manifest?"
        product=
    fi

    // 后部分赋值给变量variant,并调用函数check_variant去检查这个值是否是"eng"、"user"、"userdebug"之一
    local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")
    check_variant $variant
    if [ $? -ne 0 ]
    then
        echo
        echo "** Invalid variant: '$variant'"
        echo "** Must be one of ${VARIANT_CHOICES[@]}"
        variant=
    fi

    if [ -z "$product" -o -z "$variant" ]
    then
        echo
        return 1
    fi

    // 环境变量赋值
    export TARGET_PRODUCT=$product
    export TARGET_BUILD_VARIANT=$variant
    export TARGET_BUILD_TYPE=release

    echo

    //设置更多的环境变量
    set_stuff_for_environment
    //打印配置信息
    printconfig
}

其中整个lunch命令核心代码如下,用于配置所需编译的设备镜像。

sh 复制代码
export TARGET_PRODUCT=$product
export TARGET_BUILD_VARIANT=$variant
export TARGET_BUILD_TYPE=release

lunch执行完后,系统会打印出当前配置所生成的环境变量。

ini 复制代码
pujh@dell:~/Tiny4412/android-5.0.2$ lunch full_tiny4412-eng

============================================
PLATFORM_VERSION_CODENAME=REL                          \\平台版本名称
PLATFORM_VERSION=5.0.2                                 \\Android平台版本号
TARGET_PRODUCT=full_tiny4412                           \\所编译的产品名称
TARGET_BUILD_VARIANT=eng                               \\编译的产品类型
TARGET_BUILD_TYPE=release                              \\编译的类型
TARGET_BUILD_APPS=                                     \\当编译整个系统时,变量为null;当编译单个模块时,变量为模块的路径
TARGET_ARCH=arm                                        \\编译目标的CPU架构
TARGET_ARCH_VARIANT=armv7-a-neon                       \\编译目标的CPU架构版本
TARGET_CPU_VARIANT=cortex-a9                           \\编译目标的CPU代号
TARGET_2ND_ARCH=                                       \\编译目标的第二CPU架构
TARGET_2ND_ARCH_VARIANT=                               \\编译目标的第二CPU架构版本
TARGET_2ND_CPU_VARIANT=                                \\编译目标的第二CPU代号
HOST_ARCH=x86_64                                       \\编译平台的CPU架构
HOST_OS=linux                                          \\编译平台的操作系统
HOST_OS_EXTRA=Linux-4.4.0-21-generic-x86_64-with-Ubuntu-16.04-xenial          \\编译平台操作系统的一些额外信息
HOST_BUILD_TYPE=release
BUILD_ID=LRX22G                                        \\该值会出现在编译的版本信息中,可以利用整个环境变量来定义公司特有的标识
OUT_DIR=out                                            \\编译结果输出目录
============================================

三、make分析

Makefile文件主要由3种内容构成:变量定义、函数定义和目标依赖规则,其详细使用教程参考跟我一起写Makefile

当用户执行make命令时,会调用当前目录下Makefile文件,Android源码根目录Makefile文件内容只有一行,用于指向build/core/main.mk文件:

makefile 复制代码
include build/core/main.mk

main.mk是Android Build系统的主控文件。从main.mk开始,将通过include命令将其余所有需要的.mk文件包含进来,最终在内存中形成一个包括所有编译脚本的集合,这个集合相当于一个巨大的Makefile文件。其大致包含关系如下:

其中config.mk会调用Android.mk中常用的mk定义,例如:

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_RAW_STATIC_LIBRARY := $(BUILD_SYSTEM)/raw_static_library.mk
BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
BUILD_RAW_EXECUTABLE:= $(BUILD_SYSTEM)/raw_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_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_COPY_HEADERS := $(BUILD_SYSTEM)/copy_headers.mk
BUILD_NATIVE_TEST := $(BUILD_SYSTEM)/native_test.mk
BUILD_HOST_NATIVE_TEST := $(BUILD_SYSTEM)/host_native_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

以及C/C++代码编译时的参数以及系统常用包的后缀名。

加快编译速度CCache

预编译模块的目标定义:

brk:调整堆的高地址边界 mmap:在堆区和栈区之间寻找一块合适的内存空间 munmap mremap

内存分配器------dlmalloc dlmalloc 2.8.6 源码详解

相关推荐
666xiaoniuzi4 小时前
深入理解 C 语言中的内存操作函数:memcpy、memmove、memset 和 memcmp
android·c语言·数据库
沐言人生8 小时前
Android10 Framework—Init进程-8.服务端属性文件创建和mmap映射
android
沐言人生8 小时前
Android10 Framework—Init进程-9.服务端属性值初始化
android·android studio·android jetpack
沐言人生9 小时前
Android10 Framework—Init进程-7.服务端属性安全上下文序列化
android·android studio·android jetpack
追光天使9 小时前
【Mac】和【安卓手机】 通过有线方式实现投屏
android·macos·智能手机·投屏·有线
小雨cc5566ru9 小时前
uniapp+Android智慧居家养老服务平台 0fjae微信小程序
android·微信小程序·uni-app
一切皆是定数10 小时前
Android车载——VehicleHal初始化(Android 11)
android·gitee
一切皆是定数10 小时前
Android车载——VehicleHal运行流程(Android 11)
android
problc10 小时前
Android 组件化利器:WMRouter 与 DRouter 的选择与实践
android·java
图王大胜11 小时前
Android SystemUI组件(11)SystemUIVisibility解读
android·framework·systemui·visibility