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
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}
147