Merge "Remove unused key field from SdkMemberTypesRegistry"
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/androidmk.go b/android/androidmk.go
index f032f1b..9853d2c 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -571,7 +571,7 @@
 
 	if host {
 		makeOs := amod.Os().String()
-		if amod.Os() == Linux || amod.Os() == LinuxBionic {
+		if amod.Os() == Linux || amod.Os() == LinuxBionic || amod.Os() == LinuxMusl {
 			makeOs = "linux"
 		}
 		a.SetString("LOCAL_MODULE_HOST_OS", makeOs)
diff --git a/android/bazel.go b/android/bazel.go
index fa19e52..bebb61b 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
@@ -160,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 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..d3db68f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -66,13 +66,13 @@
 	*config
 }
 
-// BuildDir returns the build output directory for the configuration.
-func (c Config) BuildDir() string {
-	return c.buildDir
+// SoongOutDir returns the build output directory for the configuration.
+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..763cd7c 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())
 	}
 
@@ -1150,8 +1150,8 @@
 type OutputPath struct {
 	basePath
 
-	// The soong build directory, i.e. Config.BuildDir()
-	buildDir string
+	// The soong build directory, i.e. Config.SoongOutDir()
+	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 {
@@ -1544,8 +1544,8 @@
 type InstallPath struct {
 	basePath
 
-	// The soong build directory, i.e. Config.BuildDir()
-	buildDir string
+	// 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.
 	// 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/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/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/androidmk.go b/apex/androidmk.go
index ebf0833..2f10904 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -212,7 +212,7 @@
 			}
 			if host {
 				makeOs := fi.module.Target().Os.String()
-				if fi.module.Target().Os == android.Linux || fi.module.Target().Os == android.LinuxBionic {
+				if fi.module.Target().Os == android.Linux || fi.module.Target().Os == android.LinuxBionic || fi.module.Target().Os == android.LinuxMusl {
 					makeOs = "linux"
 				}
 				fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", makeOs)
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 f07bf63..e0bf5e5 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -1874,6 +1874,45 @@
 	expectNoLink("libx", "shared_apex10000", "libz", "shared")
 }
 
+func TestApexMinSdkVersion_crtobjectInVendorApex(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+			updatable: false,
+			vendor: true,
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			vendor_available: true,
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+			min_sdk_version: "29",
+		}
+	`)
+
+	vendorVariant := "android_vendor.29_arm64_armv8-a"
+
+	// First check that the correct variant of crtbegin_so is used.
+	ldRule := ctx.ModuleForTests("mylib", vendorVariant+"_shared_apex29").Rule("ld")
+	crtBegin := names(ldRule.Args["crtBegin"])
+	ensureListContains(t, crtBegin, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"crtbegin_so/"+vendorVariant+"_apex29/crtbegin_so.o")
+
+	// Ensure that the crtbegin_so used by the APEX is targeting 29
+	cflags := ctx.ModuleForTests("crtbegin_so", vendorVariant+"_apex29").Rule("cc").Args["cFlags"]
+	android.AssertStringDoesContain(t, "cflags", cflags, "-target aarch64-linux-android29")
+}
+
 func TestPlatformUsesLatestStubsFromApexes(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -4891,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()))
 		}
 	}
 
@@ -6034,6 +6073,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			apps: ["app"],
+			bpfs: ["bpf"],
 			overrides: ["oldapex"],
 			updatable: false,
 		}
@@ -6042,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",
@@ -6080,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)
@@ -6098,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" {
@@ -6120,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/Android.bp b/bp2build/Android.bp
index 78e3a74..b1ccc96 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -42,6 +42,7 @@
         "performance_test.go",
         "prebuilt_etc_conversion_test.go",
         "python_binary_conversion_test.go",
+        "python_library_conversion_test.go",
         "sh_conversion_test.go",
         "testing.go",
     ],
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
new file mode 100644
index 0000000..b6f45e5
--- /dev/null
+++ b/bp2build/python_library_conversion_test.go
@@ -0,0 +1,156 @@
+package bp2build
+
+import (
+	"fmt"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/python"
+)
+
+// 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":         "",
+			"b/d.py":         "",
+			"b/e.py":         "",
+			"files/data.txt": "",
+		},
+		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",
+        "b/d.py",
+    ],
+    srcs_version = "PY3",
+)`,
+		},
+	})
+
+	// PY2
+	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
+		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: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    },
+
+    bazel_module: { bp2build_available: true },
+}`, modType),
+		expectedBazelTargets: []string{`py_library(
+    name = "foo",
+    srcs = ["a.py"],
+    srcs_version = "PY2",
+)`,
+		},
+	})
+
+	// PY3
+	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
+		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: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+
+    bazel_module: { bp2build_available: true },
+}`, modType),
+		expectedBazelTargets: []string{`py_library(
+    name = "foo",
+    srcs = ["a.py"],
+    srcs_version = "PY3",
+)`,
+		},
+	})
+
+	// Both
+	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
+		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: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+
+    bazel_module: { bp2build_available: true },
+}`, modType),
+		expectedBazelTargets: []string{
+			// srcs_version is PY2ANDPY3 by default.
+			`py_library(
+    name = "foo",
+    srcs = ["a.py"],
+)`,
+		},
+	})
+}
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/builder.go b/cc/builder.go
index 842ce85..d011414 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -646,7 +646,7 @@
 				OrderOnly: pathDeps,
 				Args: map[string]string{
 					"cFlags":    moduleToolingFlags,
-					"tidyFlags": flags.tidyFlags,
+					"tidyFlags": config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags),
 				},
 			})
 		}
diff --git a/cc/cc.go b/cc/cc.go
index 243f0a5..9c1f559 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -975,16 +975,17 @@
 	return String(c.Properties.Min_sdk_version)
 }
 
-func (c *Module) SplitPerApiLevel() bool {
-	if !c.canUseSdk() {
-		return false
-	}
+func (c *Module) isCrt() bool {
 	if linker, ok := c.linker.(*objectLinker); ok {
 		return linker.isCrt()
 	}
 	return false
 }
 
+func (c *Module) SplitPerApiLevel() bool {
+	return c.canUseSdk() && c.isCrt()
+}
+
 func (c *Module) AlwaysSdk() bool {
 	return c.Properties.AlwaysSdk || Bool(c.Properties.Sdk_variant_only)
 }
@@ -1446,12 +1447,20 @@
 	// create versioned variants for. For example, if min_sdk_version is 16, then sdk variant of
 	// the crt object has local variants of 16, 17, ..., up to the latest version. sdk_version
 	// and min_sdk_version properties of the variants are set to the corresponding version
-	// numbers. However, the platform (non-sdk) variant of the crt object is left untouched.
-	// min_sdk_version: 16 doesn't actually mean that the platform variant has to support such
-	// an old version. Since the variant is for the platform, it's preferred to target the
-	// latest version.
-	if ctx.mod.SplitPerApiLevel() && !ctx.isSdkVariant() {
-		ver = strconv.Itoa(android.FutureApiLevelInt)
+	// numbers. However, the non-sdk variant (for apex or platform) of the crt object is left
+	// untouched.  min_sdk_version: 16 doesn't actually mean that the non-sdk variant has to
+	// support such an old version. The version is set to the later version in case when the
+	// non-sdk variant is for the platform, or the min_sdk_version of the containing APEX if
+	// it's for an APEX.
+	if ctx.mod.isCrt() && !ctx.isSdkVariant() {
+		if ctx.isForPlatform() {
+			ver = strconv.Itoa(android.FutureApiLevelInt)
+		} else { // for apex
+			ver = ctx.apexSdkVersion().String()
+			if ver == "" { // in case when min_sdk_version was not set by the APEX
+				ver = ctx.sdkVersion()
+			}
+		}
 	}
 
 	// Also make sure that minSdkVersion is not greater than sdkVersion, if they are both numbers
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/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/object_test.go b/cc/object_test.go
index 0e5508a..259a892 100644
--- a/cc/object_test.go
+++ b/cc/object_test.go
@@ -15,8 +15,9 @@
 package cc
 
 import (
-	"android/soong/android"
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestMinSdkVersionsOfCrtObjects(t *testing.T) {
@@ -27,24 +28,23 @@
 			crt: true,
 			stl: "none",
 			min_sdk_version: "28",
-
+			vendor_available: true,
 		}`)
 
-	arch := "android_arm64_armv8-a"
-	for _, v := range []string{"", "28", "29", "30", "current"} {
-		var variant string
-		// platform variant
-		if v == "" {
-			variant = arch
-		} else {
-			variant = arch + "_sdk_" + v
-		}
-		cflags := ctx.ModuleForTests("crt_foo", variant).Rule("cc").Args["cFlags"]
-		vNum := v
-		if v == "current" || v == "" {
-			vNum = "10000"
-		}
-		expected := "-target aarch64-linux-android" + vNum + " "
+	variants := []struct {
+		variant string
+		num     string
+	}{
+		{"android_arm64_armv8-a", "10000"},
+		{"android_arm64_armv8-a_sdk_28", "28"},
+		{"android_arm64_armv8-a_sdk_29", "29"},
+		{"android_arm64_armv8-a_sdk_30", "30"},
+		{"android_arm64_armv8-a_sdk_current", "10000"},
+		{"android_vendor.29_arm64_armv8-a", "29"},
+	}
+	for _, v := range variants {
+		cflags := ctx.ModuleForTests("crt_foo", v.variant).Rule("cc").Args["cFlags"]
+		expected := "-target aarch64-linux-android" + v.num + " "
 		android.AssertStringDoesContain(t, "cflag", cflags, expected)
 	}
 }
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/cc/testing.go b/cc/testing.go
index 071f1ec..d0dca6b 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -375,26 +375,42 @@
 		cc_object {
 			name: "crtbegin_so",
 			defaults: ["crt_defaults"],
+			srcs: ["crtbegin_so.c"],
+			objs: ["crtbrand"],
 		}
 
 		cc_object {
 			name: "crtbegin_dynamic",
 			defaults: ["crt_defaults"],
+			srcs: ["crtbegin.c"],
+			objs: ["crtbrand"],
 		}
 
 		cc_object {
 			name: "crtbegin_static",
 			defaults: ["crt_defaults"],
+			srcs: ["crtbegin.c"],
+			objs: ["crtbrand"],
 		}
 
 		cc_object {
 			name: "crtend_so",
 			defaults: ["crt_defaults"],
+			srcs: ["crtend_so.c"],
+			objs: ["crtbrand"],
 		}
 
 		cc_object {
 			name: "crtend_android",
 			defaults: ["crt_defaults"],
+			srcs: ["crtend.c"],
+			objs: ["crtbrand"],
+		}
+
+		cc_object {
+			name: "crtbrand",
+			defaults: ["crt_defaults"],
+			srcs: ["crtbrand.c"],
 		}
 
 		cc_library {
@@ -585,6 +601,11 @@
 		"defaults/cc/common/libm.map.txt":           nil,
 		"defaults/cc/common/ndk_libandroid_support": nil,
 		"defaults/cc/common/ndk_libc++_shared":      nil,
+		"defaults/cc/common/crtbegin_so.c":          nil,
+		"defaults/cc/common/crtbegin.c":             nil,
+		"defaults/cc/common/crtend_so.c":            nil,
+		"defaults/cc/common/crtend.c":               nil,
+		"defaults/cc/common/crtbrand.c":             nil,
 	}.AddToFixture(),
 
 	// Place the default cc test modules that are common to all platforms in a location that will not
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 8aea037..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 {
@@ -146,14 +150,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 +177,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 +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.BuildDir())
 }
 
 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.
@@ -233,27 +240,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 +311,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 +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, 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 +486,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 +494,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",
@@ -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/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/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/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 9663b3c..a132216 100644
--- a/python/library.go
+++ b/python/library.go
@@ -17,11 +17,17 @@
 // This file contains the module types for building Python library.
 
 import (
+	"fmt"
+
 	"android/soong/android"
+	"android/soong/bazel"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
 	registerPythonLibraryComponents(android.InitRegistrationContext)
+	android.RegisterBp2BuildMutator("python_library_host", PythonLibraryHostBp2Build)
+	android.RegisterBp2BuildMutator("python_library", PythonLibraryBp2Build)
 }
 
 func registerPythonLibraryComponents(ctx android.RegistrationContext) {
@@ -32,11 +38,79 @@
 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 `modType`
+	if ctx.ModuleType() != modType {
+		return
+	}
+
+	// TODO(b/182306917): this doesn't fully handle all nested props versioned
+	// by the python version, which would have been handled by the version split
+	// mutator. This is sufficient for very simple python_library modules under
+	// Bionic.
+	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
+	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
+	var python_version string
+	if py2Enabled && !py3Enabled {
+		python_version = "PY2"
+	} else if !py2Enabled && py3Enabled {
+		python_version = "PY3"
+	} else if !py2Enabled && !py3Enabled {
+		panic(fmt.Errorf(
+			"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,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		// Use the native py_library rule.
+		Rule_class: "py_library",
+	}
+
+	ctx.CreateBazelTargetModule(m.Name(), props, attrs)
+}
+
 func PythonLibraryFactory() android.Module {
 	module := newModule(android.HostAndDeviceSupported, android.MultilibBoth)
 
+	android.InitBazelModule(module)
+
 	return module.init()
 }
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..726b541 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
@@ -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,19 +180,38 @@
 		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") != "",
 	}
 
+	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")