blob: 289e91088255b578faefe622a20cdaed8f264ef6 [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//
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:
125//
126// SOONG_CONFIG_NAMESPACES += acme
127// SOONG_CONFIG_acme += \
128// board \
129// feature \
130//
131// SOONG_CONFIG_acme_board := soc_a
132// SOONG_CONFIG_acme_feature := true
Dan Willemsenb0935db2020-03-23 19:42:18 -0700133// SOONG_CONFIG_acme_width := 200
Colin Cross9d34f352019-11-22 16:03:51 -0800134//
Dan Willemsenb0935db2020-03-23 19:42:18 -0700135// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
Liz Kammer432bd592020-12-16 12:42:02 -0800136//
137// Alternatively, if acme BoardConfig.mk file contained:
138//
139// SOONG_CONFIG_NAMESPACES += acme
140// SOONG_CONFIG_acme += \
141// board \
142// feature \
143//
144// SOONG_CONFIG_acme_feature := false
145//
146// Then libacme_foo would build with cflags:
147// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
148//
149// Similarly, if acme BoardConfig.mk file contained:
150//
151// SOONG_CONFIG_NAMESPACES += acme
152// SOONG_CONFIG_acme += \
153// board \
154// feature \
155//
156// SOONG_CONFIG_acme_board := soc_c
157//
158// Then libacme_foo would build with cflags:
159// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
160
Colin Cross9d34f352019-11-22 16:03:51 -0800161func soongConfigModuleTypeImportFactory() Module {
162 module := &soongConfigModuleTypeImport{}
163
164 module.AddProperties(&module.properties)
165 AddLoadHook(module, func(ctx LoadHookContext) {
166 importModuleTypes(ctx, module.properties.From, module.properties.Module_types...)
167 })
168
169 initAndroidModuleBase(module)
170 return module
171}
172
173func (m *soongConfigModuleTypeImport) Name() string {
Sasha Smundak116ec922020-03-10 16:10:06 -0700174 // The generated name is non-deterministic, but it does not
175 // matter because this module does not emit any rules.
176 return soongconfig.CanonicalizeToProperty(m.properties.From) +
177 "soong_config_module_type_import_" + fmt.Sprintf("%p", m)
Colin Cross9d34f352019-11-22 16:03:51 -0800178}
179
180func (*soongConfigModuleTypeImport) Nameless() {}
181func (*soongConfigModuleTypeImport) GenerateAndroidBuildActions(ModuleContext) {}
182
183// Create dummy modules for soong_config_module_type and soong_config_*_variable
184
185type soongConfigModuleTypeModule struct {
186 ModuleBase
187 properties soongconfig.ModuleTypeProperties
188}
189
190// soong_config_module_type defines module types with conditionals on Soong config
Bill Peckhamc93258b2020-02-04 13:17:24 -0800191// variables. The new module type will exist for all modules after the definition
192// in an Android.bp file, and can be imported into other Android.bp files using
193// soong_config_module_type_import.
Colin Cross9d34f352019-11-22 16:03:51 -0800194//
Liz Kammer432bd592020-12-16 12:42:02 -0800195// Each soong_config_variable supports an additional value `conditions_default`. The properties
196// specified in `conditions_default` will only be used under the following conditions:
197// bool variable: the variable is unspecified or not set to a true value
198// value variable: the variable is unspecified
199// string variable: the variable is unspecified or the variable is set to a string unused in the
200// given module. For example, string variable `test` takes values: "a" and "b",
201// if the module contains a property `a` and `conditions_default`, when test=b,
202// the properties under `conditions_default` will be used. To specify that no
203// properties should be amended for `b`, you can set `b: {},`.
204//
Colin Cross9d34f352019-11-22 16:03:51 -0800205// For example, an Android.bp file could have:
206//
207// soong_config_module_type {
208// name: "acme_cc_defaults",
209// module_type: "cc_defaults",
210// config_namespace: "acme",
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700211// variables: ["board"],
212// bool_variables: ["feature"],
Dan Willemsenb0935db2020-03-23 19:42:18 -0700213// value_variables: ["width"],
Colin Cross9d34f352019-11-22 16:03:51 -0800214// properties: ["cflags", "srcs"],
215// }
216//
217// soong_config_string_variable {
218// name: "board",
219// values: ["soc_a", "soc_b"],
220// }
221//
Colin Cross9d34f352019-11-22 16:03:51 -0800222// acme_cc_defaults {
223// name: "acme_defaults",
224// cflags: ["-DGENERIC"],
225// soong_config_variables: {
226// board: {
227// soc_a: {
228// cflags: ["-DSOC_A"],
229// },
230// soc_b: {
231// cflags: ["-DSOC_B"],
232// },
Liz Kammer432bd592020-12-16 12:42:02 -0800233// conditions_default: {
234// cflags: ["-DSOC_DEFAULT"],
235// },
Colin Cross9d34f352019-11-22 16:03:51 -0800236// },
237// feature: {
238// cflags: ["-DFEATURE"],
Liz Kammer432bd592020-12-16 12:42:02 -0800239// conditions_default: {
240// cflags: ["-DFEATURE_DEFAULT"],
241// },
Colin Cross9d34f352019-11-22 16:03:51 -0800242// },
Dan Willemsenb0935db2020-03-23 19:42:18 -0700243// width: {
244// cflags: ["-DWIDTH=%s"],
Liz Kammer432bd592020-12-16 12:42:02 -0800245// conditions_default: {
246// cflags: ["-DWIDTH=DEFAULT"],
247// },
Dan Willemsenb0935db2020-03-23 19:42:18 -0700248// },
Colin Cross9d34f352019-11-22 16:03:51 -0800249// },
250// }
251//
252// cc_library {
253// name: "libacme_foo",
254// defaults: ["acme_defaults"],
255// srcs: ["*.cpp"],
256// }
257//
Colin Cross9d34f352019-11-22 16:03:51 -0800258// If an acme BoardConfig.mk file contained:
259//
260// SOONG_CONFIG_NAMESPACES += acme
261// SOONG_CONFIG_acme += \
262// board \
263// feature \
264//
265// SOONG_CONFIG_acme_board := soc_a
266// SOONG_CONFIG_acme_feature := true
Dan Willemsenb0935db2020-03-23 19:42:18 -0700267// SOONG_CONFIG_acme_width := 200
Colin Cross9d34f352019-11-22 16:03:51 -0800268//
269// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
270func soongConfigModuleTypeFactory() Module {
271 module := &soongConfigModuleTypeModule{}
272
273 module.AddProperties(&module.properties)
274
275 AddLoadHook(module, func(ctx LoadHookContext) {
276 // A soong_config_module_type module should implicitly import itself.
277 importModuleTypes(ctx, ctx.BlueprintsFile(), module.properties.Name)
278 })
279
280 initAndroidModuleBase(module)
281
282 return module
283}
284
285func (m *soongConfigModuleTypeModule) Name() string {
286 return m.properties.Name
287}
288func (*soongConfigModuleTypeModule) Nameless() {}
289func (*soongConfigModuleTypeModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
290
291type soongConfigStringVariableDummyModule struct {
292 ModuleBase
293 properties soongconfig.VariableProperties
294 stringProperties soongconfig.StringVariableProperties
295}
296
297type soongConfigBoolVariableDummyModule struct {
298 ModuleBase
299 properties soongconfig.VariableProperties
300}
301
302// soong_config_string_variable defines a variable and a set of possible string values for use
303// in a soong_config_module_type definition.
304func soongConfigStringVariableDummyFactory() Module {
305 module := &soongConfigStringVariableDummyModule{}
306 module.AddProperties(&module.properties, &module.stringProperties)
307 initAndroidModuleBase(module)
308 return module
309}
310
311// soong_config_string_variable defines a variable with true or false values for use
312// in a soong_config_module_type definition.
313func soongConfigBoolVariableDummyFactory() Module {
314 module := &soongConfigBoolVariableDummyModule{}
315 module.AddProperties(&module.properties)
316 initAndroidModuleBase(module)
317 return module
318}
319
320func (m *soongConfigStringVariableDummyModule) Name() string {
321 return m.properties.Name
322}
323func (*soongConfigStringVariableDummyModule) Nameless() {}
324func (*soongConfigStringVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
325
326func (m *soongConfigBoolVariableDummyModule) Name() string {
327 return m.properties.Name
328}
329func (*soongConfigBoolVariableDummyModule) Nameless() {}
330func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
331
332func 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 }
382
383 mtDef, errs := soongconfig.Parse(r, from)
384
385 if len(errs) > 0 {
386 reportErrors(ctx, from, errs...)
387 return (map[string]blueprint.ModuleFactory)(nil)
388 }
389
390 globalModuleTypes := ctx.moduleFactories()
391
392 factories := make(map[string]blueprint.ModuleFactory)
393
394 for name, moduleType := range mtDef.ModuleTypes {
395 factory := globalModuleTypes[moduleType.BaseModuleType]
396 if factory != nil {
397 factories[name] = soongConfigModuleFactory(factory, moduleType)
398 } else {
399 reportErrors(ctx, from,
400 fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
401 }
402 }
403
404 if ctx.Failed() {
405 return (map[string]blueprint.ModuleFactory)(nil)
406 }
407
408 return factories
409 }).(map[string]blueprint.ModuleFactory)
410}
411
412// soongConfigModuleFactory takes an existing soongConfigModuleFactory and a ModuleType and returns
413// a new soongConfigModuleFactory that wraps the existing soongConfigModuleFactory and adds conditional on Soong config
414// variables.
415func soongConfigModuleFactory(factory blueprint.ModuleFactory,
416 moduleType *soongconfig.ModuleType) blueprint.ModuleFactory {
417
418 conditionalFactoryProps := soongconfig.CreateProperties(factory, moduleType)
419 if conditionalFactoryProps.IsValid() {
420 return func() (blueprint.Module, []interface{}) {
421 module, props := factory()
422
Colin Cross43e789d2020-01-28 09:46:50 -0800423 conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
Colin Cross9d34f352019-11-22 16:03:51 -0800424 props = append(props, conditionalProps.Interface())
425
426 AddLoadHook(module, func(ctx LoadHookContext) {
427 config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700428 newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
429 if err != nil {
430 ctx.ModuleErrorf("%s", err)
431 return
432 }
433 for _, ps := range newProps {
Colin Cross9d34f352019-11-22 16:03:51 -0800434 ctx.AppendProperties(ps)
435 }
436 })
437
438 return module, props
439 }
440 } else {
441 return factory
442 }
443}