blob: 91bbce68e0366052d454644900dbc1c24599a4f4 [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:
193// bool variable: the variable is unspecified or not set to a true value
194// value variable: the variable is unspecified
195// string variable: the variable is unspecified or the variable is set to a string unused in the
196// given module. For example, string variable `test` takes values: "a" and "b",
197// if the module contains a property `a` and `conditions_default`, when test=b,
198// the properties under `conditions_default` will be used. To specify that no
199// properties should be amended for `b`, you can set `b: {},`.
200//
Colin Cross9d34f352019-11-22 16:03:51 -0800201// For example, an Android.bp file could have:
202//
203// soong_config_module_type {
204// name: "acme_cc_defaults",
205// module_type: "cc_defaults",
206// config_namespace: "acme",
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700207// variables: ["board"],
208// bool_variables: ["feature"],
Dan Willemsenb0935db2020-03-23 19:42:18 -0700209// value_variables: ["width"],
Colin Cross9d34f352019-11-22 16:03:51 -0800210// properties: ["cflags", "srcs"],
211// }
212//
213// soong_config_string_variable {
214// name: "board",
215// values: ["soc_a", "soc_b"],
216// }
217//
Colin Cross9d34f352019-11-22 16:03:51 -0800218// acme_cc_defaults {
219// name: "acme_defaults",
220// cflags: ["-DGENERIC"],
221// soong_config_variables: {
222// board: {
223// soc_a: {
224// cflags: ["-DSOC_A"],
225// },
226// soc_b: {
227// cflags: ["-DSOC_B"],
228// },
Liz Kammer432bd592020-12-16 12:42:02 -0800229// conditions_default: {
230// cflags: ["-DSOC_DEFAULT"],
231// },
Colin Cross9d34f352019-11-22 16:03:51 -0800232// },
233// feature: {
234// cflags: ["-DFEATURE"],
Liz Kammer432bd592020-12-16 12:42:02 -0800235// conditions_default: {
236// cflags: ["-DFEATURE_DEFAULT"],
237// },
Colin Cross9d34f352019-11-22 16:03:51 -0800238// },
Dan Willemsenb0935db2020-03-23 19:42:18 -0700239// width: {
240// cflags: ["-DWIDTH=%s"],
Liz Kammer432bd592020-12-16 12:42:02 -0800241// conditions_default: {
242// cflags: ["-DWIDTH=DEFAULT"],
243// },
Dan Willemsenb0935db2020-03-23 19:42:18 -0700244// },
Colin Cross9d34f352019-11-22 16:03:51 -0800245// },
246// }
247//
248// cc_library {
249// name: "libacme_foo",
250// defaults: ["acme_defaults"],
251// srcs: ["*.cpp"],
252// }
253//
Colin Cross9d34f352019-11-22 16:03:51 -0800254// If an acme BoardConfig.mk file contained:
255//
256// SOONG_CONFIG_NAMESPACES += acme
257// SOONG_CONFIG_acme += \
258// board \
259// feature \
260//
261// SOONG_CONFIG_acme_board := soc_a
262// SOONG_CONFIG_acme_feature := true
Dan Willemsenb0935db2020-03-23 19:42:18 -0700263// SOONG_CONFIG_acme_width := 200
Colin Cross9d34f352019-11-22 16:03:51 -0800264//
265// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
Jingwen Chena47f28d2021-11-02 16:43:57 +0000266func SoongConfigModuleTypeFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800267 module := &soongConfigModuleTypeModule{}
268
269 module.AddProperties(&module.properties)
270
271 AddLoadHook(module, func(ctx LoadHookContext) {
272 // A soong_config_module_type module should implicitly import itself.
273 importModuleTypes(ctx, ctx.BlueprintsFile(), module.properties.Name)
274 })
275
276 initAndroidModuleBase(module)
277
278 return module
279}
280
281func (m *soongConfigModuleTypeModule) Name() string {
282 return m.properties.Name
283}
284func (*soongConfigModuleTypeModule) Nameless() {}
285func (*soongConfigModuleTypeModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
286
287type soongConfigStringVariableDummyModule struct {
288 ModuleBase
289 properties soongconfig.VariableProperties
290 stringProperties soongconfig.StringVariableProperties
291}
292
293type soongConfigBoolVariableDummyModule struct {
294 ModuleBase
295 properties soongconfig.VariableProperties
296}
297
298// soong_config_string_variable defines a variable and a set of possible string values for use
299// in a soong_config_module_type definition.
Jingwen Chena47f28d2021-11-02 16:43:57 +0000300func SoongConfigStringVariableDummyFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800301 module := &soongConfigStringVariableDummyModule{}
302 module.AddProperties(&module.properties, &module.stringProperties)
303 initAndroidModuleBase(module)
304 return module
305}
306
307// soong_config_string_variable defines a variable with true or false values for use
308// in a soong_config_module_type definition.
Jingwen Chena47f28d2021-11-02 16:43:57 +0000309func SoongConfigBoolVariableDummyFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800310 module := &soongConfigBoolVariableDummyModule{}
311 module.AddProperties(&module.properties)
312 initAndroidModuleBase(module)
313 return module
314}
315
316func (m *soongConfigStringVariableDummyModule) Name() string {
317 return m.properties.Name
318}
319func (*soongConfigStringVariableDummyModule) Nameless() {}
320func (*soongConfigStringVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
321
322func (m *soongConfigBoolVariableDummyModule) Name() string {
323 return m.properties.Name
324}
325func (*soongConfigBoolVariableDummyModule) Nameless() {}
326func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
327
Jingwen Chena47f28d2021-11-02 16:43:57 +0000328// importModuleTypes registers the module factories for a list of module types defined
329// in an Android.bp file. These module factories are scoped for the current Android.bp
330// file only.
Colin Cross9d34f352019-11-22 16:03:51 -0800331func importModuleTypes(ctx LoadHookContext, from string, moduleTypes ...string) {
332 from = filepath.Clean(from)
333 if filepath.Ext(from) != ".bp" {
334 ctx.PropertyErrorf("from", "%q must be a file with extension .bp", from)
335 return
336 }
337
338 if strings.HasPrefix(from, "../") {
339 ctx.PropertyErrorf("from", "%q must not use ../ to escape the source tree",
340 from)
341 return
342 }
343
344 moduleTypeDefinitions := loadSoongConfigModuleTypeDefinition(ctx, from)
345 if moduleTypeDefinitions == nil {
346 return
347 }
348 for _, moduleType := range moduleTypes {
349 if factory, ok := moduleTypeDefinitions[moduleType]; ok {
350 ctx.registerScopedModuleType(moduleType, factory)
351 } else {
352 ctx.PropertyErrorf("module_types", "module type %q not defined in %q",
353 moduleType, from)
354 }
355 }
356}
357
358// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
359// result so each file is only parsed once.
360func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[string]blueprint.ModuleFactory {
361 type onceKeyType string
362 key := NewCustomOnceKey(onceKeyType(filepath.Clean(from)))
363
364 reportErrors := func(ctx LoadHookContext, filename string, errs ...error) {
365 for _, err := range errs {
366 if parseErr, ok := err.(*parser.ParseError); ok {
367 ctx.Errorf(parseErr.Pos, "%s", parseErr.Err)
368 } else {
369 ctx.Errorf(scanner.Position{Filename: filename}, "%s", err)
370 }
371 }
372 }
373
374 return ctx.Config().Once(key, func() interface{} {
Colin Cross39e545c2020-02-05 16:26:19 -0800375 ctx.AddNinjaFileDeps(from)
Colin Cross9d34f352019-11-22 16:03:51 -0800376 r, err := ctx.Config().fs.Open(from)
377 if err != nil {
378 ctx.PropertyErrorf("from", "failed to open %q: %s", from, err)
379 return (map[string]blueprint.ModuleFactory)(nil)
380 }
381
382 mtDef, errs := soongconfig.Parse(r, from)
Jingwen Chen01812022021-11-19 14:29:43 +0000383 if ctx.Config().runningAsBp2Build {
384 ctx.Config().Bp2buildSoongConfigDefinitions.AddVars(*mtDef)
385 }
Colin Cross9d34f352019-11-22 16:03:51 -0800386
387 if len(errs) > 0 {
388 reportErrors(ctx, from, errs...)
389 return (map[string]blueprint.ModuleFactory)(nil)
390 }
391
392 globalModuleTypes := ctx.moduleFactories()
393
394 factories := make(map[string]blueprint.ModuleFactory)
395
396 for name, moduleType := range mtDef.ModuleTypes {
397 factory := globalModuleTypes[moduleType.BaseModuleType]
398 if factory != nil {
Jingwen Chena47f28d2021-11-02 16:43:57 +0000399 factories[name] = configModuleFactory(factory, moduleType, ctx.Config().runningAsBp2Build)
Colin Cross9d34f352019-11-22 16:03:51 -0800400 } else {
401 reportErrors(ctx, from,
402 fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
403 }
404 }
405
406 if ctx.Failed() {
407 return (map[string]blueprint.ModuleFactory)(nil)
408 }
409
410 return factories
411 }).(map[string]blueprint.ModuleFactory)
412}
413
Jingwen Chena47f28d2021-11-02 16:43:57 +0000414// configModuleFactory takes an existing soongConfigModuleFactory and a
415// ModuleType to create a new ModuleFactory that uses a custom loadhook.
416func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType, bp2build bool) blueprint.ModuleFactory {
Colin Cross9d34f352019-11-22 16:03:51 -0800417 conditionalFactoryProps := soongconfig.CreateProperties(factory, moduleType)
Jingwen Chena47f28d2021-11-02 16:43:57 +0000418 if !conditionalFactoryProps.IsValid() {
419 return factory
420 }
Colin Cross9d34f352019-11-22 16:03:51 -0800421
Jingwen Chena47f28d2021-11-02 16:43:57 +0000422 return func() (blueprint.Module, []interface{}) {
423 module, props := factory()
424 conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
425 props = append(props, conditionalProps.Interface())
Colin Cross9d34f352019-11-22 16:03:51 -0800426
Jingwen Chen01812022021-11-19 14:29:43 +0000427 if bp2build {
Jingwen Chena47f28d2021-11-02 16:43:57 +0000428 // The loadhook is different for bp2build, since we don't want to set a specific
429 // set of property values based on a vendor var -- we want __all of them__ to
430 // generate select statements, so we put the entire soong_config_variables
431 // struct, together with the namespace representing those variables, while
432 // creating the custom module with the factory.
433 AddLoadHook(module, func(ctx LoadHookContext) {
434 if m, ok := module.(Bazelable); ok {
435 m.SetBaseModuleType(moduleType.BaseModuleType)
436 // Instead of applying all properties, keep the entire conditionalProps struct as
437 // part of the custom module so dependent modules can create the selects accordingly
438 m.setNamespacedVariableProps(namespacedVariableProperties{
Jingwen Chen84817de2021-11-17 10:57:35 +0000439 moduleType.ConfigNamespace: []interface{}{conditionalProps.Interface()},
Jingwen Chena47f28d2021-11-02 16:43:57 +0000440 })
441 }
442 })
443 } else {
444 // Regular Soong operation wraps the existing module factory with a
445 // conditional on Soong config variables by reading the product
446 // config variables from Make.
Colin Cross9d34f352019-11-22 16:03:51 -0800447 AddLoadHook(module, func(ctx LoadHookContext) {
448 config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700449 newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
450 if err != nil {
451 ctx.ModuleErrorf("%s", err)
452 return
453 }
454 for _, ps := range newProps {
Colin Cross9d34f352019-11-22 16:03:51 -0800455 ctx.AppendProperties(ps)
456 }
457 })
Colin Cross9d34f352019-11-22 16:03:51 -0800458 }
Jingwen Chena47f28d2021-11-02 16:43:57 +0000459 return module, props
Colin Cross9d34f352019-11-22 16:03:51 -0800460 }
461}