Merge "Add new team members to OWNERS"
diff --git a/android/bazel.go b/android/bazel.go
index fa19e52..b01bd70 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -137,12 +137,14 @@
 		// 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,
 		"build/bazel/tests":/* recursive = */ true,
 		"build/bazel/platforms":/* recursive = */ true,
 		"build/bazel/product_variables":/* recursive = */ true,
+		"build/bazel_common_rules":/* recursive = */ true,
 		"build/pesto":/* recursive = */ true,
 
 		// external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 312f009..9c922dd 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -106,7 +106,7 @@
 	bazelPath    string
 	outputBase   string
 	workspaceDir string
-	buildDir     string
+	soongOutDir  string
 	metricsDir   string
 }
 
@@ -254,7 +254,7 @@
 
 func bazelPathsFromConfig(c *config) (*bazelPaths, error) {
 	p := bazelPaths{
-		buildDir: c.buildDir,
+		soongOutDir: c.soongOutDir,
 	}
 	missingEnvVars := []string{}
 	if len(c.Getenv("BAZEL_HOME")) > 1 {
@@ -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))
 
@@ -382,7 +391,7 @@
 	bazelCmd.Env = append(os.Environ(),
 		"HOME="+paths.homeDir,
 		pwdPrefix(),
-		"BUILD_DIR="+absolutePath(paths.buildDir),
+		"BUILD_DIR="+absolutePath(paths.soongOutDir),
 		// Make OUT_DIR absolute here so tools/bazel.sh uses the correct
 		// OUT_DIR at <root>/out, instead of <root>/out/soong/workspace/out.
 		"OUT_DIR="+absolutePath(paths.outDir()),
@@ -599,24 +608,24 @@
 // Returns a path containing build-related metadata required for interfacing
 // with Bazel. Example: out/soong/bazel.
 func (p *bazelPaths) intermediatesDir() string {
-	return filepath.Join(p.buildDir, "bazel")
+	return filepath.Join(p.soongOutDir, "bazel")
 }
 
 // Returns the path where the contents of the @soong_injection repository live.
 // It is used by Soong to tell Bazel things it cannot over the command line.
 func (p *bazelPaths) injectedFilesDir() string {
-	return filepath.Join(p.buildDir, bazel.SoongInjectionDirName)
+	return filepath.Join(p.soongOutDir, bazel.SoongInjectionDirName)
 }
 
 // Returns the path of the synthetic Bazel workspace that contains a symlink
 // forest composed the whole source tree and BUILD files generated by bp2build.
 func (p *bazelPaths) syntheticWorkspaceDir() string {
-	return filepath.Join(p.buildDir, "workspace")
+	return filepath.Join(p.soongOutDir, "workspace")
 }
 
 // Returns the path to the top level out dir ($OUT_DIR).
 func (p *bazelPaths) outDir() string {
-	return filepath.Dir(p.buildDir)
+	return filepath.Dir(p.soongOutDir)
 }
 
 // Issues commands to Bazel to receive results for all cquery requests
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index f1fabec..557faea 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -101,7 +101,7 @@
 func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*bazelContext, string) {
 	t.Helper()
 	p := bazelPaths{
-		buildDir:     t.TempDir(),
+		soongOutDir:  t.TempDir(),
 		outputBase:   "outputbase",
 		workspaceDir: "workspace_dir",
 	}
@@ -114,5 +114,5 @@
 		bazelRunner: runner,
 		paths:       &p,
 		requests:    map[cqueryKey]bool{},
-	}, p.buildDir
+	}, p.soongOutDir
 }
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index c09d218..a4bd2ef 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -414,7 +414,7 @@
 	}
 
 	outputPath := OutputPath{basePath{"", ""},
-		ctx.Config().buildDir,
+		ctx.Config().soongOutDir,
 		ctx.Config().BazelContext.OutputBase()}
 
 	return BazelOutPath{
diff --git a/android/config.go b/android/config.go
index 23423b7..b69963f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -67,12 +67,12 @@
 }
 
 // BuildDir returns the build output directory for the configuration.
-func (c Config) BuildDir() string {
-	return c.buildDir
+func (c Config) SoongOutDir() string {
+	return c.soongOutDir
 }
 
-func (c Config) NinjaBuildDir() string {
-	return c.buildDir
+func (c Config) OutDir() string {
+	return c.soongOutDir
 }
 
 func (c Config) DebugCompilation() bool {
@@ -122,7 +122,7 @@
 
 	deviceConfig *deviceConfig
 
-	buildDir       string // the path of the build output directory
+	soongOutDir    string // the path of the build output directory
 	moduleListFile string // the path to the file which lists blueprint files to parse.
 
 	env       map[string]string
@@ -283,11 +283,11 @@
 
 // NullConfig returns a mostly empty Config for use by standalone tools like dexpreopt_gen that
 // use the android package.
-func NullConfig(buildDir string) Config {
+func NullConfig(soongOutDir string) Config {
 	return Config{
 		config: &config{
-			buildDir: buildDir,
-			fs:       pathtools.OsFs,
+			soongOutDir: soongOutDir,
+			fs:          pathtools.OsFs,
 		},
 	}
 }
@@ -319,7 +319,7 @@
 			ShippingApiLevel:                  stringPtr("30"),
 		},
 
-		buildDir:     buildDir,
+		soongOutDir:  buildDir,
 		captureBuild: true,
 		env:          envCopy,
 
@@ -397,7 +397,7 @@
 // multiple runs in the same program execution is carried over (such as Bazel
 // context or environment deps).
 func ConfigForAdditionalRun(c Config) (Config, error) {
-	newConfig, err := NewConfig(c.buildDir, c.moduleListFile, c.env)
+	newConfig, err := NewConfig(c.soongOutDir, c.moduleListFile, c.env)
 	if err != nil {
 		return Config{}, err
 	}
@@ -408,14 +408,14 @@
 
 // NewConfig creates a new Config object. The srcDir argument specifies the path
 // to the root source directory. It also loads the config file, if found.
-func NewConfig(buildDir string, moduleListFile string, availableEnv map[string]string) (Config, error) {
+func NewConfig(soongOutDir string, moduleListFile string, availableEnv map[string]string) (Config, error) {
 	// Make a config with default options.
 	config := &config{
-		ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
+		ProductVariablesFileName: filepath.Join(soongOutDir, productVariablesFileName),
 
 		env: availableEnv,
 
-		buildDir:          buildDir,
+		soongOutDir:       soongOutDir,
 		multilibConflicts: make(map[ArchType]bool),
 
 		moduleListFile: moduleListFile,
@@ -428,7 +428,7 @@
 
 	// Soundness check of the build and source directories. This won't catch strange
 	// configurations with symlinks, but at least checks the obvious case.
-	absBuildDir, err := filepath.Abs(buildDir)
+	absBuildDir, err := filepath.Abs(soongOutDir)
 	if err != nil {
 		return Config{}, err
 	}
@@ -448,7 +448,7 @@
 		return Config{}, err
 	}
 
-	KatiEnabledMarkerFile := filepath.Join(buildDir, ".soong.kati_enabled")
+	KatiEnabledMarkerFile := filepath.Join(soongOutDir, ".soong.kati_enabled")
 	if _, err := os.Stat(absolutePath(KatiEnabledMarkerFile)); err == nil {
 		config.katiEnabled = true
 	}
@@ -556,7 +556,7 @@
 // BlueprintToolLocation returns the directory containing build system tools
 // from Blueprint, like soong_zip and merge_zips.
 func (c *config) BlueprintToolLocation() string {
-	return filepath.Join(c.buildDir, "host", c.PrebuiltOS(), "bin")
+	return filepath.Join(c.soongOutDir, "host", c.PrebuiltOS(), "bin")
 }
 
 var _ bootstrap.ConfigBlueprintToolLocation = (*config)(nil)
diff --git a/android/fixture.go b/android/fixture.go
index fd051a7..728f031 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -834,7 +834,7 @@
 func (r *TestResult) NormalizePathForTesting(path Path) string {
 	pathContext := PathContextForTesting(r.Config)
 	pathAsString := path.String()
-	if rel, isRel := MaybeRel(pathContext, r.Config.BuildDir(), pathAsString); isRel {
+	if rel, isRel := MaybeRel(pathContext, r.Config.SoongOutDir(), pathAsString); isRel {
 		return rel
 	}
 	return pathAsString
diff --git a/android/paths.go b/android/paths.go
index 71caaab..a733558 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -186,13 +186,13 @@
 	// A standard build has the following structure:
 	//   ../top/
 	//          out/ - make install files go here.
-	//          out/soong - this is the buildDir passed to NewTestConfig()
+	//          out/soong - this is the soongOutDir passed to NewTestConfig()
 	//          ... - the source files
 	//
 	// This function converts a path so that it appears relative to the ../top/ directory, i.e.
-	// * Make install paths, which have the pattern "buildDir/../<path>" are converted into the top
+	// * Make install paths, which have the pattern "soongOutDir/../<path>" are converted into the top
 	//   relative path "out/<path>"
-	// * Soong install paths and other writable paths, which have the pattern "buildDir/<path>" are
+	// * Soong install paths and other writable paths, which have the pattern "soongOutDir/<path>" are
 	//   converted into the top relative path "out/soong/<path>".
 	// * Source paths are already relative to the top.
 	// * Phony paths are not relative to anything.
@@ -211,7 +211,7 @@
 	Path
 
 	// return the path to the build directory.
-	getBuildDir() string
+	getSoongOutDir() string
 
 	// the writablePath method doesn't directly do anything,
 	// but it allows a struct to distinguish between whether or not it implements the WritablePath interface
@@ -992,7 +992,7 @@
 	}
 
 	// absolute path already checked by validateSafePath
-	if strings.HasPrefix(ret.String(), ctx.Config().buildDir) {
+	if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) {
 		return ret, fmt.Errorf("source path %q is in output", ret.String())
 	}
 
@@ -1008,7 +1008,7 @@
 	}
 
 	// absolute path already checked by validatePath
-	if strings.HasPrefix(ret.String(), ctx.Config().buildDir) {
+	if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) {
 		return ret, fmt.Errorf("source path %q is in output", ret.String())
 	}
 
@@ -1151,7 +1151,7 @@
 	basePath
 
 	// The soong build directory, i.e. Config.BuildDir()
-	buildDir string
+	soongOutDir string
 
 	fullPath string
 }
@@ -1167,8 +1167,8 @@
 	return p
 }
 
-func (p OutputPath) getBuildDir() string {
-	return p.buildDir
+func (p OutputPath) getSoongOutDir() string {
+	return p.soongOutDir
 }
 
 func (p OutputPath) RelativeToTop() Path {
@@ -1176,8 +1176,8 @@
 }
 
 func (p OutputPath) outputPathRelativeToTop() OutputPath {
-	p.fullPath = StringPathRelativeToTop(p.buildDir, p.fullPath)
-	p.buildDir = OutSoongDir
+	p.fullPath = StringPathRelativeToTop(p.soongOutDir, p.fullPath)
+	p.soongOutDir = OutSoongDir
 	return p
 }
 
@@ -1218,12 +1218,12 @@
 	if err != nil {
 		reportPathError(ctx, err)
 	}
-	fullPath := filepath.Join(ctx.Config().buildDir, path)
+	fullPath := filepath.Join(ctx.Config().soongOutDir, path)
 	path = fullPath[len(fullPath)-len(path):]
-	return OutputPath{basePath{path, ""}, ctx.Config().buildDir, fullPath}
+	return OutputPath{basePath{path, ""}, ctx.Config().soongOutDir, fullPath}
 }
 
-// PathsForOutput returns Paths rooted from buildDir
+// PathsForOutput returns Paths rooted from soongOutDir
 func PathsForOutput(ctx PathContext, paths []string) WritablePaths {
 	ret := make(WritablePaths, len(paths))
 	for i, path := range paths {
@@ -1545,7 +1545,7 @@
 	basePath
 
 	// The soong build directory, i.e. Config.BuildDir()
-	buildDir string
+	soongOutDir string
 
 	// partitionDir is the part of the InstallPath that is automatically determined according to the context.
 	// For example, it is host/<os>-<arch> for host modules, and target/product/<device>/<partition> for device modules.
@@ -1565,12 +1565,12 @@
 
 func (p InstallPath) RelativeToTop() Path {
 	ensureTestOnly()
-	p.buildDir = OutSoongDir
+	p.soongOutDir = OutSoongDir
 	return p
 }
 
-func (p InstallPath) getBuildDir() string {
-	return p.buildDir
+func (p InstallPath) getSoongOutDir() string {
+	return p.soongOutDir
 }
 
 func (p InstallPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
@@ -1585,9 +1585,9 @@
 func (p InstallPath) String() string {
 	if p.makePath {
 		// Make path starts with out/ instead of out/soong.
-		return filepath.Join(p.buildDir, "../", p.path)
+		return filepath.Join(p.soongOutDir, "../", p.path)
 	} else {
-		return filepath.Join(p.buildDir, p.path)
+		return filepath.Join(p.soongOutDir, p.path)
 	}
 }
 
@@ -1596,9 +1596,9 @@
 // The ./soong is dropped if the install path is for Make.
 func (p InstallPath) PartitionDir() string {
 	if p.makePath {
-		return filepath.Join(p.buildDir, "../", p.partitionDir)
+		return filepath.Join(p.soongOutDir, "../", p.partitionDir)
 	} else {
-		return filepath.Join(p.buildDir, p.partitionDir)
+		return filepath.Join(p.soongOutDir, p.partitionDir)
 	}
 }
 
@@ -1694,7 +1694,7 @@
 
 	base := InstallPath{
 		basePath:     basePath{partionPath, ""},
-		buildDir:     ctx.Config().buildDir,
+		soongOutDir:  ctx.Config().soongOutDir,
 		partitionDir: partionPath,
 		makePath:     false,
 	}
@@ -1705,7 +1705,7 @@
 func pathForNdkOrSdkInstall(ctx PathContext, prefix string, paths []string) InstallPath {
 	base := InstallPath{
 		basePath:     basePath{prefix, ""},
-		buildDir:     ctx.Config().buildDir,
+		soongOutDir:  ctx.Config().soongOutDir,
 		partitionDir: prefix,
 		makePath:     false,
 	}
@@ -1851,7 +1851,7 @@
 
 func (p PhonyPath) writablePath() {}
 
-func (p PhonyPath) getBuildDir() string {
+func (p PhonyPath) getSoongOutDir() string {
 	// A phone path cannot contain any / so cannot be relative to the build directory.
 	return ""
 }
diff --git a/android/singleton.go b/android/singleton.go
index bb6614d..7ff96c9 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -54,10 +54,10 @@
 
 	RequireNinjaVersion(major, minor, micro int)
 
-	// SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable
+	// SetOutDir sets the value of the top-level "builddir" Ninja variable
 	// that controls where Ninja stores its build log files.  This value can be
 	// set at most one time for a single build, later calls are ignored.
-	SetNinjaBuildDir(pctx PackageContext, value string)
+	SetOutDir(pctx PackageContext, value string)
 
 	// Eval takes a string with embedded ninja variables, and returns a string
 	// with all of the variables recursively expanded. Any variables references
@@ -180,8 +180,8 @@
 	addPhony(s.Config(), name, deps...)
 }
 
-func (s *singletonContextAdaptor) SetNinjaBuildDir(pctx PackageContext, value string) {
-	s.SingletonContext.SetNinjaBuildDir(pctx.PackageContext, value)
+func (s *singletonContextAdaptor) SetOutDir(pctx PackageContext, value string) {
+	s.SingletonContext.SetOutDir(pctx.PackageContext, value)
 }
 
 func (s *singletonContextAdaptor) Eval(pctx PackageContext, ninjaStr string) (string, error) {
diff --git a/android/test_asserts.go b/android/test_asserts.go
index edeb408..064f656 100644
--- a/android/test_asserts.go
+++ b/android/test_asserts.go
@@ -77,14 +77,14 @@
 // StringPathRelativeToTop on the actual string path.
 func AssertStringPathRelativeToTopEquals(t *testing.T, message string, config Config, expected string, actual string) {
 	t.Helper()
-	AssertStringEquals(t, message, expected, StringPathRelativeToTop(config.buildDir, actual))
+	AssertStringEquals(t, message, expected, StringPathRelativeToTop(config.soongOutDir, actual))
 }
 
 // AssertStringPathsRelativeToTopEquals checks if the expected value is equal to the result of
 // calling StringPathsRelativeToTop on the actual string paths.
 func AssertStringPathsRelativeToTopEquals(t *testing.T, message string, config Config, expected []string, actual []string) {
 	t.Helper()
-	AssertDeepEquals(t, message, expected, StringPathsRelativeToTop(config.buildDir, actual))
+	AssertDeepEquals(t, message, expected, StringPathsRelativeToTop(config.soongOutDir, actual))
 }
 
 // AssertErrorMessageEquals checks if the error is not nil and has the expected message. If it does
diff --git a/android/testing.go b/android/testing.go
index 6ba8e3c..e25e5c5 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -664,15 +664,15 @@
 // containing at most one instance of the temporary build directory at the start of the path while
 // this assumes that there can be any number at any position.
 func normalizeStringRelativeToTop(config Config, s string) string {
-	// The buildDir usually looks something like: /tmp/testFoo2345/001
+	// The soongOutDir usually looks something like: /tmp/testFoo2345/001
 	//
-	// Replace any usage of the buildDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
+	// Replace any usage of the soongOutDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
 	// "out/soong".
-	outSoongDir := filepath.Clean(config.buildDir)
+	outSoongDir := filepath.Clean(config.soongOutDir)
 	re := regexp.MustCompile(`\Q` + outSoongDir + `\E\b`)
 	s = re.ReplaceAllString(s, "out/soong")
 
-	// Replace any usage of the buildDir/.. with out, e.g. replace "/tmp/testFoo2345" with
+	// Replace any usage of the soongOutDir/.. with out, e.g. replace "/tmp/testFoo2345" with
 	// "out". This must come after the previous replacement otherwise this would replace
 	// "/tmp/testFoo2345/001" with "out/001" instead of "out/soong".
 	outDir := filepath.Dir(outSoongDir)
@@ -991,7 +991,7 @@
 	}
 	p := path.String()
 	if w, ok := path.(WritablePath); ok {
-		rel, err := filepath.Rel(w.getBuildDir(), p)
+		rel, err := filepath.Rel(w.getSoongOutDir(), p)
 		if err != nil {
 			panic(err)
 		}
diff --git a/android/writedocs.go b/android/writedocs.go
index 67b9aa3..c380a3d 100644
--- a/android/writedocs.go
+++ b/android/writedocs.go
@@ -34,12 +34,12 @@
 type docsSingleton struct{}
 
 func primaryBuilderPath(ctx SingletonContext) Path {
-	buildDir := absolutePath(ctx.Config().BuildDir())
+	soongOutDir := absolutePath(ctx.Config().SoongOutDir())
 	binary := absolutePath(os.Args[0])
-	primaryBuilder, err := filepath.Rel(buildDir, binary)
+	primaryBuilder, err := filepath.Rel(soongOutDir, binary)
 	if err != nil {
 		ctx.Errorf("path to primary builder %q is not in build dir %q (%q)",
-			os.Args[0], ctx.Config().BuildDir(), err)
+			os.Args[0], ctx.Config().SoongOutDir(), err)
 	}
 
 	return PathForOutput(ctx, primaryBuilder)
diff --git a/apex/apex.go b/apex/apex.go
index e1fca67..6bafcae 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -117,9 +117,6 @@
 	// List of platform_compat_config files that are embedded inside this APEX bundle.
 	Compat_configs []string
 
-	// List of BPF programs inside this APEX bundle.
-	Bpfs []string
-
 	// List of filesystem images that are embedded inside this APEX bundle.
 	Filesystems []string
 
@@ -297,6 +294,9 @@
 	// List of runtime resource overlays (RROs) that are embedded inside this APEX.
 	Rros []string
 
+	// List of BPF programs inside this APEX bundle.
+	Bpfs []string
+
 	// Names of modules to be overridden. Listed modules can only be other binaries (in Make or
 	// Soong). This does not completely prevent installation of the overridden binaries, but if
 	// both binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will
@@ -780,7 +780,6 @@
 	ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments...)
 	ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.Systemserverclasspath_fragments...)
 	ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
-	ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...)
 	ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
 	ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
 
@@ -813,6 +812,7 @@
 
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
 	ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps...)
+	ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.overridableProperties.Bpfs...)
 	ctx.AddFarVariationDependencies(commonVariation, rroTag, a.overridableProperties.Rros...)
 
 	// Dependencies for signing
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 600f6c2..e0bf5e5 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4930,7 +4930,7 @@
 			}
 		}
 		if !foundLibfooJar {
-			t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().BuildDir(), s.AllOutputs()))
+			t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().SoongOutDir(), s.AllOutputs()))
 		}
 	}
 
@@ -6073,6 +6073,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			apps: ["app"],
+			bpfs: ["bpf"],
 			overrides: ["oldapex"],
 			updatable: false,
 		}
@@ -6081,6 +6082,7 @@
 			name: "override_myapex",
 			base: "myapex",
 			apps: ["override_app"],
+			bpfs: ["override_bpf"],
 			overrides: ["unknownapex"],
 			logging_parent: "com.foo.bar",
 			package_name: "test.overridden.package",
@@ -6119,6 +6121,16 @@
 			base: "app",
 			package_name: "bar",
 		}
+
+		bpf {
+			name: "bpf",
+			srcs: ["bpf.c"],
+		}
+
+		bpf {
+			name: "override_bpf",
+			srcs: ["override_bpf.c"],
+		}
 	`, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"}))
 
 	originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(android.OverridableModule)
@@ -6137,6 +6149,9 @@
 	ensureNotContains(t, copyCmds, "image.apex/app/app/app.apk")
 	ensureContains(t, copyCmds, "image.apex/app/override_app/override_app.apk")
 
+	ensureNotContains(t, copyCmds, "image.apex/etc/bpf/bpf.o")
+	ensureContains(t, copyCmds, "image.apex/etc/bpf/override_bpf.o")
+
 	apexBundle := module.Module().(*apexBundle)
 	name := apexBundle.Name()
 	if name != "override_myapex" {
@@ -6159,10 +6174,12 @@
 	data.Custom(&builder, name, "TARGET_", "", data)
 	androidMk := builder.String()
 	ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
+	ensureContains(t, androidMk, "LOCAL_MODULE := override_bpf.o.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex")
 	ensureContains(t, androidMk, "LOCAL_OVERRIDES_MODULES := unknownapex myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex")
+	ensureNotContains(t, androidMk, "LOCAL_MODULE := bpf.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_app.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex")
diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go
index cf46322..6f6fc11 100644
--- a/bp2build/python_binary_conversion_test.go
+++ b/bp2build/python_binary_conversion_test.go
@@ -3,11 +3,19 @@
 import (
 	"testing"
 
+	"android/soong/android"
 	"android/soong/python"
 )
 
+func runBp2BuildTestCaseWithLibs(t *testing.T, tc bp2buildTestCase) {
+	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("python_library", python.PythonLibraryFactory)
+		ctx.RegisterModuleType("python_library_host", python.PythonLibraryHostFactory)
+	}, tc)
+}
+
 func TestPythonBinaryHostSimple(t *testing.T) {
-	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
+	runBp2BuildTestCaseWithLibs(t, bp2buildTestCase{
 		description:                        "simple python_binary_host converts to a native py_binary",
 		moduleTypeUnderTest:                "python_binary_host",
 		moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
@@ -25,12 +33,18 @@
     srcs: ["**/*.py"],
     exclude_srcs: ["b/e.py"],
     data: ["files/data.txt",],
+    libs: ["bar"],
     bazel_module: { bp2build_available: true },
 }
-`,
+    python_library_host {
+      name: "bar",
+      srcs: ["b/e.py"],
+      bazel_module: { bp2build_available: true },
+    }`,
 		expectedBazelTargets: []string{`py_binary(
     name = "foo",
     data = ["files/data.txt"],
+    deps = [":bar"],
     main = "a.py",
     srcs = [
         "a.py",
diff --git a/bp2build/python_library_conversion_test.go b/bp2build/python_library_conversion_test.go
index e337965..b6f45e5 100644
--- a/bp2build/python_library_conversion_test.go
+++ b/bp2build/python_library_conversion_test.go
@@ -1,17 +1,40 @@
 package bp2build
 
 import (
+	"fmt"
 	"testing"
 
+	"android/soong/android"
 	"android/soong/python"
 )
 
-func TestPythonLibrarySimple(t *testing.T) {
-	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
-		description:                        "simple python_library converts to a native py_library",
-		moduleTypeUnderTest:                "python_library",
-		moduleTypeUnderTestFactory:         python.PythonLibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: python.PythonLibraryBp2Build,
+// TODO(alexmarquez): Should be lifted into a generic Bp2Build file
+type PythonLibBp2Build func(ctx android.TopDownMutatorContext)
+
+func TestPythonLibrary(t *testing.T) {
+	testPythonLib(t, "python_library",
+		python.PythonLibraryFactory, python.PythonLibraryBp2Build,
+		func(ctx android.RegistrationContext) {})
+}
+
+func TestPythonLibraryHost(t *testing.T) {
+	testPythonLib(t, "python_library_host",
+		python.PythonLibraryHostFactory, python.PythonLibraryHostBp2Build,
+		func(ctx android.RegistrationContext) {
+			ctx.RegisterModuleType("python_library", python.PythonLibraryFactory)
+		})
+}
+
+func testPythonLib(t *testing.T, modType string,
+	factory android.ModuleFactory, mutator PythonLibBp2Build,
+	registration func(ctx android.RegistrationContext)) {
+	t.Helper()
+	// Simple
+	runBp2BuildTestCase(t, registration, bp2buildTestCase{
+		description:                        fmt.Sprintf("simple %s converts to a native py_library", modType),
+		moduleTypeUnderTest:                modType,
+		moduleTypeUnderTestFactory:         factory,
+		moduleTypeUnderTestBp2BuildMutator: mutator,
 		filesystem: map[string]string{
 			"a.py":           "",
 			"b/c.py":         "",
@@ -19,17 +42,23 @@
 			"b/e.py":         "",
 			"files/data.txt": "",
 		},
-		blueprint: `python_library {
+		blueprint: fmt.Sprintf(`%s {
     name: "foo",
     srcs: ["**/*.py"],
     exclude_srcs: ["b/e.py"],
     data: ["files/data.txt",],
+    libs: ["bar"],
     bazel_module: { bp2build_available: true },
 }
-`,
+    python_library {
+      name: "bar",
+      srcs: ["b/e.py"],
+      bazel_module: { bp2build_available: false },
+    }`, modType),
 		expectedBazelTargets: []string{`py_library(
     name = "foo",
     data = ["files/data.txt"],
+    deps = [":bar"],
     srcs = [
         "a.py",
         "b/c.py",
@@ -39,15 +68,14 @@
 )`,
 		},
 	})
-}
 
-func TestPythonLibraryPy2(t *testing.T) {
+	// PY2
 	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
-		description:                        "py2 python_library",
-		moduleTypeUnderTest:                "python_library",
-		moduleTypeUnderTestFactory:         python.PythonLibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: python.PythonLibraryBp2Build,
-		blueprint: `python_library {
+		description:                        fmt.Sprintf("py2 %s converts to a native py_library", modType),
+		moduleTypeUnderTest:                modType,
+		moduleTypeUnderTestFactory:         factory,
+		moduleTypeUnderTestBp2BuildMutator: mutator,
+		blueprint: fmt.Sprintf(`%s {
     name: "foo",
     srcs: ["a.py"],
     version: {
@@ -60,8 +88,7 @@
     },
 
     bazel_module: { bp2build_available: true },
-}
-`,
+}`, modType),
 		expectedBazelTargets: []string{`py_library(
     name = "foo",
     srcs = ["a.py"],
@@ -69,15 +96,14 @@
 )`,
 		},
 	})
-}
 
-func TestPythonLibraryPy3(t *testing.T) {
+	// PY3
 	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
-		description:                        "py3 python_library",
-		moduleTypeUnderTest:                "python_library",
-		moduleTypeUnderTestFactory:         python.PythonLibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: python.PythonLibraryBp2Build,
-		blueprint: `python_library {
+		description:                        fmt.Sprintf("py3 %s converts to a native py_library", modType),
+		moduleTypeUnderTest:                modType,
+		moduleTypeUnderTestFactory:         factory,
+		moduleTypeUnderTestBp2BuildMutator: mutator,
+		blueprint: fmt.Sprintf(`%s {
     name: "foo",
     srcs: ["a.py"],
     version: {
@@ -90,25 +116,22 @@
     },
 
     bazel_module: { bp2build_available: true },
-}
-`,
-		expectedBazelTargets: []string{
-			`py_library(
+}`, modType),
+		expectedBazelTargets: []string{`py_library(
     name = "foo",
     srcs = ["a.py"],
     srcs_version = "PY3",
 )`,
 		},
 	})
-}
 
-func TestPythonLibraryPyBoth(t *testing.T) {
+	// Both
 	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
-		description:                        "py3 python_library",
-		moduleTypeUnderTest:                "python_library",
-		moduleTypeUnderTestFactory:         python.PythonLibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: python.PythonLibraryBp2Build,
-		blueprint: `python_library {
+		description:                        fmt.Sprintf("py2&3 %s converts to a native py_library", modType),
+		moduleTypeUnderTest:                modType,
+		moduleTypeUnderTestFactory:         factory,
+		moduleTypeUnderTestBp2BuildMutator: mutator,
+		blueprint: fmt.Sprintf(`%s {
     name: "foo",
     srcs: ["a.py"],
     version: {
@@ -121,8 +144,7 @@
     },
 
     bazel_module: { bp2build_available: true },
-}
-`,
+}`, modType),
 		expectedBazelTargets: []string{
 			// srcs_version is PY2ANDPY3 by default.
 			`py_library(
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 3a77d0e..a549a93 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -101,7 +101,8 @@
 	codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
 	bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
 	if actualCount, expectedCount := len(bazelTargets), len(tc.expectedBazelTargets); actualCount != expectedCount {
-		t.Errorf("%s: Expected %d bazel target, got %d", tc.description, expectedCount, actualCount)
+		t.Errorf("%s: Expected %d bazel target, got %d; %v",
+			tc.description, expectedCount, actualCount, bazelTargets)
 	} else {
 		for i, target := range bazelTargets {
 			if w, g := tc.expectedBazelTargets[i], target.content; w != g {
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/linker.go b/cc/linker.go
index 7c710d7..8671dec 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -95,6 +95,9 @@
 	// vendor variants and this module uses VNDK.
 	Runtime_libs []string `android:"arch_variant"`
 
+	// list of runtime libs that should not be installed along with this module.
+	Exclude_runtime_libs []string `android:"arch_variant"`
+
 	Target struct {
 		Vendor, Product struct {
 			// list of shared libs that only should be used to build vendor or
@@ -121,8 +124,8 @@
 			// product variant of the C/C++ module.
 			Exclude_header_libs []string
 
-			// list of runtime libs that should not be installed along with
-			// vendor or variant of the C/C++ module.
+			// list of runtime libs that should not be installed along with the
+			// vendor or product variant of the C/C++ module.
 			Exclude_runtime_libs []string
 
 			// version script for vendor or product variant
@@ -148,6 +151,10 @@
 			// list of header libs that should not be used to build the recovery variant
 			// of the C/C++ module.
 			Exclude_header_libs []string
+
+			// list of runtime libs that should not be installed along with the
+			// recovery variant of the C/C++ module.
+			Exclude_runtime_libs []string
 		}
 		Ramdisk struct {
 			// list of static libs that only should be used to build the recovery
@@ -161,6 +168,10 @@
 			// list of static libs that should not be used to build
 			// the ramdisk variant of the C/C++ module.
 			Exclude_static_libs []string
+
+			// list of runtime libs that should not be installed along with the
+			// ramdisk variant of the C/C++ module.
+			Exclude_runtime_libs []string
 		}
 		Vendor_ramdisk struct {
 			// list of shared libs that should not be used to build
@@ -170,6 +181,10 @@
 			// list of static libs that should not be used to build
 			// the vendor ramdisk variant of the C/C++ module.
 			Exclude_static_libs []string
+
+			// list of runtime libs that should not be installed along with the
+			// vendor ramdisk variant of the C/C++ module.
+			Exclude_runtime_libs []string
 		}
 		Platform struct {
 			// list of shared libs that should be use to build the platform variant
@@ -191,7 +206,7 @@
 			// the C/C++ module.
 			Exclude_shared_libs []string
 
-			// list of static libs that should not be used to build the apex ramdisk
+			// list of static libs that should not be used to build the apex
 			// variant of the C/C++ module.
 			Exclude_static_libs []string
 		}
@@ -275,6 +290,7 @@
 	deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Exclude_shared_libs)
 	deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Exclude_static_libs)
 	deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Exclude_static_libs)
+	deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Exclude_runtime_libs)
 
 	// Record the libraries that need to be excluded when building for APEX. Unlike other
 	// target.*.exclude_* properties, SharedLibs and StaticLibs are not modified here because
@@ -325,6 +341,7 @@
 		deps.ReexportHeaderLibHeaders = removeListFromList(deps.ReexportHeaderLibHeaders, linker.Properties.Target.Recovery.Exclude_header_libs)
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Recovery.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
+		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Recovery.Exclude_runtime_libs)
 	}
 
 	if ctx.inRamdisk() {
@@ -334,6 +351,7 @@
 		deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Ramdisk.Exclude_static_libs)
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Ramdisk.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Ramdisk.Exclude_static_libs)
+		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Ramdisk.Exclude_runtime_libs)
 	}
 
 	if ctx.inVendorRamdisk() {
@@ -342,6 +360,7 @@
 		deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs)
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs)
+		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_runtime_libs)
 	}
 
 	if !ctx.useSdk() {
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 8aea037..beda184 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
@@ -62,6 +64,7 @@
 	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(&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")
@@ -71,7 +74,6 @@
 	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")
@@ -146,14 +148,10 @@
 	ninjaDeps := bootstrap.RunBlueprint(secondArgs, secondCtx.Context, secondConfig)
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
-	globListFiles := writeBuildGlobsNinjaFile(secondCtx.SrcDir(), configuration.BuildDir(), secondCtx.Globs, configuration)
+	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.
@@ -177,7 +175,7 @@
 }
 
 func writeMetrics(configuration android.Config) {
-	metricsFile := filepath.Join(configuration.BuildDir(), "soong_build_metrics.pb")
+	metricsFile := filepath.Join(configuration.SoongOutDir(), "soong_build_metrics.pb")
 	err := android.WriteMetrics(configuration, metricsFile)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "error writing soong_build metrics %s: %s", metricsFile, err)
@@ -185,8 +183,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 +192,6 @@
 
 	defer f.Close()
 	ctx.Context.PrintJSONGraph(f)
-	writeFakeNinjaFile(extraNinjaDeps, configuration.BuildDir())
 }
 
 func writeBuildGlobsNinjaFile(srcDir, buildDir string, globs func() pathtools.MultipleGlobResults, config interface{}) []string {
@@ -208,6 +205,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 +221,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.
@@ -233,27 +238,24 @@
 		ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, ctx.Context, configuration)
 		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
-		globListFiles := writeBuildGlobsNinjaFile(ctx.SrcDir(), configuration.BuildDir(), ctx.Globs, configuration)
+		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
 }
@@ -307,7 +309,7 @@
 	if shared.IsDebugging() {
 		// Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is
 		// enabled even if it completed successfully.
-		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve"))
+		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.SoongOutDir(), "always_rerun_for_delve"))
 	}
 
 	if docFile != "" {
@@ -348,29 +350,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, buildDir string) {
-	extraNinjaDepsString := strings.Join(extraNinjaDeps, " \\\n ")
-
-	ninjaFileName := "build.ninja"
-	ninjaFile := shared.JoinPath(topDir, buildDir, ninjaFileName)
-	ninjaFileD := shared.JoinPath(topDir, buildDir, 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 {
@@ -505,7 +484,7 @@
 	ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bp2buildCtx.Context, configuration)
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
-	globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx.SrcDir(), configuration.BuildDir(), bp2buildCtx.Globs, configuration)
+	globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx.SrcDir(), configuration.SoongOutDir(), bp2buildCtx.Globs, configuration)
 	ninjaDeps = append(ninjaDeps, globListFiles...)
 
 	// Run the code-generation phase to convert BazelTargetModules to BUILD files
@@ -513,8 +492,8 @@
 	codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx, bp2build.Bp2Build)
 	metrics := bp2build.Codegen(codegenContext)
 
-	generatedRoot := shared.JoinPath(configuration.BuildDir(), "bp2build")
-	workspaceRoot := shared.JoinPath(configuration.BuildDir(), "workspace")
+	generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
+	workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
 
 	excludes := []string{
 		"bazel-bin",
@@ -550,12 +529,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/dexpreopt/config.go b/dexpreopt/config.go
index 7a74506..de3666a 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -159,7 +159,7 @@
 }
 
 func constructPath(ctx android.PathContext, path string) android.Path {
-	buildDirPrefix := ctx.Config().BuildDir() + "/"
+	buildDirPrefix := ctx.Config().SoongOutDir() + "/"
 	if path == "" {
 		return nil
 	} else if strings.HasPrefix(path, buildDirPrefix) {
diff --git a/java/app_test.go b/java/app_test.go
index 56ad28d..07439fc 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1737,7 +1737,7 @@
 
 			foo := result.ModuleForTests("foo", "android_common")
 
-			outSoongDir := result.Config.BuildDir()
+			outSoongDir := result.Config.SoongOutDir()
 
 			outputs := foo.AllOutputs()
 			outputMap := make(map[string]bool)
diff --git a/java/base.go b/java/base.go
index 8e6d1cd..86022c3 100644
--- a/java/base.go
+++ b/java/base.go
@@ -791,7 +791,7 @@
 			// Manually specify build directory in case it is not under the repo root.
 			// (javac doesn't seem to expand into symbolic links when searching for patch-module targets, so
 			// just adding a symlink under the root doesn't help.)
-			patchPaths := []string{".", ctx.Config().BuildDir()}
+			patchPaths := []string{".", ctx.Config().SoongOutDir()}
 
 			// b/150878007
 			//
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/java_test.go b/java/java_test.go
index b6780c2..8bb017f 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1183,7 +1183,7 @@
 			break
 		}
 	}
-	if expected != android.StringPathRelativeToTop(ctx.Config().BuildDir(), got) {
+	if expected != android.StringPathRelativeToTop(ctx.Config().SoongOutDir(), got) {
 		t.Errorf("Unexpected patch-module flag for module %q - expected %q, but got %q", moduleName, expected, got)
 	}
 }
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/python/binary.go b/python/binary.go
index b106536..bc2768c 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -38,6 +38,7 @@
 	Main           string
 	Srcs           bazel.LabelListAttribute
 	Data           bazel.LabelListAttribute
+	Deps           bazel.LabelListAttribute
 	Python_version string
 }
 
@@ -81,11 +82,13 @@
 
 	srcs := android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
 	data := android.BazelLabelForModuleSrc(ctx, m.properties.Data)
+	deps := android.BazelLabelForModuleDeps(ctx, m.properties.Libs)
 
 	attrs := &bazelPythonBinaryAttributes{
 		Main:           main,
 		Srcs:           bazel.MakeLabelListAttribute(srcs),
 		Data:           bazel.MakeLabelListAttribute(data),
+		Deps:           bazel.MakeLabelListAttribute(deps),
 		Python_version: python_version,
 	}
 
diff --git a/python/library.go b/python/library.go
index c255190..a132216 100644
--- a/python/library.go
+++ b/python/library.go
@@ -26,6 +26,7 @@
 
 func init() {
 	registerPythonLibraryComponents(android.InitRegistrationContext)
+	android.RegisterBp2BuildMutator("python_library_host", PythonLibraryHostBp2Build)
 	android.RegisterBp2BuildMutator("python_library", PythonLibraryBp2Build)
 }
 
@@ -37,23 +38,34 @@
 func PythonLibraryHostFactory() android.Module {
 	module := newModule(android.HostSupported, android.MultilibFirst)
 
+	android.InitBazelModule(module)
+
 	return module.init()
 }
 
 type bazelPythonLibraryAttributes struct {
 	Srcs         bazel.LabelListAttribute
 	Data         bazel.LabelListAttribute
+	Deps         bazel.LabelListAttribute
 	Srcs_version string
 }
 
+func PythonLibraryHostBp2Build(ctx android.TopDownMutatorContext) {
+	pythonLibBp2Build(ctx, "python_library_host")
+}
+
 func PythonLibraryBp2Build(ctx android.TopDownMutatorContext) {
+	pythonLibBp2Build(ctx, "python_library")
+}
+
+func pythonLibBp2Build(ctx android.TopDownMutatorContext, modType string) {
 	m, ok := ctx.Module().(*Module)
 	if !ok || !m.ConvertWithBp2build(ctx) {
 		return
 	}
 
-	// a Module can be something other than a python_library
-	if ctx.ModuleType() != "python_library" {
+	// a Module can be something other than a `modType`
+	if ctx.ModuleType() != modType {
 		return
 	}
 
@@ -70,18 +82,20 @@
 		python_version = "PY3"
 	} else if !py2Enabled && !py3Enabled {
 		panic(fmt.Errorf(
-			"error for '%s' module: bp2build's python_library converter doesn't understand having "+
-				"neither py2 nor py3 enabled", m.Name()))
+			"error for '%s' module: bp2build's %s converter doesn't understand having "+
+				"neither py2 nor py3 enabled", m.Name(), modType))
 	} else {
 		// do nothing, since python_version defaults to PY2ANDPY3
 	}
 
 	srcs := android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
 	data := android.BazelLabelForModuleSrc(ctx, m.properties.Data)
+	deps := android.BazelLabelForModuleDeps(ctx, m.properties.Libs)
 
 	attrs := &bazelPythonLibraryAttributes{
 		Srcs:         bazel.MakeLabelListAttribute(srcs),
 		Data:         bazel.MakeLabelListAttribute(data),
+		Deps:         bazel.MakeLabelListAttribute(deps),
 		Srcs_version: python_version,
 	}
 
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index ee9be6e..63a8f04 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -13,6 +13,7 @@
 		"external/minijail",
 		"external/rust",
 		"external/selinux/libselinux",
+		"external/uwb",
 		"external/vm_tools/p9",
 		"frameworks/native/libs/binder/rust",
 		"frameworks/proto_logging/stats",
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index bdd54c5..f7b6681 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -36,7 +36,7 @@
 	// The JSON file is generated via WriteFileToOutputDir. Therefore, it
 	// won't appear in the Output of the TestingSingleton. Manually verify
 	// it exists.
-	content, err := ioutil.ReadFile(filepath.Join(result.Config.BuildDir(), rustProjectJsonFileName))
+	content, err := ioutil.ReadFile(filepath.Join(result.Config.SoongOutDir(), rustProjectJsonFileName))
 	if err != nil {
 		t.Errorf("rust-project.json has not been generated")
 	}
diff --git a/scripts/construct_context.py b/scripts/construct_context.py
index f0658ba..3f601c3 100755
--- a/scripts/construct_context.py
+++ b/scripts/construct_context.py
@@ -25,57 +25,78 @@
 
 
 def parse_args(args):
-  """Parse commandline arguments."""
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--target-sdk-version', default='', dest='sdk',
-    help='specify target SDK version (as it appears in the manifest)')
-  parser.add_argument('--host-context-for-sdk', dest='host_contexts',
-    action='append', nargs=2, metavar=('sdk','context'),
-    help='specify context on host for a given SDK version or "any" version')
-  parser.add_argument('--target-context-for-sdk', dest='target_contexts',
-    action='append', nargs=2, metavar=('sdk','context'),
-    help='specify context on target for a given SDK version or "any" version')
-  return parser.parse_args(args)
+    """Parse commandline arguments."""
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        '--target-sdk-version',
+        default='',
+        dest='sdk',
+        help='specify target SDK version (as it appears in the manifest)')
+    parser.add_argument(
+        '--host-context-for-sdk',
+        dest='host_contexts',
+        action='append',
+        nargs=2,
+        metavar=('sdk', 'context'),
+        help='specify context on host for a given SDK version or "any" version')
+    parser.add_argument(
+        '--target-context-for-sdk',
+        dest='target_contexts',
+        action='append',
+        nargs=2,
+        metavar=('sdk', 'context'),
+        help='specify context on target for a given SDK version or "any" '
+        'version'
+    )
+    return parser.parse_args(args)
+
 
 # Special keyword that means that the context should be added to class loader
 # context regardless of the target SDK version.
 any_sdk = 'any'
 
+
 # We assume that the order of context arguments passed to this script is
 # correct (matches the order computed by package manager). It is possible to
 # sort them here, but Soong needs to use deterministic order anyway, so it can
 # as well use the correct order.
 def construct_context(versioned_contexts, target_sdk):
-  context = []
-  for [sdk, ctx] in versioned_contexts:
-    if sdk == any_sdk or compare_version_gt(sdk, target_sdk):
-      context.append(ctx)
-  return context
+    context = []
+    for [sdk, ctx] in versioned_contexts:
+        if sdk == any_sdk or compare_version_gt(sdk, target_sdk):
+            context.append(ctx)
+    return context
+
 
 def construct_contexts(args):
-  host_context = construct_context(args.host_contexts, args.sdk)
-  target_context = construct_context(args.target_contexts, args.sdk)
-  context_sep = '#'
-  return ('class_loader_context_arg=--class-loader-context=PCL[]{%s} ; ' % context_sep.join(host_context) +
-    'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{%s}' % context_sep.join(target_context))
+    host_context = construct_context(args.host_contexts, args.sdk)
+    target_context = construct_context(args.target_contexts, args.sdk)
+    context_sep = '#'
+    return (
+        'class_loader_context_arg=--class-loader-context=PCL[]{%s} ; ' %
+        context_sep.join(host_context) +
+        'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{%s}' #pylint: disable=line-too-long
+        % context_sep.join(target_context))
+
 
 def main():
-  """Program entry point."""
-  try:
-    args = parse_args(sys.argv[1:])
-    if not args.sdk:
-      raise SystemExit('target sdk version is not set')
-    if not args.host_contexts:
-      args.host_contexts = []
-    if not args.target_contexts:
-      args.target_contexts = []
+    """Program entry point."""
+    try:
+        args = parse_args(sys.argv[1:])
+        if not args.sdk:
+            raise SystemExit('target sdk version is not set')
+        if not args.host_contexts:
+            args.host_contexts = []
+        if not args.target_contexts:
+            args.target_contexts = []
 
-    print(construct_contexts(args))
+        print(construct_contexts(args))
 
-  # pylint: disable=broad-except
-  except Exception as err:
-    print('error: ' + str(err), file=sys.stderr)
-    sys.exit(-1)
+    # pylint: disable=broad-except
+    except Exception as err:
+        print('error: ' + str(err), file=sys.stderr)
+        sys.exit(-1)
+
 
 if __name__ == '__main__':
-  main()
+    main()
diff --git a/scripts/construct_context_test.py b/scripts/construct_context_test.py
index 3b05f90..2ff5ac5 100755
--- a/scripts/construct_context_test.py
+++ b/scripts/construct_context_test.py
@@ -23,53 +23,63 @@
 
 sys.dont_write_bytecode = True
 
+
 def construct_contexts(arglist):
-  args = cc.parse_args(arglist)
-  return cc.construct_contexts(args)
+    args = cc.parse_args(arglist)
+    return cc.construct_contexts(args)
+
 
 contexts = [
-  '--host-context-for-sdk', '28', 'PCL[out/zdir/z.jar]',
-  '--target-context-for-sdk', '28', 'PCL[/system/z.jar]',
-  '--host-context-for-sdk', '29', 'PCL[out/xdir/x.jar]#PCL[out/ydir/y.jar]',
-  '--target-context-for-sdk', '29', 'PCL[/system/x.jar]#PCL[/product/y.jar]',
-  '--host-context-for-sdk', 'any', 'PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]',
-  '--target-context-for-sdk', 'any', 'PCL[/system/a.jar]#PCL[/product/b.jar]',
+    '--host-context-for-sdk',
+    '28',
+    'PCL[out/zdir/z.jar]',
+    '--target-context-for-sdk',
+    '28',
+    'PCL[/system/z.jar]',
+    '--host-context-for-sdk',
+    '29',
+    'PCL[out/xdir/x.jar]#PCL[out/ydir/y.jar]',
+    '--target-context-for-sdk',
+    '29',
+    'PCL[/system/x.jar]#PCL[/product/y.jar]',
+    '--host-context-for-sdk',
+    'any',
+    'PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]',
+    '--target-context-for-sdk',
+    'any',
+    'PCL[/system/a.jar]#PCL[/product/b.jar]',
 ]
 
+#pylint: disable=line-too-long
 class ConstructContextTest(unittest.TestCase):
-  def test_construct_context_28(self):
-    args = ['--target-sdk-version', '28'] + contexts
-    result = construct_contexts(args)
-    expect = ('class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/xdir/x.jar]'
-      '#PCL[out/ydir/y.jar]'
-      '#PCL[out/adir/a.jar]'
-      '#PCL[out/bdir/b.jar]}'
-      ' ; '
-      'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/x.jar]'
-      '#PCL[/product/y.jar]'
-      '#PCL[/system/a.jar]'
-      '#PCL[/product/b.jar]}')
-    self.assertEqual(result, expect)
 
-  def test_construct_context_29(self):
-    args = ['--target-sdk-version', '29'] + contexts
-    result = construct_contexts(args)
-    expect = ('class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]'
-      '#PCL[out/bdir/b.jar]}'
-      ' ; '
-      'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]'
-      '#PCL[/product/b.jar]}')
-    self.assertEqual(result, expect)
+    def test_construct_context_28(self):
+        args = ['--target-sdk-version', '28'] + contexts
+        result = construct_contexts(args)
+        expect = (
+            'class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/xdir/x.jar]#PCL[out/ydir/y.jar]#PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]}'
+            ' ; '
+            'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/x.jar]#PCL[/product/y.jar]#PCL[/system/a.jar]#PCL[/product/b.jar]}')
+        self.assertEqual(result, expect)
 
-  def test_construct_context_S(self):
-    args = ['--target-sdk-version', 'S'] + contexts
-    result = construct_contexts(args)
-    expect = ('class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]'
-      '#PCL[out/bdir/b.jar]}'
-      ' ; '
-      'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]'
-      '#PCL[/product/b.jar]}')
-    self.assertEqual(result, expect)
+    def test_construct_context_29(self):
+        args = ['--target-sdk-version', '29'] + contexts
+        result = construct_contexts(args)
+        expect = (
+            'class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]}'
+            ' ; '
+            'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]#PCL[/product/b.jar]}')
+        self.assertEqual(result, expect)
+
+    def test_construct_context_S(self):
+        args = ['--target-sdk-version', 'S'] + contexts
+        result = construct_contexts(args)
+        expect = (
+            'class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]}'
+            ' ; '
+            'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]#PCL[/product/b.jar]}')
+        self.assertEqual(result, expect)
+#pylint: enable=line-too-long
 
 if __name__ == '__main__':
-  unittest.main(verbosity=2)
+    unittest.main(verbosity=2)
diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py
index 92f79da..e46efe4 100644
--- a/scripts/conv_linker_config.py
+++ b/scripts/conv_linker_config.py
@@ -20,178 +20,181 @@
 import json
 import os
 
-import linker_config_pb2
+import linker_config_pb2 #pylint: disable=import-error
 from google.protobuf.descriptor import FieldDescriptor
 from google.protobuf.json_format import ParseDict
 from google.protobuf.text_format import MessageToString
 
 
 def Proto(args):
-  json_content = ''
-  with open(args.source) as f:
-    for line in f:
-      if not line.lstrip().startswith('//'):
-        json_content += line
-  obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
-  pb = ParseDict(obj, linker_config_pb2.LinkerConfig())
-  with open(args.output, 'wb') as f:
-    f.write(pb.SerializeToString())
+    json_content = ''
+    with open(args.source) as f:
+        for line in f:
+            if not line.lstrip().startswith('//'):
+                json_content += line
+    obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
+    pb = ParseDict(obj, linker_config_pb2.LinkerConfig())
+    with open(args.output, 'wb') as f:
+        f.write(pb.SerializeToString())
 
 
 def Print(args):
-  with open(args.source, 'rb') as f:
-    pb = linker_config_pb2.LinkerConfig()
-    pb.ParseFromString(f.read())
-  print(MessageToString(pb))
+    with open(args.source, 'rb') as f:
+        pb = linker_config_pb2.LinkerConfig()
+        pb.ParseFromString(f.read())
+    print(MessageToString(pb))
 
 
 def SystemProvide(args):
-  pb = linker_config_pb2.LinkerConfig()
-  with open(args.source, 'rb') as f:
-    pb.ParseFromString(f.read())
-  libraries = args.value.split()
+    pb = linker_config_pb2.LinkerConfig()
+    with open(args.source, 'rb') as f:
+        pb.ParseFromString(f.read())
+    libraries = args.value.split()
 
-  def IsInLibPath(lib_name):
-    lib_path = os.path.join(args.system, 'lib', lib_name)
-    lib64_path = os.path.join(args.system, 'lib64', lib_name)
-    return os.path.exists(lib_path) or os.path.islink(lib_path) or os.path.exists(lib64_path) or os.path.islink(lib64_path)
+    def IsInLibPath(lib_name):
+        lib_path = os.path.join(args.system, 'lib', lib_name)
+        lib64_path = os.path.join(args.system, 'lib64', lib_name)
+        return os.path.exists(lib_path) or os.path.islink(
+            lib_path) or os.path.exists(lib64_path) or os.path.islink(
+                lib64_path)
 
-  installed_libraries = list(filter(IsInLibPath, libraries))
-  for item in installed_libraries:
-    if item not in getattr(pb, 'provideLibs'):
-      getattr(pb, 'provideLibs').append(item)
-  with open(args.output, 'wb') as f:
-    f.write(pb.SerializeToString())
+    installed_libraries = [lib for lib in libraries if IsInLibPath(lib)]
+    for item in installed_libraries:
+        if item not in getattr(pb, 'provideLibs'):
+            getattr(pb, 'provideLibs').append(item)
+    with open(args.output, 'wb') as f:
+        f.write(pb.SerializeToString())
 
 
 def Append(args):
-  pb = linker_config_pb2.LinkerConfig()
-  with open(args.source, 'rb') as f:
-    pb.ParseFromString(f.read())
+    pb = linker_config_pb2.LinkerConfig()
+    with open(args.source, 'rb') as f:
+        pb.ParseFromString(f.read())
 
-  if getattr(type(pb), args.key).DESCRIPTOR.label == FieldDescriptor.LABEL_REPEATED:
-    for value in args.value.split():
-      getattr(pb, args.key).append(value)
-  else:
-    setattr(pb, args.key, args.value)
+    if getattr(type(pb),
+               args.key).DESCRIPTOR.label == FieldDescriptor.LABEL_REPEATED:
+        for value in args.value.split():
+            getattr(pb, args.key).append(value)
+    else:
+        setattr(pb, args.key, args.value)
 
-  with open(args.output, 'wb') as f:
-    f.write(pb.SerializeToString())
+    with open(args.output, 'wb') as f:
+        f.write(pb.SerializeToString())
+
 
 def Merge(args):
-  pb = linker_config_pb2.LinkerConfig()
-  for other in args.input:
-    with open(other, 'rb') as f:
-      pb.MergeFromString(f.read())
+    pb = linker_config_pb2.LinkerConfig()
+    for other in args.input:
+        with open(other, 'rb') as f:
+            pb.MergeFromString(f.read())
 
-  with open(args.out, 'wb') as f:
-    f.write(pb.SerializeToString())
+    with open(args.out, 'wb') as f:
+        f.write(pb.SerializeToString())
+
 
 def GetArgParser():
-  parser = argparse.ArgumentParser()
-  subparsers = parser.add_subparsers()
+    parser = argparse.ArgumentParser()
+    subparsers = parser.add_subparsers()
 
-  parser_proto = subparsers.add_parser(
-      'proto', help='Convert the input JSON configuration file into protobuf.')
-  parser_proto.add_argument(
-      '-s',
-      '--source',
-      required=True,
-      type=str,
-      help='Source linker configuration file in JSON.')
-  parser_proto.add_argument(
-      '-o',
-      '--output',
-      required=True,
-      type=str,
-      help='Target path to create protobuf file.')
-  parser_proto.set_defaults(func=Proto)
+    parser_proto = subparsers.add_parser(
+        'proto',
+        help='Convert the input JSON configuration file into protobuf.')
+    parser_proto.add_argument(
+        '-s',
+        '--source',
+        required=True,
+        type=str,
+        help='Source linker configuration file in JSON.')
+    parser_proto.add_argument(
+        '-o',
+        '--output',
+        required=True,
+        type=str,
+        help='Target path to create protobuf file.')
+    parser_proto.set_defaults(func=Proto)
 
-  print_proto = subparsers.add_parser(
-      'print', help='Print configuration in human-readable text format.')
-  print_proto.add_argument(
-      '-s',
-      '--source',
-      required=True,
-      type=str,
-      help='Source linker configuration file in protobuf.')
-  print_proto.set_defaults(func=Print)
+    print_proto = subparsers.add_parser(
+        'print', help='Print configuration in human-readable text format.')
+    print_proto.add_argument(
+        '-s',
+        '--source',
+        required=True,
+        type=str,
+        help='Source linker configuration file in protobuf.')
+    print_proto.set_defaults(func=Print)
 
-  system_provide_libs = subparsers.add_parser(
-      'systemprovide', help='Append system provide libraries into the configuration.')
-  system_provide_libs.add_argument(
-      '-s',
-      '--source',
-      required=True,
-      type=str,
-      help='Source linker configuration file in protobuf.')
-  system_provide_libs.add_argument(
-      '-o',
-      '--output',
-      required=True,
-      type=str,
-      help='Target linker configuration file to write in protobuf.')
-  system_provide_libs.add_argument(
-      '--value',
-      required=True,
-      type=str,
-      help='Values of the libraries to append. If there are more than one it should be separated by empty space')
-  system_provide_libs.add_argument(
-      '--system',
-      required=True,
-      type=str,
-      help='Path of the system image.')
-  system_provide_libs.set_defaults(func=SystemProvide)
+    system_provide_libs = subparsers.add_parser(
+        'systemprovide',
+        help='Append system provide libraries into the configuration.')
+    system_provide_libs.add_argument(
+        '-s',
+        '--source',
+        required=True,
+        type=str,
+        help='Source linker configuration file in protobuf.')
+    system_provide_libs.add_argument(
+        '-o',
+        '--output',
+        required=True,
+        type=str,
+        help='Target linker configuration file to write in protobuf.')
+    system_provide_libs.add_argument(
+        '--value',
+        required=True,
+        type=str,
+        help='Values of the libraries to append. If there are more than one '
+        'it should be separated by empty space'
+    )
+    system_provide_libs.add_argument(
+        '--system', required=True, type=str, help='Path of the system image.')
+    system_provide_libs.set_defaults(func=SystemProvide)
 
-  append = subparsers.add_parser(
-      'append', help='Append value(s) to given key.')
-  append.add_argument(
-      '-s',
-      '--source',
-      required=True,
-      type=str,
-      help='Source linker configuration file in protobuf.')
-  append.add_argument(
-      '-o',
-      '--output',
-      required=True,
-      type=str,
-      help='Target linker configuration file to write in protobuf.')
-  append.add_argument(
-      '--key',
-      required=True,
-      type=str,
-      help='.')
-  append.add_argument(
-      '--value',
-      required=True,
-      type=str,
-      help='Values of the libraries to append. If there are more than one it should be separated by empty space')
-  append.set_defaults(func=Append)
+    append = subparsers.add_parser(
+        'append', help='Append value(s) to given key.')
+    append.add_argument(
+        '-s',
+        '--source',
+        required=True,
+        type=str,
+        help='Source linker configuration file in protobuf.')
+    append.add_argument(
+        '-o',
+        '--output',
+        required=True,
+        type=str,
+        help='Target linker configuration file to write in protobuf.')
+    append.add_argument('--key', required=True, type=str, help='.')
+    append.add_argument(
+        '--value',
+        required=True,
+        type=str,
+        help='Values of the libraries to append. If there are more than one'
+        'it should be separated by empty space'
+    )
+    append.set_defaults(func=Append)
 
-  append = subparsers.add_parser(
-      'merge', help='Merge configurations')
-  append.add_argument(
-      '-o',
-      '--out',
-      required=True,
-      type=str,
-      help='Ouptut linker configuration file to write in protobuf.')
-  append.add_argument(
-      '-i',
-      '--input',
-      nargs='+',
-      type=str,
-      help='Linker configuration files to merge.')
-  append.set_defaults(func=Merge)
+    append = subparsers.add_parser('merge', help='Merge configurations')
+    append.add_argument(
+        '-o',
+        '--out',
+        required=True,
+        type=str,
+        help='Output linker configuration file to write in protobuf.')
+    append.add_argument(
+        '-i',
+        '--input',
+        nargs='+',
+        type=str,
+        help='Linker configuration files to merge.')
+    append.set_defaults(func=Merge)
 
-  return parser
+    return parser
 
 
 def main():
-  args = GetArgParser().parse_args()
-  args.func(args)
+    args = GetArgParser().parse_args()
+    args.func(args)
 
 
 if __name__ == '__main__':
-  main()
+    main()
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 8ef8c74..058f819 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -71,17 +71,17 @@
 // A tiny struct used to tell Blueprint that it's in bootstrap mode. It would
 // probably be nicer to use a flag in bootstrap.Args instead.
 type BlueprintConfig struct {
-	buildDir         string
-	ninjaBuildDir    string
+	soongOutDir      string
+	outDir           string
 	debugCompilation bool
 }
 
-func (c BlueprintConfig) BuildDir() string {
-	return c.buildDir
+func (c BlueprintConfig) SoongOutDir() string {
+	return c.soongOutDir
 }
 
-func (c BlueprintConfig) NinjaBuildDir() string {
-	return c.ninjaBuildDir
+func (c BlueprintConfig) OutDir() string {
+	return c.outDir
 }
 
 func (c BlueprintConfig) DebugCompilation() bool {
@@ -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
@@ -181,16 +182,34 @@
 		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()
 	blueprintCtx.SetIgnoreUnknownModuleTypes(true)
 	blueprintConfig := BlueprintConfig{
-		buildDir:         config.SoongOutDir(),
-		ninjaBuildDir:    config.OutDir(),
+		soongOutDir:      config.SoongOutDir(),
+		outDir:           config.OutDir(),
 		debugCompilation: os.Getenv("SOONG_DELVE") != "",
 	}
 
@@ -307,6 +326,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()