美文网首页我爱编程
Android O 编译探析

Android O 编译探析

作者: leer_happy | 来源:发表于2018-02-28 10:53 被阅读0次

    根目录下的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上面已备注。

    相关文章

      网友评论

        本文标题:Android O 编译探析

        本文链接:https://www.haomeiwen.com/subject/pwkbxftx.html