blob: f54e774f897bdf21a949ee9903a5b75bd100f523 [file] [log] [blame]
Colin Cross9d34f352019-11-22 16:03:51 -08001// Copyright 2019 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
17// This file provides module types that implement wrapper module types that add conditionals on
18// Soong config variables.
19
20import (
21 "fmt"
22 "path/filepath"
23 "strings"
24 "text/scanner"
25
26 "github.com/google/blueprint"
27 "github.com/google/blueprint/parser"
28 "github.com/google/blueprint/proptools"
29
30 "android/soong/android/soongconfig"
31)
32
33func init() {
34 RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
35 RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
36 RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
37 RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
38}
39
40type soongConfigModuleTypeImport struct {
41 ModuleBase
42 properties soongConfigModuleTypeImportProperties
43}
44
45type soongConfigModuleTypeImportProperties struct {
46 From string
47 Module_types []string
48}
49
50// soong_config_module_type_import imports module types with conditionals on Soong config
51// variables from another Android.bp file. The imported module type will exist for all
52// modules after the import in the Android.bp file.
53//
54// For example, an Android.bp file could have:
55//
56// soong_config_module_type_import {
Bill Peckhamc93258b2020-02-04 13:17:24 -080057// from: "device/acme/Android.bp",
Colin Cross9d34f352019-11-22 16:03:51 -080058// module_types: ["acme_cc_defaults"],
59// }
60//
61// acme_cc_defaults {
62// name: "acme_defaults",
63// cflags: ["-DGENERIC"],
64// soong_config_variables: {
65// board: {
66// soc_a: {
67// cflags: ["-DSOC_A"],
68// },
69// soc_b: {
70// cflags: ["-DSOC_B"],
71// },
72// },
73// feature: {
74// cflags: ["-DFEATURE"],
75// },
76// },
77// }
78//
79// cc_library {
80// name: "libacme_foo",
81// defaults: ["acme_defaults"],
82// srcs: ["*.cpp"],
83// }
84//
85// And device/acme/Android.bp could have:
86//
87// soong_config_module_type {
88// name: "acme_cc_defaults",
89// module_type: "cc_defaults",
90// config_namespace: "acme",
91// variables: ["board", "feature"],
92// properties: ["cflags", "srcs"],
93// }
94//
95// soong_config_string_variable {
96// name: "board",
97// values: ["soc_a", "soc_b"],
98// }
99//
100// soong_config_bool_variable {
101// name: "feature",
102// }
103//
104// If an acme BoardConfig.mk file contained:
105//
106// SOONG_CONFIG_NAMESPACES += acme
107// SOONG_CONFIG_acme += \
108// board \
109// feature \
110//
111// SOONG_CONFIG_acme_board := soc_a
112// SOONG_CONFIG_acme_feature := true
113//
114// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
115func soongConfigModuleTypeImportFactory() Module {
116 module := &soongConfigModuleTypeImport{}
117
118 module.AddProperties(&module.properties)
119 AddLoadHook(module, func(ctx LoadHookContext) {
120 importModuleTypes(ctx, module.properties.From, module.properties.Module_types...)
121 })
122
123 initAndroidModuleBase(module)
124 return module
125}
126
127func (m *soongConfigModuleTypeImport) Name() string {
128 return "soong_config_module_type_import_" + soongconfig.CanonicalizeToProperty(m.properties.From)
129}
130
131func (*soongConfigModuleTypeImport) Nameless() {}
132func (*soongConfigModuleTypeImport) GenerateAndroidBuildActions(ModuleContext) {}
133
134// Create dummy modules for soong_config_module_type and soong_config_*_variable
135
136type soongConfigModuleTypeModule struct {
137 ModuleBase
138 properties soongconfig.ModuleTypeProperties
139}
140
141// soong_config_module_type defines module types with conditionals on Soong config
Bill Peckhamc93258b2020-02-04 13:17:24 -0800142// variables. The new module type will exist for all modules after the definition
143// in an Android.bp file, and can be imported into other Android.bp files using
144// soong_config_module_type_import.
Colin Cross9d34f352019-11-22 16:03:51 -0800145//
146// For example, an Android.bp file could have:
147//
148// soong_config_module_type {
149// name: "acme_cc_defaults",
150// module_type: "cc_defaults",
151// config_namespace: "acme",
152// variables: ["board", "feature"],
153// properties: ["cflags", "srcs"],
154// }
155//
156// soong_config_string_variable {
157// name: "board",
158// values: ["soc_a", "soc_b"],
159// }
160//
161// soong_config_bool_variable {
162// name: "feature",
163// }
164//
165// acme_cc_defaults {
166// name: "acme_defaults",
167// cflags: ["-DGENERIC"],
168// soong_config_variables: {
169// board: {
170// soc_a: {
171// cflags: ["-DSOC_A"],
172// },
173// soc_b: {
174// cflags: ["-DSOC_B"],
175// },
176// },
177// feature: {
178// cflags: ["-DFEATURE"],
179// },
180// },
181// }
182//
183// cc_library {
184// name: "libacme_foo",
185// defaults: ["acme_defaults"],
186// srcs: ["*.cpp"],
187// }
188//
Colin Cross9d34f352019-11-22 16:03:51 -0800189// If an acme BoardConfig.mk file contained:
190//
191// SOONG_CONFIG_NAMESPACES += acme
192// SOONG_CONFIG_acme += \
193// board \
194// feature \
195//
196// SOONG_CONFIG_acme_board := soc_a
197// SOONG_CONFIG_acme_feature := true
198//
199// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
200func soongConfigModuleTypeFactory() Module {
201 module := &soongConfigModuleTypeModule{}
202
203 module.AddProperties(&module.properties)
204
205 AddLoadHook(module, func(ctx LoadHookContext) {
206 // A soong_config_module_type module should implicitly import itself.
207 importModuleTypes(ctx, ctx.BlueprintsFile(), module.properties.Name)
208 })
209
210 initAndroidModuleBase(module)
211
212 return module
213}
214
215func (m *soongConfigModuleTypeModule) Name() string {
216 return m.properties.Name
217}
218func (*soongConfigModuleTypeModule) Nameless() {}
219func (*soongConfigModuleTypeModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
220
221type soongConfigStringVariableDummyModule struct {
222 ModuleBase
223 properties soongconfig.VariableProperties
224 stringProperties soongconfig.StringVariableProperties
225}
226
227type soongConfigBoolVariableDummyModule struct {
228 ModuleBase
229 properties soongconfig.VariableProperties
230}
231
232// soong_config_string_variable defines a variable and a set of possible string values for use
233// in a soong_config_module_type definition.
234func soongConfigStringVariableDummyFactory() Module {
235 module := &soongConfigStringVariableDummyModule{}
236 module.AddProperties(&module.properties, &module.stringProperties)
237 initAndroidModuleBase(module)
238 return module
239}
240
241// soong_config_string_variable defines a variable with true or false values for use
242// in a soong_config_module_type definition.
243func soongConfigBoolVariableDummyFactory() Module {
244 module := &soongConfigBoolVariableDummyModule{}
245 module.AddProperties(&module.properties)
246 initAndroidModuleBase(module)
247 return module
248}
249
250func (m *soongConfigStringVariableDummyModule) Name() string {
251 return m.properties.Name
252}
253func (*soongConfigStringVariableDummyModule) Nameless() {}
254func (*soongConfigStringVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
255
256func (m *soongConfigBoolVariableDummyModule) Name() string {
257 return m.properties.Name
258}
259func (*soongConfigBoolVariableDummyModule) Nameless() {}
260func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
261
262func importModuleTypes(ctx LoadHookContext, from string, moduleTypes ...string) {
263 from = filepath.Clean(from)
264 if filepath.Ext(from) != ".bp" {
265 ctx.PropertyErrorf("from", "%q must be a file with extension .bp", from)
266 return
267 }
268
269 if strings.HasPrefix(from, "../") {
270 ctx.PropertyErrorf("from", "%q must not use ../ to escape the source tree",
271 from)
272 return
273 }
274
275 moduleTypeDefinitions := loadSoongConfigModuleTypeDefinition(ctx, from)
276 if moduleTypeDefinitions == nil {
277 return
278 }
279 for _, moduleType := range moduleTypes {
280 if factory, ok := moduleTypeDefinitions[moduleType]; ok {
281 ctx.registerScopedModuleType(moduleType, factory)
282 } else {
283 ctx.PropertyErrorf("module_types", "module type %q not defined in %q",
284 moduleType, from)
285 }
286 }
287}
288
289// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
290// result so each file is only parsed once.
291func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[string]blueprint.ModuleFactory {
292 type onceKeyType string
293 key := NewCustomOnceKey(onceKeyType(filepath.Clean(from)))
294
295 reportErrors := func(ctx LoadHookContext, filename string, errs ...error) {
296 for _, err := range errs {
297 if parseErr, ok := err.(*parser.ParseError); ok {
298 ctx.Errorf(parseErr.Pos, "%s", parseErr.Err)
299 } else {
300 ctx.Errorf(scanner.Position{Filename: filename}, "%s", err)
301 }
302 }
303 }
304
305 return ctx.Config().Once(key, func() interface{} {
306 r, err := ctx.Config().fs.Open(from)
307 if err != nil {
308 ctx.PropertyErrorf("from", "failed to open %q: %s", from, err)
309 return (map[string]blueprint.ModuleFactory)(nil)
310 }
311
312 mtDef, errs := soongconfig.Parse(r, from)
313
314 if len(errs) > 0 {
315 reportErrors(ctx, from, errs...)
316 return (map[string]blueprint.ModuleFactory)(nil)
317 }
318
319 globalModuleTypes := ctx.moduleFactories()
320
321 factories := make(map[string]blueprint.ModuleFactory)
322
323 for name, moduleType := range mtDef.ModuleTypes {
324 factory := globalModuleTypes[moduleType.BaseModuleType]
325 if factory != nil {
326 factories[name] = soongConfigModuleFactory(factory, moduleType)
327 } else {
328 reportErrors(ctx, from,
329 fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
330 }
331 }
332
333 if ctx.Failed() {
334 return (map[string]blueprint.ModuleFactory)(nil)
335 }
336
337 return factories
338 }).(map[string]blueprint.ModuleFactory)
339}
340
341// soongConfigModuleFactory takes an existing soongConfigModuleFactory and a ModuleType and returns
342// a new soongConfigModuleFactory that wraps the existing soongConfigModuleFactory and adds conditional on Soong config
343// variables.
344func soongConfigModuleFactory(factory blueprint.ModuleFactory,
345 moduleType *soongconfig.ModuleType) blueprint.ModuleFactory {
346
347 conditionalFactoryProps := soongconfig.CreateProperties(factory, moduleType)
348 if conditionalFactoryProps.IsValid() {
349 return func() (blueprint.Module, []interface{}) {
350 module, props := factory()
351
Colin Cross43e789d2020-01-28 09:46:50 -0800352 conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
Colin Cross9d34f352019-11-22 16:03:51 -0800353 props = append(props, conditionalProps.Interface())
354
355 AddLoadHook(module, func(ctx LoadHookContext) {
356 config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
357 for _, ps := range soongconfig.PropertiesToApply(moduleType, conditionalProps, config) {
358 ctx.AppendProperties(ps)
359 }
360 })
361
362 return module, props
363 }
364 } else {
365 return factory
366 }
367}