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
diff --git a/android/Android.bp b/android/Android.bp
index 2ac1d5f..26317b8 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -79,6 +79,7 @@
"prebuilt.go",
"prebuilt_build_tool.go",
"proto.go",
+ "provider.go",
"register.go",
"rule_builder.go",
"sandbox.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index c4b93c7..097075e 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -159,7 +159,7 @@
}
type AndroidMkExtraEntriesContext interface {
- Provider(provider blueprint.ProviderKey) interface{}
+ Provider(provider blueprint.AnyProviderKey) (any, bool)
}
type androidMkExtraEntriesContext struct {
@@ -167,8 +167,8 @@
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 @@
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
}
diff --git a/android/base_module_context.go b/android/base_module_context.go
index 4312e9b..2a4b12e 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -79,26 +79,30 @@
// 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) 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)
}
diff --git a/android/makevars.go b/android/makevars.go
index 0800190..5a9fe7c 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -92,7 +92,8 @@
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{})
diff --git a/android/mutator.go b/android/mutator.go
index 0d391a4..93c519d 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -325,7 +325,7 @@
// 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 @@
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)
}
diff --git a/android/provider.go b/android/provider.go
new file mode 100644
index 0000000..b2cc7c0
--- /dev/null
+++ b/android/provider.go
@@ -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}
+}
diff --git a/android/singleton.go b/android/singleton.go
index 7c6cf4f..8936cac 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -20,6 +20,8 @@
// SingletonContext
type SingletonContext interface {
+ blueprintSingletonContext() blueprint.SingletonContext
+
Config() Config
DeviceConfig() DeviceConfig
@@ -38,10 +40,12 @@
// 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 @@
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 @@
}
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)
+}
diff --git a/android/testing.go b/android/testing.go
index fa4dffd..39a268b 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -203,7 +203,17 @@
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 @@
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.