提出问题 为什么有2个 -j 参数

这里的 make -j16 -j32 怎么来的呢? 为什么有2个 -j 参数????

这个来自 ckati –ninja 这个命令,带上了这个 –ninja参数,就会设置一个 MAKE 环境变量,然后带上 -j16 这个参数了。

make的调用来自 device/qcom/common/dlkm/AndroidKernelModule.mk, 这个是 在 vendor/qcom/opensource/wlan/qcacld-3.0/Android.mk 里面引入的。

1
2
3
4
5
177 $(KBUILD_TARGET): $(TARGET_PREBUILT_INT_KERNEL) $(GKI_TARGET_PREBUILT_KERNEL) $(LOCAL_ADDITIONAL_DEPENDENCIES) $(KBUILD_TARGET_GKI)
178 @mkdir -p $(kbuild_out_dir)
179 $(hide) cp -f $(local_path)/Kbuild $(kbuild_out_dir)/Kbuild
180 $(MAKE) -C $(KERNEL_OUT) M=$(KERNEL_TO_BUILD_ROOT_OFFSET)$(local_path) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(real_cc) $(KERNEL_CFLAGS) modules $(kbuild_options) ANDROID_BUILD_TOP=$$(pwd)
181 touch $@

通过上面的 makefile 内容可以看到 make 命令 来自 MAKE 这个变量,那就需要全局搜索这个变量在哪里定义了?

1
2
3
grep -E "MAKE\s+.=" -rn device/ build 
device/qcom/msmnile_gvmq/AndroidBoard.mk:140: MAKE := $(shell pwd)/prebuilts/build-tools/linux-x86/bin/$(MAKE)
build/make/core/config.mk:572:LPMAKE := $(HOST_OUT_EXECUTABLES)/lpmake$(HOST_EXECUTABLE_SUFFIX)

device/zzzz/sa8295_xxxx/AndroidBoard.mk --> device/qcom/msmnile_gvmq/AndroidBoard.mk

最终查看 device/qcom/msmnile_gvmq/AndroidBoard.mk:140: MAKE := $(shell pwd)/prebuilts/build-tools/linux-x86/bin/$(MAKE) 这里

在这里也没看到 $(MAKE) 来自哪里,还需要继续查找

build/soong/ui/build/dumpvars.go 中 打印显示 MAKE变量如下内容:

1
MAKE=prebuilts/build-tools/linux-x86/bin/ckati --color_warnings --kati_stats MAKECMDGOALS=

命令行上如果make后面跟上参数就会如下内容:

1
MAKE=prebuilts/build-tools/linux-x86/bin/ckati --color_warnings --kati_stats MAKECMDGOALS=dump-many-vars

function make() 函数 在文件 build/envsetup.sh 中定义

build/soong/soong_ui.bash --make-mode $@ 然后调用 到了 soong_ui.bash,

soong_build_go soong_ui android/soong/cmd/soong_ui 这个用来编译出来 soong_ui 命令,
这个命令编译出来放到 out/soong_ui 这里。 源码入口 build/soong/cmd/soong_ui/main.go

exec "$(getoutdir)/soong_ui" "$@" 最后调用 soong_ui 命令了 例如 exec ./out/soong_ui --make-mode -j4

1️⃣先这样, 其实这里 带的有 –ninja 参数了,在这之前会有个 ckati dump-many-vars 的执行。

1
2
3
4
5
6
7
8
9
10
11
12
soong_ui --make-mode -j4  # source, lunch 之后,直接执行这个好像也可以编译
|-ckati --ninja --ninja_dir=out --ninja_suffix=-sa8295_xxxx --no_ninja_prelude
--use_ninja_phony_output --use_ninja_symlink_outputs --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 --werror_real_to_phony --top_level_phony
--werror_phony_looks_real --werror_writable --kati_stats --writable out/
--werror_implicit_rules -f build/make/core/main.mk
SOONG_MAKEVARS_MK=out/soong/make_vars-sa8295_xxxx.mk
SOONG_ANDROID_MK=out/soong/Android-sa8295_xxxx.mk TARGET_DEVICE_DIR=device/zzzz/sa8295_xxxx
KATI_PACKAGE_MK_DIR=out/target/product/sa8295_xxxx/obj/CONFIG/kati_packaging

soong_ui --dumpvars-mode -vars=WLAN_BUILD_DEBUG 还可以这么使用,打印某个环境变量的值。

2️⃣后变成 ninja 进程, 这个命令来自 prebuilts/build-tools/linux-x86/bin/ninja。

1
2
3
soong_ui --make-mode -j4
├─ninja -d keepdepfile -d keeprsp -d stats --frontend_file out/.ninja_fifo droid -j 4
-f out/combined-sa8295_xxxx.ninja -o usesphonyoutputs=yes -w dupbuild=err -w missingdepfile=err

分析 soong_ui 执行过程

源码入口 build/soong/cmd/soong_ui/main.go,soong_ui --make-mode -j4 的情况

1.main函数入口

1.main函数入口,初始化 buildCtx build.Context结构体。config 调用了 command 结构体中的config函数。args 命令行参数。logsDir 就是 out 目录。

调用 c.run(。。。)

2.最后调用 c.run(buildCtx, config, args, logsDir) 这里的 c 就是一个 command 结构体的实例。

调用 func make

3.这个函数对应的就是func make(ctx build.Context, config build.Config, _ []string, logsDir string)这个函数。

调用不同的函数

4.main函数中 根据RunAll = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunNinja会调用不同的函数执行。

调用 runMakeProductConfig

4.1 调用 runMakeProductConfig(ctx, config)

根据 RunAll = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunNinja 会调用不同的函数执行。

分析 runMakeProductConfig 中是怎么影响 MAKE 这个变量的初始化的。

调用的是 ckati -f build/make/core/config.mk dump-many-vars 这样的命令。

调用 runMakeProductConfig(ctx, config) 设置一些环境变量,打印横幅 Banner,就是那个 等号 2行之间的 环境变量的打印显示。ui/build/dumpvars.go文件中。

在 runMakeProductConfig(ctx, config) 执行下面这个命令,然后就会stdout打印出来一些环境变量的值。

比较关键的是一个 DUMP_MANY_VARS=”NINJA_GOALS MAKE” 变量,这个决定会打印哪些变量。

下面是一个模拟调用的例子:

1
2
3
DUMP_MANY_VARS="NINJA_GOALS MAKE" BUILD_DATETIME_FILE=out/build_date.txt TMPDIR=out/.temp OUT_DIR=out \
./prebuilts/build-tools/linux-x86/bin/ckati -f build/make/core/config.mk \
--color_warnings --kati_stats dump-many-vars "MAKECMDGOALS=" 2>/dev/null
1
2
3
DUMP_MANY_VARS="MAKE" BUILD_DATETIME_FILE=out/build_date.txt TMPDIR=out/.temp OUT_DIR=out \
./prebuilts/build-tools/linux-x86/bin/ckati -f build/make/core/config.mk \
--color_warnings --kati_stats dump-many-vars "MAKECMDGOALS=" 2>/dev/null

这么执行 其实是调用了 build/make/core/dumpvar.mk 中的 dump-many-vars 目标。

打印出来 MAKE 这个变量的值是这个

1
MAKE='./prebuilts/build-tools/linux-x86/bin/ckati --color_warnings --kati_stats MAKECMDGOALS='

去chati中的源码寻找答案

在 ckati 源码中 src/main.cc 文件中 找到如下代码

1
2
3
4
5
6
7
if (g_flags.generate_ninja) {   // 如果命令行 带上 "--ninja" 选项 这里就会是 true,进入if 判断中了
bootstrap += StringPrintf("MAKE?=make -j%d\n",
g_flags.num_jobs <= 1 ? 1 : g_flags.num_jobs / 2);
} else { /// 其实上面的 ckati 命令 执行到这里 else 了。然后 把 命令行上的 其他 args 都放到 MAKE 变量里面了。。。。。。。。
bootstrap += StringPrintf("MAKE?=%s\n",
JoinStrings(g_flags.subkati_args, " ").c_str());
}

调用 runSoong(ctx, config)

4.2 调用 runSoong(ctx, config)

根据 RunAll = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunNinja 会调用不同的函数执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

prebuilts/build-tools/linux-x86/bin/ninja -d keepdepfile -d stats -o usesphonyoutputs=yes -o preremoveoutputs=yes
-w dupbuild=err -w outputdir=err -w missingoutfile=err -j 4 --frontend_file out/.ninja_fifo -f out/soong/.bootstrap/build.ninja

[sa8295_xxxx-userdebug]05:11:05
"soong bootstrap"
executing
"prebuilts/build-tools/linux-x86/bin/ninja" [
prebuilts/build-tools/linux-x86/bin/ninja -d keepdepfile -d stats -o usesphonyoutputs=yes -o preremoveoutputs=yes
-w dupbuild=err -w outputdir=err -w missingoutfile=err -j 4 --frontend_file out/.ninja_fifo -f out/soong/.bootstrap/build.ninja
]

cmd := Command(ctx, config, "soong "+name,
config.PrebuiltBuildTool("ninja"),
"-d", "keepdepfile",
"-d", "stats",
"-o", "usesphonyoutputs=yes",
"-o", "preremoveoutputs=yes",
"-w", "dupbuild=err",
"-w", "outputdir=err",
"-w", "missingoutfile=err",
"-j", strconv.Itoa(config.Parallel()),
"--frontend_file", fifo,
"-f", filepath.Join(config.SoongOutDir(), file))

这一步说是 要 生成 build.ninja

调用 runKatiCleanSpec(ctx, config)

4.3 调用 runKatiCleanSpec(ctx, config)

根据RunAll = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunNinja 会调用不同的函数执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
prebuilts/build-tools/linux-x86/bin/ckati --ninja --ninja_dir=out --ninja_suffix=-sa8295_xxxx-cleanspec
--no_ninja_prelude --use_ninja_phony_output --use_ninja_symlink_outputs --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
--werror_real_to_phony --top_level_phony --werror_phony_looks_real --werror_writable
--kati_stats --werror_implicit_rules --werror_overriding_commands
-f build/make/core/cleanbuild.mk SOONG_MAKEVARS_MK=out/soong/make_vars-sa8295_xxxx.mk TARGET_DEVICE_DIR=device/zzzz/sa8295_xxxx

[sa8295_xxxx-userdebug]05:16:55
"ckati"
executing
"prebuilts/build-tools/linux-x86/bin/ckati" [
prebuilts/build-tools/linux-x86/bin/ckati --ninja --ninja_dir=out --ninja_suffix=-sa8295_xxxx-cleanspec
--no_ninja_prelude --use_ninja_phony_output --use_ninja_symlink_outputs --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 --werror_real_to_phony --top_level_phony --werror_phony_looks_real
--werror_writable --kati_stats --werror_implicit_rules --werror_overriding_commands
-f build/make/core/cleanbuild.mk SOONG_MAKEVARS_MK=out/soong/make_vars-sa8295_xxxx.mk TARGET_DEVICE_DIR=device/zzzz/sa8295_xxxx
]
1
runKatiCleanSpec(ctx, config)  -> runKati() --> cmd := Command(ctx, config, "ckati", executable, args...)

调用 runKatiBuild

4.4 调用 runKatiBuild(ctx Context, config Config)

1
prebuilts/build-tools/linux-x86/bin/ckati --ninja --ninja_dir=out --ninja_suffix=-sa8295_xxxx --no_ninja_prelude --use_ninja_phony_output --use_ninja_symlink_outputs --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 --werror_real_to_phony --top_level_phony --werror_phony_looks_real --werror_writable --kati_stats --writable out/ --werror_implicit_rules -f build/make/core/main.mk SOONG_MAKEVARS_MK=out/soong/make_vars-sa8295_xxxx.mk SOONG_ANDROID_MK=out/soong/Android-sa8295_xxxx.mk TARGET_DEVICE_DIR=device/zzzz/sa8295_xxxx KATI_PACKAGE_MK_DIR=out/target/product/sa8295_xxxx/obj/CONFIG/kati_packaging
1
runKatiBuild(ctx Context, config Config) -> runKati() --> cmd := Command(ctx, config, "ckati", executable, args...) 

调用 runKatiPackage

4.5 调用 runKatiPackage(ctx Context, config Config)

1
prebuilts/build-tools/linux-x86/bin/ckati --ninja --ninja_dir=out --ninja_suffix=-sa8295_xxxx-package --no_ninja_prelude --use_ninja_phony_output --use_ninja_symlink_outputs --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 --werror_real_to_phony --top_level_phony --werror_phony_looks_real --werror_writable --kati_stats --writable out/dist/ --werror_implicit_rules --werror_overriding_commands -f build/make/packaging/main.mk KATI_PACKAGE_MK_DIR=out/target/product/sa8295_xxxx/obj/CONFIG/kati_packaging
1
2
runKatiPackage(ctx Context, config Config) -> runKati() --> cmd := Command(ctx, config, "ckati", executable, args...) 

调用 runNinjaForBuild

  1. main函数的最后 会有个 runNinjaForBuild(ctx, config) 的调用,这里就又 调用到 ninja 这个命令上了。
1
2
3
prebuilts/build-tools/linux-x86/bin/ninja -d keepdepfile -d keeprsp -d stats \
--frontend_file out/.ninja_fifo droid -j 4 -f out/combined-sa8295_xxxx.ninja \
-o usesphonyoutputs=yes -w dupbuild=err -w missingdepfile=err