Use generics for providers API

Using generics for the providers API allows a type to be associated
with a ProviderKey, resulting in a type-safe API without that doesn't
require runtime type assertions by every caller.

Unfortunately, Go does not allow generic types in methods, only in
functions [1].  This prevents a type-safe API on ModuleContext, and
requires moving the API to be functions that take a ModuleContext as
a parameter.

This CL creates the new API, but doesn't convert all of the callers.

[1] https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#no-parameterized-methods)

Bug: 316410648
Test: builds
Change-Id: I3e30d68b966b730efd968166a38a25cc144bd6de
This commit is contained in:
Colin Cross 2023-12-12 14:13:26 -08:00
parent e8eeec913f
commit 3c0a83d19f
12 changed files with 212 additions and 30 deletions

View file

@ -79,6 +79,7 @@ bootstrap_go_package {
"prebuilt.go",
"prebuilt_build_tool.go",
"proto.go",
"provider.go",
"register.go",
"rule_builder.go",
"sandbox.go",

View file

@ -159,7 +159,7 @@ type AndroidMkEntriesContext interface {
}
type AndroidMkExtraEntriesContext interface {
Provider(provider blueprint.ProviderKey) interface{}
Provider(provider blueprint.AnyProviderKey) (any, bool)
}
type androidMkExtraEntriesContext struct {
@ -167,8 +167,8 @@ type androidMkExtraEntriesContext struct {
mod blueprint.Module
}
func (a *androidMkExtraEntriesContext) Provider(provider blueprint.ProviderKey) interface{} {
return a.ctx.ModuleProvider(a.mod, provider)
func (a *androidMkExtraEntriesContext) Provider(provider blueprint.AnyProviderKey) (any, bool) {
return a.ctx.moduleProvider(a.mod, provider)
}
type AndroidMkExtraEntriesFunc func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries)
@ -492,8 +492,9 @@ type fillInEntriesContext interface {
ModuleDir(module blueprint.Module) string
ModuleSubDir(module blueprint.Module) string
Config() Config
ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
ModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool
ModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) any
ModuleHasProvider(module blueprint.Module, provider blueprint.AnyProviderKey) bool
moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
ModuleType(module blueprint.Module) string
}

View file

@ -79,26 +79,30 @@ type BaseModuleContext interface {
// not set it returns the zero value of the type of the provider, so the return value can always
// be type asserted to the type of the provider. The value returned may be a deep copy of the
// value originally passed to SetProvider.
OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{}
OtherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) any
// OtherModuleHasProvider returns true if the provider for the given module has been set.
OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool
OtherModuleHasProvider(m blueprint.Module, provider blueprint.AnyProviderKey) bool
otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
// Provider returns the value for a provider for the current module. If the value is
// not set it returns the zero value of the type of the provider, so the return value can always
// be type asserted to the type of the provider. It panics if called before the appropriate
// mutator or GenerateBuildActions pass for the provider. The value returned may be a deep
// copy of the value originally passed to SetProvider.
Provider(provider blueprint.ProviderKey) interface{}
Provider(provider blueprint.AnyProviderKey) any
// HasProvider returns true if the provider for the current module has been set.
HasProvider(provider blueprint.ProviderKey) bool
HasProvider(provider blueprint.AnyProviderKey) bool
provider(provider blueprint.AnyProviderKey) (any, bool)
// SetProvider sets the value for a provider for the current module. It panics if not called
// during the appropriate mutator or GenerateBuildActions pass for the provider, if the value
// is not of the appropriate type, or if the value has already been set. The value should not
// be modified after being passed to SetProvider.
SetProvider(provider blueprint.ProviderKey, value interface{})
SetProvider(provider blueprint.AnyProviderKey, value interface{})
GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module
@ -260,19 +264,35 @@ func (b *baseModuleContext) OtherModuleReverseDependencyVariantExists(name strin
func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string {
return b.bp.OtherModuleType(m)
}
func (b *baseModuleContext) OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} {
func (b *baseModuleContext) OtherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) any {
value, _ := b.bp.OtherModuleProvider(m, provider)
return value
}
func (b *baseModuleContext) OtherModuleHasProvider(m blueprint.Module, provider blueprint.AnyProviderKey) bool {
_, ok := b.bp.OtherModuleProvider(m, provider)
return ok
}
func (b *baseModuleContext) otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
return b.bp.OtherModuleProvider(m, provider)
}
func (b *baseModuleContext) OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool {
return b.bp.OtherModuleHasProvider(m, provider)
func (b *baseModuleContext) Provider(provider blueprint.AnyProviderKey) any {
value, _ := b.bp.Provider(provider)
return value
}
func (b *baseModuleContext) Provider(provider blueprint.ProviderKey) interface{} {
func (b *baseModuleContext) HasProvider(provider blueprint.AnyProviderKey) bool {
_, ok := b.bp.Provider(provider)
return ok
}
func (b *baseModuleContext) provider(provider blueprint.AnyProviderKey) (any, bool) {
return b.bp.Provider(provider)
}
func (b *baseModuleContext) HasProvider(provider blueprint.ProviderKey) bool {
return b.bp.HasProvider(provider)
}
func (b *baseModuleContext) SetProvider(provider blueprint.ProviderKey, value interface{}) {
func (b *baseModuleContext) SetProvider(provider blueprint.AnyProviderKey, value any) {
b.bp.SetProvider(provider, value)
}

View file

@ -92,7 +92,8 @@ type MakeVarsContext interface {
ModuleDir(module blueprint.Module) string
ModuleSubDir(module blueprint.Module) string
ModuleType(module blueprint.Module) string
ModuleProvider(module blueprint.Module, key blueprint.ProviderKey) interface{}
ModuleProvider(module blueprint.Module, key blueprint.AnyProviderKey) any
moduleProvider(module blueprint.Module, key blueprint.AnyProviderKey) (any, bool)
BlueprintFile(module blueprint.Module) string
ModuleErrorf(module blueprint.Module, format string, args ...interface{})

View file

@ -325,7 +325,7 @@ type BottomUpMutatorContext interface {
// if the value is not of the appropriate type, or if the module is not a newly created
// variant of the current module. The value should not be modified after being passed to
// SetVariationProvider.
SetVariationProvider(module blueprint.Module, provider blueprint.ProviderKey, value interface{})
SetVariationProvider(module blueprint.Module, provider blueprint.AnyProviderKey, value interface{})
}
type bottomUpMutatorContext struct {
@ -746,6 +746,6 @@ func (b *bottomUpMutatorContext) CreateAliasVariation(fromVariationName, toVaria
b.bp.CreateAliasVariation(fromVariationName, toVariationName)
}
func (b *bottomUpMutatorContext) SetVariationProvider(module blueprint.Module, provider blueprint.ProviderKey, value interface{}) {
func (b *bottomUpMutatorContext) SetVariationProvider(module blueprint.Module, provider blueprint.AnyProviderKey, value interface{}) {
b.bp.SetVariationProvider(module, provider, value)
}

120
android/provider.go Normal file
View file

@ -0,0 +1,120 @@
package android
import (
"github.com/google/blueprint"
)
// OtherModuleProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
// TopDownMutatorContext for use in OtherModuleProvider.
type OtherModuleProviderContext interface {
otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
}
var _ OtherModuleProviderContext = BaseModuleContext(nil)
var _ OtherModuleProviderContext = ModuleContext(nil)
var _ OtherModuleProviderContext = BottomUpMutatorContext(nil)
var _ OtherModuleProviderContext = TopDownMutatorContext(nil)
// OtherModuleProvider reads the provider for the given module. If the provider has been set the value is
// returned and the boolean is true. If it has not been set the zero value of the provider's type is returned
// and the boolean is false. The value returned may be a deep copy of the value originally passed to SetProvider.
//
// OtherModuleProviderContext is a helper interface that accepts ModuleContext, BottomUpMutatorContext, or
// TopDownMutatorContext.
func OtherModuleProvider[K any](ctx OtherModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) (K, bool) {
value, ok := ctx.otherModuleProvider(module, provider)
if !ok {
var k K
return k, false
}
return value.(K), ok
}
// ModuleProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
// TopDownMutatorContext for use in ModuleProvider.
type ModuleProviderContext interface {
provider(provider blueprint.AnyProviderKey) (any, bool)
}
var _ ModuleProviderContext = BaseModuleContext(nil)
var _ ModuleProviderContext = ModuleContext(nil)
var _ ModuleProviderContext = BottomUpMutatorContext(nil)
var _ ModuleProviderContext = TopDownMutatorContext(nil)
// ModuleProvider reads the provider for the current module. If the provider has been set the value is
// returned and the boolean is true. If it has not been set the zero value of the provider's type is returned
// and the boolean is false. The value returned may be a deep copy of the value originally passed to SetProvider.
//
// ModuleProviderContext is a helper interface that accepts ModuleContext, BottomUpMutatorContext, or
// TopDownMutatorContext.
func ModuleProvider[K any](ctx ModuleProviderContext, provider blueprint.ProviderKey[K]) (K, bool) {
value, ok := ctx.provider(provider)
if !ok {
var k K
return k, false
}
return value.(K), ok
}
type SingletonModuleProviderContext interface {
moduleProvider(blueprint.Module, blueprint.AnyProviderKey) (any, bool)
}
var _ SingletonModuleProviderContext = SingletonContext(nil)
var _ SingletonModuleProviderContext = (*TestContext)(nil)
// SingletonModuleProvider wraps blueprint.SingletonModuleProvider to provide a type-safe method to retrieve the value
// of the given provider from a module using a SingletonContext. If the provider has not been set the first return
// value will be the zero value of the provider's type, and the second return value will be false. If the provider has
// been set the second return value will be true.
func SingletonModuleProvider[K any](ctx SingletonModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) (K, bool) {
value, ok := ctx.moduleProvider(module, provider)
if !ok {
var k K
return k, false
}
return value.(K), ok
}
// SetProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
// TopDownMutatorContext for use in SetProvider.
type SetProviderContext interface {
SetProvider(provider blueprint.AnyProviderKey, value any)
}
var _ SetProviderContext = BaseModuleContext(nil)
var _ SetProviderContext = ModuleContext(nil)
var _ SetProviderContext = BottomUpMutatorContext(nil)
var _ SetProviderContext = TopDownMutatorContext(nil)
// SetProvider sets the value for a provider for the current module. It panics if not called
// during the appropriate mutator or GenerateBuildActions pass for the provider, if the value
// is not of the appropriate type, or if the value has already been set. The value should not
// be modified after being passed to SetProvider.
//
// SetProviderContext is a helper interface that accepts ModuleContext, BottomUpMutatorContext, or
// TopDownMutatorContext.
func SetProvider[K any](ctx SetProviderContext, provider blueprint.ProviderKey[K], value K) {
ctx.SetProvider(provider, value)
}
var _ OtherModuleProviderContext = (*otherModuleProviderAdaptor)(nil)
// An OtherModuleProviderFunc can be passed to NewOtherModuleProviderAdaptor to create an OtherModuleProviderContext
// for use in tests.
type OtherModuleProviderFunc func(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
type otherModuleProviderAdaptor struct {
otherModuleProviderFunc OtherModuleProviderFunc
}
func (p *otherModuleProviderAdaptor) otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
return p.otherModuleProviderFunc(module, provider)
}
// NewOtherModuleProviderAdaptor returns an OtherModuleProviderContext that proxies calls to otherModuleProvider to
// the provided OtherModuleProviderFunc. It can be used in tests to unit test methods that need to call
// android.OtherModuleProvider.
func NewOtherModuleProviderAdaptor(otherModuleProviderFunc OtherModuleProviderFunc) OtherModuleProviderContext {
return &otherModuleProviderAdaptor{otherModuleProviderFunc}
}

View file

@ -20,6 +20,8 @@ import (
// SingletonContext
type SingletonContext interface {
blueprintSingletonContext() blueprint.SingletonContext
Config() Config
DeviceConfig() DeviceConfig
@ -38,10 +40,12 @@ type SingletonContext interface {
// return value can always be type-asserted to the type of the provider. The return value should
// always be considered read-only. It panics if called before the appropriate mutator or
// GenerateBuildActions pass for the provider on the module.
ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
ModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) any
// ModuleHasProvider returns true if the provider for the given module has been set.
ModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool
ModuleHasProvider(module blueprint.Module, provider blueprint.AnyProviderKey) bool
moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
ModuleErrorf(module blueprint.Module, format string, args ...interface{})
Errorf(format string, args ...interface{})
@ -135,6 +139,10 @@ type singletonContextAdaptor struct {
ruleParams map[blueprint.Rule]blueprint.RuleParams
}
func (s *singletonContextAdaptor) blueprintSingletonContext() blueprint.SingletonContext {
return s.SingletonContext
}
func (s *singletonContextAdaptor) Config() Config {
return s.SingletonContext.Config().(Config)
}
@ -282,3 +290,18 @@ func (s *singletonContextAdaptor) ModuleVariantsFromName(referer Module, name st
}
return result
}
func (s *singletonContextAdaptor) ModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) any {
value, _ := s.SingletonContext.ModuleProvider(module, provider)
return value
}
// ModuleHasProvider returns true if the provider for the given module has been set.
func (s *singletonContextAdaptor) ModuleHasProvider(module blueprint.Module, provider blueprint.AnyProviderKey) bool {
_, ok := s.SingletonContext.ModuleProvider(module, provider)
return ok
}
func (s *singletonContextAdaptor) moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
return s.SingletonContext.ModuleProvider(module, provider)
}

View file

@ -203,7 +203,17 @@ func (ctx *TestContext) HardCodedPreArchMutators(f RegisterMutatorFunc) {
ctx.PreArchMutators(f)
}
func (ctx *TestContext) ModuleProvider(m blueprint.Module, p blueprint.ProviderKey) interface{} {
func (ctx *TestContext) ModuleProvider(m blueprint.Module, p blueprint.AnyProviderKey) any {
value, _ := ctx.Context.ModuleProvider(m, p)
return value
}
func (ctx *TestContext) ModuleHasProvider(m blueprint.Module, p blueprint.AnyProviderKey) bool {
_, ok := ctx.Context.ModuleProvider(m, p)
return ok
}
func (ctx *TestContext) moduleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) {
return ctx.Context.ModuleProvider(m, p)
}
@ -225,6 +235,12 @@ func (ctx *TestContext) PreArchBp2BuildMutators(f RegisterMutatorFunc) {
ctx.bp2buildPreArch = append(ctx.bp2buildPreArch, f)
}
func (ctx *TestContext) OtherModuleProviderAdaptor() OtherModuleProviderContext {
return NewOtherModuleProviderAdaptor(func(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
return ctx.moduleProvider(module, provider)
})
}
// registeredComponentOrder defines the order in which a sortableComponent type is registered at
// runtime and provides support for reordering the components registered for a test in the same
// way.

View file

@ -33,11 +33,11 @@ type testClasspathElementContext struct {
errs []error
}
func (t *testClasspathElementContext) OtherModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool {
func (t *testClasspathElementContext) OtherModuleHasProvider(module blueprint.Module, provider blueprint.AnyProviderKey) bool {
return t.testContext.ModuleHasProvider(module, provider)
}
func (t *testClasspathElementContext) OtherModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{} {
func (t *testClasspathElementContext) OtherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) interface{} {
return t.testContext.ModuleProvider(module, provider)
}

View file

@ -26,7 +26,7 @@ const bloatyDescriptorExt = ".bloaty.csv"
const protoFilename = "binary_sizes.pb.gz"
var (
fileSizeMeasurerKey blueprint.ProviderKey
fileSizeMeasurerKey blueprint.ProviderKey[measuredFiles]
pctx = android.NewPackageContext("android/soong/bloaty")
// bloaty is used to measure a binary section sizes.

View file

@ -49,7 +49,7 @@ var prepareForTsanTest = android.FixtureAddFile("tsan/Android.bp", []byte(`
`))
type providerInterface interface {
ModuleProvider(blueprint.Module, blueprint.ProviderKey) interface{}
ModuleProvider(blueprint.Module, blueprint.AnyProviderKey) interface{}
}
// expectSharedLinkDep verifies that the from module links against the to module as a

View file

@ -72,8 +72,8 @@ var _ ClasspathElement = (*ClasspathLibraryElement)(nil)
// ClasspathElementContext defines the context methods needed by CreateClasspathElements
type ClasspathElementContext interface {
OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool
OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{}
OtherModuleHasProvider(m blueprint.Module, provider blueprint.AnyProviderKey) bool
OtherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) interface{}
ModuleErrorf(fmt string, args ...interface{})
}