blob: b25f248910b30da761b49dd9f240e8186ff292b1 [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() {
Jingwen Chena47f28d2021-11-02 16:43:57 +000034 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)
Colin Cross9d34f352019-11-22 16:03:51 -080038}
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//
Liz Kammer432bd592020-12-16 12:42:02 -080054// Each soong_config_variable supports an additional value `conditions_default`. The properties
55// specified in `conditions_default` will only be used under the following conditions:
56// bool variable: the variable is unspecified or not set to a true value
57// value variable: the variable is unspecified
58// string variable: the variable is unspecified or the variable is set to a string unused in the
59// given module. For example, string variable `test` takes values: "a" and "b",
60// if the module contains a property `a` and `conditions_default`, when test=b,
61// the properties under `conditions_default` will be used. To specify that no
62// properties should be amended for `b`, you can set `b: {},`.
63//
Colin Cross9d34f352019-11-22 16:03:51 -080064// For example, an Android.bp file could have:
65//
66// soong_config_module_type_import {
Bill Peckhamc93258b2020-02-04 13:17:24 -080067// from: "device/acme/Android.bp",
Colin Cross9d34f352019-11-22 16:03:51 -080068// module_types: ["acme_cc_defaults"],
69// }
70//
71// acme_cc_defaults {
72// name: "acme_defaults",
73// cflags: ["-DGENERIC"],
74// soong_config_variables: {
75// board: {
76// soc_a: {
77// cflags: ["-DSOC_A"],
78// },
79// soc_b: {
80// cflags: ["-DSOC_B"],
81// },
Liz Kammer432bd592020-12-16 12:42:02 -080082// conditions_default: {
83// cflags: ["-DSOC_DEFAULT"],
84// },
Colin Cross9d34f352019-11-22 16:03:51 -080085// },
86// feature: {
87// cflags: ["-DFEATURE"],
Liz Kammer432bd592020-12-16 12:42:02 -080088// conditions_default: {
89// cflags: ["-DFEATURE_DEFAULT"],
90// },
Colin Cross9d34f352019-11-22 16:03:51 -080091// },
Dan Willemsenb0935db2020-03-23 19:42:18 -070092// width: {
93// cflags: ["-DWIDTH=%s"],
Liz Kammer432bd592020-12-16 12:42:02 -080094// conditions_default: {
95// cflags: ["-DWIDTH=DEFAULT"],
96// },
Dan Willemsenb0935db2020-03-23 19:42:18 -070097// },
Colin Cross9d34f352019-11-22 16:03:51 -080098// },
99// }
100//
101// cc_library {
102// name: "libacme_foo",
103// defaults: ["acme_defaults"],
104// srcs: ["*.cpp"],
105// }
106//
107// And device/acme/Android.bp could have:
108//
109// soong_config_module_type {
110// name: "acme_cc_defaults",
111// module_type: "cc_defaults",
112// config_namespace: "acme",
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700113// variables: ["board"],
114// bool_variables: ["feature"],
Dan Willemsenb0935db2020-03-23 19:42:18 -0700115// value_variables: ["width"],
Colin Cross9d34f352019-11-22 16:03:51 -0800116// properties: ["cflags", "srcs"],
117// }
118//
119// soong_config_string_variable {
120// name: "board",
Liz Kammer432bd592020-12-16 12:42:02 -0800121// values: ["soc_a", "soc_b", "soc_c"],
Colin Cross9d34f352019-11-22 16:03:51 -0800122// }
123//
Colin Cross9d34f352019-11-22 16:03:51 -0800124// If an acme BoardConfig.mk file contained:
Sasha Smundak18fd0992021-08-24 14:05:19 -0700125// $(call add_sonng_config_namespace, acme)
126// $(call add_soong_config_var_value, acme, board, soc_a)
127// $(call add_soong_config_var_value, acme, feature, true)
128// $(call add_soong_config_var_value, acme, width, 200)
Colin Cross9d34f352019-11-22 16:03:51 -0800129//
Dan Willemsenb0935db2020-03-23 19:42:18 -0700130// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
Liz Kammer432bd592020-12-16 12:42:02 -0800131//
132// Alternatively, if acme BoardConfig.mk file contained:
133//
134// SOONG_CONFIG_NAMESPACES += acme
135// SOONG_CONFIG_acme += \
136// board \
137// feature \
138//
139// SOONG_CONFIG_acme_feature := false
140//
141// Then libacme_foo would build with cflags:
142// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
143//
144// Similarly, if acme BoardConfig.mk file contained:
145//
146// SOONG_CONFIG_NAMESPACES += acme
147// SOONG_CONFIG_acme += \
148// board \
149// feature \
150//
151// SOONG_CONFIG_acme_board := soc_c
152//
153// Then libacme_foo would build with cflags:
154// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
155
Jingwen Chena47f28d2021-11-02 16:43:57 +0000156func SoongConfigModuleTypeImportFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800157 module := &soongConfigModuleTypeImport{}
158
159 module.AddProperties(&module.properties)
160 AddLoadHook(module, func(ctx LoadHookContext) {
161 importModuleTypes(ctx, module.properties.From, module.properties.Module_types...)
162 })
163
164 initAndroidModuleBase(module)
165 return module
166}
167
168func (m *soongConfigModuleTypeImport) Name() string {
Sasha Smundak116ec922020-03-10 16:10:06 -0700169 // The generated name is non-deterministic, but it does not
170 // matter because this module does not emit any rules.
171 return soongconfig.CanonicalizeToProperty(m.properties.From) +
172 "soong_config_module_type_import_" + fmt.Sprintf("%p", m)
Colin Cross9d34f352019-11-22 16:03:51 -0800173}
174
175func (*soongConfigModuleTypeImport) Nameless() {}
176func (*soongConfigModuleTypeImport) GenerateAndroidBuildActions(ModuleContext) {}
177
178// Create dummy modules for soong_config_module_type and soong_config_*_variable
179
180type soongConfigModuleTypeModule struct {
181 ModuleBase
Jingwen Chena47f28d2021-11-02 16:43:57 +0000182 BazelModuleBase
Colin Cross9d34f352019-11-22 16:03:51 -0800183 properties soongconfig.ModuleTypeProperties
184}
185
186// soong_config_module_type defines module types with conditionals on Soong config
Bill Peckhamc93258b2020-02-04 13:17:24 -0800187// variables. The new module type will exist for all modules after the definition
188// in an Android.bp file, and can be imported into other Android.bp files using
189// soong_config_module_type_import.
Colin Cross9d34f352019-11-22 16:03:51 -0800190//
Liz Kammer432bd592020-12-16 12:42:02 -0800191// Each soong_config_variable supports an additional value `conditions_default`. The properties
192// specified in `conditions_default` will only be used under the following conditions:
Colin Crossd079e0b2022-08-16 10:27:33 -0700193//
194// bool variable: the variable is unspecified or not set to a true value
195// value variable: the variable is unspecified
196// string variable: the variable is unspecified or the variable is set to a string unused in the
197// given module. For example, string variable `test` takes values: "a" and "b",
198// if the module contains a property `a` and `conditions_default`, when test=b,
199// the properties under `conditions_default` will be used. To specify that no
200// properties should be amended for `b`, you can set `b: {},`.
Liz Kammer432bd592020-12-16 12:42:02 -0800201//
Colin Cross9d34f352019-11-22 16:03:51 -0800202// For example, an Android.bp file could have:
203//
Colin Crossd079e0b2022-08-16 10:27:33 -0700204// soong_config_module_type {
205// name: "acme_cc_defaults",
206// module_type: "cc_defaults",
207// config_namespace: "acme",
208// variables: ["board"],
209// bool_variables: ["feature"],
210// value_variables: ["width"],
211// properties: ["cflags", "srcs"],
212// }
Colin Cross9d34f352019-11-22 16:03:51 -0800213//
Colin Crossd079e0b2022-08-16 10:27:33 -0700214// soong_config_string_variable {
215// name: "board",
216// values: ["soc_a", "soc_b"],
217// }
Colin Cross9d34f352019-11-22 16:03:51 -0800218//
Colin Crossd079e0b2022-08-16 10:27:33 -0700219// acme_cc_defaults {
220// name: "acme_defaults",
221// cflags: ["-DGENERIC"],
222// soong_config_variables: {
223// board: {
224// soc_a: {
225// cflags: ["-DSOC_A"],
226// },
227// soc_b: {
228// cflags: ["-DSOC_B"],
229// },
230// conditions_default: {
231// cflags: ["-DSOC_DEFAULT"],
232// },
233// },
234// feature: {
235// cflags: ["-DFEATURE"],
236// conditions_default: {
237// cflags: ["-DFEATURE_DEFAULT"],
238// },
239// },
240// width: {
241// cflags: ["-DWIDTH=%s"],
242// conditions_default: {
243// cflags: ["-DWIDTH=DEFAULT"],
244// },
245// },
246// },
247// }
Colin Cross9d34f352019-11-22 16:03:51 -0800248//
Colin Crossd079e0b2022-08-16 10:27:33 -0700249// cc_library {
250// name: "libacme_foo",
251// defaults: ["acme_defaults"],
252// srcs: ["*.cpp"],
253// }
Colin Cross9d34f352019-11-22 16:03:51 -0800254//
Colin Cross9d34f352019-11-22 16:03:51 -0800255// If an acme BoardConfig.mk file contained:
256//
Colin Crossd079e0b2022-08-16 10:27:33 -0700257// SOONG_CONFIG_NAMESPACES += acme
258// SOONG_CONFIG_acme += \
259// board \
260// feature \
Colin Cross9d34f352019-11-22 16:03:51 -0800261//
Colin Crossd079e0b2022-08-16 10:27:33 -0700262// SOONG_CONFIG_acme_board := soc_a
263// SOONG_CONFIG_acme_feature := true
264// SOONG_CONFIG_acme_width := 200
Colin Cross9d34f352019-11-22 16:03:51 -0800265//
266// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
Jingwen Chena47f28d2021-11-02 16:43:57 +0000267func SoongConfigModuleTypeFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800268 module := &soongConfigModuleTypeModule{}
269
270 module.AddProperties(&module.properties)
271
272 AddLoadHook(module, func(ctx LoadHookContext) {
273 // A soong_config_module_type module should implicitly import itself.
274 importModuleTypes(ctx, ctx.BlueprintsFile(), module.properties.Name)
275 })
276
277 initAndroidModuleBase(module)
278
279 return module
280}
281
282func (m *soongConfigModuleTypeModule) Name() string {
283 return m.properties.Name
284}
285func (*soongConfigModuleTypeModule) Nameless() {}
286func (*soongConfigModuleTypeModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
287
288type soongConfigStringVariableDummyModule struct {
289 ModuleBase
290 properties soongconfig.VariableProperties
291 stringProperties soongconfig.StringVariableProperties
292}
293
294type soongConfigBoolVariableDummyModule struct {
295 ModuleBase
296 properties soongconfig.VariableProperties
297}
298
299// soong_config_string_variable defines a variable and a set of possible string values for use
300// in a soong_config_module_type definition.
Jingwen Chena47f28d2021-11-02 16:43:57 +0000301func SoongConfigStringVariableDummyFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800302 module := &soongConfigStringVariableDummyModule{}
303 module.AddProperties(&module.properties, &module.stringProperties)
304 initAndroidModuleBase(module)
305 return module
306}
307
308// soong_config_string_variable defines a variable with true or false values for use
309// in a soong_config_module_type definition.
Jingwen Chena47f28d2021-11-02 16:43:57 +0000310func SoongConfigBoolVariableDummyFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800311 module := &soongConfigBoolVariableDummyModule{}
312 module.AddProperties(&module.properties)
313 initAndroidModuleBase(module)
314 return module
315}
316
317func (m *soongConfigStringVariableDummyModule) Name() string {
318 return m.properties.Name
319}
320func (*soongConfigStringVariableDummyModule) Nameless() {}
321func (*soongConfigStringVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
322
323func (m *soongConfigBoolVariableDummyModule) Name() string {
324 return m.properties.Name
325}
326func (*soongConfigBoolVariableDummyModule) Nameless() {}
327func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
328
Jingwen Chena47f28d2021-11-02 16:43:57 +0000329// importModuleTypes registers the module factories for a list of module types defined
330// in an Android.bp file. These module factories are scoped for the current Android.bp
331// file only.
Colin Cross9d34f352019-11-22 16:03:51 -0800332func importModuleTypes(ctx LoadHookContext, from string, moduleTypes ...string) {
333 from = filepath.Clean(from)
334 if filepath.Ext(from) != ".bp" {
335 ctx.PropertyErrorf("from", "%q must be a file with extension .bp", from)
336 return
337 }
338
339 if strings.HasPrefix(from, "../") {
340 ctx.PropertyErrorf("from", "%q must not use ../ to escape the source tree",
341 from)
342 return
343 }
344
345 moduleTypeDefinitions := loadSoongConfigModuleTypeDefinition(ctx, from)
346 if moduleTypeDefinitions == nil {
347 return
348 }
349 for _, moduleType := range moduleTypes {
350 if factory, ok := moduleTypeDefinitions[moduleType]; ok {
351 ctx.registerScopedModuleType(moduleType, factory)
352 } else {
353 ctx.PropertyErrorf("module_types", "module type %q not defined in %q",
354 moduleType, from)
355 }
356 }
357}
358
359// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
360// result so each file is only parsed once.
361func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[string]blueprint.ModuleFactory {
362 type onceKeyType string
363 key := NewCustomOnceKey(onceKeyType(filepath.Clean(from)))
364
365 reportErrors := func(ctx LoadHookContext, filename string, errs ...error) {
366 for _, err := range errs {
367 if parseErr, ok := err.(*parser.ParseError); ok {
368 ctx.Errorf(parseErr.Pos, "%s", parseErr.Err)
369 } else {
370 ctx.Errorf(scanner.Position{Filename: filename}, "%s", err)
371 }
372 }
373 }
374
375 return ctx.Config().Once(key, func() interface{} {
Colin Cross39e545c2020-02-05 16:26:19 -0800376 ctx.AddNinjaFileDeps(from)
Colin Cross9d34f352019-11-22 16:03:51 -0800377 r, err := ctx.Config().fs.Open(from)
378 if err != nil {
379 ctx.PropertyErrorf("from", "failed to open %q: %s", from, err)
380 return (map[string]blueprint.ModuleFactory)(nil)
381 }
Liz Kammer0fe123d2022-02-07 10:17:35 -0500382 defer r.Close()
Colin Cross9d34f352019-11-22 16:03:51 -0800383
384 mtDef, errs := soongconfig.Parse(r, from)
Jingwen Chen01812022021-11-19 14:29:43 +0000385 if ctx.Config().runningAsBp2Build {
386 ctx.Config().Bp2buildSoongConfigDefinitions.AddVars(*mtDef)
387 }
Colin Cross9d34f352019-11-22 16:03:51 -0800388
389 if len(errs) > 0 {
390 reportErrors(ctx, from, errs...)
391 return (map[string]blueprint.ModuleFactory)(nil)
392 }
393
394 globalModuleTypes := ctx.moduleFactories()
395
396 factories := make(map[string]blueprint.ModuleFactory)
397
398 for name, moduleType := range mtDef.ModuleTypes {
399 factory := globalModuleTypes[moduleType.BaseModuleType]
400 if factory != nil {
Jingwen Chena47f28d2021-11-02 16:43:57 +0000401 factories[name] = configModuleFactory(factory, moduleType, ctx.Config().runningAsBp2Build)
Colin Cross9d34f352019-11-22 16:03:51 -0800402 } else {
403 reportErrors(ctx, from,
404 fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
405 }
406 }
407
408 if ctx.Failed() {
409 return (map[string]blueprint.ModuleFactory)(nil)
410 }
411
412 return factories
413 }).(map[string]blueprint.ModuleFactory)
414}
415
Jingwen Chena47f28d2021-11-02 16:43:57 +0000416// configModuleFactory takes an existing soongConfigModuleFactory and a
417// ModuleType to create a new ModuleFactory that uses a custom loadhook.
418func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType, bp2build bool) blueprint.ModuleFactory {
Colin Cross9d34f352019-11-22 16:03:51 -0800419 conditionalFactoryProps := soongconfig.CreateProperties(factory, moduleType)
Jingwen Chena47f28d2021-11-02 16:43:57 +0000420 if !conditionalFactoryProps.IsValid() {
421 return factory
422 }
Colin Cross9d34f352019-11-22 16:03:51 -0800423
Jingwen Chena47f28d2021-11-02 16:43:57 +0000424 return func() (blueprint.Module, []interface{}) {
425 module, props := factory()
426 conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
427 props = append(props, conditionalProps.Interface())
Colin Cross9d34f352019-11-22 16:03:51 -0800428
Jingwen Chen01812022021-11-19 14:29:43 +0000429 if bp2build {
Jingwen Chena47f28d2021-11-02 16:43:57 +0000430 // The loadhook is different for bp2build, since we don't want to set a specific
431 // set of property values based on a vendor var -- we want __all of them__ to
432 // generate select statements, so we put the entire soong_config_variables
433 // struct, together with the namespace representing those variables, while
434 // creating the custom module with the factory.
435 AddLoadHook(module, func(ctx LoadHookContext) {
436 if m, ok := module.(Bazelable); ok {
437 m.SetBaseModuleType(moduleType.BaseModuleType)
438 // Instead of applying all properties, keep the entire conditionalProps struct as
439 // part of the custom module so dependent modules can create the selects accordingly
440 m.setNamespacedVariableProps(namespacedVariableProperties{
Jingwen Chen84817de2021-11-17 10:57:35 +0000441 moduleType.ConfigNamespace: []interface{}{conditionalProps.Interface()},
Jingwen Chena47f28d2021-11-02 16:43:57 +0000442 })
443 }
444 })
445 } else {
446 // Regular Soong operation wraps the existing module factory with a
447 // conditional on Soong config variables by reading the product
448 // config variables from Make.
Colin Cross9d34f352019-11-22 16:03:51 -0800449 AddLoadHook(module, func(ctx LoadHookContext) {
450 config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700451 newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
452 if err != nil {
453 ctx.ModuleErrorf("%s", err)
454 return
455 }
456 for _, ps := range newProps {
Colin Cross9d34f352019-11-22 16:03:51 -0800457 ctx.AppendProperties(ps)
458 }
459 })
Colin Cross9d34f352019-11-22 16:03:51 -0800460 }
Jingwen Chena47f28d2021-11-02 16:43:57 +0000461 return module, props
Colin Cross9d34f352019-11-22 16:03:51 -0800462 }
463}