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