Colin Cross | 2da8492 | 2020-07-02 10:08:12 -0700 | [diff] [blame] | 1 | // Copyright 2020 Google Inc. All rights reserved. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package blueprint |
| 16 | |
| 17 | import ( |
| 18 | "fmt" |
| 19 | "reflect" |
| 20 | ) |
| 21 | |
| 22 | // This file implements Providers, modelled after Bazel |
| 23 | // (https://docs.bazel.build/versions/master/skylark/rules.html#providers). |
| 24 | // Each provider can be associated with a mutator, in which case the value for the provider for a |
| 25 | // module can only be set during the mutator call for the module, and the value can only be |
| 26 | // retrieved after the mutator call for the module. For providers not associated with a mutator, the |
| 27 | // value can for the provider for a module can only be set during GenerateBuildActions for the |
| 28 | // module, and the value can only be retrieved after GenerateBuildActions for the module. |
| 29 | // |
| 30 | // Providers are globally registered during init() and given a unique ID. The value of a provider |
| 31 | // for a module is stored in an []interface{} indexed by the ID. If the value of a provider has |
| 32 | // not been set, the value in the []interface{} will be nil. |
| 33 | // |
| 34 | // If the storage used by the provider value arrays becomes too large: |
| 35 | // sizeof([]interface) * number of providers * number of modules that have a provider value set |
| 36 | // then the storage can be replaced with something like a bitwise trie. |
| 37 | // |
| 38 | // The purpose of providers is to provide a serializable checkpoint between modules to enable |
| 39 | // Blueprint to skip parts of the analysis phase when inputs haven't changed. To that end, |
| 40 | // values passed to providers should be treated as immutable by callers to both the getters and |
| 41 | // setters. Go doesn't provide any way to enforce immutability on arbitrary types, so it may be |
| 42 | // necessary for the getters and setters to make deep copies of the values, likely extending |
| 43 | // proptools.CloneProperties to do so. |
| 44 | |
| 45 | type provider struct { |
| 46 | id int |
| 47 | typ reflect.Type |
| 48 | zero interface{} |
| 49 | mutator string |
| 50 | } |
| 51 | |
| 52 | type ProviderKey *provider |
| 53 | |
| 54 | var providerRegistry []ProviderKey |
| 55 | |
| 56 | // NewProvider returns a ProviderKey for the type of the given example value. The example value |
| 57 | // is otherwise unused. |
| 58 | // |
| 59 | // The returned ProviderKey can be used to set a value of the ProviderKey's type for a module |
| 60 | // inside GenerateBuildActions for the module, and to get the value from GenerateBuildActions from |
| 61 | // any module later in the build graph. |
| 62 | // |
| 63 | // Once Go has generics the exampleValue parameter will not be necessary: |
| 64 | // NewProvider(type T)() ProviderKey(T) |
| 65 | func NewProvider(exampleValue interface{}) ProviderKey { |
| 66 | return NewMutatorProvider(exampleValue, "") |
| 67 | } |
| 68 | |
| 69 | // NewMutatorProvider returns a ProviderKey for the type of the given example value. The example |
| 70 | // value is otherwise unused. |
| 71 | // |
| 72 | // The returned ProviderKey can be used to set a value of the ProviderKey's type for a module inside |
| 73 | // the given mutator for the module, and to get the value from GenerateBuildActions from any |
| 74 | // module later in the build graph in the same mutator, or any module in a later mutator or during |
| 75 | // GenerateBuildActions. |
| 76 | // |
| 77 | // Once Go has generics the exampleValue parameter will not be necessary: |
| 78 | // NewMutatorProvider(type T)(mutator string) ProviderKey(T) |
| 79 | func NewMutatorProvider(exampleValue interface{}, mutator string) ProviderKey { |
| 80 | checkCalledFromInit() |
| 81 | |
| 82 | typ := reflect.TypeOf(exampleValue) |
| 83 | zero := reflect.Zero(typ).Interface() |
| 84 | |
| 85 | provider := &provider{ |
| 86 | id: len(providerRegistry), |
| 87 | typ: typ, |
| 88 | zero: zero, |
| 89 | mutator: mutator, |
| 90 | } |
| 91 | |
| 92 | providerRegistry = append(providerRegistry, provider) |
| 93 | |
| 94 | return provider |
| 95 | } |
| 96 | |
| 97 | // initProviders fills c.providerMutators with the *mutatorInfo associated with each provider ID, |
| 98 | // if any. |
| 99 | func (c *Context) initProviders() { |
| 100 | c.providerMutators = make([]*mutatorInfo, len(providerRegistry)) |
| 101 | for _, provider := range providerRegistry { |
| 102 | for _, mutator := range c.mutatorInfo { |
| 103 | if mutator.name == provider.mutator { |
| 104 | c.providerMutators[provider.id] = mutator |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | // setProvider sets the value for a provider on a moduleInfo. Verifies that it is called during the |
| 111 | // appropriate mutator or GenerateBuildActions pass for the provider, and that the value is of the |
| 112 | // appropriate type. The value should not be modified after being passed to setProvider. |
| 113 | // |
| 114 | // Once Go has generics the value parameter can be typed: |
| 115 | // setProvider(type T)(m *moduleInfo, provider ProviderKey(T), value T) |
| 116 | func (c *Context) setProvider(m *moduleInfo, provider ProviderKey, value interface{}) { |
| 117 | if provider.mutator == "" { |
| 118 | if !m.startedGenerateBuildActions { |
| 119 | panic(fmt.Sprintf("Can't set value of provider %s before GenerateBuildActions started", |
| 120 | provider.typ)) |
| 121 | } else if m.finishedGenerateBuildActions { |
| 122 | panic(fmt.Sprintf("Can't set value of provider %s after GenerateBuildActions finished", |
| 123 | provider.typ)) |
| 124 | } |
| 125 | } else { |
| 126 | expectedMutator := c.providerMutators[provider.id] |
| 127 | if expectedMutator == nil { |
| 128 | panic(fmt.Sprintf("Can't set value of provider %s associated with unregistered mutator %s", |
| 129 | provider.typ, provider.mutator)) |
| 130 | } else if c.mutatorFinishedForModule(expectedMutator, m) { |
| 131 | panic(fmt.Sprintf("Can't set value of provider %s after mutator %s finished", |
| 132 | provider.typ, provider.mutator)) |
| 133 | } else if !c.mutatorStartedForModule(expectedMutator, m) { |
| 134 | panic(fmt.Sprintf("Can't set value of provider %s before mutator %s started", |
| 135 | provider.typ, provider.mutator)) |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | if typ := reflect.TypeOf(value); typ != provider.typ { |
| 140 | panic(fmt.Sprintf("Value for provider has incorrect type, wanted %s, got %s", |
| 141 | provider.typ, typ)) |
| 142 | } |
| 143 | |
| 144 | if m.providers == nil { |
| 145 | m.providers = make([]interface{}, len(providerRegistry)) |
| 146 | } |
| 147 | |
| 148 | if m.providers[provider.id] != nil { |
| 149 | panic(fmt.Sprintf("Value of provider %s is already set", provider.typ)) |
| 150 | } |
| 151 | |
| 152 | m.providers[provider.id] = value |
| 153 | } |
| 154 | |
| 155 | // provider returns the value, if any, for a given provider for a module. Verifies that it is |
| 156 | // called after the appropriate mutator or GenerateBuildActions pass for the provider on the module. |
| 157 | // If the value for the provider was not set it returns the zero value of the type of the provider, |
| 158 | // which means the return value can always be type-asserted to the type of the provider. The return |
| 159 | // value should always be considered read-only. |
| 160 | // |
| 161 | // Once Go has generics the return value can be typed and the type assert by callers can be dropped: |
| 162 | // provider(type T)(m *moduleInfo, provider ProviderKey(T)) T |
| 163 | func (c *Context) provider(m *moduleInfo, provider ProviderKey) (interface{}, bool) { |
| 164 | if provider.mutator == "" { |
| 165 | if !m.finishedGenerateBuildActions { |
| 166 | panic(fmt.Sprintf("Can't get value of provider %s before GenerateBuildActions finished", |
| 167 | provider.typ)) |
| 168 | } |
| 169 | } else { |
| 170 | expectedMutator := c.providerMutators[provider.id] |
| 171 | if expectedMutator != nil && !c.mutatorFinishedForModule(expectedMutator, m) { |
| 172 | panic(fmt.Sprintf("Can't get value of provider %s before mutator %s finished", |
| 173 | provider.typ, provider.mutator)) |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | if len(m.providers) > provider.id { |
| 178 | if p := m.providers[provider.id]; p != nil { |
| 179 | return p, true |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return provider.zero, false |
| 184 | } |
| 185 | |
| 186 | func (c *Context) mutatorFinishedForModule(mutator *mutatorInfo, m *moduleInfo) bool { |
| 187 | if c.finishedMutators[mutator] { |
| 188 | // mutator pass finished for all modules |
| 189 | return true |
| 190 | } |
| 191 | |
| 192 | if c.startedMutator == mutator { |
| 193 | // mutator pass started, check if it is finished for this module |
| 194 | return m.finishedMutator == mutator |
| 195 | } |
| 196 | |
| 197 | // mutator pass hasn't started |
| 198 | return false |
| 199 | } |
| 200 | |
| 201 | func (c *Context) mutatorStartedForModule(mutator *mutatorInfo, m *moduleInfo) bool { |
| 202 | if c.finishedMutators[mutator] { |
| 203 | // mutator pass finished for all modules |
| 204 | return true |
| 205 | } |
| 206 | |
| 207 | if c.startedMutator == mutator { |
| 208 | // mutator pass is currently running |
| 209 | if m.startedMutator == mutator { |
| 210 | // mutator has started for this module |
| 211 | return true |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | return false |
| 216 | } |