Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 138d37dd authored by Colin Cross's avatar Colin Cross Committed by Gerrit Code Review
Browse files

Merge "Support generating module_info.json in Soong" into main

parents 070490ce d6fd0133
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ bootstrap_go_package {
        "metrics.go",
        "module.go",
        "module_context.go",
        "module_info_json.go",
        "mutator.go",
        "namespace.go",
        "neverallow.go",
+75 −24
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import (
	"reflect"
	"runtime"
	"sort"
	"strconv"
	"strings"

	"github.com/google/blueprint"
@@ -626,6 +627,10 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint
		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) fillInEntries(ctx fillInEntriesContext, mod blueprint
	}
}

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 @@ func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
		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 (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
	})
}

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 @@ func translateAndroidMk(ctx SingletonContext, absMkFile string, mods []blueprint
		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 @@ func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.M
	}()

	// 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 @@ func (data *AndroidMkData) fillInData(ctx fillInEntriesContext, mod blueprint.Mo

// 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) {
@@ -865,17 +906,19 @@ func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Mod
		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
	}

@@ -890,18 +933,26 @@ func WriteAndroidMkData(w io.Writer, data AndroidMkData) {
	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
}

+84 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import (
	"net/url"
	"path/filepath"
	"reflect"
	"slices"
	"sort"
	"strings"

@@ -876,6 +877,10 @@ type ModuleBase struct {

	// The path to the generated license metadata file for the module.
	licenseMetadataFile WritablePath

	// moduleInfoJSON can be filled out by GenerateAndroidBuildActions to write a JSON file that will
	// be included in the final module-info.json produced by Make.
	moduleInfoJSON *ModuleInfoJSON
}

func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
@@ -1771,11 +1776,90 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext)

	buildLicenseMetadata(ctx, m.licenseMetadataFile)

	if m.moduleInfoJSON != nil {
		var installed InstallPaths
		installed = append(installed, m.katiInstalls.InstallPaths()...)
		installed = append(installed, m.katiSymlinks.InstallPaths()...)
		installed = append(installed, m.katiInitRcInstalls.InstallPaths()...)
		installed = append(installed, m.katiVintfInstalls.InstallPaths()...)
		installedStrings := installed.Strings()

		var targetRequired, hostRequired []string
		if ctx.Host() {
			targetRequired = m.commonProperties.Target_required
		} else {
			hostRequired = m.commonProperties.Host_required
		}

		var data []string
		for _, d := range m.testData {
			data = append(data, d.ToRelativeInstallPath())
		}

		if m.moduleInfoJSON.Uninstallable {
			installedStrings = nil
			if len(m.moduleInfoJSON.CompatibilitySuites) == 1 && m.moduleInfoJSON.CompatibilitySuites[0] == "null-suite" {
				m.moduleInfoJSON.CompatibilitySuites = nil
				m.moduleInfoJSON.TestConfig = nil
				m.moduleInfoJSON.AutoTestConfig = nil
				data = nil
			}
		}

		m.moduleInfoJSON.core = CoreModuleInfoJSON{
			RegisterName:       m.moduleInfoRegisterName(ctx, m.moduleInfoJSON.SubName),
			Path:               []string{ctx.ModuleDir()},
			Installed:          installedStrings,
			ModuleName:         m.BaseModuleName() + m.moduleInfoJSON.SubName,
			SupportedVariants:  []string{m.moduleInfoVariant(ctx)},
			TargetDependencies: targetRequired,
			HostDependencies:   hostRequired,
			Data:               data,
		}
		SetProvider(ctx, ModuleInfoJSONProvider, m.moduleInfoJSON)
	}

	m.buildParams = ctx.buildParams
	m.ruleParams = ctx.ruleParams
	m.variables = ctx.variables
}

func (m *ModuleBase) moduleInfoRegisterName(ctx ModuleContext, subName string) string {
	name := m.BaseModuleName()

	prefix := ""
	if ctx.Host() {
		if ctx.Os() != ctx.Config().BuildOS {
			prefix = "host_cross_"
		}
	}
	suffix := ""
	arches := slices.Clone(ctx.Config().Targets[ctx.Os()])
	arches = slices.DeleteFunc(arches, func(target Target) bool {
		return target.NativeBridge != ctx.Target().NativeBridge
	})
	if len(arches) > 0 && ctx.Arch().ArchType != arches[0].Arch.ArchType {
		if ctx.Arch().ArchType.Multilib == "lib32" {
			suffix = "_32"
		} else {
			suffix = "_64"
		}
	}
	return prefix + name + subName + suffix
}

func (m *ModuleBase) moduleInfoVariant(ctx ModuleContext) string {
	variant := "DEVICE"
	if ctx.Host() {
		if ctx.Os() != ctx.Config().BuildOS {
			variant = "HOST_CROSS"
		} else {
			variant = "HOST"
		}
	}
	return variant
}

// Check the supplied dist structure to make sure that it is valid.
//
// property - the base property, e.g. dist or dists[1], which is combined with the
+16 −0
Original line number Diff line number Diff line
@@ -210,6 +210,11 @@ type ModuleContext interface {
	// LicenseMetadataFile returns the path where the license metadata for this module will be
	// generated.
	LicenseMetadataFile() Path

	// ModuleInfoJSON returns a pointer to the ModuleInfoJSON struct that can be filled out by
	// GenerateAndroidBuildActions.  If it is called then the struct will be written out and included in
	// the module-info.json generated by Make, and Make will not generate its own data for this module.
	ModuleInfoJSON() *ModuleInfoJSON
}

type moduleContext struct {
@@ -518,6 +523,8 @@ func (m *moduleContext) installFile(installPath InstallPath, name string, srcPat

	if !m.skipInstall() {
		deps = append(deps, InstallPaths(m.module.base().installFilesDepSet.ToList())...)
		deps = append(deps, m.module.base().installedInitRcPaths...)
		deps = append(deps, m.module.base().installedVintfFragmentsPaths...)

		var implicitDeps, orderOnlyDeps Paths

@@ -695,6 +702,15 @@ func (m *moduleContext) LicenseMetadataFile() Path {
	return m.module.base().licenseMetadataFile
}

func (m *moduleContext) ModuleInfoJSON() *ModuleInfoJSON {
	if moduleInfoJSON := m.module.base().moduleInfoJSON; moduleInfoJSON != nil {
		return moduleInfoJSON
	}
	moduleInfoJSON := &ModuleInfoJSON{}
	m.module.base().moduleInfoJSON = moduleInfoJSON
	return moduleInfoJSON
}

// Returns a list of paths expanded from globs and modules referenced using ":module" syntax.  The property must
// be tagged with `android:"path" to support automatic source module dependency resolution.
//
+103 −0
Original line number Diff line number Diff line
package android

import (
	"encoding/json"
	"io"
	"slices"

	"github.com/google/blueprint"
)

type CoreModuleInfoJSON struct {
	RegisterName       string   `json:"-"`
	Path               []string `json:"path,omitempty"`                // $(sort $(ALL_MODULES.$(m).PATH))
	Installed          []string `json:"installed,omitempty"`           // $(sort $(ALL_MODULES.$(m).INSTALLED))
	ModuleName         string   `json:"module_name,omitempty"`         // $(ALL_MODULES.$(m).MODULE_NAME)
	SupportedVariants  []string `json:"supported_variants,omitempty"`  // $(sort $(ALL_MODULES.$(m).SUPPORTED_VARIANTS))
	HostDependencies   []string `json:"host_dependencies,omitempty"`   // $(sort $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET))
	TargetDependencies []string `json:"target_dependencies,omitempty"` // $(sort $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST))
	Data               []string `json:"data,omitempty"`                // $(sort $(ALL_MODULES.$(m).TEST_DATA))
}

type ModuleInfoJSON struct {
	core                CoreModuleInfoJSON
	SubName             string   `json:"-"`
	Uninstallable       bool     `json:"-"`
	Class               []string `json:"class,omitempty"`                 // $(sort $(ALL_MODULES.$(m).CLASS))
	Tags                []string `json:"tags,omitempty"`                  // $(sort $(ALL_MODULES.$(m).TAGS))
	Dependencies        []string `json:"dependencies,omitempty"`          // $(sort $(ALL_DEPS.$(m).ALL_DEPS))
	SharedLibs          []string `json:"shared_libs,omitempty"`           // $(sort $(ALL_MODULES.$(m).SHARED_LIBS))
	StaticLibs          []string `json:"static_libs,omitempty"`           // $(sort $(ALL_MODULES.$(m).STATIC_LIBS))
	SystemSharedLibs    []string `json:"system_shared_libs,omitempty"`    // $(sort $(ALL_MODULES.$(m).SYSTEM_SHARED_LIBS))
	Srcs                []string `json:"srcs,omitempty"`                  // $(sort $(ALL_MODULES.$(m).SRCS))
	SrcJars             []string `json:"srcjars,omitempty"`               // $(sort $(ALL_MODULES.$(m).SRCJARS))
	ClassesJar          []string `json:"classes_jar,omitempty"`           // $(sort $(ALL_MODULES.$(m).CLASSES_JAR))
	TestMainlineModules []string `json:"test_mainline_modules,omitempty"` // $(sort $(ALL_MODULES.$(m).TEST_MAINLINE_MODULES))
	IsUnitTest          bool     `json:"is_unit_test,omitempty"`          // $(ALL_MODULES.$(m).IS_UNIT_TEST)
	TestOptionsTags     []string `json:"test_options_tags,omitempty"`     // $(sort $(ALL_MODULES.$(m).TEST_OPTIONS_TAGS))
	RuntimeDependencies []string `json:"runtime_dependencies,omitempty"`  // $(sort $(ALL_MODULES.$(m).LOCAL_RUNTIME_LIBRARIES))
	StaticDependencies  []string `json:"static_dependencies,omitempty"`   // $(sort $(ALL_MODULES.$(m).LOCAL_STATIC_LIBRARIES))
	DataDependencies    []string `json:"data_dependencies,omitempty"`     // $(sort $(ALL_MODULES.$(m).TEST_DATA_BINS))

	CompatibilitySuites []string `json:"compatibility_suites,omitempty"` // $(sort $(ALL_MODULES.$(m).COMPATIBILITY_SUITES))
	AutoTestConfig      []string `json:"auto_test_config,omitempty"`     // $(ALL_MODULES.$(m).auto_test_config)
	TestConfig          []string `json:"test_config,omitempty"`          // $(strip $(ALL_MODULES.$(m).TEST_CONFIG) $(ALL_MODULES.$(m).EXTRA_TEST_CONFIGS)
}

//ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS := $(sort \
//$(ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS) \
//$(LOCAL_STATIC_LIBRARIES) \
//$(LOCAL_WHOLE_STATIC_LIBRARIES) \
//$(LOCAL_SHARED_LIBRARIES) \
//$(LOCAL_DYLIB_LIBRARIES) \
//$(LOCAL_RLIB_LIBRARIES) \
//$(LOCAL_PROC_MACRO_LIBRARIES) \
//$(LOCAL_HEADER_LIBRARIES) \
//$(LOCAL_STATIC_JAVA_LIBRARIES) \
//$(LOCAL_JAVA_LIBRARIES) \
//$(LOCAL_JNI_SHARED_LIBRARIES))

type combinedModuleInfoJSON struct {
	*CoreModuleInfoJSON
	*ModuleInfoJSON
}

func encodeModuleInfoJSON(w io.Writer, moduleInfoJSON *ModuleInfoJSON) error {
	moduleInfoJSONCopy := *moduleInfoJSON

	sortAndUnique := func(s *[]string) {
		*s = slices.Clone(*s)
		slices.Sort(*s)
		*s = slices.Compact(*s)
	}

	sortAndUnique(&moduleInfoJSONCopy.core.Path)
	sortAndUnique(&moduleInfoJSONCopy.core.Installed)
	sortAndUnique(&moduleInfoJSONCopy.core.SupportedVariants)
	sortAndUnique(&moduleInfoJSONCopy.core.HostDependencies)
	sortAndUnique(&moduleInfoJSONCopy.core.TargetDependencies)
	sortAndUnique(&moduleInfoJSONCopy.core.Data)

	sortAndUnique(&moduleInfoJSONCopy.Class)
	sortAndUnique(&moduleInfoJSONCopy.Tags)
	sortAndUnique(&moduleInfoJSONCopy.Dependencies)
	sortAndUnique(&moduleInfoJSONCopy.SharedLibs)
	sortAndUnique(&moduleInfoJSONCopy.StaticLibs)
	sortAndUnique(&moduleInfoJSONCopy.SystemSharedLibs)
	sortAndUnique(&moduleInfoJSONCopy.Srcs)
	sortAndUnique(&moduleInfoJSONCopy.SrcJars)
	sortAndUnique(&moduleInfoJSONCopy.ClassesJar)
	sortAndUnique(&moduleInfoJSONCopy.TestMainlineModules)
	sortAndUnique(&moduleInfoJSONCopy.TestOptionsTags)
	sortAndUnique(&moduleInfoJSONCopy.RuntimeDependencies)
	sortAndUnique(&moduleInfoJSONCopy.StaticDependencies)
	sortAndUnique(&moduleInfoJSONCopy.DataDependencies)
	sortAndUnique(&moduleInfoJSONCopy.CompatibilitySuites)
	sortAndUnique(&moduleInfoJSONCopy.AutoTestConfig)
	sortAndUnique(&moduleInfoJSONCopy.TestConfig)

	encoder := json.NewEncoder(w)
	return encoder.Encode(combinedModuleInfoJSON{&moduleInfoJSONCopy.core, &moduleInfoJSONCopy})
}

var ModuleInfoJSONProvider = blueprint.NewProvider[*ModuleInfoJSON]()
Loading