blob: 7434c89127d7d7ff94361f5b00a48ce83c0f87bf [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() {
Paul Duffin32299982023-01-09 14:02:06 +000034 RegisterSoongConfigModuleBuildComponents(InitRegistrationContext)
Colin Cross9d34f352019-11-22 16:03:51 -080035}
36
Paul Duffin32299982023-01-09 14:02:06 +000037func RegisterSoongConfigModuleBuildComponents(ctx RegistrationContext) {
38 ctx.RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
39 ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
40 ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
41 ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
42}
43
44var PrepareForTestWithSoongConfigModuleBuildComponents = FixtureRegisterWithContext(RegisterSoongConfigModuleBuildComponents)
45
Colin Cross9d34f352019-11-22 16:03:51 -080046type soongConfigModuleTypeImport struct {
47 ModuleBase
48 properties soongConfigModuleTypeImportProperties
49}
50
51type soongConfigModuleTypeImportProperties struct {
52 From string
53 Module_types []string
54}
55
56// soong_config_module_type_import imports module types with conditionals on Soong config
57// variables from another Android.bp file. The imported module type will exist for all
58// modules after the import in the Android.bp file.
59//
Liz Kammer432bd592020-12-16 12:42:02 -080060// Each soong_config_variable supports an additional value `conditions_default`. The properties
61// specified in `conditions_default` will only be used under the following conditions:
62// bool variable: the variable is unspecified or not set to a true value
63// value variable: the variable is unspecified
64// string variable: the variable is unspecified or the variable is set to a string unused in the
65// given module. For example, string variable `test` takes values: "a" and "b",
66// if the module contains a property `a` and `conditions_default`, when test=b,
67// the properties under `conditions_default` will be used. To specify that no
68// properties should be amended for `b`, you can set `b: {},`.
69//
Colin Cross9d34f352019-11-22 16:03:51 -080070// For example, an Android.bp file could have:
71//
72// soong_config_module_type_import {
Bill Peckhamc93258b2020-02-04 13:17:24 -080073// from: "device/acme/Android.bp",
Colin Cross9d34f352019-11-22 16:03:51 -080074// module_types: ["acme_cc_defaults"],
75// }
76//
77// acme_cc_defaults {
78// name: "acme_defaults",
79// cflags: ["-DGENERIC"],
80// soong_config_variables: {
81// board: {
82// soc_a: {
83// cflags: ["-DSOC_A"],
84// },
85// soc_b: {
86// cflags: ["-DSOC_B"],
87// },
Liz Kammer432bd592020-12-16 12:42:02 -080088// conditions_default: {
89// cflags: ["-DSOC_DEFAULT"],
90// },
Colin Cross9d34f352019-11-22 16:03:51 -080091// },
92// feature: {
93// cflags: ["-DFEATURE"],
Liz Kammer432bd592020-12-16 12:42:02 -080094// conditions_default: {
95// cflags: ["-DFEATURE_DEFAULT"],
96// },
Colin Cross9d34f352019-11-22 16:03:51 -080097// },
Dan Willemsenb0935db2020-03-23 19:42:18 -070098// width: {
99// cflags: ["-DWIDTH=%s"],
Liz Kammer432bd592020-12-16 12:42:02 -0800100// conditions_default: {
101// cflags: ["-DWIDTH=DEFAULT"],
102// },
Dan Willemsenb0935db2020-03-23 19:42:18 -0700103// },
Colin Cross9d34f352019-11-22 16:03:51 -0800104// },
105// }
106//
107// cc_library {
108// name: "libacme_foo",
109// defaults: ["acme_defaults"],
110// srcs: ["*.cpp"],
111// }
112//
113// And device/acme/Android.bp could have:
114//
115// soong_config_module_type {
116// name: "acme_cc_defaults",
117// module_type: "cc_defaults",
118// config_namespace: "acme",
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700119// variables: ["board"],
120// bool_variables: ["feature"],
Dan Willemsenb0935db2020-03-23 19:42:18 -0700121// value_variables: ["width"],
Colin Cross9d34f352019-11-22 16:03:51 -0800122// properties: ["cflags", "srcs"],
123// }
124//
125// soong_config_string_variable {
126// name: "board",
Liz Kammer432bd592020-12-16 12:42:02 -0800127// values: ["soc_a", "soc_b", "soc_c"],
Colin Cross9d34f352019-11-22 16:03:51 -0800128// }
129//
Colin Cross9d34f352019-11-22 16:03:51 -0800130// If an acme BoardConfig.mk file contained:
Sasha Smundak18fd0992021-08-24 14:05:19 -0700131// $(call add_sonng_config_namespace, acme)
132// $(call add_soong_config_var_value, acme, board, soc_a)
133// $(call add_soong_config_var_value, acme, feature, true)
134// $(call add_soong_config_var_value, acme, width, 200)
Colin Cross9d34f352019-11-22 16:03:51 -0800135//
Dan Willemsenb0935db2020-03-23 19:42:18 -0700136// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
Liz Kammer432bd592020-12-16 12:42:02 -0800137//
138// Alternatively, if acme BoardConfig.mk file contained:
139//
140// SOONG_CONFIG_NAMESPACES += acme
141// SOONG_CONFIG_acme += \
142// board \
143// feature \
144//
145// SOONG_CONFIG_acme_feature := false
146//
147// Then libacme_foo would build with cflags:
148// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
149//
150// Similarly, if acme BoardConfig.mk file contained:
151//
152// SOONG_CONFIG_NAMESPACES += acme
153// SOONG_CONFIG_acme += \
154// board \
155// feature \
156//
157// SOONG_CONFIG_acme_board := soc_c
158//
159// Then libacme_foo would build with cflags:
160// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
161
Jingwen Chena47f28d2021-11-02 16:43:57 +0000162func SoongConfigModuleTypeImportFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800163 module := &soongConfigModuleTypeImport{}
164
165 module.AddProperties(&module.properties)
166 AddLoadHook(module, func(ctx LoadHookContext) {
167 importModuleTypes(ctx, module.properties.From, module.properties.Module_types...)
168 })
169
170 initAndroidModuleBase(module)
171 return module
172}
173
174func (m *soongConfigModuleTypeImport) Name() string {
Sasha Smundak116ec922020-03-10 16:10:06 -0700175 // The generated name is non-deterministic, but it does not
176 // matter because this module does not emit any rules.
177 return soongconfig.CanonicalizeToProperty(m.properties.From) +
178 "soong_config_module_type_import_" + fmt.Sprintf("%p", m)
Colin Cross9d34f352019-11-22 16:03:51 -0800179}
180
Colin Crossa6389e92022-06-22 16:44:07 -0700181func (*soongConfigModuleTypeImport) Namespaceless() {}
Colin Cross9d34f352019-11-22 16:03:51 -0800182func (*soongConfigModuleTypeImport) GenerateAndroidBuildActions(ModuleContext) {}
183
184// Create dummy modules for soong_config_module_type and soong_config_*_variable
185
186type soongConfigModuleTypeModule struct {
187 ModuleBase
Jingwen Chena47f28d2021-11-02 16:43:57 +0000188 BazelModuleBase
Colin Cross9d34f352019-11-22 16:03:51 -0800189 properties soongconfig.ModuleTypeProperties
190}
191
192// soong_config_module_type defines module types with conditionals on Soong config
Bill Peckhamc93258b2020-02-04 13:17:24 -0800193// variables. The new module type will exist for all modules after the definition
194// in an Android.bp file, and can be imported into other Android.bp files using
195// soong_config_module_type_import.
Colin Cross9d34f352019-11-22 16:03:51 -0800196//
Liz Kammer432bd592020-12-16 12:42:02 -0800197// Each soong_config_variable supports an additional value `conditions_default`. The properties
198// specified in `conditions_default` will only be used under the following conditions:
Colin Crossd079e0b2022-08-16 10:27:33 -0700199//
200// bool variable: the variable is unspecified or not set to a true value
201// value variable: the variable is unspecified
202// string variable: the variable is unspecified or the variable is set to a string unused in the
203// given module. For example, string variable `test` takes values: "a" and "b",
204// if the module contains a property `a` and `conditions_default`, when test=b,
205// the properties under `conditions_default` will be used. To specify that no
206// properties should be amended for `b`, you can set `b: {},`.
Liz Kammer432bd592020-12-16 12:42:02 -0800207//
Colin Cross9d34f352019-11-22 16:03:51 -0800208// For example, an Android.bp file could have:
209//
Colin Crossd079e0b2022-08-16 10:27:33 -0700210// soong_config_module_type {
211// name: "acme_cc_defaults",
212// module_type: "cc_defaults",
213// config_namespace: "acme",
214// variables: ["board"],
215// bool_variables: ["feature"],
216// value_variables: ["width"],
217// properties: ["cflags", "srcs"],
218// }
Colin Cross9d34f352019-11-22 16:03:51 -0800219//
Colin Crossd079e0b2022-08-16 10:27:33 -0700220// soong_config_string_variable {
221// name: "board",
222// values: ["soc_a", "soc_b"],
223// }
Colin Cross9d34f352019-11-22 16:03:51 -0800224//
Colin Crossd079e0b2022-08-16 10:27:33 -0700225// acme_cc_defaults {
226// name: "acme_defaults",
227// cflags: ["-DGENERIC"],
228// soong_config_variables: {
229// board: {
230// soc_a: {
231// cflags: ["-DSOC_A"],
232// },
233// soc_b: {
234// cflags: ["-DSOC_B"],
235// },
236// conditions_default: {
237// cflags: ["-DSOC_DEFAULT"],
238// },
239// },
240// feature: {
241// cflags: ["-DFEATURE"],
242// conditions_default: {
243// cflags: ["-DFEATURE_DEFAULT"],
244// },
245// },
246// width: {
247// cflags: ["-DWIDTH=%s"],
248// conditions_default: {
249// cflags: ["-DWIDTH=DEFAULT"],
250// },
251// },
252// },
253// }
Colin Cross9d34f352019-11-22 16:03:51 -0800254//
Colin Crossd079e0b2022-08-16 10:27:33 -0700255// cc_library {
256// name: "libacme_foo",
257// defaults: ["acme_defaults"],
258// srcs: ["*.cpp"],
259// }
Colin Cross9d34f352019-11-22 16:03:51 -0800260//
Colin Cross9d34f352019-11-22 16:03:51 -0800261// If an acme BoardConfig.mk file contained:
262//
Colin Crossd079e0b2022-08-16 10:27:33 -0700263// SOONG_CONFIG_NAMESPACES += acme
264// SOONG_CONFIG_acme += \
265// board \
266// feature \
Colin Cross9d34f352019-11-22 16:03:51 -0800267//
Colin Crossd079e0b2022-08-16 10:27:33 -0700268// SOONG_CONFIG_acme_board := soc_a
269// SOONG_CONFIG_acme_feature := true
270// SOONG_CONFIG_acme_width := 200
Colin Cross9d34f352019-11-22 16:03:51 -0800271//
272// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
Jingwen Chena47f28d2021-11-02 16:43:57 +0000273func SoongConfigModuleTypeFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800274 module := &soongConfigModuleTypeModule{}
275
276 module.AddProperties(&module.properties)
277
278 AddLoadHook(module, func(ctx LoadHookContext) {
279 // A soong_config_module_type module should implicitly import itself.
280 importModuleTypes(ctx, ctx.BlueprintsFile(), module.properties.Name)
281 })
282
283 initAndroidModuleBase(module)
284
285 return module
286}
287
288func (m *soongConfigModuleTypeModule) Name() string {
Colin Crossa6389e92022-06-22 16:44:07 -0700289 return m.properties.Name + fmt.Sprintf("%p", m)
Colin Cross9d34f352019-11-22 16:03:51 -0800290}
Colin Crossa6389e92022-06-22 16:44:07 -0700291func (*soongConfigModuleTypeModule) Namespaceless() {}
Colin Cross9d34f352019-11-22 16:03:51 -0800292func (*soongConfigModuleTypeModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
293
294type soongConfigStringVariableDummyModule struct {
295 ModuleBase
296 properties soongconfig.VariableProperties
297 stringProperties soongconfig.StringVariableProperties
298}
299
300type soongConfigBoolVariableDummyModule struct {
301 ModuleBase
302 properties soongconfig.VariableProperties
303}
304
305// soong_config_string_variable defines a variable and a set of possible string values for use
306// in a soong_config_module_type definition.
Jingwen Chena47f28d2021-11-02 16:43:57 +0000307func SoongConfigStringVariableDummyFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800308 module := &soongConfigStringVariableDummyModule{}
309 module.AddProperties(&module.properties, &module.stringProperties)
310 initAndroidModuleBase(module)
311 return module
312}
313
314// soong_config_string_variable defines a variable with true or false values for use
315// in a soong_config_module_type definition.
Jingwen Chena47f28d2021-11-02 16:43:57 +0000316func SoongConfigBoolVariableDummyFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800317 module := &soongConfigBoolVariableDummyModule{}
318 module.AddProperties(&module.properties)
319 initAndroidModuleBase(module)
320 return module
321}
322
323func (m *soongConfigStringVariableDummyModule) Name() string {
Colin Crossa6389e92022-06-22 16:44:07 -0700324 return m.properties.Name + fmt.Sprintf("%p", m)
Colin Cross9d34f352019-11-22 16:03:51 -0800325}
Colin Crossa6389e92022-06-22 16:44:07 -0700326func (*soongConfigStringVariableDummyModule) Namespaceless() {}
Colin Cross9d34f352019-11-22 16:03:51 -0800327func (*soongConfigStringVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
328
329func (m *soongConfigBoolVariableDummyModule) Name() string {
Colin Crossa6389e92022-06-22 16:44:07 -0700330 return m.properties.Name + fmt.Sprintf("%p", m)
Colin Cross9d34f352019-11-22 16:03:51 -0800331}
Colin Crossa6389e92022-06-22 16:44:07 -0700332func (*soongConfigBoolVariableDummyModule) Namespaceless() {}
Colin Cross9d34f352019-11-22 16:03:51 -0800333func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
334
Jingwen Chena47f28d2021-11-02 16:43:57 +0000335// importModuleTypes registers the module factories for a list of module types defined
336// in an Android.bp file. These module factories are scoped for the current Android.bp
337// file only.
Colin Cross9d34f352019-11-22 16:03:51 -0800338func importModuleTypes(ctx LoadHookContext, from string, moduleTypes ...string) {
339 from = filepath.Clean(from)
340 if filepath.Ext(from) != ".bp" {
341 ctx.PropertyErrorf("from", "%q must be a file with extension .bp", from)
342 return
343 }
344
345 if strings.HasPrefix(from, "../") {
346 ctx.PropertyErrorf("from", "%q must not use ../ to escape the source tree",
347 from)
348 return
349 }
350
351 moduleTypeDefinitions := loadSoongConfigModuleTypeDefinition(ctx, from)
352 if moduleTypeDefinitions == nil {
353 return
354 }
355 for _, moduleType := range moduleTypes {
356 if factory, ok := moduleTypeDefinitions[moduleType]; ok {
357 ctx.registerScopedModuleType(moduleType, factory)
358 } else {
359 ctx.PropertyErrorf("module_types", "module type %q not defined in %q",
360 moduleType, from)
361 }
362 }
363}
364
365// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
366// result so each file is only parsed once.
367func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[string]blueprint.ModuleFactory {
368 type onceKeyType string
369 key := NewCustomOnceKey(onceKeyType(filepath.Clean(from)))
370
371 reportErrors := func(ctx LoadHookContext, filename string, errs ...error) {
372 for _, err := range errs {
373 if parseErr, ok := err.(*parser.ParseError); ok {
374 ctx.Errorf(parseErr.Pos, "%s", parseErr.Err)
375 } else {
376 ctx.Errorf(scanner.Position{Filename: filename}, "%s", err)
377 }
378 }
379 }
380
381 return ctx.Config().Once(key, func() interface{} {
Colin Cross39e545c2020-02-05 16:26:19 -0800382 ctx.AddNinjaFileDeps(from)
Colin Cross9d34f352019-11-22 16:03:51 -0800383 r, err := ctx.Config().fs.Open(from)
384 if err != nil {
385 ctx.PropertyErrorf("from", "failed to open %q: %s", from, err)
386 return (map[string]blueprint.ModuleFactory)(nil)
387 }
Liz Kammer0fe123d2022-02-07 10:17:35 -0500388 defer r.Close()
Colin Cross9d34f352019-11-22 16:03:51 -0800389
390 mtDef, errs := soongconfig.Parse(r, from)
Colin Cross9d34f352019-11-22 16:03:51 -0800391 if len(errs) > 0 {
392 reportErrors(ctx, from, errs...)
393 return (map[string]blueprint.ModuleFactory)(nil)
394 }
395
Liz Kammer44bc9a32022-12-21 14:43:46 -0500396 if ctx.Config().BuildMode == Bp2build {
397 ctx.Config().Bp2buildSoongConfigDefinitions.AddVars(*mtDef)
398 }
399
Colin Cross9d34f352019-11-22 16:03:51 -0800400 globalModuleTypes := ctx.moduleFactories()
401
402 factories := make(map[string]blueprint.ModuleFactory)
403
404 for name, moduleType := range mtDef.ModuleTypes {
405 factory := globalModuleTypes[moduleType.BaseModuleType]
406 if factory != nil {
Chris Parsonsad876012022-08-20 14:48:32 -0400407 factories[name] = configModuleFactory(factory, moduleType, ctx.Config().BuildMode == Bp2build)
Colin Cross9d34f352019-11-22 16:03:51 -0800408 } else {
409 reportErrors(ctx, from,
410 fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
411 }
412 }
413
414 if ctx.Failed() {
415 return (map[string]blueprint.ModuleFactory)(nil)
416 }
417
418 return factories
419 }).(map[string]blueprint.ModuleFactory)
420}
421
Jingwen Chena47f28d2021-11-02 16:43:57 +0000422// configModuleFactory takes an existing soongConfigModuleFactory and a
423// ModuleType to create a new ModuleFactory that uses a custom loadhook.
424func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType, bp2build bool) blueprint.ModuleFactory {
Colin Cross9d34f352019-11-22 16:03:51 -0800425 conditionalFactoryProps := soongconfig.CreateProperties(factory, moduleType)
Jingwen Chena47f28d2021-11-02 16:43:57 +0000426 if !conditionalFactoryProps.IsValid() {
427 return factory
428 }
Colin Cross9d34f352019-11-22 16:03:51 -0800429
Jingwen Chena47f28d2021-11-02 16:43:57 +0000430 return func() (blueprint.Module, []interface{}) {
431 module, props := factory()
432 conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
433 props = append(props, conditionalProps.Interface())
Colin Cross9d34f352019-11-22 16:03:51 -0800434
Jingwen Chen01812022021-11-19 14:29:43 +0000435 if bp2build {
Jingwen Chena47f28d2021-11-02 16:43:57 +0000436 // The loadhook is different for bp2build, since we don't want to set a specific
437 // set of property values based on a vendor var -- we want __all of them__ to
438 // generate select statements, so we put the entire soong_config_variables
439 // struct, together with the namespace representing those variables, while
440 // creating the custom module with the factory.
441 AddLoadHook(module, func(ctx LoadHookContext) {
442 if m, ok := module.(Bazelable); ok {
443 m.SetBaseModuleType(moduleType.BaseModuleType)
444 // Instead of applying all properties, keep the entire conditionalProps struct as
445 // part of the custom module so dependent modules can create the selects accordingly
446 m.setNamespacedVariableProps(namespacedVariableProperties{
Jingwen Chen84817de2021-11-17 10:57:35 +0000447 moduleType.ConfigNamespace: []interface{}{conditionalProps.Interface()},
Jingwen Chena47f28d2021-11-02 16:43:57 +0000448 })
449 }
450 })
451 } else {
452 // Regular Soong operation wraps the existing module factory with a
453 // conditional on Soong config variables by reading the product
454 // config variables from Make.
Colin Cross9d34f352019-11-22 16:03:51 -0800455 AddLoadHook(module, func(ctx LoadHookContext) {
456 config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700457 newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
458 if err != nil {
459 ctx.ModuleErrorf("%s", err)
460 return
461 }
462 for _, ps := range newProps {
Colin Cross9d34f352019-11-22 16:03:51 -0800463 ctx.AppendProperties(ps)
464 }
465 })
Colin Cross9d34f352019-11-22 16:03:51 -0800466 }
Jingwen Chena47f28d2021-11-02 16:43:57 +0000467 return module, props
Colin Cross9d34f352019-11-22 16:03:51 -0800468 }
469}