根目錄下的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.
根據(jù)build/envsetup.sh,MAKECMDGOALS會(huì)被mm/mmm/mma/mmma設(shè)置,結(jié)構(gòu)大致是MODULES-IN-$DIR, 執(zhí)行make/m時(shí)為空
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++編寫的,可以自行執(zhí)行make編譯
makeparallel.cpp
int main(int argc, char* argv[]) {
解析參數(shù)
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);
}
// 執(zhí)行 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();
}
// 父進(jìn)程等待子進(jìn)程退出
}
build/soong/soong_ui.bash
...
run_go "$@"
核心是執(zhí)行函數(shù)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
// 第一次直接運(yùn)行g(shù)o文件并生成可執(zhí)行文件microfactory_Linux,之后直接執(zhí)行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
// 執(zhí)行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)
根據(jù)之前what值決定執(zhí)行哪些操作,根據(jù)上面的代碼,可以看到調(diào)用的是build.BuildAll
下面看一下每項(xiàng)操作具體做了哪些工作
- BuildProductConfig
>runMakeProductConfig(ctx, config)
build/soong/ui/build/make.go
核心操作是執(zhí)行了make -f build/core/config.mk剩胁,解析命令執(zhí)行結(jié)果獲得變量/值的集合,同步環(huán)境變>量。為config設(shè)置KATI_GOALS扒最、NINJA_GOALS,其實(shí)就是之前的MAKECMDGOALS旭从。
- BuildSoong
runSoongBootstrap(ctx, config)
runSoong(ctx, config)
- runSoongBootstrap
核心邏輯是執(zhí)行build/soong/bootstrap.bash- build/soong/bootstrap.bash
輸出模板build/soong/soong.bootstrap.in到out/soong/.soong.bootstrap,執(zhí)行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.in到out/soong/.minibootstrap/build.ninja,變量存儲(chǔ)到out/soong/.blueprint.bootstrap$ cat out/soong/.blueprint.bootstrap BOOTSTRAP="./bootstrap.bash" BOOTSTRAP_MANIFEST="./build/soong/build.ninja.in"
- runSoong
執(zhí)行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 核心是執(zhí)行ckati命令。ckati的源碼位于build/kati下敷待,通過make執(zhí)行
- build/soong/ui/build/kati.go
// 生成KatiSuffix间涵,后面會(huì)需要
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函數(shù),先進(jìn)行Init()榜揖,初始化了一些東西勾哩,先不care。把參數(shù)做個(gè)備份保存到orig_args(不知道什么意圖)举哟。把參數(shù)拿去解析(flags.cc)思劳,解析完存放結(jié)構(gòu)體Flags中。
??FindFirstMakefie尋找第一個(gè)makefile妨猩,上面已經(jīng)通過-f指定了makefile潜叛,未指定則順序?qū)ふ耶?dāng)前目錄下的GNUmakefile、makefile壶硅、Makefile威兜。
??然后進(jìn)入Run函數(shù)。根據(jù)之前參數(shù)需要生成Ninja文件庐椒。NeedsRegen判斷是否需要重新生成ninja文件椒舵。函數(shù)IsMissingOutputs判斷GetNinjaFilename/GetNinjaShellScriptFilename是否存在,涉及到config.ninja_suffix约谈,也就是KatiSuffix上面已備注逮栅。