Embed minibp into soong_ui.

This requires linking Blueprint into soong_ui. It lets us avoid the
complicated dance of Ninja files and shell scripts: now the information
as to how soong_build is built is passed directly to Blueprint using a
struct that contains all the information the command line arguments used
to contain.

The ability to run Blueprint from the command line is kept (for now).

Some variables in bootstrap/command.go needed public accessor functions
because soong_build reads them. This will be disentangled by moving the
flag parsing to soong_build.

The presence of the flag definitions in Blueprint means that soong_ui
now also accepts them. This is not a problem in practice because they
are ignored and because soong_ui itself is hidden behind a few layers of
shell scripts.

Test: Presubmits + the new bootstrap_test.sh .
Change-Id: I6dca478f356f56a8aee1e457d71439272351390b
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