blob: b83e1d4ccfc226e0c00ff55ea184c879b7b5c2e7 [file] [log] [blame]
Colin Cross2da84922020-07-02 10:08:12 -07001// 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
15package blueprint
16
17import (
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
45type provider struct {
46 id int
47 typ reflect.Type
48 zero interface{}
49 mutator string
50}
51
52type ProviderKey *provider
53
54var 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)
65func 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)
79func 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.
99func (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)
116func (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
163func (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
186func (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
201func (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}