Support generating module_info.json in Soong

Generate module_info.json for some Soong modules in Soong in order to
pass fewer properties to Kati, which can prevent Kati reanalysis when
some Android.bp changes are made.

Soong modules can export a ModuleInfoJSONProvider containing the
data that should be included in module-info.json.  During the androidmk
singleton the providers are collected and written to a single JSON
file.  Make then merges the Soong modules into its own modules.

For now, to keep the result as similar as possible to the
module-info.json currently being generated by Make, only modules that
are exported to Make are written to the Soong module-info.json.

Bug: 309006256
Test: Compare module-info.json
Change-Id: I996520eb48e04743d43ac11c9aba0f3ada7745de
diff --git a/android/androidmk.go b/android/androidmk.go
index a0ed1e4..a3334dc 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -30,6 +30,7 @@
 	"reflect"
 	"runtime"
 	"sort"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -626,6 +627,10 @@
 		a.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath)
 	}
 
+	if _, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+		a.SetBool("LOCAL_SOONG_MODULE_INFO_JSON", true)
+	}
+
 	extraCtx := &androidMkExtraEntriesContext{
 		ctx: ctx,
 		mod: mod,
@@ -643,14 +648,14 @@
 	}
 }
 
+func (a *AndroidMkEntries) disabled() bool {
+	return a.Disabled || !a.OutputFile.Valid()
+}
+
 // write  flushes the AndroidMkEntries's in-struct data populated by AndroidMkEntries into the
 // given Writer object.
 func (a *AndroidMkEntries) write(w io.Writer) {
-	if a.Disabled {
-		return
-	}
-
-	if !a.OutputFile.Valid() {
+	if a.disabled() {
 		return
 	}
 
@@ -696,7 +701,9 @@
 		return
 	}
 
-	err := translateAndroidMk(ctx, absolutePath(transMk.String()), androidMkModulesList)
+	moduleInfoJSON := PathForOutput(ctx, "module-info"+String(ctx.Config().productVariables.Make_suffix)+".json")
+
+	err := translateAndroidMk(ctx, absolutePath(transMk.String()), moduleInfoJSON, androidMkModulesList)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
@@ -707,14 +714,16 @@
 	})
 }
 
-func translateAndroidMk(ctx SingletonContext, absMkFile string, mods []blueprint.Module) error {
+func translateAndroidMk(ctx SingletonContext, absMkFile string, moduleInfoJSONPath WritablePath, mods []blueprint.Module) error {
 	buf := &bytes.Buffer{}
 
+	var moduleInfoJSONs []*ModuleInfoJSON
+
 	fmt.Fprintln(buf, "LOCAL_MODULE_MAKEFILE := $(lastword $(MAKEFILE_LIST))")
 
 	typeStats := make(map[string]int)
 	for _, mod := range mods {
-		err := translateAndroidMkModule(ctx, buf, mod)
+		err := translateAndroidMkModule(ctx, buf, &moduleInfoJSONs, mod)
 		if err != nil {
 			os.Remove(absMkFile)
 			return err
@@ -736,10 +745,36 @@
 		fmt.Fprintf(buf, "STATS.SOONG_MODULE_TYPE.%s := %d\n", mod_type, typeStats[mod_type])
 	}
 
-	return pathtools.WriteFileIfChanged(absMkFile, buf.Bytes(), 0666)
+	err := pathtools.WriteFileIfChanged(absMkFile, buf.Bytes(), 0666)
+	if err != nil {
+		return err
+	}
+
+	return writeModuleInfoJSON(ctx, moduleInfoJSONs, moduleInfoJSONPath)
 }
 
-func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.Module) error {
+func writeModuleInfoJSON(ctx SingletonContext, moduleInfoJSONs []*ModuleInfoJSON, moduleInfoJSONPath WritablePath) error {
+	moduleInfoJSONBuf := &strings.Builder{}
+	moduleInfoJSONBuf.WriteString("[")
+	for i, moduleInfoJSON := range moduleInfoJSONs {
+		if i != 0 {
+			moduleInfoJSONBuf.WriteString(",\n")
+		}
+		moduleInfoJSONBuf.WriteString("{")
+		moduleInfoJSONBuf.WriteString(strconv.Quote(moduleInfoJSON.core.RegisterName))
+		moduleInfoJSONBuf.WriteString(":")
+		err := encodeModuleInfoJSON(moduleInfoJSONBuf, moduleInfoJSON)
+		moduleInfoJSONBuf.WriteString("}")
+		if err != nil {
+			return err
+		}
+	}
+	moduleInfoJSONBuf.WriteString("]")
+	WriteFileRule(ctx, moduleInfoJSONPath, moduleInfoJSONBuf.String())
+	return nil
+}
+
+func translateAndroidMkModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON, mod blueprint.Module) error {
 	defer func() {
 		if r := recover(); r != nil {
 			panic(fmt.Errorf("%s in translateAndroidMkModule for module %s variant %s",
@@ -748,17 +783,23 @@
 	}()
 
 	// Additional cases here require review for correct license propagation to make.
+	var err error
 	switch x := mod.(type) {
 	case AndroidMkDataProvider:
-		return translateAndroidModule(ctx, w, mod, x)
+		err = translateAndroidModule(ctx, w, moduleInfoJSONs, mod, x)
 	case bootstrap.GoBinaryTool:
-		return translateGoBinaryModule(ctx, w, mod, x)
+		err = translateGoBinaryModule(ctx, w, mod, x)
 	case AndroidMkEntriesProvider:
-		return translateAndroidMkEntriesModule(ctx, w, mod, x)
+		err = translateAndroidMkEntriesModule(ctx, w, moduleInfoJSONs, mod, x)
 	default:
 		// Not exported to make so no make variables to set.
-		return nil
 	}
+
+	if err != nil {
+		return err
+	}
+
+	return err
 }
 
 // A simple, special Android.mk entry output func to make it possible to build blueprint tools using
@@ -801,8 +842,8 @@
 
 // A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider
 // instead.
-func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
-	provider AndroidMkDataProvider) error {
+func translateAndroidModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
+	mod blueprint.Module, provider AndroidMkDataProvider) error {
 
 	amod := mod.(Module).base()
 	if shouldSkipAndroidMkProcessing(amod) {
@@ -864,17 +905,19 @@
 		WriteAndroidMkData(w, data)
 	}
 
+	if !data.Entries.disabled() {
+		if moduleInfoJSON, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
+		}
+	}
+
 	return nil
 }
 
 // A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider
 // instead.
 func WriteAndroidMkData(w io.Writer, data AndroidMkData) {
-	if data.Disabled {
-		return
-	}
-
-	if !data.OutputFile.Valid() {
+	if data.Entries.disabled() {
 		return
 	}
 
@@ -889,18 +932,26 @@
 	fmt.Fprintln(w, "include "+data.Include)
 }
 
-func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
-	provider AndroidMkEntriesProvider) error {
+func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
+	mod blueprint.Module, provider AndroidMkEntriesProvider) error {
 	if shouldSkipAndroidMkProcessing(mod.(Module).base()) {
 		return nil
 	}
 
+	entriesList := provider.AndroidMkEntries()
+
 	// Any new or special cases here need review to verify correct propagation of license information.
-	for _, entries := range provider.AndroidMkEntries() {
+	for _, entries := range entriesList {
 		entries.fillInEntries(ctx, mod)
 		entries.write(w)
 	}
 
+	if len(entriesList) > 0 && !entriesList[0].disabled() {
+		if moduleInfoJSON, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
+		}
+	}
+
 	return nil
 }