Android编译系统——Kati执行过程(十一)

        在 Android 构建系统中,Soong UI 是 Soong 构建框架的入口点。通过前面的学习我们知道,在执行 make 兼容模式编译时,soong_ui 会调用 ckati 来将传统的 Android.mk 和其他 Makefile 文件转换为 Ninja 构建文件(如 build-aosp_arm.ninja 和 build-aosp_arm-package.ninja)。这些 .ninja 文件随后会被 ninja 工具调用以进行真正的构建流程。

        在这一过程中,kati.go 是 Kati 在 Go 层的封装实现,它负责协调并驱动整个 ckati 的执行过程。下面我们将对 kati.go 的内容进行详细解析,并完善其术语描述,以便更清晰地理解 Kati 执行的逻辑和作用。

一、Kati执行

        kati.go 是 Android 构建系统中用于启动和管理 ckati 的 Go 实现模块,位于 Soong 框架中。该模块定义了与 Kati 相关的主要函数和逻辑,包括如何启动 ckati 进程、传递参数、处理输入输出等。

        它本质上是对底层 C++ 编写的 ckati 可执行程序的一层封装,使得 Soong 能够统一调度 ckati 并集成到现代的构建流程中。

1、kati.go

源码位置:/build/soong/ui/build/kati.go

runKatiBuild

func runKatiBuild(ctx Context, config Config) {
	ctx.BeginTrace(metrics.RunKati, "kati build")
	defer ctx.EndTrace()

	args := []string{
		// Mark the output directory as writable.
		"--writable", config.OutDir() + "/",
		// Fail when encountering implicit rules. e.g.
		// %.foo: %.bar
		//   cp $< $@
		"--werror_implicit_rules",
		// Entry point for the Kati Ninja file generation.
		"-f", "build/make/core/main.mk",
	}

	if !config.BuildBrokenDupRules() {
		// Fail when redefining / duplicating a target.
		args = append(args, "--werror_overriding_commands")
	}

	args = append(args, config.KatiArgs()...)

	args = append(args,
		// Location of the Make vars .mk file generated by Soong.
		"SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk(),
		// Location of the Android.mk file generated by Soong. This
		// file contains Soong modules represented as Kati modules,
		// allowing Kati modules to depend on Soong modules.
		"SOONG_ANDROID_MK="+config.SoongAndroidMk(),
		// Directory containing outputs for the target device.
		"TARGET_DEVICE_DIR="+config.TargetDeviceDir(),
		// Directory containing .mk files for packaging purposes, such as
		// the dist.mk file, containing dist-for-goals data.
		"KATI_PACKAGE_MK_DIR="+config.KatiPackageMkDir())

	runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {})

	// compress and dist the main build ninja file.
	distGzipFile(ctx, config, config.KatiBuildNinjaFile())

	// Cleanup steps.
	cleanCopyHeaders(ctx, config)
	cleanOldInstalledFiles(ctx, config)
}

        该函数是 Android 构建系统中 Soong UI 模块调用 Kati 生成主构建 Ninja 文件的核心函数之一。它的主要职责是:

  • 调用底层的 ckati 工具;
  • 将传统的 Makefile(如 build/make/core/main.mk)解析并转换为 Ninja 构建规则文件;
  • 设置必要的参数和环境变量,以确保构建逻辑正确无误;
  • 最后进行一些清理和分发操作。

        这里的参数 args,通过 fmt 打印后,内容为:

[
  --writable out/,
  -f build/make/core/main.mk,
  --werror_implicit_rules,
  --werror_overriding_commands,
  --werror_real_to_phony,
  --werror_phony_looks_real,
  --werror_writable,

  SOONG_MAKEVARS_MK=out/soong/make_vars-aosp_arm.mk,
  SOONG_ANDROID_MK=out/soong/Android-aosp_arm.mk,
  TARGET_DEVICE_DIR=build/target/board/generic,
  KATI_PACKAGE_MK_DIR=out/target/product/generic/obj/CONFIG/kati_packaging
]

        同时,这里指定了 makefile 的入口为 build/make/core/main.mk,编译 target 的目录为 build/target/board/generic。

runKati

func runKati(ctx Context, config Config, extraSuffix string, args []string, envFunc func(*Environment)) {
	executable := config.PrebuiltBuildTool("ckati")
	// cKati arguments.
	args = append([]string{
		// Instead of executing commands directly, generate a Ninja file.
		"--ninja",
		// Generate Ninja files in the output directory.
		"--ninja_dir=" + config.OutDir(),
		// Filename suffix of the generated Ninja file.
		"--ninja_suffix=" + config.KatiSuffix() + extraSuffix,
		// Remove common parts at the beginning of a Ninja file, like build_dir,
		// local_pool and _kati_always_build_. Allows Kati to be run multiple
		// times, with generated Ninja files combined in a single invocation
		// using 'include'.
		"--no_ninja_prelude",
		// Support declaring phony outputs in AOSP Ninja.
		"--use_ninja_phony_output",
		// Support declaring symlink outputs in AOSP Ninja.
		"--use_ninja_symlink_outputs",
		// Regenerate the Ninja file if environment inputs have changed. e.g.
		// CLI flags, .mk file timestamps, env vars, $(wildcard ..) and some
		// $(shell ..) results.
		"--regen",
		// Skip '-include' directives starting with the specified path. Used to
		// ignore generated .mk files.
		"--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
		// Detect the use of $(shell echo ...).
		"--detect_android_echo",
		// Colorful ANSI-based warning and error messages.
		"--color_warnings",
		// Generate all targets, not just the top level requested ones.
		"--gen_all_targets",
		// Use the built-in emulator of GNU find for better file finding
		// performance. Used with $(shell find ...).
		"--use_find_emulator",
		// Fail when the find emulator encounters problems.
		"--werror_find_emulator",
		// Do not provide any built-in rules.
		"--no_builtin_rules",
		// Fail when suffix rules are used.
		"--werror_suffix_rules",
		// Fail when a real target depends on a phony target.
		"--werror_real_to_phony",
		// Makes real_to_phony checks assume that any top-level or leaf
		// dependencies that does *not* have a '/' in it is a phony target.
		"--top_level_phony",
		// Fail when a phony target contains slashes.
		"--werror_phony_looks_real",
		// Fail when writing to a read-only directory.
		"--werror_writable",
		// Print Kati's internal statistics, such as the number of variables,
		// implicit/explicit/suffix rules, and so on.
		"--kati_stats",
	}, args...)

	// Generate a minimal Ninja file.
	//
	// Used for build_test and multiproduct_kati, which runs Kati several
	// hundred times for different configurations to test file generation logic.
	// These can result in generating Ninja files reaching ~1GB or more,
	// resulting in ~hundreds of GBs of writes.
	//
	// Since we don't care about executing the Ninja files in these test cases,
	// generating the Ninja file content wastes time, so skip writing any
	// information out with --empty_ninja_file.
	//
	// From https://github.com/google/kati/commit/87b8da7af2c8bea28b1d8ab17679453d859f96e5
	if config.EmptyNinjaFile() {
		args = append(args, "--empty_ninja_file")
	}

	// Apply 'local_pool' to to all rules that don't specify a pool.
	if config.UseRemoteBuild() {
		args = append(args, "--default_pool=local_pool")
	}

	cmd := Command(ctx, config, "ckati", executable, args...)

	// Set up the nsjail sandbox.
	cmd.Sandbox = katiSandbox

	// Set up stdout and stderr.
	pipe, err := cmd.StdoutPipe()
	if err != nil {
		ctx.Fatalln("Error getting output pipe for ckati:", err)
	}
	cmd.Stderr = cmd.Stdout

	// Apply the caller's function closure to mutate the environment variables.
	envFunc(cmd.Environment)

	// Pass on various build environment metadata to Kati.
	if _, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok {
		username := "unknown"
		if u, err := user.Current(); err == nil {
			username = u.Username
		} else {
			ctx.Println("Failed to get current user:", err)
		}
		cmd.Environment.Set("BUILD_USERNAME", username)
	}

	if _, ok := cmd.Environment.Get("BUILD_HOSTNAME"); !ok {
		hostname, err := os.Hostname()
		if err != nil {
			ctx.Println("Failed to read hostname:", err)
			hostname = "unknown"
		}
		cmd.Environment.Set("BUILD_HOSTNAME", hostname)
	}

	cmd.StartOrFatal()
	// Set up the ToolStatus command line reader for Kati for a consistent UI
	// for the user.
	status.KatiReader(ctx.Status.StartTool(), pipe)
	cmd.WaitOrFatal()
}

        该函数是 Android 构建系统中 Soong UI 调用底层 ckati 的核心封装函数,负责将 Makefile 解析为 Ninja 构建规则,并生成对应的 .ninja 文件。它不仅构造了完整的命令行参数,还处理了环境变量、沙箱控制、日志输出等关键构建流程。 这里调用 Command() 函数,根据传入的参数,生成一个 cmd 的结构,其中相关参数如下:

1)args

[
  --ninja,
  --ninja_dir=out,
  --ninja_suffix=-aosp_arm,
  --no_ninja_prelude,
  --regen,
  --ignore_optional_include=out/%.P,
  --detect_android_echo,
  --color_warnings,
  --gen_all_targets,
  --use_find_emulator,
  --werror_find_emulator,
  --no_builtin_rules,
  --werror_suffix_rules,
  --warn_real_to_phony,
  --warn_phony_looks_real,
  --top_level_phony,
  --kati_stats,

  --writable out/,
  -f build/make/core/main.mk,
  --werror_implicit_rules,
  --werror_overriding_commands,
  --werror_real_to_phony,
  --werror_phony_looks_real,
  --werror_writable,

  SOONG_MAKEVARS_MK=out/soong/make_vars-aosp_arm.mk,
  SOONG_ANDROID_MK=out/soong/Android-aosp_arm.mk,
  TARGET_DEVICE_DIR=build/target/board/generic,
  KATI_PACKAGE_MK_DIR=out/target/product/generic/obj/CONFIG/kati_packaging
]

        这里是 ckati 执行时使用的完整命令行参数,这些参数组合起来,构成了一个完整的 ckati 构建上下文,确保从传统的 Makefile 构建方式向现代 Ninja 构建方式的平滑过渡。 

2)config

%!s(*build.configImpl=&{
    [] false 0xc00000ecc0 
    out/dist 
    16 
    1 
    false false false false 
    [] [droid] 
    -aosp_arm 
    generic 
    build/target/board/generic 
    false false false false true
})

        这里是当前构建配置对象(config)的内容。 

3)executable

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

        这是 Android 构建系统中预编译好的 ckati 工具路径。表明当前正在调用的是官方预编译的高性能版本,而不是从源码编译的调试版本。

2、exec.go 

源码位置:/build/soong/ui/build/exec.go

Command

func Command(ctx Context, config Config, name string, executable string, args ...string) *Cmd {
	ret := &Cmd{
		Cmd:         exec.CommandContext(ctx.Context, executable, args...),
		Environment: config.Environment().Copy(),
		Sandbox:     noSandbox,

		ctx:    ctx,
		config: config,
		name:   name,
	}

	return ret
}

        根据上述的相关参数可知,最终是调用系统准备好的 prebuilts/build-tools/linux-x86/bin/ckati 参与编译,其中传入的参数有 --ninja\ --regen--detect_android_echo 等,最终编译出 build-aosp_arm.ninja。

        综合分析,Kati 的主要功能就是把 Makefile 转换成 build-xxx.ninja 文件从而来参与系统编译,随着 Android 逐步消除版本中的 Makefile 文件,Kati 最终也会退出 Android 的历史舞台。

 

你可能感兴趣的:(android,系统编译)