blob: 235173809bf57547d2955a4fc27d5f123801e972 [file] [log] [blame]
Colin Cross9aed5bc2020-12-28 15:15:34 -08001// 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 android
16
17import (
18 "fmt"
19 "sync"
20
21 "github.com/google/blueprint"
22)
23
24// A SingletonModule is halfway between a Singleton and a Module. It has access to visiting
25// other modules via its GenerateSingletonBuildActions method, but must be defined in an Android.bp
26// file and can also be depended on like a module. It must be used zero or one times in an
27// Android.bp file, and it can only have a single variant.
28//
29// The SingletonModule's GenerateAndroidBuildActions method will be called before any normal or
30// singleton module that depends on it, but its GenerateSingletonBuildActions method will be called
31// after all modules, in registration order with other singletons and singleton modules.
32// GenerateAndroidBuildActions and GenerateSingletonBuildActions will not be called if the
33// SingletonModule was not instantiated in an Android.bp file.
34//
35// Since the SingletonModule rules likely depend on the modules visited during
36// GenerateSingletonBuildActions, the GenerateAndroidBuildActions is unlikely to produce any
37// rules directly. Instead, it will probably set some providers to paths that will later have rules
38// generated to produce them in GenerateSingletonBuildActions.
39//
40// The expected use case for a SingletonModule is a module that produces files that depend on all
41// modules in the tree and will be used by other modules. For example it could produce a text
42// file that lists all modules that meet a certain criteria, and that text file could be an input
43// to another module. Care must be taken that the ninja rules produced by the SingletonModule
44// don't produce a cycle by referencing output files of rules of modules that depend on the
45// SingletonModule.
46//
47// A SingletonModule must embed a SingletonModuleBase struct, and its factory method must be
48// registered with RegisterSingletonModuleType from an init() function.
49//
50// A SingletonModule can also implement SingletonMakeVarsProvider to export values to Make.
51type SingletonModule interface {
52 Module
53 GenerateSingletonBuildActions(SingletonContext)
54 singletonModuleBase() *SingletonModuleBase
55}
56
57// SingletonModuleBase must be embedded into implementers of the SingletonModule interface.
58type SingletonModuleBase struct {
59 ModuleBase
60
61 lock sync.Mutex
62 bp string
63 variant string
64}
65
66// GenerateBuildActions wraps the ModuleBase GenerateBuildActions method, verifying it was only
67// called once to prevent multiple variants of a SingletonModule.
68func (smb *SingletonModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
69 smb.lock.Lock()
70 if smb.variant != "" {
71 ctx.ModuleErrorf("GenerateAndroidBuildActions already called for variant %q, SingletonModules can only have one variant", smb.variant)
72 }
73 smb.variant = ctx.ModuleSubDir()
74 smb.lock.Unlock()
75
76 smb.ModuleBase.GenerateBuildActions(ctx)
77}
78
79// InitAndroidSingletonModule must be called from the SingletonModule's factory function to
80// initialize SingletonModuleBase.
81func InitAndroidSingletonModule(sm SingletonModule) {
82 InitAndroidModule(sm)
83}
84
85// singletonModuleBase retrieves the embedded SingletonModuleBase from a SingletonModule.
86func (smb *SingletonModuleBase) singletonModuleBase() *SingletonModuleBase { return smb }
87
88// SingletonModuleFactory is a factory method that returns a SingletonModule.
89type SingletonModuleFactory func() SingletonModule
90
91// SingletonModuleFactoryAdaptor converts a SingletonModuleFactory into a SingletonFactory and a
92// ModuleFactory.
93func SingletonModuleFactoryAdaptor(name string, factory SingletonModuleFactory) (SingletonFactory, ModuleFactory) {
94 // The sm variable acts as a static holder of the only SingletonModule instance. Calls to the
95 // returned SingletonFactory and ModuleFactory lambdas will always return the same sm value.
96 // The SingletonFactory is only expected to be called once, but the ModuleFactory may be
97 // called multiple times if the module is replaced with a clone of itself at the end of
98 // blueprint.ResolveDependencies.
99 var sm SingletonModule
100 s := func() Singleton {
101 sm = factory()
102 return &singletonModuleSingletonAdaptor{sm}
103 }
104 m := func() Module {
105 if sm == nil {
106 panic(fmt.Errorf("Singleton %q for SingletonModule was not instantiated", name))
107 }
108
109 // Check for multiple uses of a SingletonModule in a LoadHook. Checking directly in the
110 // factory would incorrectly flag when the factory was called again when the module is
111 // replaced with a clone of itself at the end of blueprint.ResolveDependencies.
112 AddLoadHook(sm, func(ctx LoadHookContext) {
113 smb := sm.singletonModuleBase()
114 smb.lock.Lock()
115 defer smb.lock.Unlock()
116 if smb.bp != "" {
117 ctx.ModuleErrorf("Duplicate SingletonModule %q, previously used in %s", name, smb.bp)
118 }
119 smb.bp = ctx.BlueprintsFile()
120 })
121 return sm
122 }
123 return s, m
124}
125
126// singletonModuleSingletonAdaptor makes a SingletonModule into a Singleton by translating the
127// GenerateSingletonBuildActions method to Singleton.GenerateBuildActions.
128type singletonModuleSingletonAdaptor struct {
129 sm SingletonModule
130}
131
132// GenerateBuildActions calls the SingletonModule's GenerateSingletonBuildActions method, but only
133// if the module was defined in an Android.bp file.
134func (smsa *singletonModuleSingletonAdaptor) GenerateBuildActions(ctx SingletonContext) {
135 if smsa.sm.singletonModuleBase().bp != "" {
136 smsa.sm.GenerateSingletonBuildActions(ctx)
137 }
138}
139
140func (smsa *singletonModuleSingletonAdaptor) MakeVars(ctx MakeVarsContext) {
141 if smsa.sm.singletonModuleBase().bp != "" {
142 if makeVars, ok := smsa.sm.(SingletonMakeVarsProvider); ok {
143 makeVars.MakeVars(ctx)
144 }
145 }
146}