Skip to content

Commit

Permalink
Support generating module_info.json in Soong
Browse files Browse the repository at this point in the history
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
  • Loading branch information
colincross committed Jan 3, 2024
1 parent d9bbf4b commit d6fd013
Show file tree
Hide file tree
Showing 8 changed files with 590 additions and 24 deletions.
1 change: 1 addition & 0 deletions android/Android.bp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ bootstrap_go_package {
"metrics.go",
"module.go",
"module_context.go",
"module_info_json.go",
"mutator.go",
"namespace.go",
"neverallow.go",
Expand Down
99 changes: 75 additions & 24 deletions android/androidmk.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"reflect"
"runtime"
"sort"
"strconv"
"strings"

"github.com/google/blueprint"
Expand Down Expand Up @@ -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,
Expand All @@ -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
}

Expand Down Expand Up @@ -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())
}
Expand All @@ -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
Expand All @@ -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",
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -864,17 +905,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
}

Expand All @@ -889,18 +932,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
}

Expand Down
84 changes: 84 additions & 0 deletions android/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"net/url"
"path/filepath"
"reflect"
"slices"
"sort"
"strings"

Expand Down Expand Up @@ -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{}) {
Expand Down Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions android/module_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.
//
Expand Down
Loading

0 comments on commit d6fd013

Please sign in to comment.