Merge changes from topic "rule_builder_rsp"

* changes:
  Remove unescaped spans support from RuleBuilder
  Add explicit rspfile argument to RuleBuilderCommand.FlagWithRspFileInputList
  Ninja escape RuleBuilder rule params
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index bbec389..0595d68 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -717,7 +717,7 @@
 
 	// Add ninja file dependencies for files which all bazel invocations require.
 	bazelBuildList := absolutePath(filepath.Join(
-		filepath.Dir(bootstrap.ModuleListFile), "bazel.list"))
+		filepath.Dir(bootstrap.CmdlineModuleListFile()), "bazel.list"))
 	ctx.AddNinjaFileDeps(bazelBuildList)
 
 	data, err := ioutil.ReadFile(bazelBuildList)
diff --git a/android/config.go b/android/config.go
index 614386f..f4685a1 100644
--- a/android/config.go
+++ b/android/config.go
@@ -68,6 +68,14 @@
 	return c.buildDir
 }
 
+func (c Config) NinjaBuildDir() string {
+	return c.buildDir
+}
+
+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.
@@ -498,6 +506,10 @@
 	c.stopBefore = stopBefore
 }
 
+func (c *config) SetAllowMissingDependencies() {
+	c.productVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+}
+
 var _ bootstrap.ConfigStopBefore = (*config)(nil)
 
 // BlueprintToolLocation returns the directory containing build system tools
diff --git a/bootstrap_test.sh b/bootstrap_test.sh
new file mode 100755
index 0000000..68067ee
--- /dev/null
+++ b/bootstrap_test.sh
@@ -0,0 +1,376 @@
+#!/bin/bash -eu
+
+# This test exercises the bootstrapping process of the build system
+# in a source tree that only contains enough files for Bazel and Soong to work.
+
+HARDWIRED_MOCK_TOP=
+# Uncomment this for to be able to view the source tree after a test is run
+# HARDWIRED_MOCK_TOP=/tmp/td
+
+REAL_TOP="$(readlink -f "$(dirname "$0")"/../..)"
+
+function fail {
+  echo ERROR: $1
+  exit 1
+}
+
+function copy_directory() {
+  local dir="$1"
+  local parent="$(dirname "$dir")"
+
+  mkdir -p "$MOCK_TOP/$parent"
+  cp -R "$REAL_TOP/$dir" "$MOCK_TOP/$parent"
+}
+
+function symlink_file() {
+  local file="$1"
+
+  mkdir -p "$MOCK_TOP/$(dirname "$file")"
+  ln -s "$REAL_TOP/$file" "$MOCK_TOP/$file"
+}
+
+function symlink_directory() {
+  local dir="$1"
+
+  mkdir -p "$MOCK_TOP/$dir"
+  # We need to symlink the contents of the directory individually instead of
+  # using one symlink for the whole directory because finder.go doesn't follow
+  # symlinks when looking for Android.bp files
+  for i in $(ls "$REAL_TOP/$dir"); do
+    local target="$MOCK_TOP/$dir/$i"
+    local source="$REAL_TOP/$dir/$i"
+
+    if [[ -e "$target" ]]; then
+      if [[ ! -d "$source" || ! -d "$target" ]]; then
+        fail "Trying to symlink $dir twice"
+      fi
+    else
+      ln -s "$REAL_TOP/$dir/$i" "$MOCK_TOP/$dir/$i";
+    fi
+  done
+}
+
+function setup_bazel() {
+  copy_directory build/bazel
+
+  symlink_directory prebuilts/bazel
+  symlink_directory prebuilts/jdk
+
+  symlink_file WORKSPACE
+  symlink_file tools/bazel
+}
+
+function setup() {
+  if [[ ! -z "$HARDWIRED_MOCK_TOP" ]]; then
+    MOCK_TOP="$HARDWIRED_MOCK_TOP"
+    rm -fr "$MOCK_TOP"
+    mkdir -p "$MOCK_TOP"
+  else
+    MOCK_TOP=$(mktemp -t -d st.XXXXX)
+    trap 'echo cd / && echo rm -fr "$MOCK_TOP"' EXIT
+  fi
+
+  echo "Test case: ${FUNCNAME[1]}, mock top path: $MOCK_TOP"
+  cd "$MOCK_TOP"
+
+  copy_directory build/blueprint
+  copy_directory build/soong
+
+  symlink_directory prebuilts/go
+  symlink_directory prebuilts/build-tools
+  symlink_directory external/golang-protobuf
+
+  touch "$MOCK_TOP/Android.bp"
+
+  export ALLOW_MISSING_DEPENDENCIES=true
+
+  mkdir -p out/soong
+  # This is necessary because the empty soong.variables file written to satisfy
+  # Ninja would contain "BootJars: {}" instead of "BootJars: []" which cannot
+  # be parsed back
+  # TODO(b/182965747): Fix this.
+  cat > out/soong/soong.variables <<'EOF'
+{
+    "BuildNumberFile": "build_number.txt",
+    "Platform_version_name": "S",
+    "Platform_sdk_version": 30,
+    "Platform_sdk_codename": "S",
+    "Platform_sdk_final": false,
+    "Platform_version_active_codenames": [
+        "S"
+    ],
+    "Platform_vndk_version": "S",
+    "DeviceName": "generic_arm64",
+    "DeviceArch": "arm64",
+    "DeviceArchVariant": "armv8-a",
+    "DeviceCpuVariant": "generic",
+    "DeviceAbi": [
+        "arm64-v8a"
+    ],
+    "DeviceSecondaryArch": "arm",
+    "DeviceSecondaryArchVariant": "armv8-a",
+    "DeviceSecondaryCpuVariant": "generic",
+    "DeviceSecondaryAbi": [
+        "armeabi-v7a",
+        "armeabi"
+    ],
+    "HostArch": "x86_64",
+    "HostSecondaryArch": "x86",
+    "CrossHost": "windows",
+    "CrossHostArch": "x86",
+    "CrossHostSecondaryArch": "x86_64",
+    "AAPTCharacteristics": "nosdcard",
+    "AAPTConfig": [
+        "normal",
+        "large",
+        "xlarge",
+        "hdpi",
+        "xhdpi",
+        "xxhdpi"
+    ],
+    "AAPTPreferredConfig": "xhdpi",
+    "AAPTPrebuiltDPI": [
+        "xhdpi",
+        "xxhdpi"
+    ],
+    "Malloc_not_svelte": true,
+    "Malloc_zero_contents": true,
+    "Malloc_pattern_fill_contents": false,
+    "Safestack": false,
+    "BootJars": [],
+    "UpdatableBootJars": [],
+    "Native_coverage": null
+}
+EOF
+}
+
+function run_soong() {
+  build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests
+}
+
+function test_smoke {
+  setup
+  run_soong
+}
+
+function test_bazel_smoke {
+  setup
+  setup_bazel
+
+  tools/bazel info
+
+}
+function test_null_build() {
+  setup
+  run_soong
+  local bootstrap_mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+  local output_mtime1=$(stat -c "%y" out/soong/build.ninja)
+  run_soong
+  local bootstrap_mtime2=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+  local output_mtime2=$(stat -c "%y" out/soong/build.ninja)
+
+  if [[ "$bootstrap_mtime1" == "$bootstrap_mtime2" ]]; then
+    # Bootstrapping is always done. It doesn't take a measurable amount of time.
+    fail "Bootstrap Ninja file did not change on null build"
+  fi
+
+  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
+    fail "Output Ninja file changed on null build"
+  fi
+}
+
+function test_soong_build_rebuilt_if_blueprint_changes() {
+  setup
+  run_soong
+  local mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+
+  sed -i 's/pluginGenSrcCmd/pluginGenSrcCmd2/g' build/blueprint/bootstrap/bootstrap.go
+
+  run_soong
+  local mtime2=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+
+  if [[ "$mtime1" == "$mtime2" ]]; then
+    fail "Bootstrap Ninja file did not change"
+  fi
+}
+
+function test_change_android_bp() {
+  setup
+  mkdir -p a
+  cat > a/Android.bp <<'EOF'
+python_binary_host {
+  name: "my_little_binary_host",
+  srcs: ["my_little_binary_host.py"]
+}
+EOF
+  touch a/my_little_binary_host.py
+  run_soong
+
+  grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja || fail "module not found"
+
+  cat > a/Android.bp <<'EOF'
+python_binary_host {
+  name: "my_great_binary_host",
+  srcs: ["my_great_binary_host.py"]
+}
+EOF
+  touch a/my_great_binary_host.py
+  run_soong
+
+  grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja && fail "old module found"
+  grep -q "^# Module:.*my_great_binary_host" out/soong/build.ninja || fail "new module not found"
+}
+
+
+function test_add_android_bp() {
+  setup
+  run_soong
+  local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  mkdir -p a
+  cat > a/Android.bp <<'EOF'
+python_binary_host {
+  name: "my_little_binary_host",
+  srcs: ["my_little_binary_host.py"]
+}
+EOF
+  touch a/my_little_binary_host.py
+  run_soong
+
+  local mtime2=$(stat -c "%y" out/soong/build.ninja)
+  if [[ "$mtime1" == "$mtime2" ]]; then
+    fail "Output Ninja file did not change"
+  fi
+
+  grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "New module not in output"
+
+  run_soong
+}
+
+function test_delete_android_bp() {
+  setup
+  mkdir -p a
+  cat > a/Android.bp <<'EOF'
+python_binary_host {
+  name: "my_little_binary_host",
+  srcs: ["my_little_binary_host.py"]
+}
+EOF
+  touch a/my_little_binary_host.py
+  run_soong
+
+  grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "Module not in output"
+
+  rm a/Android.bp
+  run_soong
+
+  grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja && fail "Old module in output"
+}
+
+function test_add_file_to_glob() {
+  setup
+
+  mkdir -p a
+  cat > a/Android.bp <<'EOF'
+python_binary_host {
+  name: "my_little_binary_host",
+  srcs: ["*.py"],
+}
+EOF
+  touch a/my_little_binary_host.py
+  run_soong
+  local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  touch a/my_little_library.py
+  run_soong
+
+  local mtime2=$(stat -c "%y" out/soong/build.ninja)
+  if [[ "$mtime1" == "$mtime2" ]]; then
+    fail "Output Ninja file did not change"
+  fi
+
+  grep -q my_little_library.py out/soong/build.ninja || fail "new file is not in output"
+}
+
+function test_add_file_to_soong_build() {
+  setup
+  run_soong
+  local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  mkdir -p a
+  cat > a/Android.bp <<'EOF'
+bootstrap_go_package {
+  name: "picard-soong-rules",
+  pkgPath: "android/soong/picard",
+  deps: [
+    "blueprint",
+    "soong",
+    "soong-android",
+  ],
+  srcs: [
+    "picard.go",
+  ],
+  pluginFor: ["soong_build"],
+}
+EOF
+
+  cat > a/picard.go <<'EOF'
+package picard
+
+import (
+  "android/soong/android"
+  "github.com/google/blueprint"
+)
+
+var (
+  pctx = android.NewPackageContext("picard")
+)
+
+func init() {
+  android.RegisterSingletonType("picard", PicardSingleton)
+}
+
+func PicardSingleton() android.Singleton {
+  return &picardSingleton{}
+}
+
+type picardSingleton struct{}
+
+func (p *picardSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+  picardRule := ctx.Rule(pctx, "picard",
+    blueprint.RuleParams{
+      Command: "echo Make it so. > ${out}",
+      CommandDeps: []string{},
+      Description: "Something quotable",
+    })
+
+  outputFile := android.PathForOutput(ctx, "picard", "picard.txt")
+  var deps android.Paths
+
+  ctx.Build(pctx, android.BuildParams{
+    Rule: picardRule,
+    Output: outputFile,
+    Inputs: deps,
+  })
+}
+
+EOF
+
+  run_soong
+  local mtime2=$(stat -c "%y" out/soong/build.ninja)
+  if [[ "$mtime1" == "$mtime2" ]]; then
+    fail "Output Ninja file did not change"
+  fi
+
+  grep -q "Make it so" out/soong/build.ninja || fail "New action not present"
+}
+
+test_bazel_smoke
+test_smoke
+test_null_build
+test_soong_build_rebuilt_if_blueprint_changes
+test_add_file_to_glob
+test_add_android_bp
+test_change_android_bp
+test_delete_android_bp
+test_add_file_to_soong_build
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 8322fbe..11d3620 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -80,7 +80,7 @@
 }
 
 func newConfig(srcDir string) android.Config {
-	configuration, err := android.NewConfig(srcDir, bootstrap.BuildDir, bootstrap.ModuleListFile)
+	configuration, err := android.NewConfig(srcDir, bootstrap.CmdlineBuildDir(), bootstrap.CmdlineModuleListFile())
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
@@ -101,6 +101,10 @@
 	configuration := newConfig(srcDir)
 	extraNinjaDeps := []string{configuration.ProductVariablesFileName}
 
+	if configuration.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
+		configuration.SetAllowMissingDependencies()
+	}
+
 	// These two are here so that we restart a non-debugged soong_build when the
 	// user sets SOONG_DELVE the first time.
 	configuration.Getenv("SOONG_DELVE")
@@ -127,7 +131,7 @@
 		// the incorrect results from the first pass, and file I/O is expensive.
 		firstCtx := newContext(configuration)
 		configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
-		bootstrap.Main(firstCtx.Context, configuration, extraNinjaDeps...)
+		bootstrap.Main(firstCtx.Context, configuration, false, extraNinjaDeps...)
 		// Invoke bazel commands and save results for second pass.
 		if err := configuration.BazelContext.InvokeBazel(); err != nil {
 			fmt.Fprintf(os.Stderr, "%s", err)
@@ -140,10 +144,10 @@
 			os.Exit(1)
 		}
 		ctx = newContext(secondPassConfig)
-		bootstrap.Main(ctx.Context, secondPassConfig, extraNinjaDeps...)
+		bootstrap.Main(ctx.Context, secondPassConfig, false, extraNinjaDeps...)
 	} else {
 		ctx = newContext(configuration)
-		bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...)
+		bootstrap.Main(ctx.Context, configuration, false, extraNinjaDeps...)
 	}
 
 	// Convert the Soong module graph into Bazel BUILD files.
@@ -158,7 +162,7 @@
 	}
 
 	if docFile != "" {
-		if err := writeDocs(ctx, docFile); err != nil {
+		if err := writeDocs(ctx, configuration, docFile); err != nil {
 			fmt.Fprintf(os.Stderr, "%s", err)
 			os.Exit(1)
 		}
@@ -167,7 +171,7 @@
 	// TODO(ccross): make this a command line argument.  Requires plumbing through blueprint
 	//  to affect the command line of the primary builder.
 	if shouldPrepareBuildActions(configuration) {
-		metricsFile := filepath.Join(bootstrap.BuildDir, "soong_build_metrics.pb")
+		metricsFile := filepath.Join(bootstrap.CmdlineBuildDir(), "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)
@@ -193,7 +197,7 @@
 	// 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.ModuleListFile)
+	bp2buildCtx.SetModuleListFile(bootstrap.CmdlineModuleListFile())
 	extraNinjaDeps, err := bp2buildCtx.ListModulePaths(srcDir)
 	if err != nil {
 		panic(err)
@@ -202,7 +206,7 @@
 	// 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.
-	bootstrap.Main(bp2buildCtx.Context, configuration, extraNinjaDeps...)
+	bootstrap.Main(bp2buildCtx.Context, configuration, false, extraNinjaDeps...)
 
 	// Run the code-generation phase to convert BazelTargetModules to BUILD files
 	// and print conversion metrics to the user.
diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go
index f2c2c9b..a69de6a 100644
--- a/cmd/soong_build/writedocs.go
+++ b/cmd/soong_build/writedocs.go
@@ -95,13 +95,13 @@
 	return result
 }
 
-func getPackages(ctx *android.Context) ([]*bpdoc.Package, error) {
+func getPackages(ctx *android.Context, config interface{}) ([]*bpdoc.Package, error) {
 	moduleTypeFactories := android.ModuleTypeFactoriesForDocs()
-	return bootstrap.ModuleTypeDocs(ctx.Context, moduleTypeFactories)
+	return bootstrap.ModuleTypeDocs(ctx.Context, config, moduleTypeFactories)
 }
 
-func writeDocs(ctx *android.Context, filename string) error {
-	packages, err := getPackages(ctx)
+func writeDocs(ctx *android.Context, config interface{}, filename string) error {
+	packages, err := getPackages(ctx, config)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 1c5e78a..390a9ec 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -70,7 +70,7 @@
 			return build.NewConfig(ctx, args...)
 		},
 		stdio: stdio,
-		run:   make,
+		run:   runMake,
 	}, {
 		flag:         "--dumpvar-mode",
 		description:  "print the value of the legacy make variable VAR to stdout",
@@ -92,7 +92,7 @@
 		description: "build modules based on the specified build action",
 		config:      buildActionConfig,
 		stdio:       stdio,
-		run:         make,
+		run:         runMake,
 	},
 }
 
@@ -478,7 +478,7 @@
 	return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
 }
 
-func make(ctx build.Context, config build.Config, _ []string, logsDir string) {
+func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) {
 	if config.IsVerbose() {
 		writer := ctx.Writer
 		fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
diff --git a/java/boot_image.go b/java/boot_image.go
index 12e2874..25a4f17 100644
--- a/java/boot_image.go
+++ b/java/boot_image.go
@@ -20,6 +20,7 @@
 
 	"android/soong/android"
 	"android/soong/dexpreopt"
+
 	"github.com/google/blueprint"
 )
 
@@ -56,9 +57,9 @@
 func bootImageFactory() android.Module {
 	m := &BootImageModule{}
 	m.AddProperties(&m.properties)
-	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitApexModule(m)
 	android.InitSdkAwareModule(m)
+	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
 	return m
 }
 
@@ -210,11 +211,11 @@
 func prebuiltBootImageFactory() android.Module {
 	m := &prebuiltBootImageModule{}
 	m.AddProperties(&m.properties)
-	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
 	// This doesn't actually have any prebuilt files of its own so pass a placeholder for the srcs
 	// array.
 	android.InitPrebuiltModule(m, &[]string{"placeholder"})
 	android.InitApexModule(m)
 	android.InitSdkAwareModule(m)
+	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
 	return m
 }
diff --git a/java/java.go b/java/java.go
index 6982f52..8c714ee 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2520,6 +2520,10 @@
 	j.deps(ctx)
 }
 
+func (j *TestHost) AddExtraResource(p android.Path) {
+	j.extraResources = append(j.extraResources, p)
+}
+
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if j.testProperties.Test_options.Unit_test == nil && ctx.Host() {
 		// TODO(b/): Clean temporary heuristic to avoid unexpected onboarding.
@@ -2683,13 +2687,23 @@
 	module.AddProperties(&module.testProperties)
 	module.AddProperties(&module.testHostProperties)
 
-	module.Module.properties.Installable = proptools.BoolPtr(true)
+	InitTestHost(
+		module,
+		proptools.BoolPtr(true),
+		nil,
+		nil)
 
 	InitJavaModuleMultiTargets(module, android.HostSupported)
 
 	return module
 }
 
+func InitTestHost(th *TestHost, installable *bool, testSuites []string, autoGenConfig *bool) {
+	th.properties.Installable = installable
+	th.testProperties.Auto_gen_config = autoGenConfig
+	th.testProperties.Test_suites = testSuites
+}
+
 //
 // Java Binaries (.jar file plus wrapper script)
 //
diff --git a/java/java_test.go b/java/java_test.go
index 1c2ed2e..2eb7241 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -2601,3 +2601,16 @@
 		t.Errorf("Unexpected test data - expected: %q, actual: %q", expected, actual)
 	}
 }
+
+func TestDefaultInstallable(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_test_host {
+			name: "foo"
+		}
+	`)
+
+	buildOS := android.BuildOs.String()
+	module := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
+	assertDeepEquals(t, "Default installable value should be true.", proptools.BoolPtr(true),
+		module.properties.Installable)
+}
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 32b6eda..d17b464 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -32,6 +32,8 @@
     name: "soong-ui-build",
     pkgPath: "android/soong/ui/build",
     deps: [
+        "blueprint",
+        "blueprint-bootstrap",
         "soong-ui-build-paths",
         "soong-ui-logger",
         "soong-ui-metrics",
diff --git a/ui/build/build.go b/ui/build/build.go
index 215a6c8..3692f4f 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -218,6 +218,11 @@
 		what = what &^ BuildKati
 	}
 
+	if config.SkipNinja() {
+		ctx.Verboseln("Skipping Ninja as requested")
+		what = what &^ BuildNinja
+	}
+
 	if config.StartGoma() {
 		// Ensure start Goma compiler_proxy
 		startGoma(ctx, config)
@@ -290,7 +295,7 @@
 		}
 
 		// Run ninja
-		runNinja(ctx, config)
+		runNinjaForBuild(ctx, config)
 	}
 
 	// Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last.
diff --git a/ui/build/config.go b/ui/build/config.go
index 1152cd7..4816d1f 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -48,6 +48,7 @@
 	dist           bool
 	skipConfig     bool
 	skipKati       bool
+	skipNinja      bool
 	skipSoongTests bool
 
 	// From the product config
@@ -552,6 +553,8 @@
 		if arg == "--make-mode" {
 		} else if arg == "showcommands" {
 			c.verbose = true
+		} else if arg == "--skip-ninja" {
+			c.skipNinja = true
 		} else if arg == "--skip-make" {
 			c.skipConfig = true
 			c.skipKati = true
@@ -772,6 +775,10 @@
 	return c.skipKati
 }
 
+func (c *configImpl) SkipNinja() bool {
+	return c.skipNinja
+}
+
 func (c *configImpl) SkipConfig() bool {
 	return c.skipConfig
 }
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 7799766..893fd6d 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -30,7 +30,7 @@
 // Constructs and runs the Ninja command line with a restricted set of
 // environment variables. It's important to restrict the environment Ninja runs
 // for hermeticity reasons, and to avoid spurious rebuilds.
-func runNinja(ctx Context, config Config) {
+func runNinjaForBuild(ctx Context, config Config) {
 	ctx.BeginTrace(metrics.PrimaryNinja, "ninja")
 	defer ctx.EndTrace()
 
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 884e957..fee5723 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -23,6 +23,8 @@
 	"android/soong/shared"
 
 	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/bootstrap"
 
 	"github.com/golang/protobuf/proto"
 	"github.com/google/blueprint/microfactory"
@@ -42,17 +44,72 @@
 
 // This uses Android.bp files and various tools to generate <builddir>/build.ninja.
 //
-// However, the execution of <builddir>/build.ninja happens later in build/soong/ui/build/build.go#Build()
+// However, the execution of <builddir>/build.ninja happens later in
+// build/soong/ui/build/build.go#Build()
 //
-// We want to rely on as few prebuilts as possible, so there is some bootstrapping here.
+// We want to rely on as few prebuilts as possible, so we need to bootstrap
+// Soong. The process is as follows:
 //
-// "Microfactory" is a tool for compiling Go code. We use it to build two other tools:
-// - minibp, used to generate build.ninja files. This is really build/blueprint/bootstrap/command.go#Main()
-// - bpglob, used during incremental builds to identify files in a glob that have changed
+// 1. We use "Microfactory", a simple tool to compile Go code, to build
+//    first itself, then soong_ui from soong_ui.bash. This binary contains
+//    parts of soong_build that are needed to build itself.
+// 2. This simplified version of soong_build then reads the Blueprint files
+//    that describe itself and emits .bootstrap/build.ninja that describes
+//    how to build its full version and use that to produce the final Ninja
+//    file Soong emits.
+// 3. soong_ui executes .bootstrap/build.ninja
 //
-// In reality, several build.ninja files are generated and/or used during the bootstrapping and build process.
-// See build/blueprint/bootstrap/doc.go for more information.
-//
+// (After this, Kati is executed to parse the Makefiles, but that's not part of
+// bootstrapping Soong)
+
+// 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
+}
+
+func (c BlueprintConfig) SrcDir() string {
+	return "."
+}
+
+func (c BlueprintConfig) BuildDir() string {
+	return c.buildDir
+}
+
+func (c BlueprintConfig) NinjaBuildDir() string {
+	return c.ninjaBuildDir
+}
+
+func bootstrapBlueprint(ctx Context, config Config) {
+	ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
+	defer ctx.EndTrace()
+
+	var args bootstrap.Args
+
+	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.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
+	args.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja")
+	args.DepFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d")
+	args.GlobFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/soong-build-globs.ninja")
+	args.GeneratingPrimaryBuilder = true
+
+	blueprintCtx := blueprint.NewContext()
+	blueprintCtx.SetIgnoreUnknownModuleTypes(true)
+	blueprintConfig := BlueprintConfig{
+		srcDir:        os.Getenv("TOP"),
+		buildDir:      config.SoongOutDir(),
+		ninjaBuildDir: config.OutDir(),
+	}
+
+	bootstrap.RunBlueprint(args, blueprintCtx, blueprintConfig)
+}
+
 func runSoong(ctx Context, config Config) {
 	ctx.BeginTrace(metrics.RunSoong, "soong")
 	defer ctx.EndTrace()
@@ -63,33 +120,15 @@
 	// unused variables were changed?
 	envFile := filepath.Join(config.SoongOutDir(), "soong.environment.available")
 
-	// Use an anonymous inline function for tracing purposes (this pattern is used several times below).
-	func() {
-		ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
-		defer ctx.EndTrace()
-
-		// Use validations to depend on tests.
-		args := []string{"-n"}
-
-		if !config.skipSoongTests {
-			// Run tests.
-			args = append(args, "-t")
+	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)
 		}
+	}
 
-		cmd := Command(ctx, config, "blueprint bootstrap", "build/blueprint/bootstrap.bash", args...)
-
-		cmd.Environment.Set("BLUEPRINTDIR", "./build/blueprint")
-		cmd.Environment.Set("BOOTSTRAP", "./build/blueprint/bootstrap.bash")
-		cmd.Environment.Set("BUILDDIR", config.SoongOutDir())
-		cmd.Environment.Set("GOROOT", "./"+filepath.Join("prebuilts/go", config.HostPrebuiltTag()))
-		cmd.Environment.Set("BLUEPRINT_LIST_FILE", filepath.Join(config.FileListDir(), "Android.bp.list"))
-		cmd.Environment.Set("NINJA_BUILDDIR", config.OutDir())
-		cmd.Environment.Set("SRCDIR", ".")
-		cmd.Environment.Set("TOPNAME", "Android.bp")
-		cmd.Sandbox = soongSandbox
-
-		cmd.RunAndPrintOrFatal()
-	}()
+	// This is done unconditionally, but does not take a measurable amount of time
+	bootstrapBlueprint(ctx, config)
 
 	soongBuildEnv := config.Environment().Copy()
 	soongBuildEnv.Set("TOP", os.Getenv("TOP"))
@@ -105,6 +144,11 @@
 	soongBuildEnv.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
 	soongBuildEnv.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir())
 
+	// For Soong bootstrapping tests
+	if os.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
+		soongBuildEnv.Set("ALLOW_MISSING_DEPENDENCIES", "true")
+	}
+
 	err := writeEnvironmentFile(ctx, envFile, soongBuildEnv.AsMap())
 	if err != nil {
 		ctx.Fatalf("failed to write environment file %s: %s", envFile, err)
@@ -130,16 +174,6 @@
 	cfg.TrimPath = absPath(ctx, ".")
 
 	func() {
-		ctx.BeginTrace(metrics.RunSoong, "minibp")
-		defer ctx.EndTrace()
-
-		minibp := filepath.Join(config.SoongOutDir(), ".minibootstrap/minibp")
-		if _, err := microfactory.Build(&cfg, minibp, "github.com/google/blueprint/bootstrap/minibp"); err != nil {
-			ctx.Fatalln("Failed to build minibp:", err)
-		}
-	}()
-
-	func() {
 		ctx.BeginTrace(metrics.RunSoong, "bpglob")
 		defer ctx.EndTrace()
 
@@ -187,10 +221,6 @@
 		cmd.Sandbox = soongSandbox
 		cmd.RunAndStreamOrFatal()
 	}
-
-	// This build generates .bootstrap/build.ninja, which is used in the next step.
-	ninja("minibootstrap", ".minibootstrap/build.ninja")
-
 	// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
 	ninja("bootstrap", ".bootstrap/build.ninja")
 
diff --git a/zip/zip.go b/zip/zip.go
index f731329..088ed0d 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -406,6 +406,8 @@
 	buf := &bytes.Buffer{}
 	var out io.Writer = buf
 
+	var zipErr error
+
 	if !args.WriteIfChanged {
 		f, err := os.Create(args.OutputFilePath)
 		if err != nil {
@@ -414,7 +416,7 @@
 
 		defer f.Close()
 		defer func() {
-			if err != nil {
+			if zipErr != nil {
 				os.Remove(args.OutputFilePath)
 			}
 		}()
@@ -422,9 +424,9 @@
 		out = f
 	}
 
-	err := zipTo(args, out)
-	if err != nil {
-		return err
+	zipErr = zipTo(args, out)
+	if zipErr != nil {
+		return zipErr
 	}
 
 	if args.WriteIfChanged {