blob: 8126dee1832a865af0a2d76d21bb4dc64baa31c3 [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//
54// For example, an Android.bp file could have:
55//
56// soong_config_module_type_import {
Bill Peckhamc93258b2020-02-04 13:17:24 -080057// from: "device/acme/Android.bp",
Colin Cross9d34f352019-11-22 16:03:51 -080058// module_types: ["acme_cc_defaults"],
59// }
60//
61// acme_cc_defaults {
62// name: "acme_defaults",
63// cflags: ["-DGENERIC"],
64// soong_config_variables: {
65// board: {
66// soc_a: {
67// cflags: ["-DSOC_A"],
68// },
69// soc_b: {
70// cflags: ["-DSOC_B"],
71// },
72// },
73// feature: {
74// cflags: ["-DFEATURE"],
75// },
Dan Willemsenb0935db2020-03-23 19:42:18 -070076// width: {
77// cflags: ["-DWIDTH=%s"],
78// },
Colin Cross9d34f352019-11-22 16:03:51 -080079// },
80// }
81//
82// cc_library {
83// name: "libacme_foo",
84// defaults: ["acme_defaults"],
85// srcs: ["*.cpp"],
86// }
87//
88// And device/acme/Android.bp could have:
89//
90// soong_config_module_type {
91// name: "acme_cc_defaults",
92// module_type: "cc_defaults",
93// config_namespace: "acme",
Dan Willemsen2b8b89c2020-03-23 19:39:34 -070094// variables: ["board"],
95// bool_variables: ["feature"],
Dan Willemsenb0935db2020-03-23 19:42:18 -070096// value_variables: ["width"],
Colin Cross9d34f352019-11-22 16:03:51 -080097// properties: ["cflags", "srcs"],
98// }
99//
100// soong_config_string_variable {
101// name: "board",
102// values: ["soc_a", "soc_b"],
103// }
104//
Colin Cross9d34f352019-11-22 16:03:51 -0800105// If an acme BoardConfig.mk file contained:
106//
107// SOONG_CONFIG_NAMESPACES += acme
108// SOONG_CONFIG_acme += \
109// board \
110// feature \
111//
112// SOONG_CONFIG_acme_board := soc_a
113// SOONG_CONFIG_acme_feature := true
Dan Willemsenb0935db2020-03-23 19:42:18 -0700114// SOONG_CONFIG_acme_width := 200
Colin Cross9d34f352019-11-22 16:03:51 -0800115//
Dan Willemsenb0935db2020-03-23 19:42:18 -0700116// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
Colin Cross9d34f352019-11-22 16:03:51 -0800117func soongConfigModuleTypeImportFactory() Module {
118 module := &soongConfigModuleTypeImport{}
119
120 module.AddProperties(&module.properties)
121 AddLoadHook(module, func(ctx LoadHookContext) {
122 importModuleTypes(ctx, module.properties.From, module.properties.Module_types...)
123 })
124
125 initAndroidModuleBase(module)
126 return module
127}
128
129func (m *soongConfigModuleTypeImport) Name() string {
130 return "soong_config_module_type_import_" + soongconfig.CanonicalizeToProperty(m.properties.From)
131}
132
133func (*soongConfigModuleTypeImport) Nameless() {}
134func (*soongConfigModuleTypeImport) GenerateAndroidBuildActions(ModuleContext) {}
135
136// Create dummy modules for soong_config_module_type and soong_config_*_variable
137
138type soongConfigModuleTypeModule struct {
139 ModuleBase
140 properties soongconfig.ModuleTypeProperties
141}
142
143// soong_config_module_type defines module types with conditionals on Soong config
Bill Peckhamc93258b2020-02-04 13:17:24 -0800144// variables. The new module type will exist for all modules after the definition
145// in an Android.bp file, and can be imported into other Android.bp files using
146// soong_config_module_type_import.
Colin Cross9d34f352019-11-22 16:03:51 -0800147//
148// For example, an Android.bp file could have:
149//
150// soong_config_module_type {
151// name: "acme_cc_defaults",
152// module_type: "cc_defaults",
153// config_namespace: "acme",
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700154// variables: ["board"],
155// bool_variables: ["feature"],
Dan Willemsenb0935db2020-03-23 19:42:18 -0700156// value_variables: ["width"],
Colin Cross9d34f352019-11-22 16:03:51 -0800157// properties: ["cflags", "srcs"],
158// }
159//
160// soong_config_string_variable {
161// name: "board",
162// values: ["soc_a", "soc_b"],
163// }
164//
Colin Cross9d34f352019-11-22 16:03:51 -0800165// acme_cc_defaults {
166// name: "acme_defaults",
167// cflags: ["-DGENERIC"],
168// soong_config_variables: {
169// board: {
170// soc_a: {
171// cflags: ["-DSOC_A"],
172// },
173// soc_b: {
174// cflags: ["-DSOC_B"],
175// },
176// },
177// feature: {
178// cflags: ["-DFEATURE"],
179// },
Dan Willemsenb0935db2020-03-23 19:42:18 -0700180// width: {
181// cflags: ["-DWIDTH=%s"],
182// },
Colin Cross9d34f352019-11-22 16:03:51 -0800183// },
184// }
185//
186// cc_library {
187// name: "libacme_foo",
188// defaults: ["acme_defaults"],
189// srcs: ["*.cpp"],
190// }
191//
Colin Cross9d34f352019-11-22 16:03:51 -0800192// If an acme BoardConfig.mk file contained:
193//
194// SOONG_CONFIG_NAMESPACES += acme
195// SOONG_CONFIG_acme += \
196// board \
197// feature \
198//
199// SOONG_CONFIG_acme_board := soc_a
200// SOONG_CONFIG_acme_feature := true
Dan Willemsenb0935db2020-03-23 19:42:18 -0700201// SOONG_CONFIG_acme_width := 200
Colin Cross9d34f352019-11-22 16:03:51 -0800202//
203// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
204func soongConfigModuleTypeFactory() Module {
205 module := &soongConfigModuleTypeModule{}
206
207 module.AddProperties(&module.properties)
208
209 AddLoadHook(module, func(ctx LoadHookContext) {
210 // A soong_config_module_type module should implicitly import itself.
211 importModuleTypes(ctx, ctx.BlueprintsFile(), module.properties.Name)
212 })
213
214 initAndroidModuleBase(module)
215
216 return module
217}
218
219func (m *soongConfigModuleTypeModule) Name() string {
220 return m.properties.Name
221}
222func (*soongConfigModuleTypeModule) Nameless() {}
223func (*soongConfigModuleTypeModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
224
225type soongConfigStringVariableDummyModule struct {
226 ModuleBase
227 properties soongconfig.VariableProperties
228 stringProperties soongconfig.StringVariableProperties
229}
230
231type soongConfigBoolVariableDummyModule struct {
232 ModuleBase
233 properties soongconfig.VariableProperties
234}
235
236// soong_config_string_variable defines a variable and a set of possible string values for use
237// in a soong_config_module_type definition.
238func soongConfigStringVariableDummyFactory() Module {
239 module := &soongConfigStringVariableDummyModule{}
240 module.AddProperties(&module.properties, &module.stringProperties)
241 initAndroidModuleBase(module)
242 return module
243}
244
245// soong_config_string_variable defines a variable with true or false values for use
246// in a soong_config_module_type definition.
247func soongConfigBoolVariableDummyFactory() Module {
248 module := &soongConfigBoolVariableDummyModule{}
249 module.AddProperties(&module.properties)
250 initAndroidModuleBase(module)
251 return module
252}
253
254func (m *soongConfigStringVariableDummyModule) Name() string {
255 return m.properties.Name
256}
257func (*soongConfigStringVariableDummyModule) Nameless() {}
258func (*soongConfigStringVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
259
260func (m *soongConfigBoolVariableDummyModule) Name() string {
261 return m.properties.Name
262}
263func (*soongConfigBoolVariableDummyModule) Nameless() {}
264func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
265
266func importModuleTypes(ctx LoadHookContext, from string, moduleTypes ...string) {
267 from = filepath.Clean(from)
268 if filepath.Ext(from) != ".bp" {
269 ctx.PropertyErrorf("from", "%q must be a file with extension .bp", from)
270 return
271 }
272
273 if strings.HasPrefix(from, "../") {
274 ctx.PropertyErrorf("from", "%q must not use ../ to escape the source tree",
275 from)
276 return
277 }
278
279 moduleTypeDefinitions := loadSoongConfigModuleTypeDefinition(ctx, from)
280 if moduleTypeDefinitions == nil {
281 return
282 }
283 for _, moduleType := range moduleTypes {
284 if factory, ok := moduleTypeDefinitions[moduleType]; ok {
285 ctx.registerScopedModuleType(moduleType, factory)
286 } else {
287 ctx.PropertyErrorf("module_types", "module type %q not defined in %q",
288 moduleType, from)
289 }
290 }
291}
292
293// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
294// result so each file is only parsed once.
295func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[string]blueprint.ModuleFactory {
296 type onceKeyType string
297 key := NewCustomOnceKey(onceKeyType(filepath.Clean(from)))
298
299 reportErrors := func(ctx LoadHookContext, filename string, errs ...error) {
300 for _, err := range errs {
301 if parseErr, ok := err.(*parser.ParseError); ok {
302 ctx.Errorf(parseErr.Pos, "%s", parseErr.Err)
303 } else {
304 ctx.Errorf(scanner.Position{Filename: filename}, "%s", err)
305 }
306 }
307 }
308
309 return ctx.Config().Once(key, func() interface{} {
Colin Cross39e545c2020-02-05 16:26:19 -0800310 ctx.AddNinjaFileDeps(from)
Colin Cross9d34f352019-11-22 16:03:51 -0800311 r, err := ctx.Config().fs.Open(from)
312 if err != nil {
313 ctx.PropertyErrorf("from", "failed to open %q: %s", from, err)
314 return (map[string]blueprint.ModuleFactory)(nil)
315 }
316
317 mtDef, errs := soongconfig.Parse(r, from)
318
319 if len(errs) > 0 {
320 reportErrors(ctx, from, errs...)
321 return (map[string]blueprint.ModuleFactory)(nil)
322 }
323
324 globalModuleTypes := ctx.moduleFactories()
325
326 factories := make(map[string]blueprint.ModuleFactory)
327
328 for name, moduleType := range mtDef.ModuleTypes {
329 factory := globalModuleTypes[moduleType.BaseModuleType]
330 if factory != nil {
331 factories[name] = soongConfigModuleFactory(factory, moduleType)
332 } else {
333 reportErrors(ctx, from,
334 fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
335 }
336 }
337
338 if ctx.Failed() {
339 return (map[string]blueprint.ModuleFactory)(nil)
340 }
341
342 return factories
343 }).(map[string]blueprint.ModuleFactory)
344}
345
346// soongConfigModuleFactory takes an existing soongConfigModuleFactory and a ModuleType and returns
347// a new soongConfigModuleFactory that wraps the existing soongConfigModuleFactory and adds conditional on Soong config
348// variables.
349func soongConfigModuleFactory(factory blueprint.ModuleFactory,
350 moduleType *soongconfig.ModuleType) blueprint.ModuleFactory {
351
352 conditionalFactoryProps := soongconfig.CreateProperties(factory, moduleType)
353 if conditionalFactoryProps.IsValid() {
354 return func() (blueprint.Module, []interface{}) {
355 module, props := factory()
356
Colin Cross43e789d2020-01-28 09:46:50 -0800357 conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
Colin Cross9d34f352019-11-22 16:03:51 -0800358 props = append(props, conditionalProps.Interface())
359
360 AddLoadHook(module, func(ctx LoadHookContext) {
361 config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700362 newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
363 if err != nil {
364 ctx.ModuleErrorf("%s", err)
365 return
366 }
367 for _, ps := range newProps {
Colin Cross9d34f352019-11-22 16:03:51 -0800368 ctx.AppendProperties(ps)
369 }
370 })
371
372 return module, props
373 }
374 } else {
375 return factory
376 }
377}