Add android_sdk_repo_host to build platform-tools&build-tools

The Android SDK has been built with Make up until now, monolithically,
then split up into several sdk-repo zip files for different purposes.
The Mac and Windows SDKs really only need to contain the platform-tools
and build-tools pieces, but due to this monolithic sdk zip, we had to
build the whole SDK first.

This adds an `android_sdk_repo_host` module that can build these
platform-tools and build-tools zips.

Bug: 187222815
Change-Id: I55809e1d7447dd65e22461f921b2b8abb6d5f822
diff --git a/android/androidmk.go b/android/androidmk.go
index 967c550..feaef1f 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -836,6 +836,7 @@
 		case "*aidl.aidlApi": // writes non-custom before adding .phony
 		case "*aidl.aidlMapping": // writes non-custom before adding .phony
 		case "*android.customModule": // appears in tests only
+		case "*android_sdk.sdkRepoHost": // doesn't go through base_rules
 		case "*apex.apexBundle": // license properties written
 		case "*bpf.bpf": // license properties written (both for module and objs)
 		case "*genrule.Module": // writes non-custom before adding .phony
diff --git a/android/module.go b/android/module.go
index 327e6ae..02706ec 100644
--- a/android/module.go
+++ b/android/module.go
@@ -2818,11 +2818,13 @@
 }
 
 func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec {
+	licenseFiles := m.Module().EffectiveLicenseFiles()
 	spec := PackagingSpec{
-		relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
-		srcPath:          srcPath,
-		symlinkTarget:    "",
-		executable:       executable,
+		relPathInPackage:      Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
+		srcPath:               srcPath,
+		symlinkTarget:         "",
+		executable:            executable,
+		effectiveLicenseFiles: &licenseFiles,
 	}
 	m.packagingSpecs = append(m.packagingSpecs, spec)
 	return spec
diff --git a/android/neverallow.go b/android/neverallow.go
index b8517a9..9098a71 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -150,6 +150,7 @@
 
 func createJavaDeviceForHostRules() []Rule {
 	javaDeviceForHostProjectsAllowedList := []string{
+		"development/build",
 		"external/guava",
 		"external/robolectric-shadows",
 		"frameworks/layoutlib",
diff --git a/android/notices.go b/android/notices.go
index 07cf3e4..d8cfaf2 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -100,3 +100,56 @@
 		HtmlGzOutput: OptionalPathForPath(htmlGzOutput),
 	}
 }
+
+// BuildNotices merges the supplied NOTICE files into a single file that lists notices
+// for every key in noticeMap (which would normally be installed files).
+func BuildNotices(ctx ModuleContext, noticeMap map[string]Paths) NoticeOutputs {
+	// TODO(jungjw): We should just produce a well-formatted NOTICE.html file in a single pass.
+	//
+	// generate-notice-files.py, which processes the merged NOTICE file, has somewhat strict rules
+	// about input NOTICE file paths.
+	// 1. Their relative paths to the src root become their NOTICE index titles. We want to use
+	// on-device paths as titles, and so output the merged NOTICE file the corresponding location.
+	// 2. They must end with .txt extension. Otherwise, they're ignored.
+
+	mergeTool := PathForSource(ctx, "build/soong/scripts/mergenotice.py")
+	generateNoticeTool := PathForSource(ctx, "build/soong/scripts/generate-notice-files.py")
+
+	outputDir := PathForModuleOut(ctx, "notices")
+	builder := NewRuleBuilder(pctx, ctx).
+		Sbox(outputDir, PathForModuleOut(ctx, "notices.sbox.textproto"))
+	for _, installPath := range SortedStringKeys(noticeMap) {
+		noticePath := outputDir.Join(ctx, installPath+".txt")
+		// It would be nice if sbox created directories for temporaries, but until then
+		// this is simple enough.
+		builder.Command().
+			Text("(cd").OutputDir().Text("&&").
+			Text("mkdir -p").Text(filepath.Dir(installPath)).Text(")")
+		builder.Temporary(noticePath)
+		builder.Command().
+			Tool(mergeTool).
+			Flag("--output").Output(noticePath).
+			Inputs(noticeMap[installPath])
+	}
+
+	// Transform the merged NOTICE file into a gzipped HTML file.
+	txtOutput := outputDir.Join(ctx, "NOTICE.txt")
+	htmlOutput := outputDir.Join(ctx, "NOTICE.html")
+	htmlGzOutput := outputDir.Join(ctx, "NOTICE.html.gz")
+	title := "\"Notices for " + ctx.ModuleName() + "\""
+	builder.Command().Tool(generateNoticeTool).
+		FlagWithOutput("--text-output ", txtOutput).
+		FlagWithOutput("--html-output ", htmlOutput).
+		FlagWithArg("-t ", title).
+		Flag("-s").OutputDir()
+	builder.Command().BuiltTool("minigzip").
+		FlagWithInput("-c ", htmlOutput).
+		FlagWithOutput("> ", htmlGzOutput)
+	builder.Build("build_notices", "generate notice output")
+
+	return NoticeOutputs{
+		TxtOutput:    OptionalPathForPath(txtOutput),
+		HtmlOutput:   OptionalPathForPath(htmlOutput),
+		HtmlGzOutput: OptionalPathForPath(htmlGzOutput),
+	}
+}
diff --git a/android/packaging.go b/android/packaging.go
index 9065826..e3a0b54 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -38,6 +38,8 @@
 
 	// Whether relPathInPackage should be marked as executable or not
 	executable bool
+
+	effectiveLicenseFiles *Paths
 }
 
 // Get file name of installed package
@@ -54,6 +56,17 @@
 	return p.relPathInPackage
 }
 
+func (p *PackagingSpec) SetRelPathInPackage(relPathInPackage string) {
+	p.relPathInPackage = relPathInPackage
+}
+
+func (p *PackagingSpec) EffectiveLicenseFiles() Paths {
+	if p.effectiveLicenseFiles == nil {
+		return Paths{}
+	}
+	return *p.effectiveLicenseFiles
+}
+
 type PackageModule interface {
 	Module
 	packagingBase() *PackagingBase
@@ -214,15 +227,9 @@
 	return m
 }
 
-// See PackageModule.CopyDepsToZip
-func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
-	m := p.GatherPackagingSpecs(ctx)
-	builder := NewRuleBuilder(pctx, ctx)
-
-	dir := PathForModuleOut(ctx, ".zip")
-	builder.Command().Text("rm").Flag("-rf").Text(dir.String())
-	builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
-
+// CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec
+// entries into the specified directory.
+func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, m map[string]PackagingSpec, dir ModuleOutPath) (entries []string) {
 	seenDir := make(map[string]bool)
 	for _, k := range SortedStringKeys(m) {
 		ps := m[k]
@@ -243,6 +250,19 @@
 		}
 	}
 
+	return entries
+}
+
+// See PackageModule.CopyDepsToZip
+func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
+	m := p.GatherPackagingSpecs(ctx)
+	builder := NewRuleBuilder(pctx, ctx)
+
+	dir := PathForModuleOut(ctx, ".zip")
+	builder.Command().Text("rm").Flag("-rf").Text(dir.String())
+	builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
+	entries = p.CopySpecsToDir(ctx, builder, m, dir)
+
 	builder.Command().
 		BuiltTool("soong_zip").
 		FlagWithOutput("-o ", zipOut).
diff --git a/android/variable.go b/android/variable.go
index 4a82f23..5c54e94 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -40,6 +40,7 @@
 		Platform_sdk_version struct {
 			Asflags []string
 			Cflags  []string
+			Cmd     *string
 		}
 
 		Platform_sdk_version_or_codename struct {
@@ -50,6 +51,10 @@
 			Cmd *string
 		}
 
+		Platform_version_name struct {
+			Base_dir *string
+		}
+
 		// unbundled_build is a catch-all property to annotate modules that don't build in one or
 		// more unbundled branches, usually due to dependencies missing from the manifest.
 		Unbundled_build struct {