blob: 38db92995807fae8d4c0d9b4f56bfc6388bc0233 [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"
Paul Duffine8b47682023-01-09 15:42:57 +000023 "reflect"
Colin Cross9d34f352019-11-22 16:03:51 -080024 "strings"
Paul Duffine8b47682023-01-09 15:42:57 +000025 "sync"
Colin Cross9d34f352019-11-22 16:03:51 -080026 "text/scanner"
27
28 "github.com/google/blueprint"
29 "github.com/google/blueprint/parser"
30 "github.com/google/blueprint/proptools"
31
32 "android/soong/android/soongconfig"
33)
34
35func init() {
Paul Duffin32299982023-01-09 14:02:06 +000036 RegisterSoongConfigModuleBuildComponents(InitRegistrationContext)
Colin Cross9d34f352019-11-22 16:03:51 -080037}
38
Paul Duffin32299982023-01-09 14:02:06 +000039func RegisterSoongConfigModuleBuildComponents(ctx RegistrationContext) {
40 ctx.RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
41 ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
42 ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
43 ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
Cole Faust97494b12024-01-12 14:02:47 -080044 ctx.RegisterModuleType("soong_config_value_variable", SoongConfigValueVariableDummyFactory)
Paul Duffin32299982023-01-09 14:02:06 +000045}
46
47var PrepareForTestWithSoongConfigModuleBuildComponents = FixtureRegisterWithContext(RegisterSoongConfigModuleBuildComponents)
48
Colin Cross9d34f352019-11-22 16:03:51 -080049type soongConfigModuleTypeImport struct {
50 ModuleBase
51 properties soongConfigModuleTypeImportProperties
52}
53
54type soongConfigModuleTypeImportProperties struct {
55 From string
56 Module_types []string
57}
58
59// soong_config_module_type_import imports module types with conditionals on Soong config
60// variables from another Android.bp file. The imported module type will exist for all
61// modules after the import in the Android.bp file.
62//
Liz Kammer432bd592020-12-16 12:42:02 -080063// Each soong_config_variable supports an additional value `conditions_default`. The properties
64// specified in `conditions_default` will only be used under the following conditions:
65// bool variable: the variable is unspecified or not set to a true value
66// value variable: the variable is unspecified
Inseob Kim02c86182024-04-05 17:52:06 +090067// list variable: the variable is unspecified
Liz Kammer432bd592020-12-16 12:42:02 -080068// string variable: the variable is unspecified or the variable is set to a string unused in the
69// given module. For example, string variable `test` takes values: "a" and "b",
70// if the module contains a property `a` and `conditions_default`, when test=b,
71// the properties under `conditions_default` will be used. To specify that no
72// properties should be amended for `b`, you can set `b: {},`.
73//
Colin Cross9d34f352019-11-22 16:03:51 -080074// For example, an Android.bp file could have:
75//
76// soong_config_module_type_import {
Bill Peckhamc93258b2020-02-04 13:17:24 -080077// from: "device/acme/Android.bp",
Colin Cross9d34f352019-11-22 16:03:51 -080078// module_types: ["acme_cc_defaults"],
79// }
80//
81// acme_cc_defaults {
82// name: "acme_defaults",
83// cflags: ["-DGENERIC"],
84// soong_config_variables: {
85// board: {
86// soc_a: {
87// cflags: ["-DSOC_A"],
88// },
89// soc_b: {
90// cflags: ["-DSOC_B"],
91// },
Liz Kammer432bd592020-12-16 12:42:02 -080092// conditions_default: {
93// cflags: ["-DSOC_DEFAULT"],
94// },
Colin Cross9d34f352019-11-22 16:03:51 -080095// },
96// feature: {
97// cflags: ["-DFEATURE"],
Liz Kammer432bd592020-12-16 12:42:02 -080098// conditions_default: {
99// cflags: ["-DFEATURE_DEFAULT"],
100// },
Colin Cross9d34f352019-11-22 16:03:51 -0800101// },
Dan Willemsenb0935db2020-03-23 19:42:18 -0700102// width: {
103// cflags: ["-DWIDTH=%s"],
Liz Kammer432bd592020-12-16 12:42:02 -0800104// conditions_default: {
105// cflags: ["-DWIDTH=DEFAULT"],
106// },
Dan Willemsenb0935db2020-03-23 19:42:18 -0700107// },
Inseob Kim02c86182024-04-05 17:52:06 +0900108// impl: {
109// srcs: ["impl/%s"],
110// conditions_default: {
111// srcs: ["impl/default.cpp"],
112// },
113// },
Colin Cross9d34f352019-11-22 16:03:51 -0800114// },
115// }
116//
117// cc_library {
118// name: "libacme_foo",
119// defaults: ["acme_defaults"],
120// srcs: ["*.cpp"],
121// }
122//
123// And device/acme/Android.bp could have:
124//
125// soong_config_module_type {
126// name: "acme_cc_defaults",
127// module_type: "cc_defaults",
128// config_namespace: "acme",
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700129// variables: ["board"],
130// bool_variables: ["feature"],
Dan Willemsenb0935db2020-03-23 19:42:18 -0700131// value_variables: ["width"],
Inseob Kim02c86182024-04-05 17:52:06 +0900132// list_variables: ["impl"],
Colin Cross9d34f352019-11-22 16:03:51 -0800133// properties: ["cflags", "srcs"],
134// }
135//
136// soong_config_string_variable {
137// name: "board",
Liz Kammer432bd592020-12-16 12:42:02 -0800138// values: ["soc_a", "soc_b", "soc_c"],
Colin Cross9d34f352019-11-22 16:03:51 -0800139// }
140//
Colin Cross9d34f352019-11-22 16:03:51 -0800141// If an acme BoardConfig.mk file contained:
Sasha Smundak18fd0992021-08-24 14:05:19 -0700142// $(call add_sonng_config_namespace, acme)
143// $(call add_soong_config_var_value, acme, board, soc_a)
144// $(call add_soong_config_var_value, acme, feature, true)
145// $(call add_soong_config_var_value, acme, width, 200)
Inseob Kim02c86182024-04-05 17:52:06 +0900146// $(call add_soong_config_var_value, acme, impl, foo.cpp bar.cpp)
Colin Cross9d34f352019-11-22 16:03:51 -0800147//
Inseob Kim02c86182024-04-05 17:52:06 +0900148// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200" and srcs
149// ["*.cpp", "impl/foo.cpp", "impl/bar.cpp"].
Liz Kammer432bd592020-12-16 12:42:02 -0800150//
151// Alternatively, if acme BoardConfig.mk file contained:
152//
153// SOONG_CONFIG_NAMESPACES += acme
154// SOONG_CONFIG_acme += \
155// board \
156// feature \
157//
158// SOONG_CONFIG_acme_feature := false
159//
160// Then libacme_foo would build with cflags:
Inseob Kim02c86182024-04-05 17:52:06 +0900161// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT"
162// and with srcs:
163// ["*.cpp", "impl/default.cpp"].
Liz Kammer432bd592020-12-16 12:42:02 -0800164//
165// Similarly, if acme BoardConfig.mk file contained:
166//
167// SOONG_CONFIG_NAMESPACES += acme
168// SOONG_CONFIG_acme += \
169// board \
170// feature \
171//
172// SOONG_CONFIG_acme_board := soc_c
Inseob Kim02c86182024-04-05 17:52:06 +0900173// SOONG_CONFIG_acme_impl := foo.cpp bar.cpp
Liz Kammer432bd592020-12-16 12:42:02 -0800174//
175// Then libacme_foo would build with cflags:
Inseob Kim02c86182024-04-05 17:52:06 +0900176// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT"
177// and with srcs:
178// ["*.cpp", "impl/foo.cpp", "impl/bar.cpp"].
179//
Liz Kammer432bd592020-12-16 12:42:02 -0800180
Jingwen Chena47f28d2021-11-02 16:43:57 +0000181func SoongConfigModuleTypeImportFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800182 module := &soongConfigModuleTypeImport{}
183
184 module.AddProperties(&module.properties)
185 AddLoadHook(module, func(ctx LoadHookContext) {
186 importModuleTypes(ctx, module.properties.From, module.properties.Module_types...)
187 })
188
189 initAndroidModuleBase(module)
190 return module
191}
192
193func (m *soongConfigModuleTypeImport) Name() string {
Sasha Smundak116ec922020-03-10 16:10:06 -0700194 // The generated name is non-deterministic, but it does not
195 // matter because this module does not emit any rules.
196 return soongconfig.CanonicalizeToProperty(m.properties.From) +
197 "soong_config_module_type_import_" + fmt.Sprintf("%p", m)
Colin Cross9d34f352019-11-22 16:03:51 -0800198}
199
Colin Crossa6389e92022-06-22 16:44:07 -0700200func (*soongConfigModuleTypeImport) Namespaceless() {}
Colin Cross9d34f352019-11-22 16:03:51 -0800201func (*soongConfigModuleTypeImport) GenerateAndroidBuildActions(ModuleContext) {}
202
203// Create dummy modules for soong_config_module_type and soong_config_*_variable
204
205type soongConfigModuleTypeModule struct {
206 ModuleBase
207 properties soongconfig.ModuleTypeProperties
208}
209
210// soong_config_module_type defines module types with conditionals on Soong config
Bill Peckhamc93258b2020-02-04 13:17:24 -0800211// variables. The new module type will exist for all modules after the definition
212// in an Android.bp file, and can be imported into other Android.bp files using
213// soong_config_module_type_import.
Colin Cross9d34f352019-11-22 16:03:51 -0800214//
Liz Kammer432bd592020-12-16 12:42:02 -0800215// Each soong_config_variable supports an additional value `conditions_default`. The properties
216// specified in `conditions_default` will only be used under the following conditions:
Colin Crossd079e0b2022-08-16 10:27:33 -0700217//
218// bool variable: the variable is unspecified or not set to a true value
219// value variable: the variable is unspecified
Inseob Kim02c86182024-04-05 17:52:06 +0900220// list variable: the variable is unspecified
Colin Crossd079e0b2022-08-16 10:27:33 -0700221// string variable: the variable is unspecified or the variable is set to a string unused in the
222// given module. For example, string variable `test` takes values: "a" and "b",
223// if the module contains a property `a` and `conditions_default`, when test=b,
224// the properties under `conditions_default` will be used. To specify that no
225// properties should be amended for `b`, you can set `b: {},`.
Liz Kammer432bd592020-12-16 12:42:02 -0800226//
Colin Cross9d34f352019-11-22 16:03:51 -0800227// For example, an Android.bp file could have:
228//
Inseob Kim02c86182024-04-05 17:52:06 +0900229// soong_config_module_type {
230// name: "acme_cc_defaults",
231// module_type: "cc_defaults",
232// config_namespace: "acme",
233// variables: ["board"],
234// bool_variables: ["feature"],
235// value_variables: ["width"],
236// list_variables: ["impl"],
237// properties: ["cflags", "srcs"],
238// }
Colin Cross9d34f352019-11-22 16:03:51 -0800239//
Inseob Kim02c86182024-04-05 17:52:06 +0900240// soong_config_string_variable {
241// name: "board",
242// values: ["soc_a", "soc_b"],
243// }
Colin Cross9d34f352019-11-22 16:03:51 -0800244//
Inseob Kim02c86182024-04-05 17:52:06 +0900245// acme_cc_defaults {
246// name: "acme_defaults",
247// cflags: ["-DGENERIC"],
248// soong_config_variables: {
249// board: {
250// soc_a: {
251// cflags: ["-DSOC_A"],
Colin Crossd079e0b2022-08-16 10:27:33 -0700252// },
Inseob Kim02c86182024-04-05 17:52:06 +0900253// soc_b: {
254// cflags: ["-DSOC_B"],
Colin Crossd079e0b2022-08-16 10:27:33 -0700255// },
Inseob Kim02c86182024-04-05 17:52:06 +0900256// conditions_default: {
257// cflags: ["-DSOC_DEFAULT"],
Colin Crossd079e0b2022-08-16 10:27:33 -0700258// },
259// },
Inseob Kim02c86182024-04-05 17:52:06 +0900260// feature: {
261// cflags: ["-DFEATURE"],
262// conditions_default: {
263// cflags: ["-DFEATURE_DEFAULT"],
264// },
265// },
266// width: {
267// cflags: ["-DWIDTH=%s"],
268// conditions_default: {
269// cflags: ["-DWIDTH=DEFAULT"],
270// },
271// },
272// impl: {
273// srcs: ["impl/%s"],
274// conditions_default: {
275// srcs: ["impl/default.cpp"],
276// },
277// },
278// },
279// }
Colin Cross9d34f352019-11-22 16:03:51 -0800280//
Inseob Kim02c86182024-04-05 17:52:06 +0900281// cc_library {
282// name: "libacme_foo",
283// defaults: ["acme_defaults"],
284// srcs: ["*.cpp"],
285// }
Colin Cross9d34f352019-11-22 16:03:51 -0800286//
Colin Cross9d34f352019-11-22 16:03:51 -0800287// If an acme BoardConfig.mk file contained:
288//
Colin Crossd079e0b2022-08-16 10:27:33 -0700289// SOONG_CONFIG_NAMESPACES += acme
290// SOONG_CONFIG_acme += \
291// board \
292// feature \
Colin Cross9d34f352019-11-22 16:03:51 -0800293//
Colin Crossd079e0b2022-08-16 10:27:33 -0700294// SOONG_CONFIG_acme_board := soc_a
295// SOONG_CONFIG_acme_feature := true
296// SOONG_CONFIG_acme_width := 200
Inseob Kim02c86182024-04-05 17:52:06 +0900297// SOONG_CONFIG_acme_impl := foo.cpp bar.cpp
Colin Cross9d34f352019-11-22 16:03:51 -0800298//
Inseob Kim02c86182024-04-05 17:52:06 +0900299// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE" and srcs
300// ["*.cpp", "impl/foo.cpp", "impl/bar.cpp"].
Jingwen Chena47f28d2021-11-02 16:43:57 +0000301func SoongConfigModuleTypeFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800302 module := &soongConfigModuleTypeModule{}
303
304 module.AddProperties(&module.properties)
305
306 AddLoadHook(module, func(ctx LoadHookContext) {
307 // A soong_config_module_type module should implicitly import itself.
308 importModuleTypes(ctx, ctx.BlueprintsFile(), module.properties.Name)
309 })
310
311 initAndroidModuleBase(module)
312
313 return module
314}
315
316func (m *soongConfigModuleTypeModule) Name() string {
Colin Crossa6389e92022-06-22 16:44:07 -0700317 return m.properties.Name + fmt.Sprintf("%p", m)
Colin Cross9d34f352019-11-22 16:03:51 -0800318}
Colin Crossa6389e92022-06-22 16:44:07 -0700319func (*soongConfigModuleTypeModule) Namespaceless() {}
Colin Cross9d34f352019-11-22 16:03:51 -0800320func (*soongConfigModuleTypeModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
321
322type soongConfigStringVariableDummyModule struct {
323 ModuleBase
324 properties soongconfig.VariableProperties
325 stringProperties soongconfig.StringVariableProperties
326}
327
328type soongConfigBoolVariableDummyModule struct {
329 ModuleBase
330 properties soongconfig.VariableProperties
331}
332
Cole Faust97494b12024-01-12 14:02:47 -0800333type soongConfigValueVariableDummyModule struct {
334 ModuleBase
335 properties soongconfig.VariableProperties
336}
337
Colin Cross9d34f352019-11-22 16:03:51 -0800338// soong_config_string_variable defines a variable and a set of possible string values for use
339// in a soong_config_module_type definition.
Jingwen Chena47f28d2021-11-02 16:43:57 +0000340func SoongConfigStringVariableDummyFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800341 module := &soongConfigStringVariableDummyModule{}
342 module.AddProperties(&module.properties, &module.stringProperties)
343 initAndroidModuleBase(module)
344 return module
345}
346
347// soong_config_string_variable defines a variable with true or false values for use
348// in a soong_config_module_type definition.
Jingwen Chena47f28d2021-11-02 16:43:57 +0000349func SoongConfigBoolVariableDummyFactory() Module {
Colin Cross9d34f352019-11-22 16:03:51 -0800350 module := &soongConfigBoolVariableDummyModule{}
351 module.AddProperties(&module.properties)
352 initAndroidModuleBase(module)
353 return module
354}
355
Cole Faust97494b12024-01-12 14:02:47 -0800356// soong_config_value_variable defines a variable whose value can be expanded into
357// the value of a string property.
358func SoongConfigValueVariableDummyFactory() Module {
359 module := &soongConfigValueVariableDummyModule{}
360 module.AddProperties(&module.properties)
361 initAndroidModuleBase(module)
362 return module
363}
364
Colin Cross9d34f352019-11-22 16:03:51 -0800365func (m *soongConfigStringVariableDummyModule) Name() string {
Colin Crossa6389e92022-06-22 16:44:07 -0700366 return m.properties.Name + fmt.Sprintf("%p", m)
Colin Cross9d34f352019-11-22 16:03:51 -0800367}
Colin Crossa6389e92022-06-22 16:44:07 -0700368func (*soongConfigStringVariableDummyModule) Namespaceless() {}
Colin Cross9d34f352019-11-22 16:03:51 -0800369func (*soongConfigStringVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
370
371func (m *soongConfigBoolVariableDummyModule) Name() string {
Colin Crossa6389e92022-06-22 16:44:07 -0700372 return m.properties.Name + fmt.Sprintf("%p", m)
Colin Cross9d34f352019-11-22 16:03:51 -0800373}
Colin Crossa6389e92022-06-22 16:44:07 -0700374func (*soongConfigBoolVariableDummyModule) Namespaceless() {}
Colin Cross9d34f352019-11-22 16:03:51 -0800375func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
376
Cole Faust97494b12024-01-12 14:02:47 -0800377func (m *soongConfigValueVariableDummyModule) Name() string {
378 return m.properties.Name + fmt.Sprintf("%p", m)
379}
380func (*soongConfigValueVariableDummyModule) Namespaceless() {}
381func (*soongConfigValueVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
382
Jingwen Chena47f28d2021-11-02 16:43:57 +0000383// importModuleTypes registers the module factories for a list of module types defined
384// in an Android.bp file. These module factories are scoped for the current Android.bp
385// file only.
Colin Cross9d34f352019-11-22 16:03:51 -0800386func importModuleTypes(ctx LoadHookContext, from string, moduleTypes ...string) {
387 from = filepath.Clean(from)
388 if filepath.Ext(from) != ".bp" {
389 ctx.PropertyErrorf("from", "%q must be a file with extension .bp", from)
390 return
391 }
392
393 if strings.HasPrefix(from, "../") {
394 ctx.PropertyErrorf("from", "%q must not use ../ to escape the source tree",
395 from)
396 return
397 }
398
399 moduleTypeDefinitions := loadSoongConfigModuleTypeDefinition(ctx, from)
400 if moduleTypeDefinitions == nil {
401 return
402 }
403 for _, moduleType := range moduleTypes {
404 if factory, ok := moduleTypeDefinitions[moduleType]; ok {
405 ctx.registerScopedModuleType(moduleType, factory)
406 } else {
407 ctx.PropertyErrorf("module_types", "module type %q not defined in %q",
408 moduleType, from)
409 }
410 }
411}
412
413// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
414// result so each file is only parsed once.
415func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[string]blueprint.ModuleFactory {
416 type onceKeyType string
417 key := NewCustomOnceKey(onceKeyType(filepath.Clean(from)))
418
419 reportErrors := func(ctx LoadHookContext, filename string, errs ...error) {
420 for _, err := range errs {
421 if parseErr, ok := err.(*parser.ParseError); ok {
422 ctx.Errorf(parseErr.Pos, "%s", parseErr.Err)
423 } else {
424 ctx.Errorf(scanner.Position{Filename: filename}, "%s", err)
425 }
426 }
427 }
428
429 return ctx.Config().Once(key, func() interface{} {
Colin Cross39e545c2020-02-05 16:26:19 -0800430 ctx.AddNinjaFileDeps(from)
Colin Cross9d34f352019-11-22 16:03:51 -0800431 r, err := ctx.Config().fs.Open(from)
432 if err != nil {
433 ctx.PropertyErrorf("from", "failed to open %q: %s", from, err)
434 return (map[string]blueprint.ModuleFactory)(nil)
435 }
Liz Kammer0fe123d2022-02-07 10:17:35 -0500436 defer r.Close()
Colin Cross9d34f352019-11-22 16:03:51 -0800437
438 mtDef, errs := soongconfig.Parse(r, from)
Colin Cross9d34f352019-11-22 16:03:51 -0800439 if len(errs) > 0 {
440 reportErrors(ctx, from, errs...)
441 return (map[string]blueprint.ModuleFactory)(nil)
442 }
443
444 globalModuleTypes := ctx.moduleFactories()
445
446 factories := make(map[string]blueprint.ModuleFactory)
447
448 for name, moduleType := range mtDef.ModuleTypes {
449 factory := globalModuleTypes[moduleType.BaseModuleType]
450 if factory != nil {
Colin Cross8ff10582023-12-07 13:10:56 -0800451 factories[name] = configModuleFactory(factory, moduleType)
Colin Cross9d34f352019-11-22 16:03:51 -0800452 } else {
453 reportErrors(ctx, from,
454 fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
455 }
456 }
457
458 if ctx.Failed() {
459 return (map[string]blueprint.ModuleFactory)(nil)
460 }
461
462 return factories
463 }).(map[string]blueprint.ModuleFactory)
464}
465
Inseob Kim81b00a82023-05-15 18:06:31 +0900466// tracingConfig is a wrapper to soongconfig.SoongConfig which records all accesses to SoongConfig.
467type tracingConfig struct {
468 config soongconfig.SoongConfig
469 boolSet map[string]bool
470 stringSet map[string]string
471 isSetSet map[string]bool
472}
473
474func (c *tracingConfig) Bool(name string) bool {
475 c.boolSet[name] = c.config.Bool(name)
476 return c.boolSet[name]
477}
478
479func (c *tracingConfig) String(name string) string {
480 c.stringSet[name] = c.config.String(name)
481 return c.stringSet[name]
482}
483
484func (c *tracingConfig) IsSet(name string) bool {
485 c.isSetSet[name] = c.config.IsSet(name)
486 return c.isSetSet[name]
487}
488
489func (c *tracingConfig) getTrace() soongConfigTrace {
490 ret := soongConfigTrace{}
491
492 for k, v := range c.boolSet {
493 ret.Bools = append(ret.Bools, fmt.Sprintf("%q:%t", k, v))
494 }
495 for k, v := range c.stringSet {
496 ret.Strings = append(ret.Strings, fmt.Sprintf("%q:%q", k, v))
497 }
498 for k, v := range c.isSetSet {
499 ret.IsSets = append(ret.IsSets, fmt.Sprintf("%q:%t", k, v))
500 }
501
502 return ret
503}
504
505func newTracingConfig(config soongconfig.SoongConfig) *tracingConfig {
506 c := tracingConfig{
507 config: config,
508 boolSet: make(map[string]bool),
509 stringSet: make(map[string]string),
510 isSetSet: make(map[string]bool),
511 }
512 return &c
513}
514
515var _ soongconfig.SoongConfig = (*tracingConfig)(nil)
516
Jingwen Chena47f28d2021-11-02 16:43:57 +0000517// configModuleFactory takes an existing soongConfigModuleFactory and a
518// ModuleType to create a new ModuleFactory that uses a custom loadhook.
Colin Cross8ff10582023-12-07 13:10:56 -0800519func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType) blueprint.ModuleFactory {
Paul Duffine8b47682023-01-09 15:42:57 +0000520 // Defer creation of conditional properties struct until the first call from the factory
521 // method. That avoids having to make a special call to the factory to create the properties
522 // structs from which the conditional properties struct is created. This is needed in order to
523 // allow singleton modules to be customized by soong_config_module_type as the
524 // SingletonModuleFactoryAdaptor factory registers a load hook for the singleton module
525 // everytime that it is called. Calling the factory twice causes a build failure as the load
526 // hook is called twice, the first time it updates the singleton module to indicate that it has
527 // been registered as a module, and the second time it fails because it thinks it has been
528 // registered again and a singleton module can only be registered once.
529 //
530 // This is an issue for singleton modules because:
531 // * Load hooks are registered on the module object and are only called when the module object
532 // is created by Blueprint while processing the Android.bp file.
533 // * The module factory for a singleton module returns the same module object each time it is
534 // called, and registers its load hook on that same module object.
535 // * When the module factory is called by Blueprint it then calls all the load hooks that have
536 // been registered for every call to that module factory.
537 //
538 // It is not an issue for normal modules because they return a new module object each time the
539 // factory is called and so any load hooks registered on module objects which are discarded will
540 // not be run.
541 once := &sync.Once{}
542 conditionalFactoryProps := reflect.Value{}
543 getConditionalFactoryProps := func(props []interface{}) reflect.Value {
544 once.Do(func() {
545 conditionalFactoryProps = soongconfig.CreateProperties(props, moduleType)
546 })
547 return conditionalFactoryProps
Jingwen Chena47f28d2021-11-02 16:43:57 +0000548 }
Colin Cross9d34f352019-11-22 16:03:51 -0800549
Jingwen Chena47f28d2021-11-02 16:43:57 +0000550 return func() (blueprint.Module, []interface{}) {
551 module, props := factory()
Paul Duffine8b47682023-01-09 15:42:57 +0000552 conditionalFactoryProps := getConditionalFactoryProps(props)
553 if !conditionalFactoryProps.IsValid() {
554 return module, props
555 }
556
Jingwen Chena47f28d2021-11-02 16:43:57 +0000557 conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
558 props = append(props, conditionalProps.Interface())
Colin Cross9d34f352019-11-22 16:03:51 -0800559
Colin Cross8ff10582023-12-07 13:10:56 -0800560 // Regular Soong operation wraps the existing module factory with a
561 // conditional on Soong config variables by reading the product
562 // config variables from Make.
563 AddLoadHook(module, func(ctx LoadHookContext) {
564 tracingConfig := newTracingConfig(ctx.Config().VendorConfig(moduleType.ConfigNamespace))
565 newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, tracingConfig)
566 if err != nil {
567 ctx.ModuleErrorf("%s", err)
568 return
569 }
570 for _, ps := range newProps {
571 ctx.AppendProperties(ps)
572 }
Inseob Kim81b00a82023-05-15 18:06:31 +0900573
Colin Cross8ff10582023-12-07 13:10:56 -0800574 module.(Module).base().commonProperties.SoongConfigTrace = tracingConfig.getTrace()
575 })
Jingwen Chena47f28d2021-11-02 16:43:57 +0000576 return module, props
Colin Cross9d34f352019-11-22 16:03:51 -0800577 }
578}