Fix and generate vndk snapshot entirely in Soong

- VNDK snapshot now respects stem and suffix.
- ld.config.txt is removed from snapshot as linkerconfig has become default.
- Soong builds entire snapshot, and make just calls dist-for-goals.

Bug: 142589718
Test: build and install snapshot
Test: development/vndk/snapshot/update.py with past version of snapshot
Change-Id: Id1ed658c22bb2e41c0ee50d1fe2a97924a76d7dc
diff --git a/cc/vndk.go b/cc/vndk.go
index d6c2f6f..bb4aafc 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -19,6 +19,7 @@
 	"errors"
 	"fmt"
 	"path/filepath"
+	"sort"
 	"strings"
 	"sync"
 
@@ -199,8 +200,6 @@
 	llndkLibrariesKey                = android.NewOnceKey("llndkLibrarires")
 	vndkPrivateLibrariesKey          = android.NewOnceKey("vndkPrivateLibrarires")
 	vndkUsingCoreVariantLibrariesKey = android.NewOnceKey("vndkUsingCoreVariantLibrarires")
-	modulePathsKey                   = android.NewOnceKey("modulePaths")
-	vndkSnapshotOutputsKey           = android.NewOnceKey("vndkSnapshotOutputs")
 	vndkMustUseVendorVariantListKey  = android.NewOnceKey("vndkMustUseVendorVariantListKey")
 	vndkLibrariesLock                sync.Mutex
 
@@ -247,18 +246,6 @@
 	}).(map[string]string)
 }
 
-func modulePaths(config android.Config) map[string]string {
-	return config.Once(modulePathsKey, func() interface{} {
-		return make(map[string]string)
-	}).(map[string]string)
-}
-
-func vndkSnapshotOutputs(config android.Config) *android.RuleBuilderInstalls {
-	return config.Once(vndkSnapshotOutputsKey, func() interface{} {
-		return &android.RuleBuilderInstalls{}
-	}).(*android.RuleBuilderInstalls)
-}
-
 func vndkMustUseVendorVariantList(cfg android.Config) []string {
 	return cfg.Once(vndkMustUseVendorVariantListKey, func() interface{} {
 		return config.VndkMustUseVendorVariantList
@@ -297,8 +284,6 @@
 	vndkLibrariesLock.Lock()
 	defer vndkLibrariesLock.Unlock()
 
-	modulePaths(mctx.Config())[name] = mctx.ModuleDir()
-
 	if inList(name, vndkMustUseVendorVariantList(mctx.Config())) {
 		m.Properties.MustUseVendorVariant = true
 	}
@@ -376,10 +361,6 @@
 
 func init() {
 	android.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
-	android.RegisterMakeVarsProvider(pctx, func(ctx android.MakeVarsContext) {
-		outputs := vndkSnapshotOutputs(ctx.Config())
-		ctx.Strict("SOONG_VNDK_SNAPSHOT_FILES", outputs.String())
-	})
 }
 
 func VndkSnapshotSingleton() android.Singleton {
@@ -388,12 +369,13 @@
 
 type vndkSnapshotSingleton struct {
 	installedLlndkLibraries      []string
-	llnkdLibrariesFile           android.Path
+	llndkLibrariesFile           android.Path
 	vndkSpLibrariesFile          android.Path
 	vndkCoreLibrariesFile        android.Path
 	vndkPrivateLibrariesFile     android.Path
 	vndkCoreVariantLibrariesFile android.Path
 	vndkLibrariesFile            android.Path
+	vndkSnapshotZipFile          android.OptionalPath
 }
 
 func (c *vndkSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
@@ -413,15 +395,42 @@
 		return
 	}
 
-	outputs := vndkSnapshotOutputs(ctx.Config())
+	var snapshotOutputs android.Paths
+
+	/*
+		VNDK snapshot zipped artifacts directory structure:
+		{SNAPSHOT_ARCH}/
+			arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/
+				shared/
+					vndk-core/
+						(VNDK-core libraries, e.g. libbinder.so)
+					vndk-sp/
+						(VNDK-SP libraries, e.g. libc++.so)
+			arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/
+				shared/
+					vndk-core/
+						(VNDK-core libraries, e.g. libbinder.so)
+					vndk-sp/
+						(VNDK-SP libraries, e.g. libc++.so)
+			binder32/
+				(This directory is newly introduced in v28 (Android P) to hold
+				prebuilts built for 32-bit binder interface.)
+				arch-{TARGET_ARCH}-{TARGE_ARCH_VARIANT}/
+					...
+			configs/
+				(various *.txt configuration files)
+			include/
+				(header files of same directory structure with source tree)
+			NOTICE_FILES/
+				(notice files of libraries, e.g. libcutils.so.txt)
+	*/
 
 	snapshotDir := "vndk-snapshot"
+	snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
 
-	vndkLibDir := make(map[android.ArchType]string)
-
-	snapshotVariantDir := ctx.DeviceConfig().DeviceArch()
+	targetArchDirMap := make(map[android.ArchType]string)
 	for _, target := range ctx.Config().Targets[android.Android] {
-		dir := snapshotVariantDir
+		dir := snapshotArchDir
 		if ctx.DeviceConfig().BinderBitness() == "32" {
 			dir = filepath.Join(dir, "binder32")
 		}
@@ -430,64 +439,55 @@
 			arch += "-" + target.Arch.ArchVariant
 		}
 		dir = filepath.Join(dir, arch)
-		vndkLibDir[target.Arch.ArchType] = dir
+		targetArchDirMap[target.Arch.ArchType] = dir
 	}
-	configsDir := filepath.Join(snapshotVariantDir, "configs")
-	noticeDir := filepath.Join(snapshotVariantDir, "NOTICE_FILES")
-	includeDir := filepath.Join(snapshotVariantDir, "include")
+	configsDir := filepath.Join(snapshotArchDir, "configs")
+	noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
+	includeDir := filepath.Join(snapshotArchDir, "include")
+
+	// set of include paths exported by VNDK libraries
+	exportedIncludes := make(map[string]bool)
+
+	// generated header files among exported headers.
+	var generatedHeaders android.Paths
+
+	// set of notice files copied.
 	noticeBuilt := make(map[string]bool)
 
-	installSnapshotFileFromPath := func(path android.Path, out string) {
+	// paths of VNDK modules for GPL license checking
+	modulePaths := make(map[string]string)
+
+	// actual module names of .so files
+	// e.g. moduleNames["libprotobuf-cpp-full-3.9.1.so"] = "libprotobuf-cpp-full"
+	moduleNames := make(map[string]string)
+
+	installSnapshotFileFromPath := func(path android.Path, out string) android.OutputPath {
+		outPath := android.PathForOutput(ctx, out)
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        android.Cp,
 			Input:       path,
-			Output:      android.PathForOutput(ctx, snapshotDir, out),
+			Output:      outPath,
 			Description: "vndk snapshot " + out,
 			Args: map[string]string{
 				"cpFlags": "-f -L",
 			},
 		})
-		*outputs = append(*outputs, android.RuleBuilderInstall{
-			From: android.PathForOutput(ctx, snapshotDir, out),
-			To:   out,
-		})
+		return outPath
 	}
-	installSnapshotFileFromContent := func(content, out string) {
+
+	installSnapshotFileFromContent := func(content, out string) android.OutputPath {
+		outPath := android.PathForOutput(ctx, out)
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        android.WriteFile,
-			Output:      android.PathForOutput(ctx, snapshotDir, out),
+			Output:      outPath,
 			Description: "vndk snapshot " + out,
 			Args: map[string]string{
 				"content": content,
 			},
 		})
-		*outputs = append(*outputs, android.RuleBuilderInstall{
-			From: android.PathForOutput(ctx, snapshotDir, out),
-			To:   out,
-		})
+		return outPath
 	}
 
-	tryBuildNotice := func(m *Module) {
-		name := ctx.ModuleName(m) + ".so.txt"
-
-		if _, ok := noticeBuilt[name]; ok {
-			return
-		}
-
-		noticeBuilt[name] = true
-
-		if m.NoticeFile().Valid() {
-			installSnapshotFileFromPath(m.NoticeFile().Path(), filepath.Join(noticeDir, name))
-		}
-	}
-
-	vndkCoreLibraries := android.SortedStringKeys(vndkCoreLibraries(ctx.Config()))
-	vndkSpLibraries := android.SortedStringKeys(vndkSpLibraries(ctx.Config()))
-	vndkPrivateLibraries := android.SortedStringKeys(vndkPrivateLibraries(ctx.Config()))
-
-	var generatedHeaders android.Paths
-	includeDirs := make(map[string]bool)
-
 	type vndkSnapshotLibraryInterface interface {
 		exportedFlagsProducer
 		libraryInterface
@@ -496,12 +496,31 @@
 	var _ vndkSnapshotLibraryInterface = (*prebuiltLibraryLinker)(nil)
 	var _ vndkSnapshotLibraryInterface = (*libraryDecorator)(nil)
 
-	installVndkSnapshotLib := func(m *Module, l vndkSnapshotLibraryInterface, dir string) bool {
-		name := ctx.ModuleName(m)
-		libOut := filepath.Join(dir, name+".so")
+	installVndkSnapshotLib := func(m *Module, l vndkSnapshotLibraryInterface, vndkType string) (android.Paths, bool) {
+		targetArchDir, ok := targetArchDirMap[m.Target().Arch.ArchType]
+		if !ok {
+			return nil, false
+		}
 
-		installSnapshotFileFromPath(m.outputFile.Path(), libOut)
-		tryBuildNotice(m)
+		var ret android.Paths
+
+		libPath := m.outputFile.Path()
+		stem := libPath.Base()
+		snapshotLibOut := filepath.Join(targetArchDir, "shared", vndkType, stem)
+		ret = append(ret, installSnapshotFileFromPath(libPath, snapshotLibOut))
+
+		moduleNames[stem] = ctx.ModuleName(m)
+		modulePaths[stem] = ctx.ModuleDir(m)
+
+		if m.NoticeFile().Valid() {
+			noticeName := stem + ".txt"
+			// skip already copied notice file
+			if _, ok := noticeBuilt[noticeName]; !ok {
+				noticeBuilt[noticeName] = true
+				ret = append(ret, installSnapshotFileFromPath(
+					m.NoticeFile().Path(), filepath.Join(noticeDir, noticeName)))
+			}
+		}
 
 		if ctx.Config().VndkSnapshotBuildArtifacts() {
 			prop := struct {
@@ -515,20 +534,19 @@
 			prop.ExportedSystemDirs = l.exportedSystemDirs().Strings()
 			prop.RelativeInstallPath = m.RelativeInstallPath()
 
-			propOut := libOut + ".json"
+			propOut := snapshotLibOut + ".json"
 
 			j, err := json.Marshal(prop)
 			if err != nil {
 				ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
-				return false
+				return nil, false
 			}
-
-			installSnapshotFileFromContent(string(j), propOut)
+			ret = append(ret, installSnapshotFileFromContent(string(j), propOut))
 		}
-		return true
+		return ret, true
 	}
 
-	isVndkSnapshotLibrary := func(m *Module) (i vndkSnapshotLibraryInterface, libDir string, isVndkSnapshotLib bool) {
+	isVndkSnapshotLibrary := func(m *Module) (i vndkSnapshotLibraryInterface, vndkType string, isVndkSnapshotLib bool) {
 		if m.Target().NativeBridge == android.NativeBridgeEnabled {
 			return nil, "", false
 		}
@@ -539,14 +557,15 @@
 		if !ok || !l.shared() {
 			return nil, "", false
 		}
-		name := ctx.ModuleName(m)
-		if inList(name, vndkCoreLibraries) {
-			return l, filepath.Join("shared", "vndk-core"), true
-		} else if inList(name, vndkSpLibraries) {
-			return l, filepath.Join("shared", "vndk-sp"), true
-		} else {
-			return nil, "", false
+		if m.vndkVersion() == ctx.DeviceConfig().PlatformVndkVersion() && m.IsVndk() && !m.isVndkExt() {
+			if m.isVndkSp() {
+				return l, "vndk-sp", true
+			} else {
+				return l, "vndk-core", true
+			}
 		}
+
+		return nil, "", false
 	}
 
 	ctx.VisitAllModules(func(module android.Module) {
@@ -555,31 +574,32 @@
 			return
 		}
 
-		baseDir, ok := vndkLibDir[m.Target().Arch.ArchType]
+		l, vndkType, ok := isVndkSnapshotLibrary(m)
 		if !ok {
 			return
 		}
 
-		l, libDir, ok := isVndkSnapshotLibrary(m)
+		libs, ok := installVndkSnapshotLib(m, l, vndkType)
 		if !ok {
 			return
 		}
 
-		if !installVndkSnapshotLib(m, l, filepath.Join(baseDir, libDir)) {
-			return
-		}
+		snapshotOutputs = append(snapshotOutputs, libs...)
 
+		// We glob headers from include directories inside source tree. So we first gather
+		// all include directories inside our source tree. On the contrast, we manually
+		// collect generated headers from dependencies as they can't globbed.
 		generatedHeaders = append(generatedHeaders, l.exportedDeps()...)
 		for _, dir := range append(l.exportedDirs(), l.exportedSystemDirs()...) {
-			includeDirs[dir.String()] = true
+			exportedIncludes[dir.String()] = true
 		}
 	})
 
 	if ctx.Config().VndkSnapshotBuildArtifacts() {
-		headers := make(map[string]bool)
+		globbedHeaders := make(map[string]bool)
 
-		for _, dir := range android.SortedStringKeys(includeDirs) {
-			// workaround to determine if dir is under output directory
+		for _, dir := range android.SortedStringKeys(exportedIncludes) {
+			// Skip if dir is for generated headers
 			if strings.HasPrefix(dir, android.PathForOutput(ctx).String()) {
 				continue
 			}
@@ -598,14 +618,14 @@
 					if strings.HasSuffix(header, "/") {
 						continue
 					}
-					headers[header] = true
+					globbedHeaders[header] = true
 				}
 			}
 		}
 
-		for _, header := range android.SortedStringKeys(headers) {
-			installSnapshotFileFromPath(android.PathForSource(ctx, header),
-				filepath.Join(includeDir, header))
+		for _, header := range android.SortedStringKeys(globbedHeaders) {
+			snapshotOutputs = append(snapshotOutputs, installSnapshotFileFromPath(
+				android.PathForSource(ctx, header), filepath.Join(includeDir, header)))
 		}
 
 		isHeader := func(path string) bool {
@@ -617,6 +637,7 @@
 			return false
 		}
 
+		// For generated headers, manually install one by one, rather than glob
 		for _, path := range android.PathsToDirectorySortedPaths(android.FirstUniquePaths(generatedHeaders)) {
 			header := path.String()
 
@@ -624,33 +645,85 @@
 				continue
 			}
 
-			installSnapshotFileFromPath(path, filepath.Join(includeDir, header))
+			snapshotOutputs = append(snapshotOutputs, installSnapshotFileFromPath(
+				path, filepath.Join(includeDir, header)))
 		}
 	}
 
-	installSnapshotFileFromContent(android.JoinWithSuffix(vndkCoreLibraries, ".so", "\\n"),
-		filepath.Join(configsDir, "vndkcore.libraries.txt"))
-	installSnapshotFileFromContent(android.JoinWithSuffix(vndkPrivateLibraries, ".so", "\\n"),
-		filepath.Join(configsDir, "vndkprivate.libraries.txt"))
+	snapshotOutputs = append(snapshotOutputs,
+		installSnapshotFileFromPath(c.vndkCoreLibrariesFile, filepath.Join(configsDir, "vndkcore.libraries.txt")),
+		installSnapshotFileFromPath(c.vndkPrivateLibrariesFile, filepath.Join(configsDir, "vndkprivate.libraries.txt")),
+		installSnapshotFileFromPath(c.vndkSpLibrariesFile, filepath.Join(configsDir, "vndksp.libraries.txt")),
+		installSnapshotFileFromPath(c.llndkLibrariesFile, filepath.Join(configsDir, "llndk.libraries.txt")),
+	)
 
-	var modulePathTxtBuilder strings.Builder
+	/*
+		Dump a map to a list file as:
 
-	modulePaths := modulePaths(ctx.Config())
-
-	first := true
-	for _, lib := range android.SortedStringKeys(modulePaths) {
-		if first {
-			first = false
-		} else {
-			modulePathTxtBuilder.WriteString("\\n")
+		{key1} {value1}
+		{key2} {value2}
+		...
+	*/
+	installMapListFile := func(m map[string]string, path string) android.OutputPath {
+		var txtBuilder strings.Builder
+		for idx, k := range android.SortedStringKeys(m) {
+			if idx > 0 {
+				txtBuilder.WriteString("\\n")
+			}
+			txtBuilder.WriteString(k)
+			txtBuilder.WriteString(" ")
+			txtBuilder.WriteString(m[k])
 		}
-		modulePathTxtBuilder.WriteString(lib)
-		modulePathTxtBuilder.WriteString(".so ")
-		modulePathTxtBuilder.WriteString(modulePaths[lib])
+		return installSnapshotFileFromContent(txtBuilder.String(), path)
 	}
 
-	installSnapshotFileFromContent(modulePathTxtBuilder.String(),
-		filepath.Join(configsDir, "module_paths.txt"))
+	/*
+		module_paths.txt contains paths on which VNDK modules are defined.
+		e.g.,
+			libbase.so system/core/base
+			libc.so bionic/libc
+			...
+	*/
+	snapshotOutputs = append(snapshotOutputs, installMapListFile(modulePaths, filepath.Join(configsDir, "module_paths.txt")))
+
+	/*
+		module_names.txt contains names as which VNDK modules are defined,
+		because output filename and module name can be different with stem and suffix properties.
+
+		e.g.,
+			libcutils.so libcutils
+			libprotobuf-cpp-full-3.9.2.so libprotobuf-cpp-full
+			...
+	*/
+	snapshotOutputs = append(snapshotOutputs, installMapListFile(moduleNames, filepath.Join(configsDir, "module_names.txt")))
+
+	// All artifacts are ready. Sort them to normalize ninja and then zip.
+	sort.Slice(snapshotOutputs, func(i, j int) bool {
+		return snapshotOutputs[i].String() < snapshotOutputs[j].String()
+	})
+
+	zipPath := android.PathForOutput(ctx, snapshotDir, "android-vndk-"+ctx.DeviceConfig().DeviceArch()+".zip")
+	zipRule := android.NewRuleBuilder()
+
+	// If output files are too many, soong_zip command can exceed ARG_MAX.
+	// So first dump file lists into a single list file, and then feed it to Soong
+	snapshotOutputList := android.PathForOutput(ctx, snapshotDir, "android-vndk-"+ctx.DeviceConfig().DeviceArch()+"_list")
+	zipRule.Command().
+		Text("( xargs").
+		FlagWithRspFileInputList("-n1 echo < ", snapshotOutputs).
+		FlagWithOutput("| tr -d \\' > ", snapshotOutputList).
+		Text(")")
+
+	zipRule.Temporary(snapshotOutputList)
+
+	zipRule.Command().
+		BuiltTool(ctx, "soong_zip").
+		FlagWithOutput("-o ", zipPath).
+		FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
+		FlagWithInput("-l ", snapshotOutputList)
+
+	zipRule.Build(pctx, ctx, zipPath.String(), "vndk snapshot "+zipPath.String())
+	c.vndkSnapshotZipFile = android.OptionalPathForPath(zipPath)
 }
 
 func getVndkFileName(m *Module) (string, error) {
@@ -697,7 +770,7 @@
 	vndkprivate := android.SortedStringMapValues(vndkPrivateLibraries(ctx.Config()))
 	vndkcorevariant := android.SortedStringMapValues(vndkUsingCoreVariantLibraries(ctx.Config()))
 
-	c.llnkdLibrariesFile = installListFile(llndk, "llndk.libraries.txt")
+	c.llndkLibrariesFile = installListFile(llndk, "llndk.libraries.txt")
 	c.vndkCoreLibrariesFile = installListFile(vndkcore, "vndkcore.libraries.txt")
 	c.vndkSpLibrariesFile = installListFile(vndksp, "vndksp.libraries.txt")
 	c.vndkPrivateLibrariesFile = installListFile(vndkprivate, "vndkprivate.libraries.txt")
@@ -739,11 +812,12 @@
 	ctx.Strict("VNDK_PRIVATE_LIBRARIES", strings.Join(android.SortedStringKeys(vndkPrivateLibraries(ctx.Config())), " "))
 	ctx.Strict("VNDK_USING_CORE_VARIANT_LIBRARIES", strings.Join(android.SortedStringKeys(vndkUsingCoreVariantLibraries(ctx.Config())), " "))
 
-	ctx.Strict("LLNDK_LIBRARIES_FILE", c.llnkdLibrariesFile.String())
+	ctx.Strict("LLNDK_LIBRARIES_FILE", c.llndkLibrariesFile.String())
 	ctx.Strict("VNDKCORE_LIBRARIES_FILE", c.vndkCoreLibrariesFile.String())
 	ctx.Strict("VNDKSP_LIBRARIES_FILE", c.vndkSpLibrariesFile.String())
 	ctx.Strict("VNDKPRIVATE_LIBRARIES_FILE", c.vndkPrivateLibrariesFile.String())
 	ctx.Strict("VNDKCOREVARIANT_LIBRARIES_FILE", c.vndkCoreVariantLibrariesFile.String())
 
 	ctx.Strict("VNDK_LIBRARIES_FILE", c.vndkLibrariesFile.String())
+	ctx.Strict("SOONG_VNDK_SNAPSHOT_ZIP", c.vndkSnapshotZipFile.String())
 }