diff --git a/ui/build/build.go b/ui/build/build.go index 9d5c330e1..15cff5f0b 100644 --- a/ui/build/build.go +++ b/ui/build/build.go @@ -298,6 +298,8 @@ func Build(ctx Context, config Config) { runMakeProductConfig(ctx, config) } + checkForCleanPartitions(ctx, config) + // Everything below here depends on product config. if inList("installclean", config.Arguments()) || diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go index 41cb5ab6d..71149639e 100644 --- a/ui/build/cleanbuild.go +++ b/ui/build/cleanbuild.go @@ -218,6 +218,50 @@ func installCleanIfNecessary(ctx Context, config Config) { writeConfig() } +// Writes out/partitions_were_clean_at_start_of_build.txt. +// This file will contain "true" if there were no partition staging directories at the start of +// the build (most likely from having just run `m installclean`) and "false" otherwise. +// It's used to make a test that the staging directories are correct. That test can only be +// correctly run directly after `m installclean`, and this is how we check for that. +func checkForCleanPartitions(ctx Context, config Config) { + productOutPath := config.ProductOut() + productOut := func(path string) string { + return filepath.Join(productOutPath, path) + } + + notExists := func(path string) bool { + _, err := os.Stat(path) + return os.IsNotExist(err) + } + + clean := notExists(productOut("ramdisk")) && + notExists(productOut("ramdisk_16k")) && + notExists(productOut("debug_ramdisk")) && + notExists(productOut("vendor_ramdisk")) && + notExists(productOut("vendor_debug_ramdisk")) && + notExists(productOut("vendor_kernel_ramdisk")) && + notExists(productOut("test_harness_ramdisk")) && + notExists(productOut("data")) && + notExists(productOut("recovery")) && + notExists(productOut("root")) && + notExists(productOut("system")) && + notExists(productOut("system_dlkm")) && + notExists(productOut("system_other")) && + notExists(productOut("vendor")) && + notExists(productOut("vendor_dlkm")) && + notExists(productOut("product")) && + notExists(productOut("system_ext")) && + notExists(productOut("oem")) && + notExists(productOut("breakpad")) && + notExists(productOut("cache")) && + notExists(productOut("coverage")) && + notExists(productOut("installer")) && + notExists(productOut("odm")) && + notExists(productOut("odm_dlkm")) + + writeValueIfChanged(ctx, config, config.OutDir(), "partitions_were_clean_at_start_of_build.txt", fmt.Sprintf("%t\n", clean)) +} + // cleanOldFiles takes an input file (with all paths relative to basePath), and removes files from // the filesystem if they were removed from the input file since the last execution. func cleanOldFiles(ctx Context, basePath, newFile string) { diff --git a/ui/build/kati.go b/ui/build/kati.go index 31e744029..7f0ea247c 100644 --- a/ui/build/kati.go +++ b/ui/build/kati.go @@ -102,6 +102,8 @@ func runKati(ctx Context, config Config, extraSuffix string, args []string, envF "--use_ninja_phony_output", // Support declaring symlink outputs in AOSP Ninja. "--use_ninja_symlink_outputs", + // Support ninja validation actions with .KATI_VALIDATIONS: https://ninja-build.org/manual.html#validations + "--use_ninja_validations", // Regenerate the Ninja file if environment inputs have changed. e.g. // CLI flags, .mk file timestamps, env vars, $(wildcard ..) and some // $(shell ..) results. diff --git a/ui/build/test_build.go b/ui/build/test_build.go index 2efc732c2..af60e0d76 100644 --- a/ui/build/test_build.go +++ b/ui/build/test_build.go @@ -103,8 +103,10 @@ func testForDanglingRules(ctx Context, config Config) { // treated as an source file. dexpreoptConfigFilePath := filepath.Join(outDir, "soong", "dexpreopt.config") - // out/build_date.txt is considered a "source file" + // These files are written by soong_ui at the beginning of every build. + // Ninja considers them "source files" buildDatetimeFilePath := filepath.Join(outDir, "build_date.txt") + cleanPartitionsFilePath := filepath.Join(outDir, "partitions_were_clean_at_start_of_build.txt") // bpglob is built explicitly using Microfactory bpglob := filepath.Join(config.SoongOutDir(), "bpglob") @@ -122,6 +124,7 @@ func testForDanglingRules(ctx Context, config Config) { line == variablesFilePath || line == dexpreoptConfigFilePath || line == buildDatetimeFilePath || + line == cleanPartitionsFilePath || line == bpglob { // Leaf node is in one of Soong's bootstrap directories, which do not have // full build rules in the primary build.ninja file.