Android O 编译探析

根目录下的Makefile

include build/core/main.mk

build/core/main.mk

host_prebuilts := linux-x86

.PHONY: run_soong_ui
run_soong_ui:
    +@prebuilts/build-tools/$(host_prebuilts)/bin/makeparallel --ninja build/soong/soong_ui.bash --make-mode $(MAKECMDGOALS)

.PHONY: $(MAKECMDGOALS)
$(sort $(MAKECMDGOALS)) : run_soong_ui
    @#empty

MAKECMDGOALS

  • The targets given to make on the command line. Setting this variable has no effect on the operation of make.

根据build/envsetup.sh,MAKECMDGOALS会被mm/mmm/mma/mmma设置,结构大致是MODULES-IN-$DIR, 执行make/m时为空

prebuilts/build-tools/linux-x86/bin/makeparallel

file prebuilts/build-tools/linux-x86/bin/makeparallel

prebuilts/build-tools/linux-x86/bin/makeparallel: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, stripped

源代码位置 build/make/tools/makeparallel,是用C++编写的,可以自行执行make编译

makeparallel.cpp
int main(int argc, char* argv[]) {
    解析参数
    pid = fork();
  if (pid < 0) {
    error(errno, errno, "fork failed");
  } else if (pid == 0) {
    // child
    unsetenv("MAKEFLAGS");
    unsetenv("MAKELEVEL");

    // make 3.81 sets the stack ulimit to unlimited, which may cause problems
    // for child processes
    struct rlimit rlim{};
    if (getrlimit(RLIMIT_STACK, &rlim) == 0 && rlim.rlim_cur == RLIM_INFINITY) {
      rlim.rlim_cur = 8*1024*1024;
      setrlimit(RLIMIT_STACK, &rlim);
    }

    // 执行 build/soong/soong_ui.bash --make-mode XXX
    int ret = execvp(path, args.data());
    if (ret < 0) {
      error(errno, errno, "exec %s failed", path);
    }
    abort();
  }
  // 父进程等待子进程退出
}

build/soong/soong_ui.bash

...
run_go "$@"

核心是执行函数run_go

function run_go
{
    # Increment when microfactory changes enough that it cannot rebuild itself.
    # For example, if we use a new command line argument that doesn't work on older versions.
    local mf_version=2

    local mf_src="${TOP}/build/soong/cmd/microfactory"

    local out_dir="${OUT_DIR-}"
    if [ -z "${out_dir}" ]; then
        if [ "${OUT_DIR_COMMON_BASE-}" ]; then
            out_dir="${OUT_DIR_COMMON_BASE}/$(basename ${TOP})"
        else
            out_dir="${TOP}/out"
        fi
    fi

    local mf_bin="${out_dir}/microfactory_$(uname)"
    local mf_version_file="${out_dir}/.microfactory_$(uname)_version"
    local soong_ui_bin="${out_dir}/soong_ui"
    local from_src=1

    if [ -f "${mf_bin}" ] && [ -f "${mf_version_file}" ]; then
        if [ "${mf_version}" -eq "$(cat "${mf_version_file}")" ]; then
            from_src=0
        fi
    fi

    // 第一次直接运行go文件并生成可执行文件microfactory_Linux,之后直接执行microfactory_Linux
    local mf_cmd
    if [ $from_src -eq 1 ]; then
        mf_cmd="${GOROOT}/bin/go run ${mf_src}/microfactory.go"
    else
        mf_cmd="${mf_bin}"
    fi

   // 为了生成soong_ui
    ${mf_cmd} -s "${mf_src}" -b "${mf_bin}" \
            -pkg-path "android/soong=${TOP}/build/soong" -trimpath "${TOP}/build/soong" \
            -o "${soong_ui_bin}" android/soong/cmd/soong_ui

    if [ $from_src -eq 1 ]; then
        echo "${mf_version}" >"${mf_version_file}"
    fi

 // 执行soong_ui
    exec "${out_dir}/soong_ui" "$@"
}

build/soong/cmd/microfactory/microfactory.go

省略

build/soong/cmd/soong_ui/main.go

func main() {
      ......
      build.Build(buildCtx, config, build.BuildAll)
}

build/soong/ui/build/build.go

const (
    BuildNone          = iota
    BuildProductConfig = 1 << iota
    BuildSoong         = 1 << iota
    BuildKati          = 1 << iota
    BuildNinja         = 1 << iota
    BuildAll           = BuildProductConfig | BuildSoong | BuildKati | BuildNinja
)

func Build(ctx Context, config Config, what int)
根据之前what值决定执行哪些操作,根据上面的代码,可以看到调用的是build.BuildAll
下面看一下每项操作具体做了哪些工作

  • BuildProductConfig
>runMakeProductConfig(ctx, config)

build/soong/ui/build/make.go
核心操作是执行了make -f build/core/config.mk,解析命令执行结果获得变量/值的集合,同步环境变>量。为config设置KATI_GOALS、NINJA_GOALS,其实就是之前的MAKECMDGOALS。

  • BuildSoong
runSoongBootstrap(ctx, config)
runSoong(ctx, config)
  • runSoongBootstrap
    核心逻辑是执行build/soong/bootstrap.bash
  • build/soong/bootstrap.bash
    输出模板build/soong/soong.bootstrap.inout/soong/.soong.bootstrap,执行build/blueprint/bootstrap.bash,建立软链接out/soong/soong指向build/soong/soong.bash
$ cat out/soong/.soong.bootstrap 
BUILDDIR="out/soong"
SRCDIR_FROM_BUILDDIR="../.."
PREBUILTOS="linux-x86"
  • build/blueprint/bootstrap.bash
    输出模板build/soong/build.ninja.inout/soong/.minibootstrap/build.ninja,变量存储到out/soong/.blueprint.bootstrap
$ cat out/soong/.blueprint.bootstrap
BOOTSTRAP="./bootstrap.bash"
BOOTSTRAP_MANIFEST="./build/soong/build.ninja.in"
  • runSoong
    执行out/soong/soong(build/soong/soong.bash)
  • build/soong/soong.bash
BUILDDIR=out/soong NINJA="prebuilts/build-tools/linux-x86/bin/ninja" build/blueprint/blueprint.bash $@
  • build/blueprint/blueprint.bash
# Build minibp and the primary build.ninja
"${NINJA}" -w dupbuild=err -f "${BUILDDIR}/.minibootstrap/build.ninja"

# Build the primary builder and the main build.ninja
"${NINJA}" -w dupbuild=err -f "${BUILDDIR}/.bootstrap/build.ninja"

# SKIP_NINJA can be used by wrappers that wish to run ninja themselves.
if [ -z "$SKIP_NINJA" ]; then
   "${NINJA}" -w dupbuild=err -f "${BUILDDIR}/build.ninja" "$@"
else
   exit 0
fi
  • BuildKati
runKati(ctx, config)
    • build/soong/ui/build/kati.go
      runKati 核心是执行ckati命令。ckati的源码位于build/kati下,通过make执行
// 生成KatiSuffix,后面会需要
genKatiSuffix(ctx, config)
executable := "prebuilts/build-tools/" + config.HostPrebuiltTag() + "/bin/ckati"
args := []string{
        "--ninja",
        "--ninja_dir=" + config.OutDir(),
        "--ninja_suffix=" + config.KatiSuffix(),
        "--regen",
        "--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
        "--detect_android_echo",
        "--color_warnings",
        "--gen_all_targets",
        "-f", "build/core/main.mk",
    }
    • build/kati/main.cc

  跟踪main函数,先进行Init(),初始化了一些东西,先不care。把参数做个备份保存到orig_args(不知道什么意图)。把参数拿去解析(flags.cc),解析完存放结构体Flags中。
  FindFirstMakefie寻找第一个makefile,上面已经通过-f指定了makefile,未指定则顺序寻找当前目录下的GNUmakefile、makefile、Makefile。
  然后进入Run函数。根据之前参数需要生成Ninja文件。NeedsRegen判断是否需要重新生成ninja文件。函数IsMissingOutputs判断GetNinjaFilename/GetNinjaShellScriptFilename是否存在,涉及到config.ninja_suffix,也就是KatiSuffix上面已备注。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容