d5133cfc64
This is to support recalculate hash in different proceses. Bug: 335718784 Test: unit tests and CI Change-Id: I08909fe0332a7adcfcc158698c5d8ba501116ba2
325 lines
12 KiB
Go
325 lines
12 KiB
Go
// 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"
|
|
|
|
"github.com/google/blueprint/proptools"
|
|
)
|
|
|
|
// 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 []any indexed by the ID. If the value of a provider has
|
|
// not been set, the value in the []any 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 typedProviderKey[K any] struct {
|
|
providerKey
|
|
}
|
|
|
|
type providerKey struct {
|
|
id int
|
|
typ string
|
|
mutator string
|
|
}
|
|
|
|
func (p *providerKey) provider() *providerKey { return p }
|
|
|
|
type AnyProviderKey interface {
|
|
provider() *providerKey
|
|
}
|
|
type ProviderKey[K any] struct {
|
|
*typedProviderKey[K]
|
|
}
|
|
|
|
var _ AnyProviderKey = (*providerKey)(nil)
|
|
var _ AnyProviderKey = ProviderKey[bool]{}
|
|
|
|
var providerRegistry []*providerKey
|
|
|
|
// NewProvider returns a ProviderKey for the given type.
|
|
//
|
|
// 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.
|
|
func NewProvider[K any]() ProviderKey[K] {
|
|
return NewMutatorProvider[K]("")
|
|
}
|
|
|
|
// NewMutatorProvider returns a ProviderKey for the given type.
|
|
//
|
|
// 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.
|
|
func NewMutatorProvider[K any](mutator string) ProviderKey[K] {
|
|
checkCalledFromInit()
|
|
|
|
typ := fmt.Sprintf("%T", *new(K))
|
|
|
|
provider := ProviderKey[K]{
|
|
typedProviderKey: &typedProviderKey[K]{
|
|
providerKey: providerKey{
|
|
id: len(providerRegistry),
|
|
typ: typ,
|
|
mutator: mutator,
|
|
},
|
|
},
|
|
}
|
|
|
|
providerRegistry = append(providerRegistry, &provider.providerKey)
|
|
|
|
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 any) {
|
|
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 m.providers == nil {
|
|
m.providers = make([]any, 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
|
|
|
|
if c.verifyProvidersAreUnchanged {
|
|
if m.providerInitialValueHashes == nil {
|
|
m.providerInitialValueHashes = make([]uint64, len(providerRegistry))
|
|
}
|
|
hash, err := proptools.CalculateHash(value)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("Can't set value of provider %s: %s", provider.typ, err.Error()))
|
|
}
|
|
m.providerInitialValueHashes[provider.id] = hash
|
|
}
|
|
}
|
|
|
|
// 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 nil. 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) (any, 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 nil, 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
|
|
}
|
|
|
|
// OtherModuleProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
|
|
// TopDownMutatorContext for use in OtherModuleProvider.
|
|
type OtherModuleProviderContext interface {
|
|
OtherModuleProvider(m Module, provider 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 Module, provider ProviderKey[K]) (K, bool) {
|
|
value, ok := ctx.OtherModuleProvider(module, provider)
|
|
if !ok {
|
|
var k K
|
|
return k, false
|
|
}
|
|
return value.(K), ok
|
|
}
|
|
|
|
// SingletonModuleProviderContext is a helper interface that is a subset of Context and SingletonContext for use in
|
|
// SingletonModuleProvider.
|
|
type SingletonModuleProviderContext interface {
|
|
ModuleProvider(m Module, provider AnyProviderKey) (any, bool)
|
|
}
|
|
|
|
var _ SingletonModuleProviderContext = &Context{}
|
|
var _ SingletonModuleProviderContext = SingletonContext(nil)
|
|
|
|
// SingletonModuleProvider 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.
|
|
//
|
|
// SingletonModuleProviderContext is a helper interface that accepts Context or SingletonContext.
|
|
func SingletonModuleProvider[K any](ctx SingletonModuleProviderContext, module Module, provider ProviderKey[K]) (K, bool) {
|
|
value, ok := ctx.ModuleProvider(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 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 ProviderKey[K]) (K, bool) {
|
|
value, ok := ctx.Provider(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 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 ProviderKey[K], value K) {
|
|
ctx.SetProvider(provider, value)
|
|
}
|