Merge "Allow running bp2build as part of a regular build."
diff --git a/apex/apex_test.go b/apex/apex_test.go
index f39c7e3..977a954 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4566,8 +4566,8 @@
 
 	checkHiddenAPIIndexInputs := func(t *testing.T, ctx *android.TestContext, expectedInputs string) {
 		t.Helper()
-		hiddenAPIIndex := ctx.SingletonForTests("hiddenapi_index")
-		indexRule := hiddenAPIIndex.Rule("singleton-merged-hiddenapi-index")
+		platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common")
+		indexRule := platformBootclasspath.Rule("platform-bootclasspath-monolithic-hiddenapi-index")
 		java.CheckHiddenAPIRuleInputs(t, expectedInputs, indexRule)
 	}
 
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 6ba5f35..ed0b722 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -24,7 +24,6 @@
 
 func RegisterHiddenApiSingletonComponents(ctx android.RegistrationContext) {
 	ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
-	ctx.RegisterSingletonType("hiddenapi_index", hiddenAPIIndexSingletonFactory)
 }
 
 var PrepareForTestWithHiddenApiBuildComponents = android.FixtureRegisterWithContext(RegisterHiddenApiSingletonComponents)
@@ -116,7 +115,7 @@
 }
 
 type hiddenAPISingleton struct {
-	flags, metadata android.Path
+	flags android.Path
 }
 
 // hiddenAPI singleton rules
@@ -138,30 +137,25 @@
 
 	if ctx.Config().PrebuiltHiddenApiDir(ctx) != "" {
 		h.flags = prebuiltFlagsRule(ctx)
+		prebuiltIndexRule(ctx)
 		return
 	}
 
 	// These rules depend on files located in frameworks/base, skip them if running in a tree that doesn't have them.
 	if ctx.Config().FrameworksBaseDirExists(ctx) {
 		h.flags = flagsRule(ctx)
-		h.metadata = metadataRule(ctx)
 	} else {
 		h.flags = emptyFlagsRule(ctx)
 	}
 }
 
 // Export paths to Make.  INTERNAL_PLATFORM_HIDDENAPI_FLAGS is used by Make rules in art/ and cts/.
-// Both paths are used to call dist-for-goals.
 func (h *hiddenAPISingleton) MakeVars(ctx android.MakeVarsContext) {
 	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
 		return
 	}
 
 	ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", h.flags.String())
-
-	if h.metadata != nil {
-		ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA", h.metadata.String())
-	}
 }
 
 // stubFlagsRule creates the rule to build hiddenapi-stub-flags.txt out of dex jars from stub modules and boot image
@@ -321,6 +315,17 @@
 	return outputPath
 }
 
+func prebuiltIndexRule(ctx android.SingletonContext) {
+	outputPath := hiddenAPISingletonPaths(ctx).index
+	inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-index.csv")
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Output: outputPath,
+		Input:  inputPath,
+	})
+}
+
 // flagsRule is a placeholder that simply returns the location of the file, the generation of the
 // ninja rules is done in generateHiddenAPIBuildActions.
 func flagsRule(ctx android.SingletonContext) android.Path {
@@ -343,34 +348,6 @@
 	return outputPath
 }
 
-// metadataRule creates a rule to build hiddenapi-unsupported.csv out of the metadata.csv files generated for boot image
-// modules.
-func metadataRule(ctx android.SingletonContext) android.Path {
-	var metadataCSV android.Paths
-
-	ctx.VisitAllModules(func(module android.Module) {
-		if h, ok := module.(hiddenAPIIntf); ok {
-			if csv := h.metadataCSV(); csv != nil {
-				metadataCSV = append(metadataCSV, csv)
-			}
-		}
-	})
-
-	rule := android.NewRuleBuilder(pctx, ctx)
-
-	outputPath := hiddenAPISingletonPaths(ctx).metadata
-
-	rule.Command().
-		BuiltTool("merge_csv").
-		Flag("--key_field signature").
-		FlagWithOutput("--output=", outputPath).
-		Inputs(metadataCSV)
-
-	rule.Build("hiddenAPIGreylistMetadataFile", "hiddenapi greylist metadata")
-
-	return outputPath
-}
-
 // commitChangeForRestat adds a command to a rule that updates outputPath from tempPath if they are different.  It
 // also marks the rule as restat and marks the tempPath as a temporary file that should not be considered an output of
 // the rule.
@@ -388,60 +365,3 @@
 		Text("fi").
 		Text(")")
 }
-
-func hiddenAPIIndexSingletonFactory() android.Singleton {
-	return &hiddenAPIIndexSingleton{}
-}
-
-type hiddenAPIIndexSingleton struct {
-	index android.Path
-}
-
-func (h *hiddenAPIIndexSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	// Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
-		return
-	}
-
-	if ctx.Config().PrebuiltHiddenApiDir(ctx) != "" {
-		outputPath := hiddenAPISingletonPaths(ctx).index
-		inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-index.csv")
-
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   android.Cp,
-			Output: outputPath,
-			Input:  inputPath,
-		})
-
-		h.index = outputPath
-		return
-	}
-
-	indexes := android.Paths{}
-	ctx.VisitAllModules(func(module android.Module) {
-		if h, ok := module.(hiddenAPIIntf); ok {
-			if h.indexCSV() != nil {
-				indexes = append(indexes, h.indexCSV())
-			}
-		}
-	})
-
-	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().
-		BuiltTool("merge_csv").
-		Flag("--key_field signature").
-		FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties").
-		FlagWithOutput("--output=", hiddenAPISingletonPaths(ctx).index).
-		Inputs(indexes)
-	rule.Build("singleton-merged-hiddenapi-index", "Singleton merged Hidden API index")
-
-	h.index = hiddenAPISingletonPaths(ctx).index
-}
-
-func (h *hiddenAPIIndexSingleton) MakeVars(ctx android.MakeVarsContext) {
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
-		return
-	}
-
-	ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_INDEX", h.index.String())
-}
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index e5e1c25..5ea9a5b 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -50,61 +50,6 @@
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
 
-func TestHiddenAPIIndexSingleton(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		hiddenApiFixtureFactory,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("bar"),
-		FixtureConfigureBootJars("platform:foo", "platform:bar"),
-	).RunTestWithBp(t, `
-		java_library {
-			name: "foo",
-			srcs: ["a.java"],
-			compile_dex: true,
-
-			hiddenapi_additional_annotations: [
-				"foo-hiddenapi-annotations",
-			],
-		}
-
-		java_library {
-			name: "foo-hiddenapi-annotations",
-			srcs: ["a.java"],
-			compile_dex: true,
-		}
-
-		java_import {
-			name: "foo",
-			jars: ["a.jar"],
-			compile_dex: true,
-			prefer: false,
-		}
-
-		java_sdk_library {
-			name: "bar",
-			srcs: ["a.java"],
-			compile_dex: true,
-		}
-	`)
-
-	hiddenAPIIndex := result.SingletonForTests("hiddenapi_index")
-	indexRule := hiddenAPIIndex.Rule("singleton-merged-hiddenapi-index")
-	CheckHiddenAPIRuleInputs(t, `
-.intermediates/bar/android_common/hiddenapi/index.csv
-.intermediates/foo/android_common/hiddenapi/index.csv
-`,
-		indexRule)
-
-	// Make sure that the foo-hiddenapi-annotations.jar is included in the inputs to the rules that
-	// creates the index.csv file.
-	foo := result.ModuleForTests("foo", "android_common")
-	indexParams := foo.Output("hiddenapi/index.csv")
-	CheckHiddenAPIRuleInputs(t, `
-.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar
-.intermediates/foo/android_common/javac/foo.jar
-`, indexParams)
-}
-
 func TestHiddenAPISingletonWithSourceAndPrebuiltPreferredButNoDex(t *testing.T) {
 	expectedErrorMessage :=
 		"hiddenapi has determined that the source module \"foo\" should be ignored as it has been" +
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 621119e..994f1be 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -339,4 +339,42 @@
 	outputPath := hiddenAPISingletonPaths(ctx).flags
 	baseFlagsPath := hiddenAPISingletonPaths(ctx).stubFlags
 	ruleToGenerateHiddenApiFlags(ctx, outputPath, baseFlagsPath, moduleSpecificFlagsPaths, augmentationInfo)
+
+	b.generateHiddenAPIIndexRules(ctx, hiddenAPISupportingModules)
+	b.generatedHiddenAPIMetadataRules(ctx, hiddenAPISupportingModules)
+}
+
+func (b *platformBootclasspathModule) generateHiddenAPIIndexRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) {
+	indexes := android.Paths{}
+	for _, module := range modules {
+		indexes = append(indexes, module.indexCSV())
+	}
+
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Command().
+		BuiltTool("merge_csv").
+		Flag("--key_field signature").
+		FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties").
+		FlagWithOutput("--output=", hiddenAPISingletonPaths(ctx).index).
+		Inputs(indexes)
+	rule.Build("platform-bootclasspath-monolithic-hiddenapi-index", "monolithic hidden API index")
+}
+
+func (b *platformBootclasspathModule) generatedHiddenAPIMetadataRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) {
+	metadataCSVFiles := android.Paths{}
+	for _, module := range modules {
+		metadataCSVFiles = append(metadataCSVFiles, module.metadataCSV())
+	}
+
+	rule := android.NewRuleBuilder(pctx, ctx)
+
+	outputPath := hiddenAPISingletonPaths(ctx).metadata
+
+	rule.Command().
+		BuiltTool("merge_csv").
+		Flag("--key_field signature").
+		FlagWithOutput("--output=", outputPath).
+		Inputs(metadataCSVFiles)
+
+	rule.Build("platform-bootclasspath-monolithic-hiddenapi-metadata", "monolithic hidden API metadata")
 }
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index c740911..417c6bf 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -172,3 +172,62 @@
 	android.AssertStringEquals(t, "platform dist goals phony", ".PHONY: droidcore\n", goals[0])
 	android.AssertStringEquals(t, "platform dist goals call", "$(call dist-for-goals,droidcore,out/soong/hiddenapi/hiddenapi-flags.csv:hiddenapi-flags.csv)\n", android.StringRelativeToTop(result.Config, goals[1]))
 }
+
+func TestPlatformBootclasspath_HiddenAPIMonolithicFiles(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("bar"),
+		FixtureConfigureBootJars("platform:foo", "platform:bar"),
+	).RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+
+			hiddenapi_additional_annotations: [
+				"foo-hiddenapi-annotations",
+			],
+		}
+
+		java_library {
+			name: "foo-hiddenapi-annotations",
+			srcs: ["a.java"],
+			compile_dex: true,
+		}
+
+		java_import {
+			name: "foo",
+			jars: ["a.jar"],
+			compile_dex: true,
+			prefer: false,
+		}
+
+		java_sdk_library {
+			name: "bar",
+			srcs: ["a.java"],
+			compile_dex: true,
+		}
+
+		platform_bootclasspath {
+			name: "myplatform-bootclasspath",
+		}
+	`)
+
+	platformBootclasspath := result.ModuleForTests("myplatform-bootclasspath", "android_common")
+	indexRule := platformBootclasspath.Rule("platform-bootclasspath-monolithic-hiddenapi-index")
+	CheckHiddenAPIRuleInputs(t, `
+.intermediates/bar/android_common/hiddenapi/index.csv
+.intermediates/foo/android_common/hiddenapi/index.csv
+`,
+		indexRule)
+
+	// Make sure that the foo-hiddenapi-annotations.jar is included in the inputs to the rules that
+	// creates the index.csv file.
+	foo := result.ModuleForTests("foo", "android_common")
+	indexParams := foo.Output("hiddenapi/index.csv")
+	CheckHiddenAPIRuleInputs(t, `
+.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar
+.intermediates/foo/android_common/javac/foo.jar
+`, indexParams)
+}
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index da80a47..241cac6 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"android/soong/etc"
+	"fmt"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -68,6 +69,17 @@
 	return l.outputFilePath
 }
 
+var _ android.OutputFileProducer = (*linkerConfig)(nil)
+
+func (l *linkerConfig) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return android.Paths{l.outputFilePath}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
 func (l *linkerConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	inputFile := android.PathForModuleSrc(ctx, android.String(l.properties.Src))
 	l.outputFilePath = android.PathForModuleOut(ctx, "linker.config.pb").OutputPath
@@ -81,9 +93,10 @@
 	linkerConfigRule.Build("conv_linker_config",
 		"Generate linker config protobuf "+l.outputFilePath.String())
 
-	if proptools.BoolDefault(l.properties.Installable, true) {
-		ctx.InstallFile(l.installDirPath, l.outputFilePath.Base(), l.outputFilePath)
+	if !proptools.BoolDefault(l.properties.Installable, true) {
+		l.SkipInstall()
 	}
+	ctx.InstallFile(l.installDirPath, l.outputFilePath.Base(), l.outputFilePath)
 }
 
 // linker_config generates protobuf file from json file. This protobuf file will be used from
diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py
index 22fe9f6..92f79da 100644
--- a/scripts/conv_linker_config.py
+++ b/scripts/conv_linker_config.py
@@ -78,6 +78,14 @@
   with open(args.output, 'wb') as f:
     f.write(pb.SerializeToString())
 
+def Merge(args):
+  pb = linker_config_pb2.LinkerConfig()
+  for other in args.input:
+    with open(other, 'rb') as f:
+      pb.MergeFromString(f.read())
+
+  with open(args.out, 'wb') as f:
+    f.write(pb.SerializeToString())
 
 def GetArgParser():
   parser = argparse.ArgumentParser()
@@ -161,6 +169,22 @@
       help='Values of the libraries to append. If there are more than one it should be separated by empty space')
   append.set_defaults(func=Append)
 
+  append = subparsers.add_parser(
+      'merge', help='Merge configurations')
+  append.add_argument(
+      '-o',
+      '--out',
+      required=True,
+      type=str,
+      help='Ouptut linker configuration file to write in protobuf.')
+  append.add_argument(
+      '-i',
+      '--input',
+      nargs='+',
+      type=str,
+      help='Linker configuration files to merge.')
+  append.set_defaults(func=Merge)
+
   return parser
 
 
diff --git a/tests/lib.sh b/tests/lib.sh
index b61ca91..1478e37 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -6,8 +6,39 @@
 
 REAL_TOP="$(readlink -f "$(dirname "$0")"/../../..)"
 
+if [[ ! -z "$HARDWIRED_MOCK_TOP" ]]; then
+  MOCK_TOP="$HARDWIRED_MOCK_TOP"
+else
+  MOCK_TOP=$(mktemp -t -d st.XXXXX)
+  trap cleanup_mock_top EXIT
+fi
+
+WARMED_UP_MOCK_TOP=$(mktemp -t soong_integration_tests_warmup.XXXXXX.tar.gz)
+trap 'rm -f "$WARMED_UP_MOCK_TOP"' EXIT
+
+function warmup_mock_top {
+  info "Warming up mock top ..."
+  info "Mock top warmup archive: $WARMED_UP_MOCK_TOP"
+  cleanup_mock_top
+  mkdir -p "$MOCK_TOP"
+  cd "$MOCK_TOP"
+
+  create_mock_soong
+  run_soong
+  tar czf "$WARMED_UP_MOCK_TOP" *
+}
+
+function cleanup_mock_top {
+  cd /
+  rm -fr "$MOCK_TOP"
+}
+
+function info {
+  echo -e "\e[92;1m[TEST HARNESS INFO]\e[0m" $*
+}
+
 function fail {
-  echo ERROR: $1
+  echo -e "\e[91;1mFAILED:\e[0m" $*
   exit 1
 }
 
@@ -47,19 +78,7 @@
   done
 }
 
-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 'cd / && rm -fr "$MOCK_TOP"' EXIT
-  fi
-
-  echo "Test case: ${FUNCNAME[1]}, mock top path: $MOCK_TOP"
-  cd "$MOCK_TOP"
-
+function create_mock_soong {
   copy_directory build/blueprint
   copy_directory build/soong
 
@@ -68,12 +87,27 @@
   symlink_directory external/golang-protobuf
 
   touch "$MOCK_TOP/Android.bp"
+}
 
-  export ALLOW_MISSING_DEPENDENCIES=true
+function setup() {
+  cleanup_mock_top
+  mkdir -p "$MOCK_TOP"
 
-  mkdir -p out/soong
+  echo
+  echo ----------------------------------------------------------------------------
+  info "Running test case ${FUNCNAME[1]}"
+  cd "$MOCK_TOP"
+
+  tar xzf "$WARMED_UP_MOCK_TOP"
 }
 
 function run_soong() {
   build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests
 }
+
+info "Starting Soong integration test suite $(basename $0)"
+info "Mock top: $MOCK_TOP"
+
+
+export ALLOW_MISSING_DEPENDENCIES=true
+warmup_mock_top
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
index 54f0689..7dbafea 100755
--- a/tests/mixed_mode_test.sh
+++ b/tests/mixed_mode_test.sh
@@ -8,7 +8,7 @@
 
 source "$(dirname "$0")/lib.sh"
 
-function setup_bazel() {
+function create_mock_bazel() {
   copy_directory build/bazel
 
   symlink_directory prebuilts/bazel
@@ -20,7 +20,7 @@
 
 function test_bazel_smoke {
   setup
-  setup_bazel
+  create_mock_bazel
 
   tools/bazel info
 }