Android-new-build-system

来源:互联网 发布:linux nobody启动程序 编辑:程序博客网 时间:2024/05/17 04:16

1. make/core/main.mk

ifndef KATIhost_prebuilts := linux-x86  ifeq ($(shell uname),Darwin)  host_prebuilts := darwin-x86  endif  .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  

2. soong/soong_ui.bash

# Bootstrap microfactory from source if necessary and use it to build the# soong_ui binary, then run soong_ui.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    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    ${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    exec "${out_dir}/soong_ui" "$@"}

第一次执行, from_src为1:

varable value mf_cmd /home/kelvin/os/android-8.0.0_r4/prebuilts/go/linux-x86//bin/go run /home/kelvin/os/android-8.0.0_r4/build/soong/cmd/microfactory/microfactory.go mf_src /home/kelvin/os/android-8.0.0_r4/build/soong/cmd/microfactory mf_bin /home/kelvin/os/android-8.0.0_r4/out/microfactory_Linux soong_ui_bin /home/kelvin/os/android-8.0.0_r4/out/soong_ui

现在分析mf_cmd的执行过程[microfactory.go]:

func main() {    var output, mysrc, mybin, trimPath string    var pkgMap pkgPathMapping    flags := flag.NewFlagSet("", flag.ExitOnError)    flags.BoolVar(&race, "race", false, "enable data race detection.")    flags.BoolVar(&verbose, "v", false, "Verbose")    flags.StringVar(&output, "o", "", "Output file")    flags.StringVar(&mysrc, "s", "", "Microfactory source directory (for rebuilding microfactory if necessary)")    flags.StringVar(&mybin, "b", "", "Microfactory binary location")    flags.StringVar(&trimPath, "trimpath", "", "remove prefix from recorded source file paths")    flags.Var(&pkgMap, "pkg-path", "Mapping of package prefixes to file paths")    err := flags.Parse(os.Args[1:])    if err == flag.ErrHelp || flags.NArg() != 1 || output == "" {        fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "-o out/binary <main-package>")        flags.PrintDefaults()        os.Exit(1)    }    if mybin != "" && mysrc != "" {        rebuildMicrofactory(mybin, mysrc, &pkgMap)    }    mainPackage := &GoPackage{        Name: "main",    }    if path, ok, err := pkgMap.Path(flags.Arg(0)); err != nil {        fmt.Fprintln(os.Stderr, "Error finding main path:", err)        os.Exit(1)    } else if !ok {        fmt.Fprintln(os.Stderr, "Cannot find path for", flags.Arg(0))    } else {        if err := mainPackage.FindDeps(path, &pkgMap); err != nil {            fmt.Fprintln(os.Stderr, err)            os.Exit(1)        }    }    intermediates := filepath.Join(filepath.Dir(output), "."+filepath.Base(output)+"_intermediates")    err = os.MkdirAll(intermediates, 0777)    if err != nil {        fmt.Fprintln(os.Stderr, "Failed to create intermediates directory: %ve", err)        os.Exit(1)    }    err = mainPackage.Compile(intermediates, trimPath)    if err != nil {        fmt.Fprintln(os.Stderr, "Failed to compile:", err)        os.Exit(1)    }    err = mainPackage.Link(output)    if err != nil {        fmt.Fprintln(os.Stderr, "Failed to link:", err)        os.Exit(1)    }}

解析传入的参数,然后调用流程如下:

// rebuildMicrofactory checks to see if microfactory itself needs to be rebuilt,// and if does, it will launch a new copy instead of returning.func rebuildMicrofactory(mybin, mysrc string, pkgMap *pkgPathMapping) {    intermediates := filepath.Join(filepath.Dir(mybin), "."+filepath.Base(mybin)+"_intermediates")    err := os.MkdirAll(intermediates, 0777)    if err != nil {        fmt.Fprintln(os.Stderr, "Failed to create intermediates directory: %v", err)        os.Exit(1)    }    pkg := &GoPackage{        Name: "main",    }    if err := pkg.FindDeps(mysrc, pkgMap); err != nil {        fmt.Fprintln(os.Stderr, err)        os.Exit(1)    }    if err := pkg.Compile(intermediates, mysrc); err != nil {        fmt.Fprintln(os.Stderr, err)        os.Exit(1)    }    if err := pkg.Link(mybin); err != nil {        fmt.Fprintln(os.Stderr, err)        os.Exit(1)    }    if !pkg.rebuilt {        return    }    cmd := exec.Command(mybin, os.Args[1:]...)    cmd.Stdin = os.Stdin    cmd.Stdout = os.Stdout    cmd.Stderr = os.Stderr    if err := cmd.Run(); err == nil {        os.Exit(0)    } else if e, ok := err.(*exec.ExitError); ok {        os.Exit(e.ProcessState.Sys().(syscall.WaitStatus).ExitStatus())    }    os.Exit(1)}

注意,还函数会进入两次,该函数中一些重要的变量:

varable value intermediates /home/kelvin/os/android-8.0.0_r4/out/.microfactory_Linux_intermediates

寻找依赖:

// findDeps is the recursive version of FindDeps. allPackages is the map of// all locally defined packages so that the same dependency of two different// packages is only resolved once.func (p *GoPackage) findDeps(path string, pkgMap *pkgPathMapping, allPackages map[string]*GoPackage) error {    // If this ever becomes too slow, we can look at reading the files once instead of twice    // But that just complicates things today, and we're already really fast.    foundPkgs, err := parser.ParseDir(token.NewFileSet(), path, func(fi os.FileInfo) bool {        name := fi.Name()        if fi.IsDir() || strings.HasSuffix(name, "_test.go") || name[0] == '.' || name[0] == '_' {            return false        }        if runtime.GOOS != "darwin" && strings.HasSuffix(name, "_darwin.go") {            return false        }        if runtime.GOOS != "linux" && strings.HasSuffix(name, "_linux.go") {            return false        }        return true    }, parser.ImportsOnly)    if err != nil {        return fmt.Errorf("Error parsing directory %q: %v", path, err)    }    var foundPkg *ast.Package    // foundPkgs is a map[string]*ast.Package, but we only want one package    if len(foundPkgs) != 1 {        return fmt.Errorf("Expected one package in %q, got %d", path, len(foundPkgs))    }    // Extract the first (and only) entry from the map.    for _, pkg := range foundPkgs {        foundPkg = pkg    }    var deps []string    localDeps := make(map[string]bool)    for filename, astFile := range foundPkg.Files {        p.files = append(p.files, filename)        for _, importSpec := range astFile.Imports {            name, err := strconv.Unquote(importSpec.Path.Value)            if err != nil {                return fmt.Errorf("%s: invalid quoted string: <%s> %v", filename, importSpec.Path.Value, err)            }            if pkg, ok := allPackages[name]; ok && pkg != nil {                if pkg != nil {                    if _, ok := localDeps[name]; !ok {                        deps = append(deps, name)                        localDeps[name] = true                    }                }                continue            }            var pkgPath string            if path, ok, err := pkgMap.Path(name); err != nil {                return err            } else if !ok {                // Probably in the stdlib, compiler will fail we a reasonable error message otherwise.                // Mark it as such so that we don't try to decode its path again.                allPackages[name] = nil                continue            } else {                pkgPath = path            }            pkg := &GoPackage{                Name: name,            }            deps = append(deps, name)            allPackages[name] = pkg            localDeps[name] = true            if err := pkg.findDeps(pkgPath, pkgMap, allPackages); err != nil {                return err            }        }    }    sort.Strings(p.files)    if verbose {        fmt.Fprintf(os.Stderr, "Package %q depends on %v\n", p.Name, deps)    }    for _, dep := range deps {        p.deps = append(p.deps, allPackages[dep])    }    return nil}

那么还该函数怎么寻找依赖的呢?

func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, mode Mode) (pkgs map[string]*ast.Package, first error)  

ParseDir calls ParseFile for all files with names ending in “.go” in the directory specified by path and returns a map of package name -> package AST with all the packages found.

If filter != nil, only the files with os.FileInfo entries passing through the filter (and ending in “.go”) are considered. The mode bits are passed to ParseFile unchanged. Position information is recorded in fset, which must not be nil.

If the directory couldn’t be read, a nil map and the respective error are returned. If a parse error occurred, a non-nil but incomplete map and the first error encountered are returned.

这里传入的path为 mysrc,该值由soong/soong_ui.bash传入

varable value mf_cmd /home/kelvin/os/android-8.0.0_r4/prebuilts/go/linux-x86//bin/go run /home/kelvin/os/android-8.0.0_r4/build/soong/cmd/microfactory/microfactory.go mf_src /home/kelvin/os/android-8.0.0_r4/build/soong/cmd/microfactory mf_bin /home/kelvin/os/android-8.0.0_r4/out/microfactory_Linux soong_ui_bin /home/kelvin/os/android-8.0.0_r4/out/soong_ui

我们打印其中的filename的名字:
第一次调用:

intermediates /home/kelvin/os/android-8.0.0_r4/out/.microfactory_Linux_intermediates filename /home/kelvin/os/android-8.0.0_r4/build/soong/cmd/microfactory/microfactory.go

依赖就是import语句所import的包。对其中每一个import的包会调用pkgMap.Path(name);

// Path takes a package name, applies the path mappings and returns the resulting path.//// If the package isn't mapped, we'll return false to prevent compilation attempts.func (p *pkgPathMapping) Path(pkg string) (string, bool, error) {    if p.paths == nil {        return "", false, fmt.Errorf("No package mappings")    }    for _, pkgPrefix := range p.pkgs {        if pkg == pkgPrefix {            return p.paths[pkgPrefix], true, nil        } else if strings.HasPrefix(pkg, pkgPrefix+"/") {            return filepath.Join(p.paths[pkgPrefix], strings.TrimPrefix(pkg, pkgPrefix+"/")), true, nil        }    }    return "", false, nil}````对应的类型为:<div class="se-preview-section-delimiter"></div>```go// pkgPathMapping can be used with flag.Var to parse -pkg-path arguments of// <package-prefix>=<path-prefix> mappings.type pkgPathMapping struct {    pkgs []string    paths map[string]string}

其值由soong/soong_ui.bash传递,参数为”android/soong=${TOP}/build/soong”
在解析时,会调用其Set函数:

func (p *pkgPathMapping) Set(value string) error {    equalPos := strings.Index(value, "=")    if equalPos == -1 {        return fmt.Errorf("Argument must be in the form of: %q", p.String())    }    pkgPrefix := strings.TrimSuffix(value[:equalPos], "/")    pathPrefix := strings.TrimSuffix(value[equalPos+1:], "/")    if p.paths == nil {        p.paths = make(map[string]string)    }    if _, ok := p.paths[pkgPrefix]; ok {        return fmt.Errorf("Duplicate package prefix: %q", pkgPrefix)    }    p.pkgs = append(p.pkgs, pkgPrefix)    p.paths[pkgPrefix] = pathPrefix    return nil}
pkgPrefix android/soong pathPrefix /home/kelvin/os/android-8.0.0_r4/build/soong

soong/cmd/microfactory/microfactory.go import的包有:

import (    "bytes"    "crypto/sha1"    "flag"    "fmt"    "go/ast"    "go/parser"    "go/token"    "io"    "io/ioutil"    "os"    "os/exec"    "path/filepath"    "runtime"    "sort"    "strconv"    "strings"    "sync"    "syscall")

第一次执行该函数后:
Package “main” depends on []

然后执行第二阶段:rebuildMicrofactory的pkg.Compile(intermediates, mysrc)

func rebuildMicrofactory(mybin, mysrc string, pkgMap *pkgPathMapping) {    ......    fmt.Fprintln(os.Stderr, "@@@FindDeps")    if err := pkg.FindDeps(mysrc, pkgMap); err != nil {        fmt.Fprintln(os.Stderr, err)        os.Exit(1)    }    fmt.Fprintln(os.Stderr, "@@@@Compile" )    if err := pkg.Compile(intermediates, mysrc); err != nil {        fmt.Fprintln(os.Stderr, err)        os.Exit(1)    }    ......}

传入的参数:

intermediates /home/kelvin/os/android-8.0.0_r4/out/.microfactory_Linux_intermediates mysrc /home/kelvin/os/android-8.0.0_r4/build/soong/cmd/microfactory

Compile的实现如下,并行的编译所有的依赖文件,第一次进来的时候没有依赖文件。
然后调用命令/home/kelvin/os/android-8.0.0_r4/prebuilts/go/linux-x86/pkg/tool/linux_amd64/compile进行编译
函数说明:

func Command(name string, arg ...string) *Cmd

Command returns the Cmd struct to execute the named program with the given arguments.

It sets only the Path and Args in the returned structure.

If name contains no path separators, Command uses LookPath to resolve name to a complete path if possible. Otherwise it uses name directly as Path.

The returned Cmd’s Args field is constructed from the command name followed by the elements of arg, so arg should not include the command name itself. For example, Command(“echo”, “hello”). Args[0] is always name, not the possibly resolved Path.

func (c *Cmd) Run() error

Run starts the specified command and waits for it to complete.

The returned error is nil if the command runs, has no problems copying stdin, stdout, and stderr, and exits with a zero exit status.

If the command starts but does not complete successfully, the error is of type *ExitError. Other error types may be returned for other situations.

传入的参数:

p.output /home/kelvin/os/android-8.0.0_r4/out/.microfactory_Linux_intermediates/main/main.a p.name main
func (p *GoPackage) Compile(outDir, trimPath string) error {    p.mutex.Lock()    defer p.mutex.Unlock()    if p.compiled {        return p.failed    }    p.compiled = true    // Build all dependencies in parallel, then fail if any of them failed.    var wg sync.WaitGroup    for _, dep := range p.deps {        wg.Add(1)        go func(dep *GoPackage) {            defer wg.Done()            dep.Compile(outDir, trimPath)        }(dep)    }    wg.Wait()    for _, dep := range p.deps {        if dep.failed != nil {            p.failed = dep.failed            return p.failed        }    }    p.pkgDir = filepath.Join(outDir, p.Name)    p.output = filepath.Join(p.pkgDir, p.Name) + ".a"    shaFile := p.output + ".hash"    hash := sha1.New()    fmt.Fprintln(hash, runtime.GOOS, runtime.GOARCH, goVersion)    cmd := exec.Command(filepath.Join(goToolDir, "compile"),        "-o", p.output,        "-p", p.Name,        "-complete", "-pack", "-nolocalimports")    if race {        cmd.Args = append(cmd.Args, "-race")        fmt.Fprintln(hash, "-race")    }    if trimPath != "" {        cmd.Args = append(cmd.Args, "-trimpath", trimPath)        fmt.Fprintln(hash, trimPath)    }    for _, dep := range p.deps {        cmd.Args = append(cmd.Args, "-I", dep.pkgDir)        hash.Write(dep.hashResult)    }    for _, filename := range p.files {        cmd.Args = append(cmd.Args, filename)        fmt.Fprintln(hash, filename)        // Hash the contents of the input files        f, err := os.Open(filename)        if err != nil {            f.Close()            err = fmt.Errorf("%s: %v", filename, err)            p.failed = err            return err        }        _, err = io.Copy(hash, f)        if err != nil {            f.Close()            err = fmt.Errorf("%s: %v", filename, err)            p.failed = err            return err        }        f.Close()    }    p.hashResult = hash.Sum(nil)    var rebuild bool    if _, err := os.Stat(p.output); err != nil {        rebuild = true    }    if !rebuild {        if oldSha, err := ioutil.ReadFile(shaFile); err == nil {            rebuild = !bytes.Equal(oldSha, p.hashResult)        } else {            rebuild = true        }    }    if !rebuild {        return nil    }    err := os.RemoveAll(p.pkgDir)    if err != nil {        err = fmt.Errorf("%s: %v", p.Name, err)        p.failed = err        return err    }    err = os.MkdirAll(filepath.Dir(p.output), 0777)    if err != nil {        err = fmt.Errorf("%s: %v", p.Name, err)        p.failed = err        return err    }    cmd.Stdin = nil    cmd.Stdout = os.Stdout    cmd.Stderr = os.Stderr    if verbose {        fmt.Fprintln(os.Stderr, cmd.Args)    }    err = cmd.Run()    if err != nil {        err = fmt.Errorf("%s: %v", p.Name, err)        p.failed = err        return err    }    err = ioutil.WriteFile(shaFile, p.hashResult, 0666)    if err != nil {        err = fmt.Errorf("%s: %v", p.Name, err)        p.failed = err        return err    }    p.rebuilt = true    return nil}

第三步是rebuildMicrofactory.pkg.Link(mybin)

func rebuildmicrofactory(mybin, mysrc string, pkgmap *pkgpathmapping) {    ......    fmt.fprintln(os.stderr, "@@@@link" )    if err := pkg.link(mybin); err != nil {        fmt.fprintln(os.stderr, err)        os.exit(1)    }    ......}````Link的实现如下:<div class="se-preview-section-delimiter"></div>```gofunc (p *GoPackage) Link(out string) error {    if p.Name != "main" {        return fmt.Errorf("Can only link main package")    }    shaFile := filepath.Join(filepath.Dir(out), "."+filepath.Base(out)+"_hash")    if !p.rebuilt {        if _, err := os.Stat(out); err != nil {            p.rebuilt = true        } else if oldSha, err := ioutil.ReadFile(shaFile); err != nil {            p.rebuilt = true        } else {            p.rebuilt = !bytes.Equal(oldSha, p.hashResult)        }    }    if !p.rebuilt {        return nil    }    err := os.Remove(shaFile)    if err != nil && !os.IsNotExist(err) {        return err    }    err = os.Remove(out)    if err != nil && !os.IsNotExist(err) {        return err    }    cmd := exec.Command(filepath.Join(goToolDir, "link"), "-o", out)    if race {        cmd.Args = append(cmd.Args, "-race")    }    for _, dep := range p.deps {        cmd.Args = append(cmd.Args, "-L", dep.pkgDir)    }    cmd.Args = append(cmd.Args, p.output)    cmd.Stdin = nil    cmd.Stdout = os.Stdout    cmd.Stderr = os.Stderr    if verbose {        fmt.Fprintln(os.Stderr, cmd.Args)    }    err = cmd.Run()    if err != nil {        return err    }    return ioutil.WriteFile(shaFile, p.hashResult, 0666)}

与Compile类似。

最后一步:

func rebuildmicrofactory(mybin, mysrc string, pkgmap *pkgpathmapping) {    .......    cmd := exec.command(mybin, os.args[1:]...)    cmd.stdin = os.stdin    cmd.stdout = os.stdout    cmd.stderr = os.stderr    if err := cmd.run(); err == nil {        os.exit(0)    } else if e, ok := err.(*exec.exiterror); ok {        os.exit(e.processstate.sys().(syscall.waitstatus).exitstatus())    }    os.exit(1)}

此处执行的为mybin:
/home/kelvin/os/android-8.0.0_r4/out/microfactory_Linux
该文件由上一步的Link()生成。
所以,microfacrory.go又会执行一遍。那么第二次执行和第一次执行有什么不同呢?

第二次执行完后,会继续mainPackage.FindDeps

func main() {    ......    mainPackage := &GoPackage{        Name: "main",    }    if path, ok, err := pkgMap.Path(flags.Arg(0)); err != nil {        fmt.Fprintln(os.Stderr, "Error finding main path:", err)        os.Exit(1)    } else if !ok {        fmt.Fprintln(os.Stderr, "Cannot find path for", flags.Arg(0))    } else {        if err := mainPackage.FindDeps(path, &pkgMap); err != nil {            fmt.Fprintln(os.Stderr, err)            os.Exit(1)        }    }

这次传入的参数为是:
flags.Arg(0): android/soong/cmd/soong_ui,该参数同样由soong/soong_ui.bash传递。
path: /home/kelvin/os/android-8.0.0_r4/build/soong/cmd/soong_ui

那些会由依赖,import的包以android/soong开始,这些目录下的包都会被依赖。
下面soong/cmd/soong_ui/main.go

import (  "context"  "os"  "path/filepath"  "strconv"  "strings"  "android/soong/ui/build"  "android/soong/ui/logger"  "android/soong/ui/tracer")````结果如下:  Package | depends on--------|--------------------------------"android/soong/ui/logger" | []  "android/soong/ui/tracer" | [android/soong/ui/logger]  "android/soong/ui/build" | [android/soong/ui/logger android/soong/ui/tracer]"main" | [android/soong/ui/build android/soong/ui/logger android/soong/ui/tracer]  第二次调用:  intermediates |  /home/kelvin/os/android-8.0.0_r4/out/.microfactory_Linux_intermediates  --------------|------------------------------------------------filename      | /home/kelvin/os/android-8.0.0_r4/build/soong/cmd/microfactory/microfactory.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/cmd/soong_ui/main.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/build/util_linux.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/build/kati.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/build/context.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/logger/logger.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/tracer/tracer.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/tracer/ninja.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/build/ninja.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/build/signal.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/build/environment.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/build/soong.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/build/config.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/build/make.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/build/build.go  filename | /home/kelvin/os/android-8.0.0_r4/build/soong/ui/build/util.go  下一步:  <div class="se-preview-section-delimiter"></div>```gofunc main() {    ... ...    err = mainPackage.Compile(intermediates, trimPath)    if err != nil {        fmt.Fprintln(os.Stderr, "Failed to compile:", err)        os.Exit(1)    }    err = mainPackage.Link(output)    if err != nil {        fmt.Fprintln(os.Stderr, "Failed to link:", err)        os.Exit(1)    }}

传入的参数:

intermediates /home/kelvin/os/android-8.0.0_r4/out/.soong_ui_intermediates trimPath /home/kelvin/os/android-8.0.0_r4/build/soong

最后一步:

func main() {    ......    err = mainPackage.Link(output)    if err != nil {        fmt.Fprintln(os.Stderr, "Failed to link:", err)        os.Exit(1)    }}

这里需要关注的传入的参数:为 out/soong_ui, 这个即为生成的可执行文件。
该路径由soong/soong_ui.bash传入。

Step 3. soong_ui的执行

soong/soog_ui.bash

# Bootstrap microfactory from source if necessary and use it to build the# soong_ui binary, then run soong_ui.function run_go{    .....    ${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    exec "${out_dir}/soong_ui" "$@"}

soong/cmd/soong_ui/main.go

func main() {    log := logger.New(os.Stderr)    defer log.Cleanup()    if len(os.Args) < 2 || !inList("--make-mode", os.Args) {        log.Fatalln("The `soong` native UI is not yet available.") } ctx, cancel := context.WithCancel(context.Background()) defer cancel()    trace := tracer.New(log)    defer trace.Close()    build.SetupSignals(log, cancel, func() {        trace.Close()        log.Cleanup()    })    buildCtx := build.Context{&build.ContextImpl{        Context:        ctx,        Logger:         log,        Tracer:         trace,        StdioInterface: build.StdioImpl{},    }}    config := build.NewConfig(buildCtx, os.Args[1:]...)    log.SetVerbose(config.IsVerbose())    build.SetupOutDir(buildCtx, config)    if config.Dist() {        logsDir := filepath.Join(config.DistDir(), "logs")        os.MkdirAll(logsDir, 0777)        log.SetOutput(filepath.Join(logsDir, "soong.log"))        trace.SetOutput(filepath.Join(logsDir, "build.trace"))    } else {        log.SetOutput(filepath.Join(config.OutDir(), "soong.log"))        trace.SetOutput(filepath.Join(config.OutDir(), "build.trace"))    }    if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {        if !strings.HasSuffix(start, "N") {            if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {                log.Verbosef("Took %dms to start up.",                    time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())                buildCtx.CompleteTrace("startup", start_time, uint64(time.Now().UnixNano()))            }        }    }    build.Build(buildCtx, config, build.BuildAll)}

首先会注册信号处理函数:

    build.SetupSignals(log, cancel, func() {        trace.Close()        log.Cleanup()    })

最后比较关键:

    build.Build(buildCtx, config, build.BuildAll)

build/soong/ui/build/build.go

// Build the tree. The 'what' argument can be used to chose which components of// the build to run.func Build(ctx Context, config Config, what int) {    ctx.Verboseln("Starting build with args:", config.Arguments())    ctx.Verboseln("Environment:", config.Environment().Environ())    if inList("help", config.Arguments()) {        cmd := exec.CommandContext(ctx.Context, "make", "-f", "build/core/help.mk")        cmd.Env = config.Environment().Environ()        cmd.Stdout = ctx.Stdout()        cmd.Stderr = ctx.Stderr()        if err := cmd.Run(); err != nil {            ctx.Fatalln("Failed to run make:", err)        }        return    }    SetupOutDir(ctx, config)    if what&BuildProductConfig != 0 {        // Run make for product config        runMakeProductConfig(ctx, config)    }    if what&BuildSoong != 0 {        // Run Soong        runSoongBootstrap(ctx, config)        runSoong(ctx, config)    }    if what&BuildKati != 0 {        // Run ckati        runKati(ctx, config)    }    if what&BuildNinja != 0 {        // Write combined ninja file        createCombinedBuildNinjaFile(ctx, config)        // Run ninja        runNinja(ctx, config)    }}

runMakeProductConfig分析:[build/soong/ui/build/make.go]

func runMakeProductConfig(ctx Context, config Config) {    // Variables to export into the environment of Kati/Ninja    exportEnvVars := []string{        // So that we can use the correct TARGET_PRODUCT if it's been        // modified by PRODUCT-* arguments        "TARGET_PRODUCT",        // compiler wrappers set up by make        "CC_WRAPPER",        "CXX_WRAPPER",        // ccache settings        "CCACHE_COMPILERCHECK",        "CCACHE_SLOPPINESS",        "CCACHE_BASEDIR",        "CCACHE_CPP2",    }    // Variables to print out in the top banner    bannerVars := []string{        "PLATFORM_VERSION_CODENAME",        "PLATFORM_VERSION",        "TARGET_PRODUCT",        "TARGET_BUILD_VARIANT",        "TARGET_BUILD_TYPE",        "TARGET_BUILD_APPS",        "TARGET_ARCH",        "TARGET_ARCH_VARIANT",        "TARGET_CPU_VARIANT",        "TARGET_2ND_ARCH",        "TARGET_2ND_ARCH_VARIANT",        "TARGET_2ND_CPU_VARIANT",        "HOST_ARCH",        "HOST_2ND_ARCH",        "HOST_OS",        "HOST_OS_EXTRA",        "HOST_CROSS_OS",        "HOST_CROSS_ARCH",        "HOST_CROSS_2ND_ARCH",        "HOST_BUILD_TYPE",        "BUILD_ID",        "OUT_DIR",        "AUX_OS_VARIANT_LIST",        "TARGET_BUILD_PDK",        "PDK_FUSION_PLATFORM_ZIP",    }    allVars := append(append([]string{        // Used to execute Kati and Ninja        "NINJA_GOALS",        "KATI_GOALS",    }, exportEnvVars...), bannerVars...)    make_vars, err := DumpMakeVars(ctx, config, config.Arguments(), []string{        filepath.Join(config.SoongOutDir(), "soong.variables"),    }, allVars)    if err != nil {        ctx.Fatalln("Error dumping make vars:", err)    }    // Print the banner like make does    fmt.Fprintln(ctx.Stdout(), "============================================")    for _, name := range bannerVars {        if make_vars[name] != "" {            fmt.Fprintf(ctx.Stdout(), "%s=%s\n", name, make_vars[name])        }    }    fmt.Fprintln(ctx.Stdout(), "============================================")    // Populate the environment    env := config.Environment()    for _, name := range exportEnvVars {        if make_vars[name] == "" {            env.Unset(name)        } else {            env.Set(name, make_vars[name])        }    }    config.SetKatiArgs(strings.Fields(make_vars["KATI_GOALS"]))    config.SetNinjaArgs(strings.Fields(make_vars["NINJA_GOALS"]))}

DumpMakeVars分析:

// DumpMakeVars can be used to extract the values of Make variables after the// product configurations are loaded. This is roughly equivalent to the// `get_build_var` bash function.//// goals can be used to set MAKECMDGOALS, which emulates passing arguments to// Make without actually building them. So all the variables based on// MAKECMDGOALS can be read.//// extra_targets adds real arguments to the make command, in case other targets// actually need to be run (like the Soong config generator).//// vars is the list of variables to read. The values will be put in the// returned map.func DumpMakeVars(ctx Context, config Config, goals, extra_targets, vars []string) (map[string]string, error) {    ctx.BeginTrace("dumpvars")    defer ctx.EndTrace()    cmd := exec.CommandContext(ctx.Context,        "make",        "--no-print-directory",        "-f", "build/core/config.mk",        "dump-many-vars",        "CALLED_FROM_SETUP=true",        "BUILD_SYSTEM=build/core",        "MAKECMDGOALS="+strings.Join(goals, " "),        "DUMP_MANY_VARS="+strings.Join(vars, " "),        "OUT_DIR="+config.OutDir())    cmd.Env = config.Environment().Environ()    cmd.Args = append(cmd.Args, extra_targets...)    // TODO: error out when Stderr contains any content    cmd.Stderr = ctx.Stderr()    ctx.Verboseln(cmd.Path, cmd.Args)    output, err := cmd.Output()    if err != nil {        return nil, err    }    ret := make(map[string]string, len(vars))    for _, line := range strings.Split(string(output), "\n") {        if len(line) == 0 {            continue        }        if key, value, ok := decodeKeyValue(line); ok {            if value, ok = singleUnquote(value); ok {                ret[key] = value                ctx.Verboseln(key, value)            } else {                return nil, fmt.Errorf("Failed to parse make line: %q", line)            }        } else {            return nil, fmt.Errorf("Failed to parse make line: %q", line)        }    }    return ret, nil}

Go相关:

func (c *Cmd) Output() ([]byte, error)

Output runs the command and returns its standard output. Any returned error will usually be of type *ExitError.
If c.Stderr was nil, Output populates ExitError.Stderr.

分析cofig.mk
include (BUILDSYSTEM)/envsetup.mkinclude(BUILD_SYSTEM)/clang/versions.mk
include (BUILDSYSTEM)/combo/javac.mkJACK:=(HOST_OUT_EXECUTABLES)/jack
ifndef KATI
include (BUILDSYSTEM)/ninjaconfig.mkinclude(BUILD_SYSTEM)/soong_config.mk
endif

include $(BUILD_SYSTEM)/dumpvar.mk

$(BUILD_SYSTEM) 为build/core/ 但是用的是/build/make/core/
core -> /home/kelvin/os/android-8.0.0_r4/build/make/core

下一步,执行build/soong/ui/build/build.go

func Build(ctx Context, config Config, what int) {    ......    if what&BuildProductConfig != 0 {        // Run make for product config        runMakeProductConfig(ctx, config)    }    if what&BuildSoong != 0 {        // Run Soong        runSoongBootstrap(ctx, config)        runSoong(ctx, config)    }    ......}

runSoongBootstrap soong/ui/build/soong.go

func runSoongBootstrap(ctx Context, config Config) {    ctx.BeginTrace("bootstrap soong")    defer ctx.EndTrace()    cmd := exec.CommandContext(ctx.Context, "./bootstrap.bash")    env := config.Environment().Copy()    env.Set("BUILDDIR", config.SoongOutDir())    cmd.Env = env.Environ()    cmd.Stdout = ctx.Stdout()    cmd.Stderr = ctx.Stderr()    ctx.Verboseln(cmd.Path, cmd.Args)    if err := cmd.Run(); err != nil {        if e, ok := err.(*exec.ExitError); ok {            ctx.Fatalln("soong bootstrap failed with:", e.ProcessState.String())        } else {            ctx.Fatalln("Failed to run soong bootstrap:", err)        }    }}

这里的bootstrap.bash是哪一个? build/soong/bootstrap.bash

if [[ $# -eq 0 ]]; then    mkdir -p $BUILDDIR    if [[ $(find $BUILDDIR -maxdepth 1 -name Android.bp) ]]; then      echo "FAILED: The build directory must not be a source directory"      exit 1    fi    export SRCDIR_FROM_BUILDDIR=$(build/soong/scripts/reverse_path.py "$BUILDDIR")    sed -e "s|@@BuildDir@@|${BUILDDIR}|" \        -e "s|@@SrcDirFromBuildDir@@|${SRCDIR_FROM_BUILDDIR}|" \        -e "s|@@PrebuiltOS@@|${PREBUILTOS}|" \        "$SRCDIR/build/soong/soong.bootstrap.in" > $BUILDDIR/.soong.bootstrap    ln -sf "${SRCDIR_FROM_BUILDDIR}/build/soong/soong.bash" $BUILDDIR/soongfi"$SRCDIR/build/blueprint/bootstrap.bash" "$@"

这个片段会执行两次。 区别$#:Number of arguments passed to script
生成的文件:
BUILDDIR=”out/soong”
SRCDIR_FROM_BUILDDIR=”../..”
PREBUILTOS=”linux-x86”
创建软链接:out/soong/soong -> ../../build/soong/soong.bash

最后执行

"$SRCDIR/build/blueprint/bootstrap.bash" "$@"

注意两个脚本会执行多次,每次执行时,其中的某些参数会有些变化。
在这个脚本文件里,生成out/soong/.minibootstrap/目录,并生成相应的文件。

$IN > $BUILDDIR/.minibootstrap/build.ninja

第一次执行时,$IN为build/soong/build.ninja.in

echo "BOOTSTRAP=\"${BOOTSTRAP}\"" > $BUILDDIR/.blueprint.bootstrapecho "BOOTSTRAP_MANIFEST=\"${BOOTSTRAP_MANIFEST}\"" >> $BUILDDIR/.blueprint.bootstrap

生成 out/soong/.blueprint.bootstrap

sed -e "s|@@SrcDir@@|$SRCDIR|g"                        \    -e "s|@@BuildDir@@|$BUILDDIR|g"                    \    -e "s|@@GoRoot@@|$GOROOT|g"                        \    -e "s|@@GoCompile@@|$GOCOMPILE|g"                  \    -e "s|@@GoLink@@|$GOLINK|g"                        \    -e "s|@@Bootstrap@@|$BOOTSTRAP|g"                  \    -e "s|@@BootstrapManifest@@|$BOOTSTRAP_MANIFEST|g" \    $IN > $BUILDDIR/.minibootstrap/build.ninja

这段脚本的,将$BUILDDIR/.minibootstrap/build.ninja里的某些内容进行替换:

ninja_required_version = 1.7.0

g.bootstrap.buildDir = out/soong

g.bootstrap.BinDir = ${g.bootstrap.buildDir}/.bootstrap/bin

g.bootstrap.bootstrapCmd = ./bootstrap.bash

g.bootstrap.compileCmd = ./prebuilts/go/linux-x86//pkg/tool/linux_amd64/compile

g.bootstrap.goRoot = ./prebuilts/go/linux-x86/

g.bootstrap.goTestMainCmd = ${g.bootstrap.buildDir}/.bootstrap/bin/gotestmain

g.bootstrap.goTestRunnerCmd = ${g.bootstrap.buildDir}/.bootstrap/bin/gotestrunner

g.bootstrap.linkCmd = ./prebuilts/go/linux-x86//pkg/tool/linux_amd64/link

g.bootstrap.srcDir = .

builddir = ${g.bootstrap.buildDir}/.minibootstrap

回过头来,执行下面一句 runSoong(ctx, config)

func Build(ctx Context, config Config, what int) {    ......if what&BuildSoong != 0 {    // Run Soong    runSoongBootstrap(ctx, config)    runSoong(ctx, config)}if what&BuildKati != 0 {    // Run ckati    runKati(ctx, config)}if what&BuildNinja != 0 {    // Write combined ninja file    createCombinedBuildNinjaFile(ctx, config)    // Run ninja    runNinja(ctx, config)}}

其执行过程如下:build/soong/ui/build/soong.go

func runSoong(ctx Context, config Config) {    ctx.BeginTrace("soong")    defer ctx.EndTrace()    cmd := exec.CommandContext(ctx.Context, filepath.Join(config.SoongOutDir(), "soong"), "-w", "dupbuild=err")    if config.IsVerbose() {        cmd.Args = append(cmd.Args, "-v")    }    env := config.Environment().Copy()    env.Set("SKIP_NINJA", "true")    cmd.Env = env.Environ()    cmd.Stdin = ctx.Stdin()    cmd.Stdout = ctx.Stdout()    cmd.Stderr = ctx.Stderr()    ctx.Verboseln(cmd.Path, cmd.Args)    if err := cmd.Run(); err != nil {        if e, ok := err.(*exec.ExitError); ok {            ctx.Fatalln("soong bootstrap failed with:", e.ProcessState.String())        } else {            ctx.Fatalln("Failed to run soong bootstrap:", err)        }    }}

其中,filepath.Join(config.SoongOutDir(), “soong”)为out/soong/soong
而out/soong/soong为软链接,../../build/soong/soong.bash
所以,会执行该脚本。
soog/soog.bash:

BUILDDIR="${BUILDDIR}" NINJA="prebuilts/build-tools/${PREBUILTOS}/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 0fi

这里开始编译前面生成的build.ninja文件.
这里重点回顾下这两个build.ninja文件是如何生成的。
BUILDDIR/.minibootstrap/build.ninjabuild/soong/build.ninja.in{BUILDDIR}/.bootstrap/build.ninja则由${BUILDDIR}/.minibootstrap/build.ninja生成。

out/soong/.bootstrap/bin/soong_build out/soong/build.ninja则由上一步生成。

再回到build.go继续分析:,执行runKati

func Build(ctx Context, config Config, what int) {    ... ...    if what&BuildSoong != 0 {        // Run Soong        runSoongBootstrap(ctx, config)        runSoong(ctx, config)    }    if what&BuildKati != 0 {        // Run ckati        runKati(ctx, config)    }    if what&BuildNinja != 0 {        // Write combined ninja file        createCombinedBuildNinjaFile(ctx, config)        // Run ninja        runNinja(ctx, config)    }}

build/soong/ui/kati.go

func runKati(ctx Context, config Config) {    ctx.BeginTrace("kati")    defer ctx.EndTrace()    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",    }    if !config.Environment().IsFalse("KATI_EMULATE_FIND") {        args = append(args, "--use_find_emulator")    }    args = append(args, config.KatiArgs()...)    args = append(args,        "BUILDING_WITH_NINJA=true",        "SOONG_ANDROID_MK="+config.SoongAndroidMk(),        "SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk())    if config.UseGoma() {        args = append(args, "-j"+strconv.Itoa(config.Parallel()))    }    cmd := exec.CommandContext(ctx.Context, executable, args...)    cmd.Env = config.Environment().Environ()    pipe, err := cmd.StdoutPipe()    if err != nil {        ctx.Fatalln("Error getting output pipe for ckati:", err)    }    cmd.Stderr = cmd.Stdout    ctx.Verboseln(cmd.Path, cmd.Args)    if err := cmd.Start(); err != nil {        ctx.Fatalln("Failed to run ckati:", err)    }    katiRewriteOutput(ctx, pipe)    if err := cmd.Wait(); err != nil {        if e, ok := err.(*exec.ExitError); ok {            ctx.Fatalln("ckati failed with:", e.ProcessState.String())        } else {            ctx.Fatalln("Failed to run ckati:", err)        }    }}

这里config.HostPrebuiltTag()为linux-x86

这一次指定的makefile为build/core/main.mk, 再次进入该函数,但是会有一些区别:
第一次进入的时候,没有定义KATI, 这一次进入的时候则定义定KATI. 所以会进入到不同的分支。
这其中有一个很关键的函数:

$(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk)))

回到kati.go,继续分析:
在该函数内有一个关键点函数katiRewriteOutput()

var katiIncludeRe = regexp.MustCompile(`^(\[\d+/\d+] )?including [^ ]+ ...$`)verbose := katiIncludeRe.MatchString(line)

在terminal看到的include xx.mk进度的语句:

        if smartTerminal && verbose {            ... ...            // Move to the beginning on the line, print the output, then clear            // the rest of the line.            fmt.Fprint(ctx.Stdout(), "\r", line, "\x1b[K")            haveBlankLine = false            continue        } 

这里涉及到的go相关的信息,

func (c *Cmd) StdoutPipe() (io.ReadCloser, error)func (c *Cmd) Start() errorfunc NewScanner(r io.Reader) *Scannerfunc (s *Scanner) Scan() h

回到build.go继续下面的分析:

func Build(ctx Context, config Config, what int) {    ... ...    if what&BuildKati != 0 {        // Run ckati        runKati(ctx, config)    }    if what&BuildNinja != 0 {        // Write combined ninja file        createCombinedBuildNinjaFile(ctx, config)        // Run ninja        runNinja(ctx, config)    }}

现在分析createCombinedBuildNinjaFile build/soong/ui/build/build.go :

func createCombinedBuildNinjaFile(ctx Context, config Config) {    file, err := os.Create(config.CombinedNinjaFile())    if err != nil {        ctx.Fatalln("Failed to create combined ninja file:", err)    }    defer file.Close()    if err := combinedBuildNinjaTemplate.Execute(file, config); err != nil {        ctx.Fatalln("Failed to write combined ninja file:", err)    }}

config.CombinedNinjaFile()为 out/combined-aosp_arm.ninja
该文件的值为:
builddir = out
include out/build-aosp_arm.ninja
include out/soong/build.ninja
build out/combined-aosp_arm.ninja: phony out/soong/build.ninja

回到build.go继续下面的分析:

func Build(ctx Context, config Config, what int) {    .....        // Run ninja        runNinja(ctx, config)    }}

runNinja的实现 build/soong/ui/build/ninja.go

func runNinja(ctx Context, config Config) {    ctx.BeginTrace("ninja")    defer ctx.EndTrace()    executable := "prebuilts/build-tools/" + config.HostPrebuiltTag() + "/bin/ninja"    args := []string{        "-d", "keepdepfile",    }    args = append(args, config.NinjaArgs()...)    var parallel int    if config.UseGoma() {        parallel = config.RemoteParallel()    } else {        parallel = config.Parallel()    }    args = append(args, "-j", strconv.Itoa(parallel))    if config.keepGoing != 1 {        args = append(args, "-k", strconv.Itoa(config.keepGoing))    }    args = append(args, "-f", config.CombinedNinjaFile())    if config.IsVerbose() {        args = append(args, "-v")    }    args = append(args, "-w", "dupbuild=err")    env := config.Environment().Copy()    env.AppendFromKati(config.KatiEnvFile())    // Allow both NINJA_ARGS and NINJA_EXTRA_ARGS, since both have been    // used in the past to specify extra ninja arguments.    if extra, ok := env.Get("NINJA_ARGS"); ok {        args = append(args, strings.Fields(extra)...)    }    if extra, ok := env.Get("NINJA_EXTRA_ARGS"); ok {        args = append(args, strings.Fields(extra)...)    }    if _, ok := env.Get("NINJA_STATUS"); !ok {        env.Set("NINJA_STATUS", "[%p %f/%t] ")    }    cmd := exec.CommandContext(ctx.Context, executable, args...)    cmd.Env = env.Environ()    cmd.Stdin = ctx.Stdin()    cmd.Stdout = ctx.Stdout()    cmd.Stderr = ctx.Stderr()    ctx.Verboseln(cmd.Path, cmd.Args)    startTime := time.Now()    defer ctx.ImportNinjaLog(filepath.Join(config.OutDir(), ".ninja_log"), startTime)    if err := cmd.Run(); err != nil {        if e, ok := err.(*exec.ExitError); ok {            ctx.Fatalln("ninja failed with:", e.ProcessState.String())        } else {            ctx.Fatalln("Failed to run ninja:", err)        }    }}

这里的编译工具为

executable := "prebuilts/build-tools/" + config.HostPrebuiltTag() + "/bin/ninja"  

其传入的参数为:
-d keepdepfile -j 8 -f out/combined-aosp_arm.ninja -w dupbuild=err
所以,ninja执行的文件的为out/combined-aosp_arm.ninja,该文件包含了其他之前的编译生成的ninja文件

builddir = out
include out/build-aosp_arm.ninja
include out/soong/build.ninja
build out/combined-aosp_arm.ninja: phony out/soong/build.ninja

那么具体的编译过程是怎样的呢?
out/build-aosp_arm.ninja这个文件是怎么生成的呢?
逆向分析:
最后/bin/ninja执行的-f 参数来自config.CombinedNinjaFile build/soong/ui/build/config.go

func (c *configImpl) CombinedNinjaFile() string {    return filepath.Join(c.OutDir(), "combined"+c.KatiSuffix()+".ninja")}

KatiSuffix的设置/build/soong/ui/build/kati.go.

var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`builddir = {{.OutDir}}include {{.KatiNinjaFile}}include {{.SoongNinjaFile}}build {{.CombinedNinjaFile}}: phony {{.SoongNinjaFile}}`))

该文件的生成过程在kati.go
cmd.Path == prebuilts/build-tools/linux-x86/bin/ckati
cmd.Args== [prebuilts/build-tools/linux-x86/bin/ckati –ninja –ninja_dir=out
–ninja_suffix=-aosp_arm –regen –ignore_optional_include=out/%.P –detect_android_echo
–color_warnings –gen_all_targets -f build/core/main.mk –use_find_emulator BUILDING_WITH_NINJA=true
SOONG_ANDROID_MK=out/soong/Android-aosp_arm.mk SOONG_MAKEVARS_MK=out/soong/make_vars-aosp_arm.mk]

所以,也就是ckati 将makefile生成了ninjia文件。

关于该文件的生成,可以参考源代码的说明文档。kati开始由go实现,由于performace的原因,改为c++实现。

out/soong/build.ninja的生成过程

注意out/soong/soong是一个软链接。所以有会soong.bash. soong.bash会执行blueprint/blueprint.bash

# .blueprint.bootstrap provides saved values from the bootstrap.bash script:##   BOOTSTRAP#   BOOTSTRAP_MANIFEST#source "${BUILDDIR}/.blueprint.bootstrap"GEN_BOOTSTRAP_MANIFEST="${BUILDDIR}/.minibootstrap/build.ninja.in"if [ -f "${GEN_BOOTSTRAP_MANIFEST}" ]; then    if [ "${BOOTSTRAP_MANIFEST}" -nt "${GEN_BOOTSTRAP_MANIFEST}" ]; then        "${BOOTSTRAP}" -i "${BOOTSTRAP_MANIFEST}"    fielse    "${BOOTSTRAP}" -i "${BOOTSTRAP_MANIFEST}"fi

需要注意的是,第一次编译和dirty build不同。
这里的值
BOOTSTRAP=”./bootstrap.bash”
BOOTSTRAP_MANIFEST=”./build/soong/build.ninja.in”
所以,会执行代码根目录下的bootstrap.bash
所以,又会执行build/blueprint/bootstrap.bash

编译android.bp文件的命令为
out/soong/.bootstrap/bin/minibp -t -b out/soong -d out/soong/.minibootstrap/build.ninja.in.d -o out/soong/.minibootstrap/build.ninja.in Android.bp
更多参考信息:build/blueprint/bootstrap/doc.go

kati生成ninja文件的build/kati/ninja.cc

void GenerateNinja()  
原创粉丝点击