// 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 }