Merge "Remove fallback workaround for APEX variants which is now unnecessary."
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 0e7c944..312f009 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -28,8 +28,6 @@
 
 	"android/soong/bazel/cquery"
 
-	"github.com/google/blueprint/bootstrap"
-
 	"android/soong/bazel"
 	"android/soong/shared"
 )
@@ -760,7 +758,7 @@
 
 	// Add ninja file dependencies for files which all bazel invocations require.
 	bazelBuildList := absolutePath(filepath.Join(
-		filepath.Dir(bootstrap.CmdlineArgs.ModuleListFile), "bazel.list"))
+		filepath.Dir(ctx.Config().moduleListFile), "bazel.list"))
 	ctx.AddNinjaFileDeps(bazelBuildList)
 
 	data, err := ioutil.ReadFile(bazelBuildList)
diff --git a/android/config.go b/android/config.go
index e3d05a6..35403b8 100644
--- a/android/config.go
+++ b/android/config.go
@@ -79,10 +79,6 @@
 	return false // Never compile Go code in the main build for debugging
 }
 
-func (c Config) SrcDir() string {
-	return c.srcDir
-}
-
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
@@ -126,7 +122,6 @@
 
 	deviceConfig *deviceConfig
 
-	srcDir         string // the path of the root source directory
 	buildDir       string // the path of the build output directory
 	moduleListFile string // the path to the file which lists blueprint files to parse.
 
@@ -402,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.srcDir, c.buildDir, c.moduleListFile, c.env)
+	newConfig, err := NewConfig(c.buildDir, c.moduleListFile, c.env)
 	if err != nil {
 		return Config{}, err
 	}
@@ -413,14 +408,13 @@
 
 // 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(srcDir, buildDir string, moduleListFile string, availableEnv map[string]string) (Config, error) {
+func NewConfig(buildDir string, moduleListFile string, availableEnv map[string]string) (Config, error) {
 	// Make a config with default options.
 	config := &config{
 		ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
 
 		env: availableEnv,
 
-		srcDir:            srcDir,
 		buildDir:          buildDir,
 		multilibConflicts: make(map[ArchType]bool),
 
@@ -439,7 +433,7 @@
 		return Config{}, err
 	}
 
-	absSrcDir, err := filepath.Abs(srcDir)
+	absSrcDir, err := filepath.Abs(".")
 	if err != nil {
 		return Config{}, err
 	}
@@ -597,7 +591,7 @@
 
 // GoRoot returns the path to the root directory of the Go toolchain.
 func (c *config) GoRoot() string {
-	return fmt.Sprintf("%s/prebuilts/go/%s", c.srcDir, c.PrebuiltOS())
+	return fmt.Sprintf("prebuilts/go/%s", c.PrebuiltOS())
 }
 
 // PrebuiltBuildTool returns the path to a tool in the prebuilts directory containing
diff --git a/android/paths.go b/android/paths.go
index 9c9914e..71caaab 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -622,7 +622,7 @@
 // It intended for use in globs that only list files that exist, so it allows '$' in
 // filenames.
 func pathsForModuleSrcFromFullPath(ctx EarlyModulePathContext, paths []string, incDirs bool) Paths {
-	prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/"
+	prefix := ctx.ModuleDir() + "/"
 	if prefix == "./" {
 		prefix = ""
 	}
@@ -658,7 +658,7 @@
 	}
 	// Use Glob so that if the default doesn't exist, a dependency is added so that when it
 	// is created, we're run again.
-	path := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir(), def)
+	path := filepath.Join(ctx.ModuleDir(), def)
 	return Glob(ctx, path, nil)
 }
 
@@ -986,7 +986,7 @@
 // code that is embedding ninja variables in paths
 func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
 	p, err := validateSafePath(pathComponents...)
-	ret := SourcePath{basePath{p, ""}, ctx.Config().srcDir}
+	ret := SourcePath{basePath{p, ""}, "."}
 	if err != nil {
 		return ret, err
 	}
@@ -1002,7 +1002,7 @@
 // pathForSource creates a SourcePath from pathComponents, but does not check that it exists.
 func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
 	p, err := validatePath(pathComponents...)
-	ret := SourcePath{basePath{p, ""}, ctx.Config().srcDir}
+	ret := SourcePath{basePath{p, ""}, "."}
 	if err != nil {
 		return ret, err
 	}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 9ec637a..78e3a74 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -39,6 +39,7 @@
         "cc_library_static_conversion_test.go",
         "cc_object_conversion_test.go",
         "conversion_test.go",
+        "performance_test.go",
         "prebuilt_etc_conversion_test.go",
         "python_binary_conversion_test.go",
         "sh_conversion_test.go",
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index e5dbda6..eb3a73f 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -334,9 +334,7 @@
 		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
 		ctx := android.NewTestContext(config)
 
-		ctx.RegisterModuleType("custom", customModuleFactory)
-		ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator)
-		ctx.RegisterForBazelConversion()
+		registerCustomModuleForBp2buildConversion(ctx)
 
 		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
 		if errored(t, "", errs) {
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 204c519..9e0c0a1 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -85,6 +85,7 @@
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
         "arch_paths": attr.string_list(),
+        "arch_paths_exclude": attr.string_list(),
         # bazel_module start
 #         "label": attr.string(),
 #         "bp2build_available": attr.bool(),
@@ -114,6 +115,7 @@
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
         "arch_paths": attr.string_list(),
+        "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
         "int64_ptr_prop": attr.int(),
@@ -139,6 +141,7 @@
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
         "arch_paths": attr.string_list(),
+        "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
         "int64_ptr_prop": attr.int(),
diff --git a/bp2build/performance_test.go b/bp2build/performance_test.go
new file mode 100644
index 0000000..3283952
--- /dev/null
+++ b/bp2build/performance_test.go
@@ -0,0 +1,175 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+// to run the benchmarks in this file, you must run go test with the -bench.
+// The benchmarked portion will run for the specified time (can be set via -benchtime)
+// This can mean if you are benchmarking a faster portion of a larger operation, it will take
+// longer.
+// If you are seeing a small number of iterations for a specific run, the data is less reliable, to
+// run for longer, set -benchtime to a larger value.
+
+import (
+	"android/soong/android"
+	"fmt"
+	"math"
+	"strings"
+	"testing"
+)
+
+func genCustomModule(i int, convert bool) string {
+	var conversionString string
+	if convert {
+		conversionString = `bazel_module: { bp2build_available: true },`
+	}
+	return fmt.Sprintf(`
+custom {
+    name: "arch_paths_%[1]d",
+    string_list_prop: ["\t", "\n"],
+    string_prop: "a\t\n\r",
+    arch_paths: ["outer", ":outer_dep_%[1]d"],
+    arch: {
+      x86: {
+        arch_paths: ["abc", ":x86_dep_%[1]d"],
+      },
+      x86_64: {
+        arch_paths: ["64bit"],
+        arch_paths_exclude: ["outer"],
+      },
+    },
+		%[2]s
+}
+
+custom {
+    name: "outer_dep_%[1]d",
+		%[2]s
+}
+
+custom {
+    name: "x86_dep_%[1]d",
+		%[2]s
+}
+`, i, conversionString)
+}
+
+func genCustomModuleBp(pctConverted float64) string {
+	modules := 100
+
+	bp := make([]string, 0, modules)
+	toConvert := int(math.Round(float64(modules) * pctConverted))
+
+	for i := 0; i < modules; i++ {
+		bp = append(bp, genCustomModule(i, i < toConvert))
+	}
+	return strings.Join(bp, "\n\n")
+}
+
+var pctToConvert = []float64{0.0, 0.01, 0.05, 0.10, 0.25, 0.5, 0.75, 1.0}
+
+func BenchmarkManyModulesFull(b *testing.B) {
+	dir := "."
+	for _, tcSize := range pctToConvert {
+
+		b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
+			for n := 0; n < b.N; n++ {
+				b.StopTimer()
+				// setup we don't want to measure
+				config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
+				ctx := android.NewTestContext(config)
+
+				registerCustomModuleForBp2buildConversion(ctx)
+				codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+
+				b.StartTimer()
+				_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				_, errs = ctx.ResolveDependencies(config)
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				generateBazelTargetsForDir(codegenCtx, dir)
+				b.StopTimer()
+			}
+		})
+	}
+}
+
+func BenchmarkManyModulesResolveDependencies(b *testing.B) {
+	dir := "."
+	for _, tcSize := range pctToConvert {
+
+		b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
+			for n := 0; n < b.N; n++ {
+				b.StopTimer()
+				// setup we don't want to measure
+				config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
+				ctx := android.NewTestContext(config)
+
+				registerCustomModuleForBp2buildConversion(ctx)
+				codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+
+				_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				b.StartTimer()
+				_, errs = ctx.ResolveDependencies(config)
+				b.StopTimer()
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				generateBazelTargetsForDir(codegenCtx, dir)
+			}
+		})
+	}
+}
+
+func BenchmarkManyModulesGenerateBazelTargetsForDir(b *testing.B) {
+	dir := "."
+	for _, tcSize := range pctToConvert {
+
+		b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
+			for n := 0; n < b.N; n++ {
+				b.StopTimer()
+				// setup we don't want to measure
+				config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
+				ctx := android.NewTestContext(config)
+
+				registerCustomModuleForBp2buildConversion(ctx)
+				codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+
+				_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				_, errs = ctx.ResolveDependencies(config)
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				b.StartTimer()
+				generateBazelTargetsForDir(codegenCtx, dir)
+				b.StopTimer()
+			}
+		})
+	}
+}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index f3cd7f0..faf1a44 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -33,7 +33,8 @@
 	Nested_props     nestedProps
 	Nested_props_ptr *nestedProps
 
-	Arch_paths []string `android:"path,arch_variant"`
+	Arch_paths         []string `android:"path,arch_variant"`
+	Arch_paths_exclude []string `android:"path,arch_variant"`
 }
 
 type customModule struct {
@@ -155,16 +156,18 @@
 			return
 		}
 
-		paths := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.props.Arch_paths))
+		paths := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.props.Arch_paths, m.props.Arch_paths_exclude))
 
 		for axis, configToProps := range m.GetArchVariantProperties(ctx, &customProps{}) {
 			for config, props := range configToProps {
 				if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
-					paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, archProps.Arch_paths))
+					paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrcExcludes(ctx, archProps.Arch_paths, archProps.Arch_paths_exclude))
 				}
 			}
 		}
 
+		paths.ResolveExcludes()
+
 		attrs := &customBazelModuleAttributes{
 			String_prop:      m.props.String_prop,
 			String_list_prop: m.props.String_list_prop,
@@ -216,3 +219,9 @@
 	buildFileToTargets, _, _ := GenerateBazelTargets(codegenCtx, false)
 	return buildFileToTargets[dir]
 }
+
+func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
+	ctx.RegisterModuleType("custom", customModuleFactory)
+	ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator)
+	ctx.RegisterForBazelConversion()
+}
diff --git a/cc/config/global.go b/cc/config/global.go
index 248822f..9773345 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -355,6 +355,9 @@
 	exportedStringListVars.Set("CommonGlobalIncludes", commonGlobalIncludes)
 	pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", commonGlobalIncludes)
 
+	exportStringStaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion)
+	exportStringStaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion)
+
 	pctx.SourcePathVariable("ClangDefaultBase", ClangDefaultBase)
 	pctx.VariableFunc("ClangBase", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_PREBUILTS_BASE"); override != "" {
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index 31c4443..f8d1841 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -329,7 +329,7 @@
     def parse(self) -> List[Version]:
         """Parses the symbol file and returns a list of Version objects."""
         versions = []
-        while self.next_line() != '':
+        while self.next_line():
             assert self.current_line is not None
             if '{' in self.current_line:
                 versions.append(self.parse_version())
@@ -376,7 +376,7 @@
         symbols: List[Symbol] = []
         global_scope = True
         cpp_symbols = False
-        while self.next_line() != '':
+        while self.next_line():
             if '}' in self.current_line:
                 # Line is something like '} BASE; # tags'. Both base and tags
                 # are optional here.
@@ -428,11 +428,11 @@
         A return value of '' indicates EOF.
         """
         line = self.input_file.readline()
-        while line.strip() == '' or line.strip().startswith('#'):
+        while not line.strip() or line.strip().startswith('#'):
             line = self.input_file.readline()
 
             # We want to skip empty lines, but '' indicates EOF.
-            if line == '':
+            if not line:
                 break
         self.current_line = line
         return self.current_line
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 0099e87..af935db 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -44,6 +44,8 @@
 	docFile           string
 	bazelQueryViewDir string
 	bp2buildMarker    string
+
+	cmdlineArgs bootstrap.Args
 )
 
 func init() {
@@ -61,6 +63,21 @@
 	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(&cmdlineArgs.GlobFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
+	flag.StringVar(&cmdlineArgs.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")
+	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 {
@@ -90,8 +107,8 @@
 	return ctx
 }
 
-func newConfig(srcDir, outDir string, availableEnv map[string]string) android.Config {
-	configuration, err := android.NewConfig(srcDir, outDir, bootstrap.CmdlineArgs.ModuleListFile, availableEnv)
+func newConfig(outDir string, availableEnv map[string]string) android.Config {
+	configuration, err := android.NewConfig(outDir, cmdlineArgs.ModuleListFile, availableEnv)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
@@ -107,7 +124,7 @@
 func runMixedModeBuild(configuration android.Config, firstCtx *android.Context, extraNinjaDeps []string) {
 	var firstArgs, secondArgs bootstrap.Args
 
-	firstArgs = bootstrap.CmdlineArgs
+	firstArgs = cmdlineArgs
 	configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
 	bootstrap.RunBlueprint(firstArgs, firstCtx.Context, configuration)
 
@@ -123,7 +140,7 @@
 		os.Exit(1)
 	}
 	secondCtx := newContext(secondConfig, true)
-	secondArgs = bootstrap.CmdlineArgs
+	secondArgs = cmdlineArgs
 	ninjaDeps := bootstrap.RunBlueprint(secondArgs, secondCtx.Context, secondConfig)
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 	err = deptools.WriteDepFile(shared.JoinPath(topDir, secondArgs.DepFile), secondArgs.OutFile, ninjaDeps)
@@ -145,7 +162,7 @@
 
 func runSoongDocs(configuration android.Config) {
 	ctx := newContext(configuration, false)
-	soongDocsArgs := bootstrap.CmdlineArgs
+	soongDocsArgs := cmdlineArgs
 	bootstrap.RunBlueprint(soongDocsArgs, ctx.Context, configuration)
 	if err := writeDocs(ctx, configuration, docFile); err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
@@ -183,7 +200,7 @@
 	generateQueryView := bazelQueryViewDir != ""
 	jsonModuleFile := configuration.Getenv("SOONG_DUMP_JSON_MODULE_GRAPH")
 
-	blueprintArgs := bootstrap.CmdlineArgs
+	blueprintArgs := cmdlineArgs
 	prepareBuildActions := !generateQueryView && jsonModuleFile == ""
 	if bazelConversionRequested {
 		// Run the alternate pipeline of bp2build mutators and singleton to convert
@@ -208,16 +225,16 @@
 	// Convert the Soong module graph into Bazel BUILD files.
 	if generateQueryView {
 		runQueryView(configuration, ctx)
-		return bootstrap.CmdlineArgs.OutFile // TODO: This is a lie
+		return cmdlineArgs.OutFile // TODO: This is a lie
 	}
 
 	if jsonModuleFile != "" {
 		writeJsonModuleGraph(configuration, ctx, jsonModuleFile, extraNinjaDeps)
-		return bootstrap.CmdlineArgs.OutFile // TODO: This is a lie
+		return cmdlineArgs.OutFile // TODO: This is a lie
 	}
 
 	writeMetrics(configuration)
-	return bootstrap.CmdlineArgs.OutFile
+	return cmdlineArgs.OutFile
 }
 
 // soong_ui dumps the available environment variables to
@@ -256,9 +273,7 @@
 
 	availableEnv := parseAvailableEnv()
 
-	// The top-level Blueprints file is passed as the first argument.
-	srcDir := filepath.Dir(flag.Arg(0))
-	configuration := newConfig(srcDir, outDir, availableEnv)
+	configuration := newConfig(outDir, availableEnv)
 	extraNinjaDeps := []string{
 		configuration.ProductVariablesFileName,
 		usedEnvFile,
@@ -420,7 +435,7 @@
 // Read the bazel.list file that the Soong Finder already dumped earlier (hopefully)
 // It contains the locations of BUILD files, BUILD.bazel files, etc. in the source dir
 func getExistingBazelRelatedFiles(topDir string) ([]string, error) {
-	bazelFinderFile := filepath.Join(filepath.Dir(bootstrap.CmdlineArgs.ModuleListFile), "bazel.list")
+	bazelFinderFile := filepath.Join(filepath.Dir(cmdlineArgs.ModuleListFile), "bazel.list")
 	if !filepath.IsAbs(bazelFinderFile) {
 		// Assume this was a relative path under topDir
 		bazelFinderFile = filepath.Join(topDir, bazelFinderFile)
@@ -451,8 +466,8 @@
 	// Android.bp files. It must not depend on the values of per-build product
 	// configurations or variables, since those will generate different BUILD
 	// files based on how the user has configured their tree.
-	bp2buildCtx.SetModuleListFile(bootstrap.CmdlineArgs.ModuleListFile)
-	modulePaths, err := bp2buildCtx.ListModulePaths(configuration.SrcDir())
+	bp2buildCtx.SetModuleListFile(cmdlineArgs.ModuleListFile)
+	modulePaths, err := bp2buildCtx.ListModulePaths(".")
 	if err != nil {
 		panic(err)
 	}
@@ -465,17 +480,17 @@
 	// Run the loading and analysis pipeline to prepare the graph of regular
 	// Modules parsed from Android.bp files, and the BazelTargetModules mapped
 	// from the regular Modules.
-	blueprintArgs := bootstrap.CmdlineArgs
+	blueprintArgs := cmdlineArgs
 	ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bp2buildCtx.Context, configuration)
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
 	// Generate out/soong/.bootstrap/build-globs.ninja with the actions to generate flattened globfiles
 	// containing the globs seen during bp2build conversion
 	if blueprintArgs.GlobFile != "" {
-		bootstrap.WriteBuildGlobsNinjaFile(bootstrap.StageMain, bp2buildCtx.Context, blueprintArgs, configuration)
+		bootstrap.WriteBuildGlobsNinjaFile(blueprintArgs.GlobListDir, bp2buildCtx.Context, blueprintArgs, configuration)
 	}
 	// Add the depfile on the expanded globs in out/soong/.primary/globs
-	ninjaDeps = append(ninjaDeps, bootstrap.GlobFileListFiles(configuration)...)
+	ninjaDeps = append(ninjaDeps, bootstrap.GlobFileListFiles(configuration, blueprintArgs.GlobListDir)...)
 
 	// Run the code-generation phase to convert BazelTargetModules to BUILD files
 	// and print conversion metrics to the user.
@@ -493,8 +508,8 @@
 		"bazel-" + filepath.Base(topDir),
 	}
 
-	if bootstrap.CmdlineArgs.NinjaBuildDir[0] != '/' {
-		excludes = append(excludes, bootstrap.CmdlineArgs.NinjaBuildDir)
+	if cmdlineArgs.NinjaBuildDir[0] != '/' {
+		excludes = append(excludes, cmdlineArgs.NinjaBuildDir)
 	}
 
 	existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
@@ -509,7 +524,7 @@
 	excludes = append(excludes, getTemporaryExcludes()...)
 
 	symlinkForestDeps := bp2build.PlantSymlinkForest(
-		topDir, workspaceRoot, generatedRoot, configuration.SrcDir(), excludes)
+		topDir, workspaceRoot, generatedRoot, ".", excludes)
 
 	// Only report metrics when in bp2build mode. The metrics aren't relevant
 	// for queryview, since that's a total repo-wide conversion and there's a
@@ -528,12 +543,4 @@
 
 	// Create an empty bp2build marker file.
 	touch(shared.JoinPath(topDir, bp2buildMarker))
-
-	// bp2build *always* writes a fake Ninja file containing just the nothing
-	// phony target if it ever re-runs. This allows bp2build to exit early with
-	// GENERATE_BAZEL_FILES=1 m nothing.
-	//
-	// If bp2build is invoked as part of an integrated mixed build, the fake
-	// build.ninja file will be rewritten later into the real file anyway.
-	writeFakeNinjaFile(extraNinjaDeps, codegenContext.Config().BuildDir())
 }
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 245af2c..ebb8959 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -193,6 +193,9 @@
 	// The name of the library.
 	Name string
 
+	// If the library is optional or required.
+	Optional bool
+
 	// On-host build path to the library dex file (used in dex2oat argument --class-loader-context).
 	Host android.Path
 
@@ -256,7 +259,7 @@
 
 // Add class loader context for the given library to the map entry for the given SDK version.
 func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
-	hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) error {
+	optional bool, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) error {
 
 	// For prebuilts, library should have the same name as the source module.
 	lib = android.RemoveOptionalPrebuiltPrefix(lib)
@@ -304,6 +307,7 @@
 
 	clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
 		Name:        lib,
+		Optional:    optional,
 		Host:        hostPath,
 		Device:      devicePath,
 		Subcontexts: subcontexts,
@@ -316,9 +320,9 @@
 // about paths). For the subset of libraries that are used in dexpreopt, their build/install paths
 // are validated later before CLC is used (in validateClassLoaderContext).
 func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, sdkVer int,
-	lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
+	lib string, optional bool, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
 
-	err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, nestedClcMap)
+	err := clcMap.addContext(ctx, sdkVer, lib, optional, hostPath, installPath, nestedClcMap)
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 	}
@@ -361,15 +365,21 @@
 // Returns top-level libraries in the CLC (conditional CLC, i.e. compatibility libraries are not
 // included). This is the list of libraries that should be in the <uses-library> tags in the
 // manifest. Some of them may be present in the source manifest, others are added by manifest_fixer.
-func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) {
+// Required and optional libraries are in separate lists.
+func (clcMap ClassLoaderContextMap) UsesLibs() (required []string, optional []string) {
 	if clcMap != nil {
 		clcs := clcMap[AnySdkVersion]
-		ulibs = make([]string, 0, len(clcs))
+		required = make([]string, 0, len(clcs))
+		optional = make([]string, 0, len(clcs))
 		for _, clc := range clcs {
-			ulibs = append(ulibs, clc.Name)
+			if clc.Optional {
+				optional = append(optional, clc.Name)
+			} else {
+				required = append(required, clc.Name)
+			}
 		}
 	}
-	return ulibs
+	return required, optional
 }
 
 func (clcMap ClassLoaderContextMap) Dump() string {
@@ -388,7 +398,8 @@
 // TODO(b/132357300): remove "android.hidl.manager" and "android.hidl.base" for non-system apps.
 //
 func fixClassLoaderContext(clcMap ClassLoaderContextMap) {
-	usesLibs := clcMap.UsesLibs()
+	required, optional := clcMap.UsesLibs()
+	usesLibs := append(required, optional...)
 
 	for sdkVer, clcs := range clcMap {
 		if sdkVer == AnySdkVersion {
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index 610a4c9..0b7b546 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -49,32 +49,34 @@
 	//
 	ctx := testContext()
 
+	optional := false
+
 	m := make(ClassLoaderContextMap)
 
-	m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
-	m.AddContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
-	m.AddContext(ctx, AnySdkVersion, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+	m.AddContext(ctx, AnySdkVersion, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m.AddContext(ctx, AnySdkVersion, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+	m.AddContext(ctx, AnySdkVersion, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
 
 	// Add some libraries with nested subcontexts.
 
 	m1 := make(ClassLoaderContextMap)
-	m1.AddContext(ctx, AnySdkVersion, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"), nil)
-	m1.AddContext(ctx, AnySdkVersion, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"), nil)
+	m1.AddContext(ctx, AnySdkVersion, "a1", optional, buildPath(ctx, "a1"), installPath(ctx, "a1"), nil)
+	m1.AddContext(ctx, AnySdkVersion, "b1", optional, buildPath(ctx, "b1"), installPath(ctx, "b1"), nil)
 
 	m2 := make(ClassLoaderContextMap)
-	m2.AddContext(ctx, AnySdkVersion, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"), nil)
-	m2.AddContext(ctx, AnySdkVersion, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"), nil)
-	m2.AddContext(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
+	m2.AddContext(ctx, AnySdkVersion, "a2", optional, buildPath(ctx, "a2"), installPath(ctx, "a2"), nil)
+	m2.AddContext(ctx, AnySdkVersion, "b2", optional, buildPath(ctx, "b2"), installPath(ctx, "b2"), nil)
+	m2.AddContext(ctx, AnySdkVersion, "c2", optional, buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
 
 	m3 := make(ClassLoaderContextMap)
-	m3.AddContext(ctx, AnySdkVersion, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"), nil)
-	m3.AddContext(ctx, AnySdkVersion, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"), nil)
+	m3.AddContext(ctx, AnySdkVersion, "a3", optional, buildPath(ctx, "a3"), installPath(ctx, "a3"), nil)
+	m3.AddContext(ctx, AnySdkVersion, "b3", optional, buildPath(ctx, "b3"), installPath(ctx, "b3"), nil)
 
-	m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
+	m.AddContext(ctx, AnySdkVersion, "d", optional, buildPath(ctx, "d"), installPath(ctx, "d"), m2)
 	// When the same library is both in conditional and unconditional context, it should be removed
 	// from conditional context.
-	m.AddContext(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
-	m.AddContext(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+	m.AddContext(ctx, 42, "f", optional, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+	m.AddContext(ctx, AnySdkVersion, "f", optional, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
 
 	// Merge map with implicit root library that is among toplevel contexts => does nothing.
 	m.AddContextMap(m1, "c")
@@ -83,12 +85,12 @@
 	m.AddContextMap(m3, "m_g")
 
 	// Compatibility libraries with unknown install paths get default paths.
-	m.AddContext(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
-	m.AddContext(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
+	m.AddContext(ctx, 29, AndroidHidlManager, optional, buildPath(ctx, AndroidHidlManager), nil, nil)
+	m.AddContext(ctx, 29, AndroidHidlBase, optional, buildPath(ctx, AndroidHidlBase), nil, nil)
 
 	// Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
 	// needed as a compatibility library if "android.test.runner" is in CLC as well.
-	m.AddContext(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
+	m.AddContext(ctx, 30, AndroidTestMock, optional, buildPath(ctx, AndroidTestMock), nil, nil)
 
 	valid, validationError := validateClassLoaderContext(m)
 
@@ -96,10 +98,10 @@
 
 	var haveStr string
 	var havePaths android.Paths
-	var haveUsesLibs []string
+	var haveUsesLibsReq, haveUsesLibsOpt []string
 	if valid && validationError == nil {
 		haveStr, havePaths = ComputeClassLoaderContext(m)
-		haveUsesLibs = m.UsesLibs()
+		haveUsesLibsReq, haveUsesLibsOpt = m.UsesLibs()
 	}
 
 	// Test that validation is successful (all paths are known).
@@ -148,20 +150,25 @@
 
 	// Test for libraries that are added by the manifest_fixer.
 	t.Run("uses libs", func(t *testing.T) {
-		wantUsesLibs := []string{"a", "b", "c", "d", "f", "a3", "b3"}
-		if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
-			t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
+		wantUsesLibsReq := []string{"a", "b", "c", "d", "f", "a3", "b3"}
+		wantUsesLibsOpt := []string{}
+		if !reflect.DeepEqual(wantUsesLibsReq, haveUsesLibsReq) {
+			t.Errorf("\nwant required uses libs: %s\nhave required uses libs: %s", wantUsesLibsReq, haveUsesLibsReq)
+		}
+		if !reflect.DeepEqual(wantUsesLibsOpt, haveUsesLibsOpt) {
+			t.Errorf("\nwant optional uses libs: %s\nhave optional uses libs: %s", wantUsesLibsOpt, haveUsesLibsOpt)
 		}
 	})
 }
 
 func TestCLCJson(t *testing.T) {
 	ctx := testContext()
+	optional := false
 	m := make(ClassLoaderContextMap)
-	m.AddContext(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
-	m.AddContext(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
-	m.AddContext(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
-	m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
+	m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m.AddContext(ctx, 29, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+	m.AddContext(ctx, 30, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+	m.AddContext(ctx, AnySdkVersion, "d", optional, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
 	jsonCLC := toJsonClassLoaderContext(m)
 	restored := fromJsonClassLoaderContext(ctx, jsonCLC)
 	android.AssertIntEquals(t, "The size of the maps should be the same.", len(m), len(restored))
@@ -181,20 +188,25 @@
 // Test that unknown library paths cause a validation error.
 func testCLCUnknownPath(t *testing.T, whichPath string) {
 	ctx := testContext()
+	optional := false
 
 	m := make(ClassLoaderContextMap)
 	if whichPath == "build" {
-		m.AddContext(ctx, AnySdkVersion, "a", nil, nil, nil)
+		m.AddContext(ctx, AnySdkVersion, "a", optional, nil, nil, nil)
 	} else {
-		m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, nil)
+		m.AddContext(ctx, AnySdkVersion, "a", optional, buildPath(ctx, "a"), nil, nil)
 	}
 
 	// The library should be added to <uses-library> tags by the manifest_fixer.
 	t.Run("uses libs", func(t *testing.T) {
-		haveUsesLibs := m.UsesLibs()
-		wantUsesLibs := []string{"a"}
-		if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
-			t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
+		haveUsesLibsReq, haveUsesLibsOpt := m.UsesLibs()
+		wantUsesLibsReq := []string{"a"}
+		wantUsesLibsOpt := []string{}
+		if !reflect.DeepEqual(wantUsesLibsReq, haveUsesLibsReq) {
+			t.Errorf("\nwant required uses libs: %s\nhave required uses libs: %s", wantUsesLibsReq, haveUsesLibsReq)
+		}
+		if !reflect.DeepEqual(wantUsesLibsOpt, haveUsesLibsOpt) {
+			t.Errorf("\nwant optional uses libs: %s\nhave optional uses libs: %s", wantUsesLibsOpt, haveUsesLibsOpt)
 		}
 	})
 
@@ -216,10 +228,11 @@
 // An attempt to add conditional nested subcontext should fail.
 func TestCLCNestedConditional(t *testing.T) {
 	ctx := testContext()
+	optional := false
 	m1 := make(ClassLoaderContextMap)
-	m1.AddContext(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m1.AddContext(ctx, 42, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
 	m := make(ClassLoaderContextMap)
-	err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), m1)
+	err := m.addContext(ctx, AnySdkVersion, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), m1)
 	checkError(t, err, "nested class loader context shouldn't have conditional part")
 }
 
@@ -227,11 +240,12 @@
 // they end up in the order that agrees with PackageManager.
 func TestCLCSdkVersionOrder(t *testing.T) {
 	ctx := testContext()
+	optional := false
 	m := make(ClassLoaderContextMap)
-	m.AddContext(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
-	m.AddContext(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
-	m.AddContext(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
-	m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
+	m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m.AddContext(ctx, 29, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+	m.AddContext(ctx, 30, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+	m.AddContext(ctx, AnySdkVersion, "d", optional, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
 
 	valid, validationError := validateClassLoaderContext(m)
 
diff --git a/java/aar.go b/java/aar.go
index 04727e4..afbaea2 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -502,11 +502,12 @@
 	if sdkDep.hasFrameworkLibs() {
 		a.aapt.deps(ctx, sdkDep)
 	}
+	a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs())
 }
 
 func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.aapt.isLibrary = true
-	a.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
+	a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 	a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts)
 
 	a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 331f941..1f7234d 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -71,12 +71,12 @@
 		args = append(args, "--use-embedded-dex")
 	}
 
-	for _, usesLib := range classLoaderContexts.UsesLibs() {
-		if inList(usesLib, dexpreopt.OptionalCompatUsesLibs) {
-			args = append(args, "--optional-uses-library", usesLib)
-		} else {
-			args = append(args, "--uses-library", usesLib)
-		}
+	requiredUsesLibs, optionalUsesLibs := classLoaderContexts.UsesLibs()
+	for _, usesLib := range requiredUsesLibs {
+		args = append(args, "--uses-library", usesLib)
+	}
+	for _, usesLib := range optionalUsesLibs {
+		args = append(args, "--optional-uses-library", usesLib)
 	}
 
 	if hasNoCode {
diff --git a/java/androidmk.go b/java/androidmk.go
index 04357e0..68ccd82 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -114,7 +114,8 @@
 						entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", library.jacocoReportClassesFile)
 					}
 
-					entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.classLoaderContexts.UsesLibs()...)
+					requiredUsesLibs, optionalUsesLibs := library.classLoaderContexts.UsesLibs()
+					entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", append(requiredUsesLibs, optionalUsesLibs...)...)
 
 					if len(library.additionalCheckedModules) != 0 {
 						entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...)
diff --git a/java/app.go b/java/app.go
index 35ed27f..99e2ab6 100755
--- a/java/app.go
+++ b/java/app.go
@@ -649,8 +649,12 @@
 	a.usesLibrary.freezeEnforceUsesLibraries()
 
 	// Add implicit SDK libraries to <uses-library> list.
-	for _, usesLib := range a.classLoaderContexts.UsesLibs() {
-		a.usesLibrary.addLib(usesLib, inList(usesLib, dexpreopt.OptionalCompatUsesLibs))
+	requiredUsesLibs, optionalUsesLibs := a.classLoaderContexts.UsesLibs()
+	for _, usesLib := range requiredUsesLibs {
+		a.usesLibrary.addLib(usesLib, false)
+	}
+	for _, usesLib := range optionalUsesLibs {
+		a.usesLibrary.addLib(usesLib, true)
 	}
 
 	// Check that the <uses-library> list is coherent with the manifest.
@@ -1220,17 +1224,17 @@
 
 func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs bool) {
 	if !ctx.Config().UnbundledBuild() || ctx.Config().UnbundledBuildImage() {
-		ctx.AddVariationDependencies(nil, usesLibTag, u.usesLibraryProperties.Uses_libs...)
-		ctx.AddVariationDependencies(nil, usesLibTag, u.presentOptionalUsesLibs(ctx)...)
+		ctx.AddVariationDependencies(nil, usesLibReqTag, u.usesLibraryProperties.Uses_libs...)
+		ctx.AddVariationDependencies(nil, usesLibOptTag, u.presentOptionalUsesLibs(ctx)...)
 		// Only add these extra dependencies if the module depends on framework libs. This avoids
 		// creating a cyclic dependency:
 		//     e.g. framework-res -> org.apache.http.legacy -> ... -> framework-res.
 		if hasFrameworkLibs {
 			// Dexpreopt needs paths to the dex jars of these libraries in order to construct
 			// class loader context for dex2oat. Add them as a dependency with a special tag.
-			ctx.AddVariationDependencies(nil, usesLibCompat29Tag, dexpreopt.CompatUsesLibs29...)
-			ctx.AddVariationDependencies(nil, usesLibCompat28Tag, dexpreopt.OptionalCompatUsesLibs28...)
-			ctx.AddVariationDependencies(nil, usesLibCompat30Tag, dexpreopt.OptionalCompatUsesLibs30...)
+			ctx.AddVariationDependencies(nil, usesLibCompat29ReqTag, dexpreopt.CompatUsesLibs29...)
+			ctx.AddVariationDependencies(nil, usesLibCompat28OptTag, dexpreopt.OptionalCompatUsesLibs28...)
+			ctx.AddVariationDependencies(nil, usesLibCompat30OptTag, dexpreopt.OptionalCompatUsesLibs30...)
 		}
 	}
 }
@@ -1289,7 +1293,7 @@
 				replaceInList(u.usesLibraryProperties.Uses_libs, dep, libName)
 				replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName)
 			}
-			clcMap.AddContext(ctx, tag.sdkVersion, libName,
+			clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional,
 				lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts())
 		} else if ctx.Config().AllowMissingDependencies() {
 			ctx.AddMissingDependencies([]string{dep})
diff --git a/java/app_test.go b/java/app_test.go
index 7997f7a..8de6691 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -2355,8 +2355,8 @@
 		`--uses-library quuz ` +
 		`--uses-library foo ` + // TODO(b/132357300): "foo" should not be passed to manifest_fixer
 		`--uses-library com.non.sdk.lib ` + // TODO(b/132357300): "com.non.sdk.lib" should not be passed to manifest_fixer
-		`--uses-library bar ` + // TODO(b/132357300): "bar" should not be passed to manifest_fixer
-		`--uses-library runtime-library`
+		`--uses-library runtime-library ` +
+		`--optional-uses-library bar` // TODO(b/132357300): "bar" should not be passed to manifest_fixer
 	android.AssertStringEquals(t, "manifest_fixer args", expectManifestFixerArgs, actualManifestFixerArgs)
 
 	// Test that all libraries are verified (library order matters).
diff --git a/java/base.go b/java/base.go
index d8cd6b3..ea5b137 100644
--- a/java/base.go
+++ b/java/base.go
@@ -605,7 +605,12 @@
 		if dep != nil {
 			if component, ok := dep.(SdkLibraryComponentDependency); ok {
 				if lib := component.OptionalSdkLibraryImplementation(); lib != nil {
-					ctx.AddVariationDependencies(nil, usesLibTag, *lib)
+					// Add library as optional if it's one of the optional compatibility libs.
+					tag := usesLibReqTag
+					if android.InList(*lib, dexpreopt.OptionalCompatUsesLibs) {
+						tag = usesLibOptTag
+					}
+					ctx.AddVariationDependencies(nil, tag, *lib)
 				}
 			}
 		}
diff --git a/java/java.go b/java/java.go
index b6e2a54..5bf3d79 100644
--- a/java/java.go
+++ b/java/java.go
@@ -248,13 +248,15 @@
 
 type usesLibraryDependencyTag struct {
 	dependencyTag
-	sdkVersion int // SDK version in which the library appared as a standalone library.
+	sdkVersion int  // SDK version in which the library appared as a standalone library.
+	optional   bool // If the dependency is optional or required.
 }
 
-func makeUsesLibraryDependencyTag(sdkVersion int) usesLibraryDependencyTag {
+func makeUsesLibraryDependencyTag(sdkVersion int, optional bool) usesLibraryDependencyTag {
 	return usesLibraryDependencyTag{
 		dependencyTag: dependencyTag{name: fmt.Sprintf("uses-library-%d", sdkVersion)},
 		sdkVersion:    sdkVersion,
+		optional:      optional,
 	}
 }
 
@@ -283,10 +285,11 @@
 	syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
 	jniInstallTag           = installDependencyTag{name: "jni install"}
 	binaryInstallTag        = installDependencyTag{name: "binary install"}
-	usesLibTag              = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion)
-	usesLibCompat28Tag      = makeUsesLibraryDependencyTag(28)
-	usesLibCompat29Tag      = makeUsesLibraryDependencyTag(29)
-	usesLibCompat30Tag      = makeUsesLibraryDependencyTag(30)
+	usesLibReqTag           = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false)
+	usesLibOptTag           = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, true)
+	usesLibCompat28OptTag   = makeUsesLibraryDependencyTag(28, true)
+	usesLibCompat29ReqTag   = makeUsesLibraryDependencyTag(29, false)
+	usesLibCompat30OptTag   = makeUsesLibraryDependencyTag(30, true)
 )
 
 func IsLibDepTag(depTag blueprint.DependencyTag) bool {
@@ -1807,8 +1810,10 @@
 	}
 
 	depTag := ctx.OtherModuleDependencyTag(depModule)
-	if depTag == libTag || depTag == usesLibTag {
+	if depTag == libTag {
 		// Ok, propagate <uses-library> through non-static library dependencies.
+	} else if tag, ok := depTag.(usesLibraryDependencyTag); ok && tag.sdkVersion == dexpreopt.AnySdkVersion {
+		// Ok, propagate <uses-library> through non-compatibility <uses-library> dependencies.
 	} else if depTag == staticLibTag {
 		// Propagate <uses-library> through static library dependencies, unless it is a component
 		// library (such as stubs). Component libraries have a dependency on their SDK library,
@@ -1826,7 +1831,7 @@
 	// <uses_library> and should not be added to CLC, but the transitive <uses-library> dependencies
 	// from its CLC should be added to the current CLC.
 	if sdkLib != nil {
-		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib,
+		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, false,
 			dep.DexJarBuildPath(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
 	} else {
 		clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 8e0618e..eeec504 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -156,8 +156,9 @@
 	// test if baz has exported SDK lib names foo and bar to qux
 	qux := result.ModuleForTests("qux", "android_common")
 	if quxLib, ok := qux.Module().(*Library); ok {
-		sdkLibs := quxLib.ClassLoaderContexts().UsesLibs()
-		android.AssertDeepEquals(t, "qux exports", []string{"foo", "bar", "fred", "quuz"}, sdkLibs)
+		requiredSdkLibs, optionalSdkLibs := quxLib.ClassLoaderContexts().UsesLibs()
+		android.AssertDeepEquals(t, "qux exports (required)", []string{"foo", "bar", "fred", "quuz"}, requiredSdkLibs)
+		android.AssertDeepEquals(t, "qux exports (optional)", []string{}, optionalSdkLibs)
 	}
 }
 
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 7ceac41..30888c7 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -419,14 +419,7 @@
 		{"TARGET_COPY_OUT_TEST_HARNESS_RAMDISK", "test_harness_ramdisk"},
 		{"TARGET_COPY_OUT_ROOT", "root"},
 		{"TARGET_COPY_OUT_RECOVERY", "recovery"},
-		{"TARGET_COPY_OUT_VENDOR", "||VENDOR-PATH-PH||"},
 		{"TARGET_COPY_OUT_VENDOR_RAMDISK", "vendor_ramdisk"},
-		{"TARGET_COPY_OUT_PRODUCT", "||PRODUCT-PATH-PH||"},
-		{"TARGET_COPY_OUT_PRODUCT_SERVICES", "||PRODUCT-PATH-PH||"},
-		{"TARGET_COPY_OUT_SYSTEM_EXT", "||SYSTEM_EXT-PATH-PH||"},
-		{"TARGET_COPY_OUT_ODM", "||ODM-PATH-PH||"},
-		{"TARGET_COPY_OUT_VENDOR_DLKM", "||VENDOR_DLKM-PATH-PH||"},
-		{"TARGET_COPY_OUT_ODM_DLKM", "||ODM_DLKM-PATH-PH||"},
 		// TODO(asmundak): to process internal config files, we need the following variables:
 		//    BOARD_CONFIG_VENDOR_PATH
 		//    TARGET_VENDOR
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index a14c7a4..ebaae28 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -259,7 +259,7 @@
 ifdef PRODUCT_NAME
 # Comment
 else
-  TARGET_COPY_OUT_VENDOR := foo
+  TARGET_COPY_OUT_RECOVERY := foo
 endif
 `,
 		expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -270,7 +270,7 @@
     # Comment
     pass
   else:
-    # MK2RBC TRANSLATION ERROR: cannot set predefined variable TARGET_COPY_OUT_VENDOR to "foo", its value should be "||VENDOR-PATH-PH||"
+    # MK2RBC TRANSLATION ERROR: cannot set predefined variable TARGET_COPY_OUT_RECOVERY to "foo", its value should be "recovery"
     pass
   rblf.warning("product.mk", "partially successful conversion")
 `,
diff --git a/rust/compiler.go b/rust/compiler.go
index 6b3ccfc..d9e21ff 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -65,7 +65,11 @@
 )
 
 type BaseCompilerProperties struct {
-	// path to the source file that is the main entry point of the program (e.g. main.rs or lib.rs)
+	// path to the source file that is the main entry point of the program (e.g. main.rs or lib.rs).
+	// Only a single source file can be defined. Modules which generate source can be included by prefixing
+	// the module name with ":", for example ":libfoo_bindgen"
+	//
+	// If no source file is defined, a single generated source module can be defined to be used as the main source.
 	Srcs []string `android:"path,arch_variant"`
 
 	// name of the lint set that should be used to validate this module.
@@ -362,7 +366,9 @@
 	} else {
 		deps.SharedLibs = append(deps.SharedLibs, bionicLibs...)
 	}
-
+	if ctx.RustModule().StaticExecutable() {
+		deps.StaticLibs = append(deps.StaticLibs, "libunwind")
+	}
 	if libRuntimeBuiltins := config.BuiltinsRuntimeLibrary(ctx.toolchain()); libRuntimeBuiltins != "" {
 		deps.StaticLibs = append(deps.StaticLibs, libRuntimeBuiltins)
 	}
@@ -436,12 +442,18 @@
 			srcIndex = i
 		}
 	}
-	if numSrcs != 1 {
+	if numSrcs > 1 {
 		ctx.PropertyErrorf("srcs", incorrectSourcesError)
 	}
+
+	// If a main source file is not provided we expect only a single SourceProvider module to be defined
+	// within srcs, with the expectation that the first source it provides is the entry point.
 	if srcIndex != 0 {
 		ctx.PropertyErrorf("srcs", "main source file must be the first in srcs")
+	} else if numSrcs > 1 {
+		ctx.PropertyErrorf("srcs", "only a single generated source module can be defined without a main source file.")
 	}
+
 	paths := android.PathsForModuleSrc(ctx, srcs)
 	return paths[srcIndex], paths[1:]
 }
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 1258684..9342d75 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -144,7 +144,7 @@
   run_soong
   local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
 
-  local glob_deps_file=out/soong/.primary/globs/0.d
+  local glob_deps_file=out/soong/.bootstrap/globs/0.d
 
   if [ -e "$glob_deps_file" ]; then
     fail "Glob deps file unexpectedly written on first build"
@@ -526,18 +526,14 @@
   [[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
 }
 
-function test_bp2build_generates_fake_ninja_file {
+function test_bp2build_generates_marker_file {
   setup
   create_mock_bazel
 
   run_bp2build
 
-  if [[ ! -f "./out/soong/build.ninja" ]]; then
-    fail "./out/soong/build.ninja was not generated"
-  fi
-
-  if ! grep "build nothing: phony" "./out/soong/build.ninja"; then
-    fail "missing phony nothing target in out/soong/build.ninja"
+  if [[ ! -f "./out/soong/.bootstrap/bp2build_workspace_marker" ]]; then
+    fail "Marker file was not generated"
   fi
 }
 
@@ -577,10 +573,10 @@
   setup
 
   GENERATE_BAZEL_FILES=1 run_soong
-  local mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
 
   GENERATE_BAZEL_FILES=1 run_soong
-  local mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
 
   if [[ "$mtime1" != "$mtime2" ]]; then
     fail "Output Ninja file changed on null build"
@@ -712,6 +708,41 @@
   grep -q "b/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for b/${GENERATED_BUILD_FILE_NAME} not found"
 }
 
+function test_bp2build_back_and_forth_null_build {
+  setup
+
+  run_soong
+  local output_mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  GENERATE_BAZEL_FILES=1 run_soong
+  local output_mtime2=$(stat -c "%y" out/soong/build.ninja)
+  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
+    fail "Output Ninja file changed when switching to bp2build"
+  fi
+
+  local marker_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+  run_soong
+  local output_mtime3=$(stat -c "%y" out/soong/build.ninja)
+  local marker_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  if [[ "$output_mtime1" != "$output_mtime3" ]]; then
+    fail "Output Ninja file changed when switching to regular build from bp2build"
+  fi
+  if [[ "$marker_mtime1" != "$marker_mtime2" ]]; then
+    fail "bp2build marker file changed when switching to regular build from bp2build"
+  fi
+
+  GENERATE_BAZEL_FILES=1 run_soong
+  local output_mtime4=$(stat -c "%y" out/soong/build.ninja)
+  local marker_mtime3=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  if [[ "$output_mtime1" != "$output_mtime4" ]]; then
+    fail "Output Ninja file changed when switching back to bp2build"
+  fi
+  if [[ "$marker_mtime1" != "$marker_mtime3" ]]; then
+    fail "bp2build marker file changed when switching back to bp2build"
+  fi
+}
+
 test_smoke
 test_null_build
 test_null_build_after_docs
@@ -727,8 +758,9 @@
 test_dump_json_module_graph
 test_write_to_source_tree
 test_bp2build_smoke
-test_bp2build_generates_fake_ninja_file
+test_bp2build_generates_marker_file
 test_bp2build_null_build
+test_bp2build_back_and_forth_null_build
 test_bp2build_add_android_bp
 test_bp2build_add_to_glob
 test_bp2build_bazel_workspace_structure
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index b5c6863..9bd85a4 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -48,60 +48,6 @@
 
 test_bp2build_null_build_with_globs
 
-function test_soong_after_bp2build_regenerates_build_globs_ninja() {
-  setup
-
-  mkdir -p foo/bar
-  cat > foo/bar/Android.bp <<'EOF'
-filegroup {
-    name: "bp2build-files",
-    srcs: ["bp2build.*"],
-    bazel_module: { bp2build_available: true },
-}
-
-filegroup {
-    name: "soong-files",
-    srcs: ["soong.*"],
-}
-EOF
-  touch foo/bar/bp2build.txt foo/bar/soong.txt
-
-  build_globs_file="out/soong/.bootstrap/build-globs.ninja"
-
-  # Test: the build-globs file for bp2build should only contain the bp2build-files
-  # glob, whereas the build-globs file for soong should contain both bp2build-files
-  # and soong-files globs.
-
-  run_bp2build
-  local output_mtime1=$(stat -c "%y" "${build_globs_file}")
-
-  if ! grep "\"foo/bar/bp2build.*\"" "${build_globs_file}"; then
-    fail "bp2build filegroup globs not found in bp2build's globs file"
-  fi
-
-  if grep "\"foo/bar/soong.*\"" "${build_globs_file}"; then
-    fail "soong filegroup globs unexpectedly found in bp2build's globs file"
-  fi
-
-  run_soong
-  local output_mtime2=$(stat -c "%y" "${build_globs_file}")
-
-  if [[ "$output_mtime1" == "$output_mtime2" ]]; then
-    fail "Output build-globs.ninja file did not change"
-  fi
-
-  if ! grep "\"foo/bar/bp2build.*\"" "${build_globs_file}"; then
-    fail "bp2build filegroup globs not found in bp2build's globs file"
-  fi
-
-  if ! grep "\"foo/bar/soong.*\"" "${build_globs_file}"; then
-    fail "soong filegroup globs not found in bp2build's globs file"
-  fi
-
-}
-
-test_soong_after_bp2build_regenerates_build_globs_ninja
-
 function test_bp2build_generates_all_buildfiles {
   setup
   create_mock_bazel
diff --git a/ui/build/config.go b/ui/build/config.go
index 9768a44..956406d 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -739,6 +739,14 @@
 	return filepath.Join(c.OutDir(), "soong")
 }
 
+func (c *configImpl) MainNinjaFile() string {
+	return shared.JoinPath(c.SoongOutDir(), "build.ninja")
+}
+
+func (c *configImpl) Bp2BuildMarkerFile() string {
+	return shared.JoinPath(c.SoongOutDir(), ".bootstrap/bp2build_workspace_marker")
+}
+
 func (c *configImpl) TempDir() string {
 	return shared.TempDirForOutDir(c.SoongOutDir())
 }
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 190c955..8ef8c74 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -71,16 +71,11 @@
 // 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 {
-	srcDir           string
 	buildDir         string
 	ninjaBuildDir    string
 	debugCompilation bool
 }
 
-func (c BlueprintConfig) SrcDir() string {
-	return "."
-}
-
 func (c BlueprintConfig) BuildDir() string {
 	return c.buildDir
 }
@@ -114,17 +109,20 @@
 	}
 }
 
-func bootstrapBlueprint(ctx Context, config Config, integratedBp2Build bool) {
+func bootstrapBlueprint(ctx Context, config Config) {
 	ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
 	defer ctx.EndTrace()
 
 	var args bootstrap.Args
 
-	mainNinjaFile := shared.JoinPath(config.SoongOutDir(), "build.ninja")
 	bootstrapGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.ninja")
-	// .bootstrap/build.ninja "includes" .bootstrap/build-globs.ninja for incremental builds
-	// generate an empty glob before running any rule in .bootstrap/build.ninja
+	bp2buildGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.bp2build.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
+	// fail on clean builds
 	writeEmptyGlobFile(ctx, bootstrapGlobFile)
+	writeEmptyGlobFile(ctx, bp2buildGlobFile)
 	bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d")
 
 	args.RunGoTests = !config.skipSoongTests
@@ -137,7 +135,7 @@
 	// 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.GlobFile = ""
+	args.Subninjas = []string{bootstrapGlobFile, bp2buildGlobFile}
 	args.GeneratingPrimaryBuilder = true
 	args.EmptyNinjaFile = config.EmptyNinjaFile()
 
@@ -146,48 +144,51 @@
 		args.DelvePath = shared.ResolveDelveBinary()
 	}
 
-	commonArgs := bootstrap.PrimaryBuilderExtraFlags(args, bootstrapGlobFile, mainNinjaFile)
-	bp2BuildMarkerFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/bp2build_workspace_marker")
+	commonArgs := bootstrap.PrimaryBuilderExtraFlags(args, config.MainNinjaFile())
 	mainSoongBuildInputs := []string{"Android.bp"}
 
-	if integratedBp2Build {
-		mainSoongBuildInputs = append(mainSoongBuildInputs, bp2BuildMarkerFile)
+	if config.bazelBuildMode() == mixedBuild {
+		mainSoongBuildInputs = append(mainSoongBuildInputs, config.Bp2BuildMarkerFile())
 	}
 
-	soongBuildArgs := make([]string, 0)
+	soongBuildArgs := []string{
+		"--globListDir", "globs",
+		"--globFile", bootstrapGlobFile,
+	}
+
 	soongBuildArgs = append(soongBuildArgs, commonArgs...)
 	soongBuildArgs = append(soongBuildArgs, environmentArgs(config, "")...)
 	soongBuildArgs = append(soongBuildArgs, "Android.bp")
 
 	mainSoongBuildInvocation := bootstrap.PrimaryBuilderInvocation{
 		Inputs:  mainSoongBuildInputs,
-		Outputs: []string{mainNinjaFile},
+		Outputs: []string{config.MainNinjaFile()},
 		Args:    soongBuildArgs,
 	}
 
-	if integratedBp2Build {
-		bp2buildArgs := []string{"--bp2build_marker", bp2BuildMarkerFile}
-		bp2buildArgs = append(bp2buildArgs, commonArgs...)
-		bp2buildArgs = append(bp2buildArgs, environmentArgs(config, ".bp2build")...)
-		bp2buildArgs = append(bp2buildArgs, "Android.bp")
+	bp2buildArgs := []string{
+		"--bp2build_marker", config.Bp2BuildMarkerFile(),
+		"--globListDir", "globs.bp2build",
+		"--globFile", bp2buildGlobFile,
+	}
 
-		bp2buildInvocation := bootstrap.PrimaryBuilderInvocation{
-			Inputs:  []string{"Android.bp"},
-			Outputs: []string{bp2BuildMarkerFile},
-			Args:    bp2buildArgs,
-		}
-		args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{bp2buildInvocation}
-		if config.bazelBuildMode() == mixedBuild {
-			args.PrimaryBuilderInvocations = append(args.PrimaryBuilderInvocations, mainSoongBuildInvocation)
-		}
-	} else {
-		args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{mainSoongBuildInvocation}
+	bp2buildArgs = append(bp2buildArgs, commonArgs...)
+	bp2buildArgs = append(bp2buildArgs, environmentArgs(config, ".bp2build")...)
+	bp2buildArgs = append(bp2buildArgs, "Android.bp")
+
+	bp2buildInvocation := bootstrap.PrimaryBuilderInvocation{
+		Inputs:  []string{"Android.bp"},
+		Outputs: []string{config.Bp2BuildMarkerFile()},
+		Args:    bp2buildArgs,
+	}
+	args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{
+		bp2buildInvocation,
+		mainSoongBuildInvocation,
 	}
 
 	blueprintCtx := blueprint.NewContext()
 	blueprintCtx.SetIgnoreUnknownModuleTypes(true)
 	blueprintConfig := BlueprintConfig{
-		srcDir:           os.Getenv("TOP"),
 		buildDir:         config.SoongOutDir(),
 		ninjaBuildDir:    config.OutDir(),
 		debugCompilation: os.Getenv("SOONG_DELVE") != "",
@@ -220,18 +221,16 @@
 	// unused variables were changed?
 	envFile := filepath.Join(config.SoongOutDir(), availableEnvFile)
 
-	for _, n := range []string{".bootstrap", ".minibootstrap"} {
-		dir := filepath.Join(config.SoongOutDir(), n)
-		if err := os.MkdirAll(dir, 0755); err != nil {
-			ctx.Fatalf("Cannot mkdir " + dir)
-		}
+	dir := filepath.Join(config.SoongOutDir(), ".bootstrap")
+	if err := os.MkdirAll(dir, 0755); err != nil {
+		ctx.Fatalf("Cannot mkdir " + dir)
 	}
 
 	buildMode := config.bazelBuildMode()
 	integratedBp2Build := (buildMode == mixedBuild) || (buildMode == generateBuildFiles)
 
 	// This is done unconditionally, but does not take a measurable amount of time
-	bootstrapBlueprint(ctx, config, integratedBp2Build)
+	bootstrapBlueprint(ctx, config)
 
 	soongBuildEnv := config.Environment().Copy()
 	soongBuildEnv.Set("TOP", os.Getenv("TOP"))
@@ -265,10 +264,10 @@
 		}
 	}()
 
-	runMicrofactory(ctx, config, ".minibootstrap/bpglob", "github.com/google/blueprint/bootstrap/bpglob",
+	runMicrofactory(ctx, config, ".bootstrap/bpglob", "github.com/google/blueprint/bootstrap/bpglob",
 		map[string]string{"github.com/google/blueprint": "build/blueprint"})
 
-	ninja := func(name, file string) {
+	ninja := func(name, ninjaFile string, targets ...string) {
 		ctx.BeginTrace(metrics.RunSoong, name)
 		defer ctx.EndTrace()
 
@@ -276,8 +275,7 @@
 		nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
 		defer nr.Close()
 
-		cmd := Command(ctx, config, "soong "+name,
-			config.PrebuiltBuildTool("ninja"),
+		ninjaArgs := []string{
 			"-d", "keepdepfile",
 			"-d", "stats",
 			"-o", "usesphonyoutputs=yes",
@@ -287,7 +285,12 @@
 			"-w", "missingoutfile=err",
 			"-j", strconv.Itoa(config.Parallel()),
 			"--frontend_file", fifo,
-			"-f", filepath.Join(config.SoongOutDir(), file))
+			"-f", filepath.Join(config.SoongOutDir(), ninjaFile),
+		}
+
+		ninjaArgs = append(ninjaArgs, targets...)
+		cmd := Command(ctx, config, "soong "+name,
+			config.PrebuiltBuildTool("ninja"), ninjaArgs...)
 
 		var ninjaEnv Environment
 
@@ -299,8 +302,17 @@
 		cmd.Sandbox = soongSandbox
 		cmd.RunAndStreamOrFatal()
 	}
-	// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
-	ninja("bootstrap", ".bootstrap/build.ninja")
+
+	var target string
+
+	if config.bazelBuildMode() == generateBuildFiles {
+		target = config.Bp2BuildMarkerFile()
+	} else {
+		// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
+		target = config.MainNinjaFile()
+	}
+
+	ninja("bootstrap", ".bootstrap/build.ninja", target)
 
 	var soongBuildMetrics *soong_metrics_proto.SoongBuildMetrics
 	if shouldCollectBuildSoongMetrics(config) {
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index a910c06..57ceaba 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -65,7 +65,6 @@
 
 	outDir := config.OutDir()
 	bootstrapDir := filepath.Join(outDir, "soong", ".bootstrap")
-	miniBootstrapDir := filepath.Join(outDir, "soong", ".minibootstrap")
 	modulePathsDir := filepath.Join(outDir, ".module_paths")
 	variablesFilePath := filepath.Join(outDir, "soong", "soong.variables")
 
@@ -89,7 +88,6 @@
 			continue
 		}
 		if strings.HasPrefix(line, bootstrapDir) ||
-			strings.HasPrefix(line, miniBootstrapDir) ||
 			strings.HasPrefix(line, modulePathsDir) ||
 			line == variablesFilePath ||
 			line == dexpreoptConfigFilePath ||