blob: de3135306f8aa7f6a20c9098055000d0ab8eebcc [file] [log] [blame]
Colin Cross463a90e2015-06-17 14:20:06 -07001// Copyright 2015 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
Colin Cross798bfce2016-10-12 14:28:16 -070015package android
Colin Cross463a90e2015-06-17 14:20:06 -070016
Colin Cross4498afc2016-10-13 14:18:27 -070017import (
Chris Parsons5011e612023-09-13 23:33:20 +000018 "bufio"
Paul Duffin0a286832019-12-19 12:23:01 +000019 "fmt"
Chris Parsons5011e612023-09-13 23:33:20 +000020 "path/filepath"
Colin Cross9aed5bc2020-12-28 15:15:34 -080021 "reflect"
Chris Parsons5011e612023-09-13 23:33:20 +000022 "regexp"
Paul Duffin0a286832019-12-19 12:23:01 +000023
Chris Parsons5011e612023-09-13 23:33:20 +000024 "android/soong/shared"
Cole Faustae6cda62023-11-01 15:32:40 -070025
Colin Cross4498afc2016-10-13 14:18:27 -070026 "github.com/google/blueprint"
27)
Colin Cross463a90e2015-06-17 14:20:06 -070028
Paul Duffin1d2d42f2021-03-06 20:08:12 +000029// A sortable component is one whose registration order affects the order in which it is executed
30// and so affects the behavior of the build system. As a result it is important for the order in
31// which they are registered during tests to match the order used at runtime and so the test
32// infrastructure will sort them to match.
33//
34// The sortable components are mutators, singletons and pre-singletons. Module types are not
35// sortable because their order of registration does not affect the runtime behavior.
36type sortableComponent interface {
37 // componentName returns the name of the component.
38 //
Usta Shresthac725f472022-01-11 02:44:21 -050039 // Uniquely identifies the components within the set of components used at runtime and during
Paul Duffin1d2d42f2021-03-06 20:08:12 +000040 // tests.
41 componentName() string
42
Sasha Smundak1845f422022-12-13 14:18:58 -080043 // registers this component in the supplied context.
Paul Duffin1d2d42f2021-03-06 20:08:12 +000044 register(ctx *Context)
45}
46
47type sortableComponents []sortableComponent
48
49// registerAll registers all components in this slice with the supplied context.
50func (r sortableComponents) registerAll(ctx *Context) {
51 for _, c := range r {
52 c.register(ctx)
53 }
54}
55
Colin Cross463a90e2015-06-17 14:20:06 -070056type moduleType struct {
57 name string
Colin Cross7089c272019-01-25 22:43:35 -080058 factory ModuleFactory
Colin Cross463a90e2015-06-17 14:20:06 -070059}
60
Paul Duffin1d2d42f2021-03-06 20:08:12 +000061func (t moduleType) register(ctx *Context) {
62 ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
63}
64
Colin Cross463a90e2015-06-17 14:20:06 -070065var moduleTypes []moduleType
Colin Cross9aed5bc2020-12-28 15:15:34 -080066var moduleTypesForDocs = map[string]reflect.Value{}
Sam Delmerico286bf262022-03-09 20:46:37 +000067var moduleTypeByFactory = map[reflect.Value]string{}
Colin Cross463a90e2015-06-17 14:20:06 -070068
69type singleton struct {
LaMont Jonese59c0db2023-05-15 21:50:29 +000070 // True if this should be registered as a parallel singleton.
71 parallel bool
72
Colin Cross463a90e2015-06-17 14:20:06 -070073 name string
Colin Cross06fa5882020-10-29 18:21:38 -070074 factory SingletonFactory
Colin Cross463a90e2015-06-17 14:20:06 -070075}
76
LaMont Jonese59c0db2023-05-15 21:50:29 +000077func newSingleton(name string, factory SingletonFactory, parallel bool) singleton {
Cole Faustae6cda62023-11-01 15:32:40 -070078 return singleton{parallel: parallel, name: name, factory: factory}
Paul Duffin1d2d42f2021-03-06 20:08:12 +000079}
80
81func (s singleton) componentName() string {
82 return s.name
83}
84
85func (s singleton) register(ctx *Context) {
86 adaptor := SingletonFactoryAdaptor(ctx, s.factory)
Cole Faustae6cda62023-11-01 15:32:40 -070087 ctx.RegisterSingletonType(s.name, adaptor, s.parallel)
Paul Duffin1d2d42f2021-03-06 20:08:12 +000088}
89
90var _ sortableComponent = singleton{}
91
92var singletons sortableComponents
Colin Cross463a90e2015-06-17 14:20:06 -070093
Colin Cross6362e272015-10-29 15:25:03 -070094type mutator struct {
Lukacs T. Berki6c716762022-06-13 20:50:39 +020095 name string
96 bottomUpMutator blueprint.BottomUpMutator
97 topDownMutator blueprint.TopDownMutator
98 transitionMutator blueprint.TransitionMutator
99 parallel bool
Colin Cross463a90e2015-06-17 14:20:06 -0700100}
101
Paul Duffin1d2d42f2021-03-06 20:08:12 +0000102var _ sortableComponent = &mutator{}
103
Colin Cross36242852017-06-23 15:06:31 -0700104type ModuleFactory func() Module
105
Colin Cross0875c522017-11-28 17:34:01 -0800106// ModuleFactoryAdaptor wraps a ModuleFactory into a blueprint.ModuleFactory by converting a Module
Colin Cross36242852017-06-23 15:06:31 -0700107// into a blueprint.Module and a list of property structs
108func ModuleFactoryAdaptor(factory ModuleFactory) blueprint.ModuleFactory {
109 return func() (blueprint.Module, []interface{}) {
110 module := factory()
111 return module, module.GetProperties()
112 }
113}
114
Colin Cross0875c522017-11-28 17:34:01 -0800115type SingletonFactory func() Singleton
116
117// SingletonFactoryAdaptor wraps a SingletonFactory into a blueprint.SingletonFactory by converting
118// a Singleton into a blueprint.Singleton
Colin Cross06fa5882020-10-29 18:21:38 -0700119func SingletonFactoryAdaptor(ctx *Context, factory SingletonFactory) blueprint.SingletonFactory {
Colin Cross0875c522017-11-28 17:34:01 -0800120 return func() blueprint.Singleton {
121 singleton := factory()
Colin Crossed023ec2019-02-19 12:38:45 -0800122 if makevars, ok := singleton.(SingletonMakeVarsProvider); ok {
Sasha Smundak1845f422022-12-13 14:18:58 -0800123 ctx.registerSingletonMakeVarsProvider(makevars)
Colin Crossed023ec2019-02-19 12:38:45 -0800124 }
Colin Cross4c83e5c2019-02-25 14:54:28 -0800125 return &singletonAdaptor{Singleton: singleton}
Colin Cross0875c522017-11-28 17:34:01 -0800126 }
127}
128
Colin Cross36242852017-06-23 15:06:31 -0700129func RegisterModuleType(name string, factory ModuleFactory) {
Colin Cross7089c272019-01-25 22:43:35 -0800130 moduleTypes = append(moduleTypes, moduleType{name, factory})
Colin Cross9aed5bc2020-12-28 15:15:34 -0800131 RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
132}
133
134// RegisterModuleTypeForDocs associates a module type name with a reflect.Value of the factory
135// function that has documentation for the module type. It is normally called automatically
136// by RegisterModuleType, but can be called manually after RegisterModuleType in order to
137// override the factory method used for documentation, for example if the method passed to
138// RegisterModuleType was a lambda.
139func RegisterModuleTypeForDocs(name string, factory reflect.Value) {
140 moduleTypesForDocs[name] = factory
Sam Delmerico286bf262022-03-09 20:46:37 +0000141 moduleTypeByFactory[factory] = name
Colin Cross463a90e2015-06-17 14:20:06 -0700142}
143
LaMont Jonese59c0db2023-05-15 21:50:29 +0000144func registerSingletonType(name string, factory SingletonFactory, parallel bool) {
145 singletons = append(singletons, newSingleton(name, factory, parallel))
146}
147
Colin Cross0875c522017-11-28 17:34:01 -0800148func RegisterSingletonType(name string, factory SingletonFactory) {
LaMont Jonese59c0db2023-05-15 21:50:29 +0000149 registerSingletonType(name, factory, false)
150}
151
152func RegisterParallelSingletonType(name string, factory SingletonFactory) {
153 registerSingletonType(name, factory, true)
Colin Cross463a90e2015-06-17 14:20:06 -0700154}
155
Colin Crosscec81712017-07-13 14:43:27 -0700156type Context struct {
157 *blueprint.Context
Colin Crossae8600b2020-10-29 17:09:13 -0700158 config Config
Colin Crosscec81712017-07-13 14:43:27 -0700159}
Colin Cross463a90e2015-06-17 14:20:06 -0700160
Colin Crossae8600b2020-10-29 17:09:13 -0700161func NewContext(config Config) *Context {
162 ctx := &Context{blueprint.NewContext(), config}
Colin Cross988414c2020-01-11 01:11:46 +0000163 ctx.SetSrcDir(absSrcDir)
Spandan Dasc5763832022-11-08 18:42:16 +0000164 ctx.AddIncludeTags(config.IncludeTags()...)
Sam Delmerico98a73292023-02-21 11:50:29 -0500165 ctx.AddSourceRootDirs(config.SourceRootDirs()...)
Colin Cross988414c2020-01-11 01:11:46 +0000166 return ctx
Colin Crosscec81712017-07-13 14:43:27 -0700167}
168
Jingwen Chen863e3342023-10-05 10:23:58 +0000169// Helper function to register the module types used in bp2build.
Spandan Das75e139b2022-11-28 20:12:03 +0000170func registerModuleTypes(ctx *Context) {
Jingwen Chen4133ce62020-12-02 04:34:15 -0500171 for _, t := range moduleTypes {
Paul Duffin1d2d42f2021-03-06 20:08:12 +0000172 t.register(ctx)
Jingwen Chen4133ce62020-12-02 04:34:15 -0500173 }
Jingwen Chendaa54bc2020-12-14 02:58:54 -0500174 // Required for SingletonModule types, even though we are not using them.
175 for _, t := range singletons {
Paul Duffin1d2d42f2021-03-06 20:08:12 +0000176 t.register(ctx)
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800177 }
Spandan Das75e139b2022-11-28 20:12:03 +0000178}
Jingwen Chen4133ce62020-12-02 04:34:15 -0500179
Spandan Das75e139b2022-11-28 20:12:03 +0000180// RegisterForBazelConversion registers an alternate shadow pipeline of
181// singletons, module types and mutators to register for converting Blueprint
182// files to semantically equivalent BUILD files.
183func (ctx *Context) RegisterForBazelConversion() {
184 registerModuleTypes(ctx)
Liz Kammerbe46fcc2021-11-01 15:32:43 -0400185 RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators)
Jingwen Chen4133ce62020-12-02 04:34:15 -0500186}
187
Chris Parsons5011e612023-09-13 23:33:20 +0000188// RegisterExistingBazelTargets reads Bazel BUILD.bazel and BUILD files under
189// the workspace, and returns a map containing names of Bazel targets defined in
190// these BUILD files.
191// For example, maps "//foo/bar" to ["baz", "qux"] if `//foo/bar:{baz,qux}` exist.
192func (c *Context) RegisterExistingBazelTargets(topDir string, existingBazelFiles []string) error {
193 result := map[string][]string{}
194
195 // Search for instances of `name = "$NAME"` (with arbitrary spacing).
196 targetNameRegex := regexp.MustCompile(`(?m)^\s*name\s*=\s*\"([^\"]+)\"`)
197
198 parseBuildFile := func(path string) error {
199 fullPath := shared.JoinPath(topDir, path)
200 sourceDir := filepath.Dir(path)
201
202 fileInfo, err := c.Config().fs.Stat(fullPath)
203 if err != nil {
204 return fmt.Errorf("Error accessing Bazel file '%s': %s", path, err)
205 }
206 if !fileInfo.IsDir() &&
207 (fileInfo.Name() == "BUILD" || fileInfo.Name() == "BUILD.bazel") {
208 f, err := c.Config().fs.Open(fullPath)
209 if err != nil {
210 return fmt.Errorf("Error reading Bazel file '%s': %s", path, err)
211 }
212 defer f.Close()
213 scanner := bufio.NewScanner(f)
214 for scanner.Scan() {
215 line := scanner.Text()
216 matches := targetNameRegex.FindAllStringSubmatch(line, -1)
217 for _, match := range matches {
218 result[sourceDir] = append(result[sourceDir], match[1])
219 }
220 }
221 }
222 return nil
223 }
224
225 for _, path := range existingBazelFiles {
226 if !c.Config().Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(filepath.Dir(path)) {
227 continue
228 }
229 err := parseBuildFile(path)
230 if err != nil {
231 return err
232 }
233 }
234 c.Config().SetBazelBuildFileTargets(result)
235 return nil
236}
237
Jingwen Chendaa54bc2020-12-14 02:58:54 -0500238// Register the pipeline of singletons, module types, and mutators for
239// generating build.ninja and other files for Kati, from Android.bp files.
Colin Crosscec81712017-07-13 14:43:27 -0700240func (ctx *Context) Register() {
Colin Cross463a90e2015-06-17 14:20:06 -0700241 for _, t := range moduleTypes {
Paul Duffin1d2d42f2021-03-06 20:08:12 +0000242 t.register(ctx)
Colin Cross463a90e2015-06-17 14:20:06 -0700243 }
244
Paul Duffinc05b0342021-03-06 13:28:13 +0000245 mutators := collateGloballyRegisteredMutators()
246 mutators.registerAll(ctx)
Colin Cross1e676be2016-10-12 14:38:15 -0700247
Paul Duffin42d0b932021-03-06 20:24:50 +0000248 singletons := collateGloballyRegisteredSingletons()
249 singletons.registerAll(ctx)
250}
Chris Parsonsa798d962020-10-12 23:44:08 -0400251
Sasha Smundak1845f422022-12-13 14:18:58 -0800252func (ctx *Context) Config() Config {
253 return ctx.config
254}
255
256func (ctx *Context) registerSingletonMakeVarsProvider(makevars SingletonMakeVarsProvider) {
257 registerSingletonMakeVarsProvider(ctx.config, makevars)
258}
259
Paul Duffin42d0b932021-03-06 20:24:50 +0000260func collateGloballyRegisteredSingletons() sortableComponents {
261 allSingletons := append(sortableComponents(nil), singletons...)
262 allSingletons = append(allSingletons,
Cole Faustae6cda62023-11-01 15:32:40 -0700263 singleton{parallel: true, name: "bazeldeps", factory: BazelSingleton},
Colin Crossc3d87d32020-06-04 13:25:17 -0700264
Paul Duffin42d0b932021-03-06 20:24:50 +0000265 // Register phony just before makevars so it can write out its phony rules as Make rules
Cole Faustae6cda62023-11-01 15:32:40 -0700266 singleton{parallel: false, name: "phony", factory: phonySingletonFactory},
Colin Cross580d2ce2019-02-09 22:59:32 -0800267
Paul Duffin42d0b932021-03-06 20:24:50 +0000268 // Register makevars after other singletons so they can export values through makevars
Cole Faustae6cda62023-11-01 15:32:40 -0700269 singleton{parallel: false, name: "makevars", factory: makeVarsSingletonFunc},
Paul Duffin42d0b932021-03-06 20:24:50 +0000270
271 // Register env and ninjadeps last so that they can track all used environment variables and
272 // Ninja file dependencies stored in the config.
Cole Faustae6cda62023-11-01 15:32:40 -0700273 singleton{parallel: false, name: "ninjadeps", factory: ninjaDepsSingletonFactory},
Paul Duffin42d0b932021-03-06 20:24:50 +0000274 )
275
276 return allSingletons
Colin Cross463a90e2015-06-17 14:20:06 -0700277}
Colin Cross7089c272019-01-25 22:43:35 -0800278
279func ModuleTypeFactories() map[string]ModuleFactory {
280 ret := make(map[string]ModuleFactory)
281 for _, t := range moduleTypes {
282 ret[t.name] = t.factory
283 }
284 return ret
285}
Paul Duffinf9b1da02019-12-18 19:51:55 +0000286
Colin Cross9aed5bc2020-12-28 15:15:34 -0800287func ModuleTypeFactoriesForDocs() map[string]reflect.Value {
288 return moduleTypesForDocs
289}
290
Sam Delmerico286bf262022-03-09 20:46:37 +0000291func ModuleTypeByFactory() map[reflect.Value]string {
292 return moduleTypeByFactory
293}
294
Paul Duffinf9b1da02019-12-18 19:51:55 +0000295// Interface for registering build components.
296//
297// Provided to allow registration of build components to be shared between the runtime
298// and test environments.
299type RegistrationContext interface {
300 RegisterModuleType(name string, factory ModuleFactory)
Colin Cross9aed5bc2020-12-28 15:15:34 -0800301 RegisterSingletonModuleType(name string, factory SingletonModuleFactory)
LaMont Jonese59c0db2023-05-15 21:50:29 +0000302 RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory)
LaMont Jonese59c0db2023-05-15 21:50:29 +0000303 RegisterParallelSingletonType(name string, factory SingletonFactory)
Paul Duffinf9b1da02019-12-18 19:51:55 +0000304 RegisterSingletonType(name string, factory SingletonFactory)
Paul Duffina48f7582019-12-19 11:25:19 +0000305 PreArchMutators(f RegisterMutatorFunc)
Paul Duffina80ef842020-01-14 12:09:36 +0000306
307 // Register pre arch mutators that are hard coded into mutator.go.
308 //
309 // Only registers mutators for testing, is a noop on the InitRegistrationContext.
310 HardCodedPreArchMutators(f RegisterMutatorFunc)
311
Paul Duffin2ccaffd2019-12-19 15:12:58 +0000312 PreDepsMutators(f RegisterMutatorFunc)
313 PostDepsMutators(f RegisterMutatorFunc)
Martin Stjernholm710ec3a2020-01-16 15:12:04 +0000314 FinalDepsMutators(f RegisterMutatorFunc)
Paul Duffinf9b1da02019-12-18 19:51:55 +0000315}
316
317// Used to register build components from an init() method, e.g.
318//
Colin Crossd079e0b2022-08-16 10:27:33 -0700319// init() {
320// RegisterBuildComponents(android.InitRegistrationContext)
321// }
Paul Duffinf9b1da02019-12-18 19:51:55 +0000322//
Colin Crossd079e0b2022-08-16 10:27:33 -0700323// func RegisterBuildComponents(ctx android.RegistrationContext) {
324// ctx.RegisterModuleType(...)
325// ...
326// }
Paul Duffinf9b1da02019-12-18 19:51:55 +0000327//
328// Extracting the actual registration into a separate RegisterBuildComponents(ctx) function
329// allows it to be used to initialize test context, e.g.
330//
Colin Crossd079e0b2022-08-16 10:27:33 -0700331// ctx := android.NewTestContext(config)
332// RegisterBuildComponents(ctx)
Paul Duffin0a286832019-12-19 12:23:01 +0000333var InitRegistrationContext RegistrationContext = &initRegistrationContext{
Cole Faustae6cda62023-11-01 15:32:40 -0700334 moduleTypes: make(map[string]ModuleFactory),
335 singletonTypes: make(map[string]SingletonFactory),
Paul Duffin0a286832019-12-19 12:23:01 +0000336}
Paul Duffinf9b1da02019-12-18 19:51:55 +0000337
338// Make sure the TestContext implements RegistrationContext.
339var _ RegistrationContext = (*TestContext)(nil)
340
Paul Duffin0a286832019-12-19 12:23:01 +0000341type initRegistrationContext struct {
Colin Cross9aed5bc2020-12-28 15:15:34 -0800342 moduleTypes map[string]ModuleFactory
343 singletonTypes map[string]SingletonFactory
344 moduleTypesForDocs map[string]reflect.Value
Paul Duffin0a286832019-12-19 12:23:01 +0000345}
Paul Duffinf9b1da02019-12-18 19:51:55 +0000346
Paul Duffin0a286832019-12-19 12:23:01 +0000347func (ctx *initRegistrationContext) RegisterModuleType(name string, factory ModuleFactory) {
348 if _, present := ctx.moduleTypes[name]; present {
349 panic(fmt.Sprintf("module type %q is already registered", name))
350 }
351 ctx.moduleTypes[name] = factory
Paul Duffinf9b1da02019-12-18 19:51:55 +0000352 RegisterModuleType(name, factory)
Colin Cross9aed5bc2020-12-28 15:15:34 -0800353 RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
354}
355
356func (ctx *initRegistrationContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) {
LaMont Jonese59c0db2023-05-15 21:50:29 +0000357 ctx.registerSingletonModuleType(name, factory, false)
358}
359func (ctx *initRegistrationContext) RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory) {
360 ctx.registerSingletonModuleType(name, factory, true)
361}
362
363func (ctx *initRegistrationContext) registerSingletonModuleType(name string, factory SingletonModuleFactory, parallel bool) {
Colin Cross9aed5bc2020-12-28 15:15:34 -0800364 s, m := SingletonModuleFactoryAdaptor(name, factory)
LaMont Jonese59c0db2023-05-15 21:50:29 +0000365 ctx.registerSingletonType(name, s, parallel)
Colin Cross9aed5bc2020-12-28 15:15:34 -0800366 ctx.RegisterModuleType(name, m)
367 // Overwrite moduleTypesForDocs with the original factory instead of the lambda returned by
368 // SingletonModuleFactoryAdaptor so that docs can find the module type documentation on the
369 // factory method.
370 RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
Paul Duffinf9b1da02019-12-18 19:51:55 +0000371}
372
LaMont Jonese59c0db2023-05-15 21:50:29 +0000373func (ctx *initRegistrationContext) registerSingletonType(name string, factory SingletonFactory, parallel bool) {
Paul Duffin0a286832019-12-19 12:23:01 +0000374 if _, present := ctx.singletonTypes[name]; present {
375 panic(fmt.Sprintf("singleton type %q is already registered", name))
376 }
377 ctx.singletonTypes[name] = factory
LaMont Jonese59c0db2023-05-15 21:50:29 +0000378 registerSingletonType(name, factory, parallel)
379}
380
381func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) {
382 ctx.registerSingletonType(name, factory, false)
383}
384
385func (ctx *initRegistrationContext) RegisterParallelSingletonType(name string, factory SingletonFactory) {
386 ctx.registerSingletonType(name, factory, true)
Paul Duffinf9b1da02019-12-18 19:51:55 +0000387}
Paul Duffina48f7582019-12-19 11:25:19 +0000388
Paul Duffin0a286832019-12-19 12:23:01 +0000389func (ctx *initRegistrationContext) PreArchMutators(f RegisterMutatorFunc) {
Paul Duffina48f7582019-12-19 11:25:19 +0000390 PreArchMutators(f)
391}
Paul Duffin2ccaffd2019-12-19 15:12:58 +0000392
Sasha Smundak1845f422022-12-13 14:18:58 -0800393func (ctx *initRegistrationContext) HardCodedPreArchMutators(_ RegisterMutatorFunc) {
Paul Duffina80ef842020-01-14 12:09:36 +0000394 // Nothing to do as the mutators are hard code in preArch in mutator.go
395}
396
Paul Duffin2ccaffd2019-12-19 15:12:58 +0000397func (ctx *initRegistrationContext) PreDepsMutators(f RegisterMutatorFunc) {
398 PreDepsMutators(f)
399}
400
401func (ctx *initRegistrationContext) PostDepsMutators(f RegisterMutatorFunc) {
402 PostDepsMutators(f)
403}
Martin Stjernholm710ec3a2020-01-16 15:12:04 +0000404
405func (ctx *initRegistrationContext) FinalDepsMutators(f RegisterMutatorFunc) {
406 FinalDepsMutators(f)
407}