加载中……
Android编译流程

重要说明

Android 7.0以前,系统主要使用GNU make和shell编译系统,模块都使用Android.mk来定义。但是后来呢,大家都明白,Android工程越来越大,make编译所耗费的时间达到足以让程序员发狂,这时google认为这是不正常的现象,咋能编译时间还超过实际调试时间呢?因此在Android 7.0以后,google使用ninja取代make进行系统的编译,由于Android.mk数据实在巨大,Google又加入了两个工具,一个叫kati,用来专门将Android.mk转换成ninja的构建规则文件buildxxx.ninja,然后再使用ninja来进行构建工作;另一个叫blueprint,用于处理Android.bp,编译生成*.ninja文件,用于做ninja的处理。

综上,Android 7.0以后编译要用到的东西:ninja、kati、blueprint。

当然,重点是,我们要介绍的内容主要围绕Android 7.0 以后的系统版本。好,继续!

编译命令

从亲民、众所周知的命令入手,先说说编译的三条命令到底是个什么玩意儿,背后搞了那些事情。

初始化环境变量

source build/envsetup.sh    #或 . build/envsetup.sh   #.后跟空格

认真地讲,这个脚本定义了一大堆函数,但立马执行的命令只有三行。

validate_current_shell #检查当前使用的shell,说得只支持bash和zsh。但我感觉在放狗pi,之前用zsh编译了三个小时报错了,bash就正常的。这里读者自己权衡
source_vendorsetup     #查找vendorsetup.sh脚本,这个名字的脚本作用是调用add_lunch_combo将它们各自的产品添加到 LUNCH_MENU_CHOICES 变量里
addcompletions              #包含一些脚本,另外为lunch、m等函数建立自动补全

听起来似乎可有可无,但如果不执行这一步,你会惊喜地发现lunch会lunch个寂寞!那是怎么回事呢?

这需要追究一个关于shell的一个小细节。

在shell,执行脚本的命令有四种,区别如下:

  1. $PWD/XXX.sh
  2. bash $PWD/XXX.sh
  3. source $PWD/XXX.sh
  4. . $PWD/XXX.sh

第一种是使用路径直接执行一个脚本,一般脚本第一行需要指定使用的shell类型,如:#!/bin/sh,如果没指定,会使用默认shell,估计是sh,反正不是指用户默认使用的shell。

第二种使用用户指定的shell去执行脚本,会无视脚本第一行指定的shell。

第三种和第四种等价,是在当前shell执行脚本。

好了,说到这里,估计大家还是不清楚区别,连我也觉得,这TM说了个啥???哈哈哈,但是博主向来不太喜欢修修改改,所以,总结如下:

前两种方式会在后台另开一个shell,脚本执行完成后立马关闭shell,所有的资源都会被释放。

后两种方式会在当前shell执行,脚本即便执行完成后,脚本中的变量、函数都会保存到当前shell,直到用户关闭shell

(总结个pi总结,分都没有怎么总)

具体效果请看图:

小伙伴,明白了没????????????????????????????????????????

所以,画重点,我们的第二条命令lunch,是envsetup.sh的一个小小的函数。

另,强调一下,包括make,都是envsetup.sh的一个函数,而不是我们熟知的查找Makefile的命令make。

envsetup.sh抛出函数总结如下(认真的):

命令 说明
lunch lunch <product_name>-<build_variant>

选择<product_name>作为要构建的产品,<build_variant>作为要构建的变体,并将这些选择存储在环境中,以便后续调用“m”等读取。

tapas 交互方式:tapas [<App1> <App2> …] [arm|x86|mips|arm64|x86_64|mips64] [eng|userdebug|user]
croot 将目录更改到树的顶部或其子目录。
m 编译整个源码,可以不用切换到根目录
mm 编译当前目录下的源码,不包含他们的依赖模块
mmm 编译指定目录下的所有模块,不包含他们的依赖模块   例如:mmm dir/:target1,target2.
mma 编译当前目录下的源码,包含他们的依赖模块
mmma 编译指定目录下的所模块,包含他们的依赖模块
provision 具有所有必需分区的闪存设备。选项将传递给fastboot。
cgrep 对系统本地所有的C/C++ 文件执行grep命令
ggrep 对系统本地所有的Gradle文件执行grep命令
jgrep 对系统本地所有的Java文件执行grep命令
resgrep 对系统本地所有的res目录下的xml文件执行grep命令
mangrep 对系统本地所有的AndroidManifest.xml文件执行grep命令
mgrep 对系统本地所有的Makefiles文件执行grep命令
sepgrep 对系统本地所有的sepolicy文件执行grep命令
sgrep 对系统本地所有的source文件执行grep命令
godir 根据godir后的参数文件名在整个目录下查找,并且切换目录
allmod 列出所有模块
gomod 转到包含模块的目录
pathmod 获取包含模块的目录
refreshmod 刷新allmod/gomod的模块列表

选择并配置项目

lunch $TARGET

使用这个命令可以根据用户的输入来设置与具体产品相关的环境变量。如果你也像我一样每次lunch都在想“哪个东西叫啥???”,也是可以直接lunch然后再选的哦!

这个我不知道有啥好讲的,总感觉脚本写得很清楚,是吧?硬要说的话,envsetup.sh设置了公共的变量、函数,lunch引入了针对项目的变量(说了个pi)。

那好,lunch结果参考如下:

lunch结果 说明
PLATFORM_VERSION_CODENAME=REL 表示平台版本的名称
PLATFORM_VERSION=10 Android平台的版本号
TARGET_PRODUCT=aosp_arm 所编译的产品名称
TARGET_BUILD_VARIANT=userdebug 所编译产品的类型
TARGET_BUILD_TYPE=release 编译的类型,debug和release
TARGET_ARCH=arm 表示编译目标的CPU架构
TARGET_ARCH_VARIANT=armv7-a-neon 表示编译目标的CPU架构版本
TARGET_CPU_VARIANT=generic 表示编译目标的CPU代号
HOST_ARCH=x86_64 表示编译平台的架构
HOST_2ND_ARCH=x86 表示编译平台的第二CPU架构
HOST_OS=linux 表示编译平台的操作系统
HOST_OS_EXTRA=Linux-4.15.0-112-generic-x86_64-Ubuntu-16.04.6-LTS 编译系统之外的额外信息
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release 编译类型
BUILD_ID=QQ1D.200205.002 BUILD_ID会出现在版本信息中,可以利用
OUT_DIR=out 编译结果输出的路径

完毕,下一个!

正式编译

make
再次强调,这里的make是个函数,是个函数咯喂

make在source build/envsetup.sh的前后对比:

所以!所以!make第一步不是执行Makefile!

继续,get_make_command() 如下:

function get_make_command()
{
    # If we're in the top of an Android tree, use soong_ui.bash instead of make
    if [ -f build/soong/soong_ui.bash ]; then
        # Always use the real make if -C is passed in
        for arg in "$@"; do
            if [[ $arg == -C* ]]; then
                echo command make
                return
            fi
        done
        echo build/soong/soong_ui.bash --make-mode
    else
        echo command make
    fi
}

所以,最终我们可以理解为,编译开始的命令是

build/soong/soong_ui.bash --make-mode $@

发现新大陆,soong_ui.bash才是编译入口诶!!那我们就继续分析soong_ui.bash这个脚本。

function gettop
{
    local TOPFILE=build/soong/root.bp
    if [ -n "${TOP-}" -a -f "${TOP-}/${TOPFILE}" ] ; then
        # The following circumlocution ensures we remove symlinks from TOP.
        (cd $TOP; PWD= /bin/pwd)
    else
        if [ -f $TOPFILE ] ; then
            # The following circumlocution (repeated below as well) ensures
            # that we record the true directory name and not one that is
            # faked up with symlink names.
            PWD= /bin/pwd
        else
            local HERE=$PWD
            T=
            while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
                \cd ..
                T=`PWD= /bin/pwd -P`
            done
            \cd $HERE
            if [ -f "$T/$TOPFILE" ]; then
                echo $T
            fi
        fi
    fi
}

# Save the current PWD for use in soong_ui
export ORIGINAL_PWD=${PWD}
export TOP=$(gettop)
source ${TOP}/build/soong/scripts/microfactory.bash

soong_build_go soong_ui android/soong/cmd/soong_ui

cd ${TOP}
exec "$(getoutdir)/soong_ui" "$@"

代码看着难受,那你看我这样总结对不对(战术总结)

  • 脚本source了一个microfactory.bash,该脚本主要得到一些函数命令,如soong_build_go
  • 编译build/soong/cmd/soong_ui/main.go,生成 out/soong_ui这个可执行程序
  • 回到根目录
  • 执行命令:out/soong_ui –make-mode ,真正的构建,执行了make命令,会把”build/make/core/main.mk” 加到构建环境中,同时启动kati、blueprint-soong、ninja的编译。

未完待续….

版权声明: 若无特殊说明,文章均为原创,版权归本文作者所有,转载请保留出处和此说明!
本文链接: Android编译流程
本文作者: Jan.
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇