Merge pull request #309 from colincross/providers
Add Providers to Blueprint
This commit is contained in:
commit
8a747a0864
6 changed files with 786 additions and 2 deletions
|
@ -17,6 +17,7 @@ bootstrap_go_package {
|
|||
"ninja_strings.go",
|
||||
"ninja_writer.go",
|
||||
"package_ctx.go",
|
||||
"provider.go",
|
||||
"scope.go",
|
||||
"singleton_ctx.go",
|
||||
],
|
||||
|
@ -26,6 +27,7 @@ bootstrap_go_package {
|
|||
"module_ctx_test.go",
|
||||
"ninja_strings_test.go",
|
||||
"ninja_writer_test.go",
|
||||
"provider_test.go",
|
||||
"splice_modules_test.go",
|
||||
"visit_test.go",
|
||||
],
|
||||
|
|
62
context.go
62
context.go
|
@ -117,6 +117,19 @@ type Context struct {
|
|||
srcDir string
|
||||
fs pathtools.FileSystem
|
||||
moduleListFile string
|
||||
|
||||
// Mutators indexed by the ID of the provider associated with them. Not all mutators will
|
||||
// have providers, and not all providers will have a mutator, or if they do the mutator may
|
||||
// not be registered in this Context.
|
||||
providerMutators []*mutatorInfo
|
||||
|
||||
// The currently running mutator
|
||||
startedMutator *mutatorInfo
|
||||
// True for any mutators that have already run over all modules
|
||||
finishedMutators map[*mutatorInfo]bool
|
||||
|
||||
// Can be set by tests to avoid invalidating Module values after mutators.
|
||||
skipCloneModulesAfterMutators bool
|
||||
}
|
||||
|
||||
// An Error describes a problem that was encountered that is related to a
|
||||
|
@ -254,6 +267,14 @@ type moduleInfo struct {
|
|||
|
||||
// set during PrepareBuildActions
|
||||
actionDefs localBuildActions
|
||||
|
||||
providers []interface{}
|
||||
|
||||
startedMutator *mutatorInfo
|
||||
finishedMutator *mutatorInfo
|
||||
|
||||
startedGenerateBuildActions bool
|
||||
finishedGenerateBuildActions bool
|
||||
}
|
||||
|
||||
type variant struct {
|
||||
|
@ -363,6 +384,7 @@ func newContext() *Context {
|
|||
moduleInfo: make(map[Module]*moduleInfo),
|
||||
globs: make(map[string]GlobPath),
|
||||
fs: pathtools.OsFs,
|
||||
finishedMutators: make(map[*mutatorInfo]bool),
|
||||
ninjaBuildDir: nil,
|
||||
requiredNinjaMajor: 1,
|
||||
requiredNinjaMinor: 7,
|
||||
|
@ -1330,10 +1352,11 @@ func (c *Context) createVariations(origModule *moduleInfo, mutatorName string,
|
|||
|
||||
m := *origModule
|
||||
newModule := &m
|
||||
newModule.directDeps = append([]depInfo{}, origModule.directDeps...)
|
||||
newModule.directDeps = append([]depInfo(nil), origModule.directDeps...)
|
||||
newModule.logicModule = newLogicModule
|
||||
newModule.variant = newVariant(origModule, mutatorName, variationName, local)
|
||||
newModule.properties = newProperties
|
||||
newModule.providers = append([]interface{}(nil), origModule.providers...)
|
||||
|
||||
newModules = append(newModules, newModule)
|
||||
|
||||
|
@ -1518,6 +1541,8 @@ func (c *Context) ResolveDependencies(config interface{}) (deps []string, errs [
|
|||
|
||||
func (c *Context) resolveDependencies(ctx context.Context, config interface{}) (deps []string, errs []error) {
|
||||
pprof.Do(ctx, pprof.Labels("blueprint", "ResolveDependencies"), func(ctx context.Context) {
|
||||
c.initProviders()
|
||||
|
||||
c.liveGlobals = newLiveTracker(config)
|
||||
|
||||
deps, errs = c.generateSingletonBuildActions(config, c.preSingletonInfo, c.liveGlobals)
|
||||
|
@ -1537,7 +1562,9 @@ func (c *Context) resolveDependencies(ctx context.Context, config interface{}) (
|
|||
}
|
||||
deps = append(deps, mutatorDeps...)
|
||||
|
||||
c.cloneModules()
|
||||
if !c.skipCloneModulesAfterMutators {
|
||||
c.cloneModules()
|
||||
}
|
||||
|
||||
c.dependenciesReady = true
|
||||
})
|
||||
|
@ -2419,6 +2446,8 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
|
|||
pauseCh: pause,
|
||||
}
|
||||
|
||||
module.startedMutator = mutator
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -2434,6 +2463,8 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
|
|||
direction.run(mutator, mctx)
|
||||
}()
|
||||
|
||||
module.finishedMutator = mutator
|
||||
|
||||
if len(mctx.errs) > 0 {
|
||||
errsCh <- mctx.errs
|
||||
return true
|
||||
|
@ -2482,6 +2513,8 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
|
|||
}
|
||||
}()
|
||||
|
||||
c.startedMutator = mutator
|
||||
|
||||
var visitErrs []error
|
||||
if mutator.parallel {
|
||||
visitErrs = parallelVisit(c.modulesSorted, direction.orderer(), parallelVisitLimit, visit)
|
||||
|
@ -2493,6 +2526,8 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
|
|||
return nil, visitErrs
|
||||
}
|
||||
|
||||
c.finishedMutators[mutator] = true
|
||||
|
||||
done <- true
|
||||
|
||||
if len(errs) > 0 {
|
||||
|
@ -2702,6 +2737,8 @@ func (c *Context) generateModuleBuildActions(config interface{},
|
|||
handledMissingDeps: module.missingDeps == nil,
|
||||
}
|
||||
|
||||
mctx.module.startedGenerateBuildActions = true
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -2717,6 +2754,8 @@ func (c *Context) generateModuleBuildActions(config interface{},
|
|||
mctx.module.logicModule.GenerateBuildActions(mctx)
|
||||
}()
|
||||
|
||||
mctx.module.finishedGenerateBuildActions = true
|
||||
|
||||
if len(mctx.errs) > 0 {
|
||||
errsCh <- mctx.errs
|
||||
return true
|
||||
|
@ -3287,6 +3326,25 @@ func (c *Context) ModuleType(logicModule Module) string {
|
|||
return module.typeName
|
||||
}
|
||||
|
||||
// ModuleProvider returns the value, if any, for the provider for a module. If the value for the
|
||||
// provider was not set it returns the zero value of the type of the provider, which means the
|
||||
// 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. The value returned may be a deep
|
||||
// copy of the value originally passed to SetProvider.
|
||||
func (c *Context) ModuleProvider(logicModule Module, provider ProviderKey) interface{} {
|
||||
module := c.moduleInfo[logicModule]
|
||||
value, _ := c.provider(module, provider)
|
||||
return value
|
||||
}
|
||||
|
||||
// ModuleHasProvider returns true if the provider for the given module has been set.
|
||||
func (c *Context) ModuleHasProvider(logicModule Module, provider ProviderKey) bool {
|
||||
module := c.moduleInfo[logicModule]
|
||||
_, ok := c.provider(module, provider)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (c *Context) BlueprintFile(logicModule Module) string {
|
||||
module := c.moduleInfo[logicModule]
|
||||
return module.relBlueprintsFile
|
||||
|
|
|
@ -306,6 +306,31 @@ type BaseModuleContext interface {
|
|||
// other words, it checks for the module AddReverseDependency would add a
|
||||
// dependency on with the same argument.
|
||||
OtherModuleReverseDependencyVariantExists(name string) bool
|
||||
|
||||
// OtherModuleProvider returns the value for a provider for the given 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. The value returned may be a deep copy of the
|
||||
// value originally passed to SetProvider.
|
||||
OtherModuleProvider(m Module, provider ProviderKey) interface{}
|
||||
|
||||
// OtherModuleHasProvider returns true if the provider for the given module has been set.
|
||||
OtherModuleHasProvider(m Module, provider ProviderKey) 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 ProviderKey) interface{}
|
||||
|
||||
// HasProvider returns true if the provider for the current module has been set.
|
||||
HasProvider(provider ProviderKey) 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 ProviderKey, value interface{})
|
||||
}
|
||||
|
||||
type DynamicDependerModuleContext BottomUpMutatorContext
|
||||
|
@ -523,6 +548,32 @@ func (m *baseModuleContext) OtherModuleReverseDependencyVariantExists(name strin
|
|||
return found != nil
|
||||
}
|
||||
|
||||
func (m *baseModuleContext) OtherModuleProvider(logicModule Module, provider ProviderKey) interface{} {
|
||||
module := m.context.moduleInfo[logicModule]
|
||||
value, _ := m.context.provider(module, provider)
|
||||
return value
|
||||
}
|
||||
|
||||
func (m *baseModuleContext) OtherModuleHasProvider(logicModule Module, provider ProviderKey) bool {
|
||||
module := m.context.moduleInfo[logicModule]
|
||||
_, ok := m.context.provider(module, provider)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m *baseModuleContext) Provider(provider ProviderKey) interface{} {
|
||||
value, _ := m.context.provider(m.module, provider)
|
||||
return value
|
||||
}
|
||||
|
||||
func (m *baseModuleContext) HasProvider(provider ProviderKey) bool {
|
||||
_, ok := m.context.provider(m.module, provider)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m *baseModuleContext) SetProvider(provider ProviderKey, value interface{}) {
|
||||
m.context.setProvider(m.module, provider, value)
|
||||
}
|
||||
|
||||
func (m *baseModuleContext) GetDirectDep(name string) (Module, DependencyTag) {
|
||||
for _, dep := range m.module.directDeps {
|
||||
if dep.module.Name() == name {
|
||||
|
@ -882,6 +933,14 @@ type BottomUpMutatorContext interface {
|
|||
// be used to add dependencies on the toVariationName variant using the fromVariationName
|
||||
// variant.
|
||||
CreateAliasVariation(fromVariationName, toVariationName string)
|
||||
|
||||
// SetVariationProvider sets the value for a provider for the given newly created variant of
|
||||
// the current module, i.e. one of the Modules returned by CreateVariations.. 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 module is not a newly created
|
||||
// variant of the current module. The value should not be modified after being passed to
|
||||
// SetVariationProvider.
|
||||
SetVariationProvider(module Module, provider ProviderKey, value interface{})
|
||||
}
|
||||
|
||||
// A Mutator function is called for each Module, and can use
|
||||
|
@ -925,6 +984,16 @@ func (mctx *mutatorContext) CreateLocalVariations(variationNames ...string) []Mo
|
|||
return mctx.createVariations(variationNames, true)
|
||||
}
|
||||
|
||||
func (mctx *mutatorContext) SetVariationProvider(module Module, provider ProviderKey, value interface{}) {
|
||||
for _, variant := range mctx.newVariations {
|
||||
if m := variant.module(); m != nil && m.logicModule == module {
|
||||
mctx.context.setProvider(m, provider, value)
|
||||
return
|
||||
}
|
||||
}
|
||||
panic(fmt.Errorf("module %q is not a newly created variant of %q", module, mctx.module))
|
||||
}
|
||||
|
||||
type pendingAlias struct {
|
||||
fromVariant variant
|
||||
target *moduleInfo
|
||||
|
|
216
provider.go
Normal file
216
provider.go
Normal file
|
@ -0,0 +1,216 @@
|
|||
// Copyright 2020 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package blueprint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// This file implements Providers, modelled after Bazel
|
||||
// (https://docs.bazel.build/versions/master/skylark/rules.html#providers).
|
||||
// Each provider can be associated with a mutator, in which case the value for the provider for a
|
||||
// module can only be set during the mutator call for the module, and the value can only be
|
||||
// retrieved after the mutator call for the module. For providers not associated with a mutator, the
|
||||
// value can for the provider for a module can only be set during GenerateBuildActions for the
|
||||
// module, and the value can only be retrieved after GenerateBuildActions for the module.
|
||||
//
|
||||
// Providers are globally registered during init() and given a unique ID. The value of a provider
|
||||
// for a module is stored in an []interface{} indexed by the ID. If the value of a provider has
|
||||
// not been set, the value in the []interface{} will be nil.
|
||||
//
|
||||
// If the storage used by the provider value arrays becomes too large:
|
||||
// sizeof([]interface) * number of providers * number of modules that have a provider value set
|
||||
// then the storage can be replaced with something like a bitwise trie.
|
||||
//
|
||||
// The purpose of providers is to provide a serializable checkpoint between modules to enable
|
||||
// Blueprint to skip parts of the analysis phase when inputs haven't changed. To that end,
|
||||
// values passed to providers should be treated as immutable by callers to both the getters and
|
||||
// setters. Go doesn't provide any way to enforce immutability on arbitrary types, so it may be
|
||||
// necessary for the getters and setters to make deep copies of the values, likely extending
|
||||
// proptools.CloneProperties to do so.
|
||||
|
||||
type provider struct {
|
||||
id int
|
||||
typ reflect.Type
|
||||
zero interface{}
|
||||
mutator string
|
||||
}
|
||||
|
||||
type ProviderKey *provider
|
||||
|
||||
var providerRegistry []ProviderKey
|
||||
|
||||
// NewProvider returns a ProviderKey for the type of the given example value. The example value
|
||||
// is otherwise unused.
|
||||
//
|
||||
// The returned ProviderKey can be used to set a value of the ProviderKey's type for a module
|
||||
// inside GenerateBuildActions for the module, and to get the value from GenerateBuildActions from
|
||||
// any module later in the build graph.
|
||||
//
|
||||
// Once Go has generics the exampleValue parameter will not be necessary:
|
||||
// NewProvider(type T)() ProviderKey(T)
|
||||
func NewProvider(exampleValue interface{}) ProviderKey {
|
||||
return NewMutatorProvider(exampleValue, "")
|
||||
}
|
||||
|
||||
// NewMutatorProvider returns a ProviderKey for the type of the given example value. The example
|
||||
// value is otherwise unused.
|
||||
//
|
||||
// The returned ProviderKey can be used to set a value of the ProviderKey's type for a module inside
|
||||
// the given mutator for the module, and to get the value from GenerateBuildActions from any
|
||||
// module later in the build graph in the same mutator, or any module in a later mutator or during
|
||||
// GenerateBuildActions.
|
||||
//
|
||||
// Once Go has generics the exampleValue parameter will not be necessary:
|
||||
// NewMutatorProvider(type T)(mutator string) ProviderKey(T)
|
||||
func NewMutatorProvider(exampleValue interface{}, mutator string) ProviderKey {
|
||||
checkCalledFromInit()
|
||||
|
||||
typ := reflect.TypeOf(exampleValue)
|
||||
zero := reflect.Zero(typ).Interface()
|
||||
|
||||
provider := &provider{
|
||||
id: len(providerRegistry),
|
||||
typ: typ,
|
||||
zero: zero,
|
||||
mutator: mutator,
|
||||
}
|
||||
|
||||
providerRegistry = append(providerRegistry, provider)
|
||||
|
||||
return provider
|
||||
}
|
||||
|
||||
// initProviders fills c.providerMutators with the *mutatorInfo associated with each provider ID,
|
||||
// if any.
|
||||
func (c *Context) initProviders() {
|
||||
c.providerMutators = make([]*mutatorInfo, len(providerRegistry))
|
||||
for _, provider := range providerRegistry {
|
||||
for _, mutator := range c.mutatorInfo {
|
||||
if mutator.name == provider.mutator {
|
||||
c.providerMutators[provider.id] = mutator
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setProvider sets the value for a provider on a moduleInfo. Verifies that it is called during the
|
||||
// appropriate mutator or GenerateBuildActions pass for the provider, and that the value is of the
|
||||
// appropriate type. The value should not be modified after being passed to setProvider.
|
||||
//
|
||||
// Once Go has generics the value parameter can be typed:
|
||||
// setProvider(type T)(m *moduleInfo, provider ProviderKey(T), value T)
|
||||
func (c *Context) setProvider(m *moduleInfo, provider ProviderKey, value interface{}) {
|
||||
if provider.mutator == "" {
|
||||
if !m.startedGenerateBuildActions {
|
||||
panic(fmt.Sprintf("Can't set value of provider %s before GenerateBuildActions started",
|
||||
provider.typ))
|
||||
} else if m.finishedGenerateBuildActions {
|
||||
panic(fmt.Sprintf("Can't set value of provider %s after GenerateBuildActions finished",
|
||||
provider.typ))
|
||||
}
|
||||
} else {
|
||||
expectedMutator := c.providerMutators[provider.id]
|
||||
if expectedMutator == nil {
|
||||
panic(fmt.Sprintf("Can't set value of provider %s associated with unregistered mutator %s",
|
||||
provider.typ, provider.mutator))
|
||||
} else if c.mutatorFinishedForModule(expectedMutator, m) {
|
||||
panic(fmt.Sprintf("Can't set value of provider %s after mutator %s finished",
|
||||
provider.typ, provider.mutator))
|
||||
} else if !c.mutatorStartedForModule(expectedMutator, m) {
|
||||
panic(fmt.Sprintf("Can't set value of provider %s before mutator %s started",
|
||||
provider.typ, provider.mutator))
|
||||
}
|
||||
}
|
||||
|
||||
if typ := reflect.TypeOf(value); typ != provider.typ {
|
||||
panic(fmt.Sprintf("Value for provider has incorrect type, wanted %s, got %s",
|
||||
provider.typ, typ))
|
||||
}
|
||||
|
||||
if m.providers == nil {
|
||||
m.providers = make([]interface{}, len(providerRegistry))
|
||||
}
|
||||
|
||||
if m.providers[provider.id] != nil {
|
||||
panic(fmt.Sprintf("Value of provider %s is already set", provider.typ))
|
||||
}
|
||||
|
||||
m.providers[provider.id] = value
|
||||
}
|
||||
|
||||
// provider returns the value, if any, for a given provider for a module. Verifies that it is
|
||||
// called after the appropriate mutator or GenerateBuildActions pass for the provider on the module.
|
||||
// If the value for the provider was not set it returns the zero value of the type of the provider,
|
||||
// which means the return value can always be type-asserted to the type of the provider. The return
|
||||
// value should always be considered read-only.
|
||||
//
|
||||
// Once Go has generics the return value can be typed and the type assert by callers can be dropped:
|
||||
// provider(type T)(m *moduleInfo, provider ProviderKey(T)) T
|
||||
func (c *Context) provider(m *moduleInfo, provider ProviderKey) (interface{}, bool) {
|
||||
if provider.mutator == "" {
|
||||
if !m.finishedGenerateBuildActions {
|
||||
panic(fmt.Sprintf("Can't get value of provider %s before GenerateBuildActions finished",
|
||||
provider.typ))
|
||||
}
|
||||
} else {
|
||||
expectedMutator := c.providerMutators[provider.id]
|
||||
if expectedMutator != nil && !c.mutatorFinishedForModule(expectedMutator, m) {
|
||||
panic(fmt.Sprintf("Can't get value of provider %s before mutator %s finished",
|
||||
provider.typ, provider.mutator))
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.providers) > provider.id {
|
||||
if p := m.providers[provider.id]; p != nil {
|
||||
return p, true
|
||||
}
|
||||
}
|
||||
|
||||
return provider.zero, false
|
||||
}
|
||||
|
||||
func (c *Context) mutatorFinishedForModule(mutator *mutatorInfo, m *moduleInfo) bool {
|
||||
if c.finishedMutators[mutator] {
|
||||
// mutator pass finished for all modules
|
||||
return true
|
||||
}
|
||||
|
||||
if c.startedMutator == mutator {
|
||||
// mutator pass started, check if it is finished for this module
|
||||
return m.finishedMutator == mutator
|
||||
}
|
||||
|
||||
// mutator pass hasn't started
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Context) mutatorStartedForModule(mutator *mutatorInfo, m *moduleInfo) bool {
|
||||
if c.finishedMutators[mutator] {
|
||||
// mutator pass finished for all modules
|
||||
return true
|
||||
}
|
||||
|
||||
if c.startedMutator == mutator {
|
||||
// mutator pass is currently running
|
||||
if m.startedMutator == mutator {
|
||||
// mutator has started for this module
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
420
provider_test.go
Normal file
420
provider_test.go
Normal file
|
@ -0,0 +1,420 @@
|
|||
// Copyright 2020 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package blueprint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type providerTestModule struct {
|
||||
SimpleName
|
||||
properties struct {
|
||||
Deps []string
|
||||
}
|
||||
|
||||
mutatorProviderValues []string
|
||||
generateBuildActionsProviderValues []string
|
||||
}
|
||||
|
||||
func newProviderTestModule() (Module, []interface{}) {
|
||||
m := &providerTestModule{}
|
||||
return m, []interface{}{&m.properties, &m.SimpleName.Properties}
|
||||
}
|
||||
|
||||
type providerTestMutatorInfo struct {
|
||||
Values []string
|
||||
}
|
||||
|
||||
type providerTestGenerateBuildActionsInfo struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
type providerTestUnsetInfo string
|
||||
|
||||
var providerTestMutatorInfoProvider = NewMutatorProvider(&providerTestMutatorInfo{}, "provider_mutator")
|
||||
var providerTestGenerateBuildActionsInfoProvider = NewProvider(&providerTestGenerateBuildActionsInfo{})
|
||||
var providerTestUnsetInfoProvider = NewMutatorProvider((providerTestUnsetInfo)(""), "provider_mutator")
|
||||
var providerTestUnusedMutatorProvider = NewMutatorProvider(&struct{ unused string }{}, "nonexistent_mutator")
|
||||
|
||||
func (p *providerTestModule) GenerateBuildActions(ctx ModuleContext) {
|
||||
unset := ctx.Provider(providerTestUnsetInfoProvider).(providerTestUnsetInfo)
|
||||
if unset != "" {
|
||||
panic(fmt.Sprintf("expected zero value for providerTestGenerateBuildActionsInfoProvider before it was set, got %q",
|
||||
unset))
|
||||
}
|
||||
|
||||
_ = ctx.Provider(providerTestUnusedMutatorProvider)
|
||||
|
||||
ctx.SetProvider(providerTestGenerateBuildActionsInfoProvider, &providerTestGenerateBuildActionsInfo{
|
||||
Value: ctx.ModuleName(),
|
||||
})
|
||||
|
||||
mp := ctx.Provider(providerTestMutatorInfoProvider).(*providerTestMutatorInfo)
|
||||
if mp != nil {
|
||||
p.mutatorProviderValues = mp.Values
|
||||
}
|
||||
|
||||
ctx.VisitDirectDeps(func(module Module) {
|
||||
gbap := ctx.OtherModuleProvider(module, providerTestGenerateBuildActionsInfoProvider).(*providerTestGenerateBuildActionsInfo)
|
||||
if gbap != nil {
|
||||
p.generateBuildActionsProviderValues = append(p.generateBuildActionsProviderValues, gbap.Value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func providerTestDepsMutator(ctx BottomUpMutatorContext) {
|
||||
if p, ok := ctx.Module().(*providerTestModule); ok {
|
||||
ctx.AddDependency(ctx.Module(), nil, p.properties.Deps...)
|
||||
}
|
||||
}
|
||||
|
||||
func providerTestMutator(ctx BottomUpMutatorContext) {
|
||||
values := []string{strings.ToLower(ctx.ModuleName())}
|
||||
|
||||
ctx.VisitDirectDeps(func(module Module) {
|
||||
mp := ctx.OtherModuleProvider(module, providerTestMutatorInfoProvider).(*providerTestMutatorInfo)
|
||||
if mp != nil {
|
||||
values = append(values, mp.Values...)
|
||||
}
|
||||
})
|
||||
|
||||
ctx.SetProvider(providerTestMutatorInfoProvider, &providerTestMutatorInfo{
|
||||
Values: values,
|
||||
})
|
||||
}
|
||||
|
||||
func providerTestAfterMutator(ctx BottomUpMutatorContext) {
|
||||
_ = ctx.Provider(providerTestMutatorInfoProvider)
|
||||
}
|
||||
|
||||
func TestProviders(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
ctx.RegisterModuleType("provider_module", newProviderTestModule)
|
||||
ctx.RegisterBottomUpMutator("provider_deps_mutator", providerTestDepsMutator)
|
||||
ctx.RegisterBottomUpMutator("provider_mutator", providerTestMutator)
|
||||
ctx.RegisterBottomUpMutator("provider_after_mutator", providerTestAfterMutator)
|
||||
|
||||
ctx.MockFileSystem(map[string][]byte{
|
||||
"Blueprints": []byte(`
|
||||
provider_module {
|
||||
name: "A",
|
||||
deps: ["B"],
|
||||
}
|
||||
|
||||
provider_module {
|
||||
name: "B",
|
||||
deps: ["C", "D"],
|
||||
}
|
||||
|
||||
provider_module {
|
||||
name: "C",
|
||||
deps: ["D"],
|
||||
}
|
||||
|
||||
provider_module {
|
||||
name: "D",
|
||||
}
|
||||
`),
|
||||
})
|
||||
|
||||
_, errs := ctx.ParseBlueprintsFiles("Blueprints", nil)
|
||||
if len(errs) == 0 {
|
||||
_, errs = ctx.ResolveDependencies(nil)
|
||||
}
|
||||
if len(errs) == 0 {
|
||||
_, errs = ctx.PrepareBuildActions(nil)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
t.Errorf("unexpected errors:")
|
||||
for _, err := range errs {
|
||||
t.Errorf(" %s", err)
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
aModule := ctx.moduleGroupFromName("A", nil).moduleByVariantName("").logicModule.(*providerTestModule)
|
||||
if g, w := aModule.generateBuildActionsProviderValues, []string{"B"}; !reflect.DeepEqual(g, w) {
|
||||
t.Errorf("expected A.generateBuildActionsProviderValues %q, got %q", w, g)
|
||||
}
|
||||
if g, w := aModule.mutatorProviderValues, []string{"a", "b", "c", "d", "d"}; !reflect.DeepEqual(g, w) {
|
||||
t.Errorf("expected A.mutatorProviderValues %q, got %q", w, g)
|
||||
}
|
||||
|
||||
bModule := ctx.moduleGroupFromName("B", nil).moduleByVariantName("").logicModule.(*providerTestModule)
|
||||
if g, w := bModule.generateBuildActionsProviderValues, []string{"C", "D"}; !reflect.DeepEqual(g, w) {
|
||||
t.Errorf("expected B.generateBuildActionsProviderValues %q, got %q", w, g)
|
||||
}
|
||||
if g, w := bModule.mutatorProviderValues, []string{"b", "c", "d", "d"}; !reflect.DeepEqual(g, w) {
|
||||
t.Errorf("expected B.mutatorProviderValues %q, got %q", w, g)
|
||||
}
|
||||
}
|
||||
|
||||
type invalidProviderUsageMutatorInfo string
|
||||
type invalidProviderUsageGenerateBuildActionsInfo string
|
||||
|
||||
var invalidProviderUsageMutatorInfoProvider = NewMutatorProvider(invalidProviderUsageMutatorInfo(""), "mutator_under_test")
|
||||
var invalidProviderUsageGenerateBuildActionsInfoProvider = NewProvider(invalidProviderUsageGenerateBuildActionsInfo(""))
|
||||
|
||||
type invalidProviderUsageTestModule struct {
|
||||
parent *invalidProviderUsageTestModule
|
||||
|
||||
SimpleName
|
||||
properties struct {
|
||||
Deps []string
|
||||
|
||||
Early_mutator_set_of_mutator_provider bool
|
||||
Late_mutator_set_of_mutator_provider bool
|
||||
Late_build_actions_set_of_mutator_provider bool
|
||||
Early_mutator_set_of_build_actions_provider bool
|
||||
|
||||
Early_mutator_get_of_mutator_provider bool
|
||||
Early_module_get_of_mutator_provider bool
|
||||
Early_mutator_get_of_build_actions_provider bool
|
||||
Early_module_get_of_build_actions_provider bool
|
||||
|
||||
Duplicate_set bool
|
||||
}
|
||||
}
|
||||
|
||||
func invalidProviderUsageDepsMutator(ctx BottomUpMutatorContext) {
|
||||
if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok {
|
||||
ctx.AddDependency(ctx.Module(), nil, i.properties.Deps...)
|
||||
}
|
||||
}
|
||||
|
||||
func invalidProviderUsageParentMutator(ctx TopDownMutatorContext) {
|
||||
if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok {
|
||||
ctx.VisitDirectDeps(func(module Module) {
|
||||
module.(*invalidProviderUsageTestModule).parent = i
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func invalidProviderUsageBeforeMutator(ctx BottomUpMutatorContext) {
|
||||
if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok {
|
||||
if i.properties.Early_mutator_set_of_mutator_provider {
|
||||
// A mutator attempting to set the value of a provider associated with a later mutator.
|
||||
ctx.SetProvider(invalidProviderUsageMutatorInfoProvider, invalidProviderUsageMutatorInfo(""))
|
||||
}
|
||||
if i.properties.Early_mutator_get_of_mutator_provider {
|
||||
// A mutator attempting to get the value of a provider associated with a later mutator.
|
||||
_ = ctx.Provider(invalidProviderUsageMutatorInfoProvider)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func invalidProviderUsageMutatorUnderTest(ctx TopDownMutatorContext) {
|
||||
if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok {
|
||||
if i.properties.Early_mutator_set_of_build_actions_provider {
|
||||
// A mutator attempting to set the value of a non-mutator provider.
|
||||
ctx.SetProvider(invalidProviderUsageGenerateBuildActionsInfoProvider, invalidProviderUsageGenerateBuildActionsInfo(""))
|
||||
}
|
||||
if i.properties.Early_mutator_get_of_build_actions_provider {
|
||||
// A mutator attempting to get the value of a non-mutator provider.
|
||||
_ = ctx.Provider(invalidProviderUsageGenerateBuildActionsInfoProvider)
|
||||
}
|
||||
if i.properties.Early_module_get_of_mutator_provider {
|
||||
// A mutator attempting to get the value of a provider associated with this mutator on
|
||||
// a module for which this mutator hasn't run. This is a top down mutator so
|
||||
// dependencies haven't run yet.
|
||||
ctx.VisitDirectDeps(func(module Module) {
|
||||
_ = ctx.OtherModuleProvider(module, invalidProviderUsageMutatorInfoProvider)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func invalidProviderUsageAfterMutator(ctx BottomUpMutatorContext) {
|
||||
if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok {
|
||||
if i.properties.Late_mutator_set_of_mutator_provider {
|
||||
// A mutator trying to set the value of a provider associated with an earlier mutator.
|
||||
ctx.SetProvider(invalidProviderUsageMutatorInfoProvider, invalidProviderUsageMutatorInfo(""))
|
||||
}
|
||||
if i.properties.Late_mutator_set_of_mutator_provider {
|
||||
// A mutator trying to set the value of a provider associated with an earlier mutator.
|
||||
ctx.SetProvider(invalidProviderUsageMutatorInfoProvider, invalidProviderUsageMutatorInfo(""))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *invalidProviderUsageTestModule) GenerateBuildActions(ctx ModuleContext) {
|
||||
if i.properties.Late_build_actions_set_of_mutator_provider {
|
||||
// A GenerateBuildActions trying to set the value of a provider associated with a mutator.
|
||||
ctx.SetProvider(invalidProviderUsageMutatorInfoProvider, invalidProviderUsageMutatorInfo(""))
|
||||
}
|
||||
if i.properties.Early_module_get_of_build_actions_provider {
|
||||
// A GenerateBuildActions trying to get the value of a provider on a module for which
|
||||
// GenerateBuildActions hasn't run.
|
||||
_ = ctx.OtherModuleProvider(i.parent, invalidProviderUsageGenerateBuildActionsInfoProvider)
|
||||
}
|
||||
if i.properties.Duplicate_set {
|
||||
ctx.SetProvider(invalidProviderUsageGenerateBuildActionsInfoProvider, invalidProviderUsageGenerateBuildActionsInfo(""))
|
||||
ctx.SetProvider(invalidProviderUsageGenerateBuildActionsInfoProvider, invalidProviderUsageGenerateBuildActionsInfo(""))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidProvidersUsage(t *testing.T) {
|
||||
run := func(t *testing.T, module string, prop string, panicMsg string) {
|
||||
t.Helper()
|
||||
ctx := NewContext()
|
||||
ctx.RegisterModuleType("invalid_provider_usage_test_module", func() (Module, []interface{}) {
|
||||
m := &invalidProviderUsageTestModule{}
|
||||
return m, []interface{}{&m.properties, &m.SimpleName.Properties}
|
||||
})
|
||||
ctx.RegisterBottomUpMutator("deps", invalidProviderUsageDepsMutator)
|
||||
ctx.RegisterBottomUpMutator("before", invalidProviderUsageBeforeMutator)
|
||||
ctx.RegisterTopDownMutator("mutator_under_test", invalidProviderUsageMutatorUnderTest)
|
||||
ctx.RegisterBottomUpMutator("after", invalidProviderUsageAfterMutator)
|
||||
ctx.RegisterTopDownMutator("parent", invalidProviderUsageParentMutator)
|
||||
|
||||
// Don't invalidate the parent pointer and before GenerateBuildActions.
|
||||
ctx.skipCloneModulesAfterMutators = true
|
||||
|
||||
var parentBP, moduleUnderTestBP, childBP string
|
||||
|
||||
prop += ": true,"
|
||||
|
||||
switch module {
|
||||
case "parent":
|
||||
parentBP = prop
|
||||
case "module_under_test":
|
||||
moduleUnderTestBP = prop
|
||||
case "child":
|
||||
childBP = prop
|
||||
}
|
||||
|
||||
bp := fmt.Sprintf(`
|
||||
invalid_provider_usage_test_module {
|
||||
name: "parent",
|
||||
deps: ["module_under_test"],
|
||||
%s
|
||||
}
|
||||
|
||||
invalid_provider_usage_test_module {
|
||||
name: "module_under_test",
|
||||
deps: ["child"],
|
||||
%s
|
||||
}
|
||||
|
||||
invalid_provider_usage_test_module {
|
||||
name: "child",
|
||||
%s
|
||||
}
|
||||
|
||||
`,
|
||||
parentBP,
|
||||
moduleUnderTestBP,
|
||||
childBP)
|
||||
|
||||
ctx.MockFileSystem(map[string][]byte{
|
||||
"Blueprints": []byte(bp),
|
||||
})
|
||||
|
||||
_, errs := ctx.ParseBlueprintsFiles("Blueprints", nil)
|
||||
|
||||
if len(errs) == 0 {
|
||||
_, errs = ctx.ResolveDependencies(nil)
|
||||
}
|
||||
|
||||
if len(errs) == 0 {
|
||||
_, errs = ctx.PrepareBuildActions(nil)
|
||||
}
|
||||
|
||||
if len(errs) == 0 {
|
||||
t.Fatal("expected an error")
|
||||
}
|
||||
|
||||
if len(errs) > 1 {
|
||||
t.Errorf("expected a single error, got %d:", len(errs))
|
||||
for i, err := range errs {
|
||||
t.Errorf("%d: %s", i, err)
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if panicErr, ok := errs[0].(panicError); ok {
|
||||
if panicErr.panic != panicMsg {
|
||||
t.Fatalf("expected panic %q, got %q", panicMsg, panicErr.panic)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("expected a panicError, got %T: %s", errs[0], errs[0].Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
prop string
|
||||
module string
|
||||
|
||||
panicMsg string
|
||||
skip string
|
||||
}{
|
||||
{
|
||||
prop: "early_mutator_set_of_mutator_provider",
|
||||
module: "module_under_test",
|
||||
panicMsg: "Can't set value of provider blueprint.invalidProviderUsageMutatorInfo before mutator mutator_under_test started",
|
||||
},
|
||||
{
|
||||
prop: "late_mutator_set_of_mutator_provider",
|
||||
module: "module_under_test",
|
||||
panicMsg: "Can't set value of provider blueprint.invalidProviderUsageMutatorInfo after mutator mutator_under_test finished",
|
||||
},
|
||||
{
|
||||
prop: "late_build_actions_set_of_mutator_provider",
|
||||
module: "module_under_test",
|
||||
panicMsg: "Can't set value of provider blueprint.invalidProviderUsageMutatorInfo after mutator mutator_under_test finished",
|
||||
},
|
||||
{
|
||||
prop: "early_mutator_set_of_build_actions_provider",
|
||||
module: "module_under_test",
|
||||
panicMsg: "Can't set value of provider blueprint.invalidProviderUsageGenerateBuildActionsInfo before GenerateBuildActions started",
|
||||
},
|
||||
|
||||
{
|
||||
prop: "early_mutator_get_of_mutator_provider",
|
||||
module: "module_under_test",
|
||||
panicMsg: "Can't get value of provider blueprint.invalidProviderUsageMutatorInfo before mutator mutator_under_test finished",
|
||||
},
|
||||
{
|
||||
prop: "early_module_get_of_mutator_provider",
|
||||
module: "module_under_test",
|
||||
panicMsg: "Can't get value of provider blueprint.invalidProviderUsageMutatorInfo before mutator mutator_under_test finished",
|
||||
},
|
||||
{
|
||||
prop: "early_mutator_get_of_build_actions_provider",
|
||||
module: "module_under_test",
|
||||
panicMsg: "Can't get value of provider blueprint.invalidProviderUsageGenerateBuildActionsInfo before GenerateBuildActions finished",
|
||||
},
|
||||
{
|
||||
prop: "early_module_get_of_build_actions_provider",
|
||||
module: "module_under_test",
|
||||
panicMsg: "Can't get value of provider blueprint.invalidProviderUsageGenerateBuildActionsInfo before GenerateBuildActions finished",
|
||||
},
|
||||
{
|
||||
prop: "duplicate_set",
|
||||
module: "module_under_test",
|
||||
panicMsg: "Value of provider blueprint.invalidProviderUsageGenerateBuildActionsInfo is already set",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.prop, func(t *testing.T) {
|
||||
run(t, tt.module, tt.prop, tt.panicMsg)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -47,6 +47,16 @@ type SingletonContext interface {
|
|||
// BlueprintFile returns the path of the Blueprint file that defined the given module.
|
||||
BlueprintFile(module Module) string
|
||||
|
||||
// ModuleProvider returns the value, if any, for the provider for a module. If the value for the
|
||||
// provider was not set it returns the zero value of the type of the provider, which means the
|
||||
// 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 Module, provider ProviderKey) interface{}
|
||||
|
||||
// ModuleHasProvider returns true if the provider for the given module has been set.
|
||||
ModuleHasProvider(m Module, provider ProviderKey) bool
|
||||
|
||||
// ModuleErrorf reports an error at the line number of the module type in the module definition.
|
||||
ModuleErrorf(module Module, format string, args ...interface{})
|
||||
|
||||
|
@ -188,6 +198,15 @@ func (s *singletonContext) ModuleType(logicModule Module) string {
|
|||
return s.context.ModuleType(logicModule)
|
||||
}
|
||||
|
||||
func (s *singletonContext) ModuleProvider(logicModule Module, provider ProviderKey) interface{} {
|
||||
return s.context.ModuleProvider(logicModule, provider)
|
||||
}
|
||||
|
||||
// ModuleHasProvider returns true if the provider for the given module has been set.
|
||||
func (s *singletonContext) ModuleHasProvider(logicModule Module, provider ProviderKey) bool {
|
||||
return s.context.ModuleHasProvider(logicModule, provider)
|
||||
}
|
||||
|
||||
func (s *singletonContext) BlueprintFile(logicModule Module) string {
|
||||
return s.context.BlueprintFile(logicModule)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue