Merge "Deprecate `group_static_libs` property"
diff --git a/OWNERS b/OWNERS
index f15bd32..f0ccd82 100644
--- a/OWNERS
+++ b/OWNERS
@@ -3,13 +3,18 @@
 
 # AMER
 ahumesky@google.com
+alexmarquez@google.com
 asmundak@google.com
 ccross@android.com
 cparsons@google.com
 dwillemsen@google.com
 eakammer@google.com
+jobredeaux@google.com
 joeo@google.com
+lamontjones@google.com
 spandandas@google.com
+weiwli@google.com
+yudiliu@google.com
 yuntaoxu@google.com
 
 # APAC
diff --git a/android/bazel.go b/android/bazel.go
index cfb237f..bebb61b 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -137,6 +137,7 @@
 		// build/bazel explicitly.
 		"build/bazel":/* recursive = */ false,
 		"build/bazel/examples/android_app":/* recursive = */ true,
+		"build/bazel/examples/java":/* recursive = */ true,
 		"build/bazel/bazel_skylib":/* recursive = */ true,
 		"build/bazel/rules":/* recursive = */ true,
 		"build/bazel/rules_cc":/* recursive = */ true,
@@ -161,6 +162,7 @@
 	bp2buildDefaultConfig = Bp2BuildConfig{
 		"bionic":                            Bp2BuildDefaultTrueRecursively,
 		"build/bazel/examples/apex/minimal": Bp2BuildDefaultTrueRecursively,
+		"development/sdk":                   Bp2BuildDefaultTrueRecursively,
 		"external/gwp_asan":                 Bp2BuildDefaultTrueRecursively,
 		"system/core/libcutils":             Bp2BuildDefaultTrueRecursively,
 		"system/core/property_service/libpropertyinfoparser": Bp2BuildDefaultTrueRecursively,
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 40a5a73..9c922dd 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -353,7 +353,16 @@
 // the invocation returned an error code.
 func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
 	extraFlags ...string) (string, string, error) {
-	cmdFlags := []string{"--output_base=" + absolutePath(paths.outputBase), command.command}
+	cmdFlags := []string{
+		// --noautodetect_server_javabase has the practical consequence of preventing Bazel from
+		// attempting to download rules_java, which is incompatible with
+		// --experimental_repository_disable_download set further below.
+		// rules_java is also not needed until mixed builds start building java targets.
+		// TODO(b/197958133): Once rules_java is pulled into AOSP, remove this flag.
+		"--noautodetect_server_javabase",
+		"--output_base=" + absolutePath(paths.outputBase),
+		command.command,
+	}
 	cmdFlags = append(cmdFlags, command.expression)
 	cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(paths, runName))
 
diff --git a/android/config.go b/android/config.go
index b69963f..d3db68f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -66,7 +66,7 @@
 	*config
 }
 
-// BuildDir returns the build output directory for the configuration.
+// SoongOutDir returns the build output directory for the configuration.
 func (c Config) SoongOutDir() string {
 	return c.soongOutDir
 }
diff --git a/android/paths.go b/android/paths.go
index a733558..763cd7c 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1150,7 +1150,7 @@
 type OutputPath struct {
 	basePath
 
-	// The soong build directory, i.e. Config.BuildDir()
+	// The soong build directory, i.e. Config.SoongOutDir()
 	soongOutDir string
 
 	fullPath string
@@ -1544,7 +1544,7 @@
 type InstallPath struct {
 	basePath
 
-	// The soong build directory, i.e. Config.BuildDir()
+	// The soong build directory, i.e. Config.SoongOutDir()
 	soongOutDir string
 
 	// partitionDir is the part of the InstallPath that is automatically determined according to the context.
diff --git a/android/sdk.go b/android/sdk.go
index da740f3..5f4b387 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -570,9 +570,6 @@
 type SdkMemberTypesRegistry struct {
 	// The list of types sorted by property name.
 	list []SdkMemberType
-
-	// The key that uniquely identifies this registry instance.
-	key OnceKey
 }
 
 func (r *SdkMemberTypesRegistry) copyAndAppend(memberType SdkMemberType) *SdkMemberTypesRegistry {
@@ -592,18 +589,9 @@
 		return t1.SdkPropertyName() < t2.SdkPropertyName()
 	})
 
-	// Generate a key that identifies the slice of SdkMemberTypes by joining the property names
-	// from all the SdkMemberType .
-	var properties []string
-	for _, t := range list {
-		properties = append(properties, t.SdkPropertyName())
-	}
-	key := NewOnceKey(strings.Join(properties, "|"))
-
 	// Create a new registry so the pointer uniquely identifies the set of registered types.
 	return &SdkMemberTypesRegistry{
 		list: list,
-		key:  key,
 	}
 }
 
diff --git a/android/variable.go b/android/variable.go
index 5cf9aa8..9d7c1e6 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -108,6 +108,8 @@
 			Static_libs       []string
 			Whole_static_libs []string
 			Shared_libs       []string
+
+			Cmdline []string
 		}
 
 		// eng is true for -eng builds, and can be used to turn on additionaly heavyweight debugging
diff --git a/apex/apex_test.go b/apex/apex_test.go
index e0bf5e5..daaa5cb 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -5129,6 +5129,12 @@
 		// find the dex boot jar in it. We either need to disable the source libfoo
 		// or make the prebuilt libfoo preferred.
 		testDexpreoptWithApexes(t, bp, "module libfoo does not provide a dex boot jar", preparer, fragment)
+		// dexbootjar check is skipped if AllowMissingDependencies is true
+		preparerAllowMissingDeps := android.GroupFixturePreparers(
+			preparer,
+			android.PrepareForTestWithAllowMissingDependencies,
+		)
+		testDexpreoptWithApexes(t, bp, "", preparerAllowMissingDeps, fragment)
 	})
 
 	t.Run("prebuilt library preferred with source", func(t *testing.T) {
diff --git a/cc/builder.go b/cc/builder.go
index b6b00cb..d0527cb 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -643,7 +643,7 @@
 				OrderOnly: pathDeps,
 				Args: map[string]string{
 					"cFlags":    moduleToolingFlags,
-					"tidyFlags": flags.tidyFlags,
+					"tidyFlags": config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags),
 				},
 			})
 		}
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index 04536fc..ad130ba 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -316,7 +316,7 @@
 	if strings.HasPrefix(parameter, "--sysroot") {
 		return systemRoot
 	}
-	if strings.HasPrefix(parameter, "-fsanitize-blacklist") {
+	if strings.HasPrefix(parameter, "-fsanitize-ignorelist") {
 		return relativeFilePathFlag
 	}
 	if strings.HasPrefix(parameter, "-fprofile-sample-use") {
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index c4563e2..cf13503 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -106,6 +106,7 @@
 
 const tidyDefault = "${config.TidyDefaultGlobalChecks}"
 const tidyExternalVendor = "${config.TidyExternalVendorChecks}"
+const tidyDefaultNoAnalyzer = "${config.TidyDefaultGlobalChecks},-clang-analyzer-*"
 
 // This is a map of local path prefixes to the set of default clang-tidy checks
 // to be used.
@@ -139,3 +140,17 @@
 	}
 	return tidyDefault
 }
+
+func TidyFlagsForSrcFile(srcFile android.Path, flags string) string {
+	// Disable clang-analyzer-* checks globally for generated source files
+	// because some of them are too huge. Local .bp files can add wanted
+	// clang-analyzer checks through the tidy_checks property.
+	// Need to do this patch per source file, because some modules
+	// have both generated and organic source files.
+	if _, ok := srcFile.(android.WritablePath); ok {
+		if strings.Contains(flags, tidyDefault) {
+			return strings.ReplaceAll(flags, tidyDefault, tidyDefaultNoAnalyzer)
+		}
+	}
+	return flags
+}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index fbef12b..83f0037 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -123,7 +123,7 @@
 // that should be installed in the fuzz target output directories. This function
 // returns true, unless:
 //  - The module is not an installable shared library, or
-//  - The module is a header, stub, or vendor-linked library, or
+//  - The module is a header or stub, or
 //  - The module is a prebuilt and its source is available, or
 //  - The module is a versioned member of an SDK snapshot.
 func isValidSharedDependency(dependency android.Module) bool {
@@ -141,11 +141,6 @@
 		return false
 	}
 
-	if linkable.UseVndk() {
-		// Discard vendor linked libraries.
-		return false
-	}
-
 	if lib := moduleLibraryInterface(dependency); lib != nil && lib.buildStubs() && linkable.CcLibrary() {
 		// Discard stubs libs (only CCLibrary variants). Prebuilt libraries should not
 		// be excluded on the basis of they're not CCLibrary()'s.
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 90974aa..f6a9d5b 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -56,7 +56,7 @@
 	}
 
 	cfiCflags = []string{"-flto", "-fsanitize-cfi-cross-dso",
-		"-fsanitize-blacklist=external/compiler-rt/lib/cfi/cfi_blocklist.txt"}
+		"-fsanitize-ignorelist=external/compiler-rt/lib/cfi/cfi_blocklist.txt"}
 	// -flto and -fvisibility are required by clang when -fsanitize=cfi is
 	// used, but have no effect on assembly files
 	cfiAsflags = []string{"-flto", "-fvisibility=default"}
@@ -64,7 +64,7 @@
 		"-Wl,-plugin-opt,O1"}
 	cfiExportsMapPath = "build/soong/cc/config/cfi_exports.map"
 
-	intOverflowCflags = []string{"-fsanitize-blacklist=build/soong/cc/config/integer_overflow_blocklist.txt"}
+	intOverflowCflags = []string{"-fsanitize-ignorelist=build/soong/cc/config/integer_overflow_blocklist.txt"}
 
 	minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
 		"-fno-sanitize-recover=integer,undefined"}
@@ -260,7 +260,7 @@
 	// the first one
 	Recover []string
 
-	// value to pass to -fsanitize-blacklist
+	// value to pass to -fsanitize-ignorelist
 	Blocklist *string
 }
 
@@ -756,7 +756,7 @@
 
 	blocklist := android.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blocklist)
 	if blocklist.Valid() {
-		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-blacklist="+blocklist.String())
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-ignorelist="+blocklist.String())
 		flags.CFlagsDeps = append(flags.CFlagsDeps, blocklist.Path())
 	}
 
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index cff65f9..16a4e1a 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -25,6 +25,7 @@
 
 	"android/soong/bp2build"
 	"android/soong/shared"
+
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/deptools"
 	"github.com/google/blueprint/pathtools"
@@ -43,6 +44,7 @@
 	delveListen string
 	delvePath   string
 
+	moduleGraphFile   string
 	docFile           string
 	bazelQueryViewDir string
 	bp2buildMarker    string
@@ -56,30 +58,32 @@
 	flag.StringVar(&outDir, "out", "", "Soong output directory (usually $TOP/out/soong)")
 	flag.StringVar(&availableEnvFile, "available_env", "", "File containing available environment variables")
 	flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables")
+	flag.StringVar(&globFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
+	flag.StringVar(&globListDir, "globListDir", "", "the directory containing the glob list files")
+	flag.StringVar(&cmdlineArgs.SoongOutDir, "b", ".", "the build output directory")
+	flag.StringVar(&cmdlineArgs.OutDir, "n", "", "the ninja builddir directory")
+	flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
 
 	// Debug flags
 	flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging")
 	flag.StringVar(&delvePath, "delve_path", "", "Path to Delve. Only used if --delve_listen is set")
-
-	// Flags representing various modes soong_build can run in
-	flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
-	flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
-	flag.StringVar(&bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
-
-	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
-	flag.StringVar(&globFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
-	flag.StringVar(&globListDir, "globListDir", "", "the directory containing the glob list files")
-	flag.StringVar(&cmdlineArgs.BuildDir, "b", ".", "the build output directory")
-	flag.StringVar(&cmdlineArgs.NinjaBuildDir, "n", "", "the ninja builddir directory")
-	flag.StringVar(&cmdlineArgs.DepFile, "d", "", "the dependency file to output")
 	flag.StringVar(&cmdlineArgs.Cpuprofile, "cpuprofile", "", "write cpu profile to file")
 	flag.StringVar(&cmdlineArgs.TraceFile, "trace", "", "write trace to file")
 	flag.StringVar(&cmdlineArgs.Memprofile, "memprofile", "", "write memory profile to file")
 	flag.BoolVar(&cmdlineArgs.NoGC, "nogc", false, "turn off GC for debugging")
+
+	// Flags representing various modes soong_build can run in
+	flag.StringVar(&moduleGraphFile, "module_graph_file", "", "JSON module graph file to output")
+	flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
+	flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
+	flag.StringVar(&bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
+	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
+	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
+
+	// Flags that probably shouldn't be flags of soong_build but we haven't found
+	// the time to remove them yet
 	flag.BoolVar(&cmdlineArgs.RunGoTests, "t", false, "build and run go tests during bootstrap")
 	flag.BoolVar(&cmdlineArgs.UseValidations, "use-validations", false, "use validations to depend on go tests")
-	flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
-	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
 }
 
 func newNameResolver(config android.Config) *android.NameResolver {
@@ -149,11 +153,7 @@
 	globListFiles := writeBuildGlobsNinjaFile(secondCtx.SrcDir(), configuration.SoongOutDir(), secondCtx.Globs, configuration)
 	ninjaDeps = append(ninjaDeps, globListFiles...)
 
-	err = deptools.WriteDepFile(shared.JoinPath(topDir, secondArgs.DepFile), secondArgs.OutFile, ninjaDeps)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "Error writing depfile '%s': %s\n", secondArgs.DepFile, err)
-		os.Exit(1)
-	}
+	writeDepFile(secondArgs.OutFile, ninjaDeps)
 }
 
 // Run the code-generation phase to convert BazelTargetModules to BUILD files.
@@ -185,8 +185,8 @@
 	}
 }
 
-func writeJsonModuleGraph(configuration android.Config, ctx *android.Context, path string, extraNinjaDeps []string) {
-	f, err := os.Create(path)
+func writeJsonModuleGraph(ctx *android.Context, path string) {
+	f, err := os.Create(shared.JoinPath(topDir, path))
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
@@ -194,7 +194,6 @@
 
 	defer f.Close()
 	ctx.Context.PrintJSONGraph(f)
-	writeFakeNinjaFile(extraNinjaDeps, configuration.SoongOutDir())
 }
 
 func writeBuildGlobsNinjaFile(srcDir, buildDir string, globs func() pathtools.MultipleGlobResults, config interface{}) []string {
@@ -208,6 +207,15 @@
 	return bootstrap.GlobFileListFiles(globDir)
 }
 
+func writeDepFile(outputFile string, ninjaDeps []string) {
+	depFile := shared.JoinPath(topDir, outputFile+".d")
+	err := deptools.WriteDepFile(depFile, outputFile, ninjaDeps)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error writing depfile '%s': %s\n", depFile, err)
+		os.Exit(1)
+	}
+}
+
 // doChosenActivity runs Soong for a specific activity, like bp2build, queryview
 // or the actual Soong build for the build.ninja file. Returns the top level
 // output file of the specific activity.
@@ -215,10 +223,9 @@
 	bazelConversionRequested := bp2buildMarker != ""
 	mixedModeBuild := configuration.BazelContext.BazelEnabled()
 	generateQueryView := bazelQueryViewDir != ""
-	jsonModuleFile := configuration.Getenv("SOONG_DUMP_JSON_MODULE_GRAPH")
 
 	blueprintArgs := cmdlineArgs
-	prepareBuildActions := !generateQueryView && jsonModuleFile == ""
+	prepareBuildActions := !generateQueryView && moduleGraphFile == ""
 	if bazelConversionRequested {
 		// Run the alternate pipeline of bp2build mutators and singleton to convert
 		// Blueprint to BUILD files before everything else.
@@ -236,24 +243,21 @@
 		globListFiles := writeBuildGlobsNinjaFile(ctx.SrcDir(), configuration.SoongOutDir(), ctx.Globs, configuration)
 		ninjaDeps = append(ninjaDeps, globListFiles...)
 
-		err := deptools.WriteDepFile(shared.JoinPath(topDir, blueprintArgs.DepFile), blueprintArgs.OutFile, ninjaDeps)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "Error writing depfile '%s': %s\n", blueprintArgs.DepFile, err)
-			os.Exit(1)
+		// Convert the Soong module graph into Bazel BUILD files.
+		if generateQueryView {
+			runQueryView(configuration, ctx)
+			return cmdlineArgs.OutFile // TODO: This is a lie
+		} else if moduleGraphFile != "" {
+			writeJsonModuleGraph(ctx, moduleGraphFile)
+			writeDepFile(moduleGraphFile, ninjaDeps)
+			return moduleGraphFile
+		} else {
+			// The actual output (build.ninja) was written in the RunBlueprint() call
+			// above
+			writeDepFile(cmdlineArgs.OutFile, ninjaDeps)
 		}
 	}
 
-	// Convert the Soong module graph into Bazel BUILD files.
-	if generateQueryView {
-		runQueryView(configuration, ctx)
-		return cmdlineArgs.OutFile // TODO: This is a lie
-	}
-
-	if jsonModuleFile != "" {
-		writeJsonModuleGraph(configuration, ctx, jsonModuleFile, extraNinjaDeps)
-		return cmdlineArgs.OutFile // TODO: This is a lie
-	}
-
 	writeMetrics(configuration)
 	return cmdlineArgs.OutFile
 }
@@ -348,29 +352,6 @@
 	touch(shared.JoinPath(topDir, finalOutputFile))
 }
 
-// Workarounds to support running bp2build in a clean AOSP checkout with no
-// prior builds, and exiting early as soon as the BUILD files get generated,
-// therefore not creating build.ninja files that soong_ui and callers of
-// soong_build expects.
-//
-// These files are: build.ninja and build.ninja.d. Since Kati hasn't been
-// ran as well, and `nothing` is defined in a .mk file, there isn't a ninja
-// target called `nothing`, so we manually create it here.
-func writeFakeNinjaFile(extraNinjaDeps []string, soongOutDir string) {
-	extraNinjaDepsString := strings.Join(extraNinjaDeps, " \\\n ")
-
-	ninjaFileName := "build.ninja"
-	ninjaFile := shared.JoinPath(topDir, soongOutDir, ninjaFileName)
-	ninjaFileD := shared.JoinPath(topDir, soongOutDir, ninjaFileName+".d")
-	// A workaround to create the 'nothing' ninja target so `m nothing` works,
-	// since bp2build runs without Kati, and the 'nothing' target is declared in
-	// a Makefile.
-	ioutil.WriteFile(ninjaFile, []byte("build nothing: phony\n  phony_output = true\n"), 0666)
-	ioutil.WriteFile(ninjaFileD,
-		[]byte(fmt.Sprintf("%s: \\\n %s\n", ninjaFile, extraNinjaDepsString)),
-		0666)
-}
-
 func touch(path string) {
 	f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
 	if err != nil {
@@ -524,8 +505,8 @@
 		"bazel-" + filepath.Base(topDir),
 	}
 
-	if cmdlineArgs.NinjaBuildDir[0] != '/' {
-		excludes = append(excludes, cmdlineArgs.NinjaBuildDir)
+	if cmdlineArgs.OutDir[0] != '/' {
+		excludes = append(excludes, cmdlineArgs.OutDir)
 	}
 
 	existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
@@ -550,12 +531,7 @@
 	ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
 	ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
 
-	depFile := bp2buildMarker + ".d"
-	err = deptools.WriteDepFile(shared.JoinPath(topDir, depFile), bp2buildMarker, ninjaDeps)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "Cannot write depfile '%s': %s\n", depFile, err)
-		os.Exit(1)
-	}
+	writeDepFile(bp2buildMarker, ninjaDeps)
 
 	// Create an empty bp2build marker file.
 	touch(shared.JoinPath(topDir, bp2buildMarker))
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 29a8a39..73d807d 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -60,8 +60,8 @@
 	// https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions
 	Vendor_boot *bool
 
-	// Optional kernel commandline
-	Cmdline *string `android:"arch_variant"`
+	// Optional kernel commandline arguments
+	Cmdline []string `android:"arch_variant"`
 
 	// File that contains bootconfig parameters. This can be set only when `vendor_boot` is true
 	// and `header_version` is greater than or equal to 4.
@@ -152,7 +152,7 @@
 	dtb := android.PathForModuleSrc(ctx, dtbName)
 	cmd.FlagWithInput("--dtb ", dtb)
 
-	cmdline := proptools.String(b.properties.Cmdline)
+	cmdline := strings.Join(b.properties.Cmdline, " ")
 	if cmdline != "" {
 		flag := "--cmdline "
 		if vendor {
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index 51d998a..b198c24 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -24,6 +24,10 @@
 // core libraries.
 //
 // Don't use this directly, use "sdk_version: core_current".
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 java_library {
     name: "core.current.stubs",
     visibility: ["//visibility:public"],
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 1019b4c..946092c 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -500,7 +500,11 @@
 		dst := dstBootJarsByModule[name]
 
 		if src == nil {
-			ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
+			if !ctx.Config().AllowMissingDependencies() {
+				ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
+			} else {
+				ctx.AddMissingDependencies([]string{name})
+			}
 		} else if dst == nil {
 			ctx.ModuleErrorf("module %s is not part of the boot configuration", name)
 		} else {
diff --git a/mk2rbc/Android.bp b/mk2rbc/Android.bp
index 3ea3f7f..4fa3eb6 100644
--- a/mk2rbc/Android.bp
+++ b/mk2rbc/Android.bp
@@ -13,6 +13,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 blueprint_go_binary {
     name: "mk2rbc",
     srcs: ["cmd/mk2rbc.go"],
diff --git a/scripts/get_clang_version.py b/scripts/get_clang_version.py
index f6efc5f..17bc88b 100755
--- a/scripts/get_clang_version.py
+++ b/scripts/get_clang_version.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (C) 2021 The Android Open Source Project
 #
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 76b2ee9..610e427 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -607,12 +607,36 @@
 
 function test_dump_json_module_graph() {
   setup
-  SOONG_DUMP_JSON_MODULE_GRAPH="$MOCK_TOP/modules.json" run_soong
-  if [[ ! -r "$MOCK_TOP/modules.json" ]]; then
+  GENERATE_JSON_MODULE_GRAPH=1 run_soong
+  if [[ ! -r "out/soong//module-graph.json" ]]; then
     fail "JSON file was not created"
   fi
 }
 
+function test_json_module_graph_back_and_forth_null_build() {
+  setup
+
+  run_soong
+  local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  GENERATE_JSON_MODULE_GRAPH=1 run_soong
+  local json_mtime1=$(stat -c "%y" out/soong/module-graph.json)
+
+  run_soong
+  local ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
+  if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
+    fail "Output Ninja file changed after writing JSON module graph"
+  fi
+
+  GENERATE_JSON_MODULE_GRAPH=1 run_soong
+  local json_mtime2=$(stat -c "%y" out/soong/module-graph.json)
+  if [[ "$json_mtime1" != "$json_mtime2" ]]; then
+    fail "JSON module graph file changed after writing Ninja file"
+  fi
+
+}
+
+
 function test_bp2build_bazel_workspace_structure {
   setup
 
@@ -757,6 +781,7 @@
 test_glob_during_bootstrapping
 test_soong_build_rerun_iff_environment_changes
 test_dump_json_module_graph
+test_json_module_graph_back_and_forth_null_build
 test_write_to_source_tree
 test_bp2build_smoke
 test_bp2build_generates_marker_file
diff --git a/ui/build/config.go b/ui/build/config.go
index 956406d..6a05f4f 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -747,6 +747,10 @@
 	return shared.JoinPath(c.SoongOutDir(), ".bootstrap/bp2build_workspace_marker")
 }
 
+func (c *configImpl) ModuleGraphFile() string {
+	return shared.JoinPath(c.SoongOutDir(), "module-graph.json")
+}
+
 func (c *configImpl) TempDir() string {
 	return shared.TempDirForOutDir(c.SoongOutDir())
 }
@@ -919,7 +923,7 @@
 		return mixedBuild
 	} else if c.Environment().IsEnvTrue("GENERATE_BAZEL_FILES") {
 		return generateBuildFiles
-	} else if v, ok := c.Environment().Get("SOONG_DUMP_JSON_MODULE_GRAPH"); ok && v != "" {
+	} else if c.Environment().IsEnvTrue("GENERATE_JSON_MODULE_GRAPH") {
 		return generateJsonModuleGraph
 	} else {
 		return noBazel
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 06210b9..726b541 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -117,6 +117,7 @@
 
 	bootstrapGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.ninja")
 	bp2buildGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.bp2build.ninja")
+	moduleGraphGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.modulegraph.ninja")
 
 	// The glob .ninja files are subninja'd. However, they are generated during
 	// the build itself so we write an empty file so that the subninja doesn't
@@ -127,16 +128,14 @@
 
 	args.RunGoTests = !config.skipSoongTests
 	args.UseValidations = true // Use validations to depend on tests
-	args.BuildDir = config.SoongOutDir()
-	args.NinjaBuildDir = config.OutDir()
-	args.TopFile = "Android.bp"
+	args.SoongOutDir = config.SoongOutDir()
+	args.OutDir = config.OutDir()
 	args.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
 	args.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja")
 	// The primary builder (aka soong_build) will use bootstrapGlobFile as the globFile to generate build.ninja(.d)
 	// Building soong_build does not require a glob file
 	// Using "" instead of "<soong_build_glob>.ninja" will ensure that an unused glob file is not written to out/soong/.bootstrap during StagePrimary
 	args.Subninjas = []string{bootstrapGlobFile, bp2buildGlobFile}
-	args.GeneratingPrimaryBuilder = true
 	args.EmptyNinjaFile = config.EmptyNinjaFile()
 
 	args.DelveListen = os.Getenv("SOONG_DELVE")
@@ -181,9 +180,27 @@
 		Outputs: []string{config.Bp2BuildMarkerFile()},
 		Args:    bp2buildArgs,
 	}
+
+	moduleGraphArgs := []string{
+		"--module_graph_file", config.ModuleGraphFile(),
+		"--globListDir", "globs.modulegraph",
+		"--globFile", moduleGraphGlobFile,
+	}
+
+	moduleGraphArgs = append(moduleGraphArgs, commonArgs...)
+	moduleGraphArgs = append(moduleGraphArgs, environmentArgs(config, ".modulegraph")...)
+	moduleGraphArgs = append(moduleGraphArgs, "Android.bp")
+
+	moduleGraphInvocation := bootstrap.PrimaryBuilderInvocation{
+		Inputs:  []string{"Android.bp"},
+		Outputs: []string{config.ModuleGraphFile()},
+		Args:    moduleGraphArgs,
+	}
+
 	args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{
 		bp2buildInvocation,
 		mainSoongBuildInvocation,
+		moduleGraphInvocation,
 	}
 
 	blueprintCtx := blueprint.NewContext()
@@ -194,6 +211,7 @@
 		debugCompilation: os.Getenv("SOONG_DELVE") != "",
 	}
 
+	args.EmptyNinjaFile = false
 	bootstrapDeps := bootstrap.RunBlueprint(args, blueprintCtx, blueprintConfig)
 	err := deptools.WriteDepFile(bootstrapDepFile, args.OutFile, bootstrapDeps)
 	if err != nil {
@@ -307,6 +325,8 @@
 
 	if config.bazelBuildMode() == generateBuildFiles {
 		target = config.Bp2BuildMarkerFile()
+	} else if config.bazelBuildMode() == generateJsonModuleGraph {
+		target = config.ModuleGraphFile()
 	} else {
 		// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
 		target = config.MainNinjaFile()
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 57ceaba..f9a60b6 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -51,7 +51,6 @@
 	executable := config.PrebuiltBuildTool("ninja")
 
 	commonArgs := []string{}
-	commonArgs = append(commonArgs, config.NinjaArgs()...)
 	commonArgs = append(commonArgs, "-f", config.CombinedNinjaFile())
 	args := append(commonArgs, "-t", "targets", "rule")