blob: 6ce609a195ddf0d6e3f588305532b76c84cbe920 [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// },
76// },
77// }
78//
79// cc_library {
80// name: "libacme_foo",
81// defaults: ["acme_defaults"],
82// srcs: ["*.cpp"],
83// }
84//
85// And device/acme/Android.bp could have:
86//
87// soong_config_module_type {
88// name: "acme_cc_defaults",
89// module_type: "cc_defaults",
90// config_namespace: "acme",
Dan Willemsen1934adc2020-03-23 19:39:34 -070091// variables: ["board"],
92// bool_variables: ["feature"],
Colin Cross9d34f352019-11-22 16:03:51 -080093// properties: ["cflags", "srcs"],
94// }
95//
96// soong_config_string_variable {
97// name: "board",
98// values: ["soc_a", "soc_b"],
99// }
100//
Colin Cross9d34f352019-11-22 16:03:51 -0800101// If an acme BoardConfig.mk file contained:
102//
103// SOONG_CONFIG_NAMESPACES += acme
104// SOONG_CONFIG_acme += \
105// board \
106// feature \
107//
108// SOONG_CONFIG_acme_board := soc_a
109// SOONG_CONFIG_acme_feature := true
110//
111// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
112func soongConfigModuleTypeImportFactory() Module {
113 module := &soongConfigModuleTypeImport{}
114
115 module.AddProperties(&module.properties)
116 AddLoadHook(module, func(ctx LoadHookContext) {
117 importModuleTypes(ctx, module.properties.From, module.properties.Module_types...)
118 })
119
120 initAndroidModuleBase(module)
121 return module
122}
123
124func (m *soongConfigModuleTypeImport) Name() string {
Sasha Smundak227d5672020-03-10 16:10:06 -0700125 // The generated name is non-deterministic, but it does not
126 // matter because this module does not emit any rules.
127 return soongconfig.CanonicalizeToProperty(m.properties.From) +
128 "soong_config_module_type_import_" + fmt.Sprintf("%p", m)
Colin Cross9d34f352019-11-22 16:03:51 -0800129}
130
131func (*soongConfigModuleTypeImport) Nameless() {}
132func (*soongConfigModuleTypeImport) GenerateAndroidBuildActions(ModuleContext) {}
133
134// Create dummy modules for soong_config_module_type and soong_config_*_variable
135
136type soongConfigModuleTypeModule struct {
137 ModuleBase
138 properties soongconfig.ModuleTypeProperties
139}
140
141// soong_config_module_type defines module types with conditionals on Soong config
Bill Peckhamc93258b2020-02-04 13:17:24 -0800142// variables. The new module type will exist for all modules after the definition
143// in an Android.bp file, and can be imported into other Android.bp files using
144// soong_config_module_type_import.
Colin Cross9d34f352019-11-22 16:03:51 -0800145//
146// For example, an Android.bp file could have:
147//
148// soong_config_module_type {
149// name: "acme_cc_defaults",
150// module_type: "cc_defaults",
151// config_namespace: "acme",
Dan Willemsen1934adc2020-03-23 19:39:34 -0700152// variables: ["board"],
153// bool_variables: ["feature"],
Colin Cross9d34f352019-11-22 16:03:51 -0800154// properties: ["cflags", "srcs"],
155// }
156//
157// soong_config_string_variable {
158// name: "board",
159// values: ["soc_a", "soc_b"],
160// }
161//
Colin Cross9d34f352019-11-22 16:03:51 -0800162// acme_cc_defaults {
163// name: "acme_defaults",
164// cflags: ["-DGENERIC"],
165// soong_config_variables: {
166// board: {
167// soc_a: {
168// cflags: ["-DSOC_A"],
169// },
170// soc_b: {
171// cflags: ["-DSOC_B"],
172// },
173// },
174// feature: {
175// cflags: ["-DFEATURE"],
176// },
177// },
178// }
179//
180// cc_library {
181// name: "libacme_foo",
182// defaults: ["acme_defaults"],
183// srcs: ["*.cpp"],
184// }
185//
Colin Cross9d34f352019-11-22 16:03:51 -0800186// If an acme BoardConfig.mk file contained:
187//
188// SOONG_CONFIG_NAMESPACES += acme
189// SOONG_CONFIG_acme += \
190// board \
191// feature \
192//
193// SOONG_CONFIG_acme_board := soc_a
194// SOONG_CONFIG_acme_feature := true
195//
196// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
197func soongConfigModuleTypeFactory() Module {
198 module := &soongConfigModuleTypeModule{}
199
200 module.AddProperties(&module.properties)
201
202 AddLoadHook(module, func(ctx LoadHookContext) {
203 // A soong_config_module_type module should implicitly import itself.
204 importModuleTypes(ctx, ctx.BlueprintsFile(), module.properties.Name)
205 })
206
207 initAndroidModuleBase(module)
208
209 return module
210}
211
212func (m *soongConfigModuleTypeModule) Name() string {
213 return m.properties.Name
214}
215func (*soongConfigModuleTypeModule) Nameless() {}
216func (*soongConfigModuleTypeModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
217
218type soongConfigStringVariableDummyModule struct {
219 ModuleBase
220 properties soongconfig.VariableProperties
221 stringProperties soongconfig.StringVariableProperties
222}
223
224type soongConfigBoolVariableDummyModule struct {
225 ModuleBase
226 properties soongconfig.VariableProperties
227}
228
229// soong_config_string_variable defines a variable and a set of possible string values for use
230// in a soong_config_module_type definition.
231func soongConfigStringVariableDummyFactory() Module {
232 module := &soongConfigStringVariableDummyModule{}
233 module.AddProperties(&module.properties, &module.stringProperties)
234 initAndroidModuleBase(module)
235 return module
236}
237
238// soong_config_string_variable defines a variable with true or false values for use
239// in a soong_config_module_type definition.
240func soongConfigBoolVariableDummyFactory() Module {
241 module := &soongConfigBoolVariableDummyModule{}
242 module.AddProperties(&module.properties)
243 initAndroidModuleBase(module)
244 return module
245}
246
247func (m *soongConfigStringVariableDummyModule) Name() string {
248 return m.properties.Name
249}
250func (*soongConfigStringVariableDummyModule) Nameless() {}
251func (*soongConfigStringVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
252
253func (m *soongConfigBoolVariableDummyModule) Name() string {
254 return m.properties.Name
255}
256func (*soongConfigBoolVariableDummyModule) Nameless() {}
257func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
258
259func importModuleTypes(ctx LoadHookContext, from string, moduleTypes ...string) {
260 from = filepath.Clean(from)
261 if filepath.Ext(from) != ".bp" {
262 ctx.PropertyErrorf("from", "%q must be a file with extension .bp", from)
263 return
264 }
265
266 if strings.HasPrefix(from, "../") {
267 ctx.PropertyErrorf("from", "%q must not use ../ to escape the source tree",
268 from)
269 return
270 }
271
272 moduleTypeDefinitions := loadSoongConfigModuleTypeDefinition(ctx, from)
273 if moduleTypeDefinitions == nil {
274 return
275 }
276 for _, moduleType := range moduleTypes {
277 if factory, ok := moduleTypeDefinitions[moduleType]; ok {
278 ctx.registerScopedModuleType(moduleType, factory)
279 } else {
280 ctx.PropertyErrorf("module_types", "module type %q not defined in %q",
281 moduleType, from)
282 }
283 }
284}
285
286// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
287// result so each file is only parsed once.
288func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[string]blueprint.ModuleFactory {
289 type onceKeyType string
290 key := NewCustomOnceKey(onceKeyType(filepath.Clean(from)))
291
292 reportErrors := func(ctx LoadHookContext, filename string, errs ...error) {
293 for _, err := range errs {
294 if parseErr, ok := err.(*parser.ParseError); ok {
295 ctx.Errorf(parseErr.Pos, "%s", parseErr.Err)
296 } else {
297 ctx.Errorf(scanner.Position{Filename: filename}, "%s", err)
298 }
299 }
300 }
301
302 return ctx.Config().Once(key, func() interface{} {
Colin Cross39e545c2020-02-05 16:26:19 -0800303 ctx.AddNinjaFileDeps(from)
Colin Cross9d34f352019-11-22 16:03:51 -0800304 r, err := ctx.Config().fs.Open(from)
305 if err != nil {
306 ctx.PropertyErrorf("from", "failed to open %q: %s", from, err)
307 return (map[string]blueprint.ModuleFactory)(nil)
308 }
309
310 mtDef, errs := soongconfig.Parse(r, from)
311
312 if len(errs) > 0 {
313 reportErrors(ctx, from, errs...)
314 return (map[string]blueprint.ModuleFactory)(nil)
315 }
316
317 globalModuleTypes := ctx.moduleFactories()
318
319 factories := make(map[string]blueprint.ModuleFactory)
320
321 for name, moduleType := range mtDef.ModuleTypes {
322 factory := globalModuleTypes[moduleType.BaseModuleType]
323 if factory != nil {
324 factories[name] = soongConfigModuleFactory(factory, moduleType)
325 } else {
326 reportErrors(ctx, from,
327 fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
328 }
329 }
330
331 if ctx.Failed() {
332 return (map[string]blueprint.ModuleFactory)(nil)
333 }
334
335 return factories
336 }).(map[string]blueprint.ModuleFactory)
337}
338
339// soongConfigModuleFactory takes an existing soongConfigModuleFactory and a ModuleType and returns
340// a new soongConfigModuleFactory that wraps the existing soongConfigModuleFactory and adds conditional on Soong config
341// variables.
342func soongConfigModuleFactory(factory blueprint.ModuleFactory,
343 moduleType *soongconfig.ModuleType) blueprint.ModuleFactory {
344
345 conditionalFactoryProps := soongconfig.CreateProperties(factory, moduleType)
346 if conditionalFactoryProps.IsValid() {
347 return func() (blueprint.Module, []interface{}) {
348 module, props := factory()
349
Colin Cross43e789d2020-01-28 09:46:50 -0800350 conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
Colin Cross9d34f352019-11-22 16:03:51 -0800351 props = append(props, conditionalProps.Interface())
352
353 AddLoadHook(module, func(ctx LoadHookContext) {
354 config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
355 for _, ps := range soongconfig.PropertiesToApply(moduleType, conditionalProps, config) {
356 ctx.AppendProperties(ps)
357 }
358 })
359
360 return module, props
361 }
362 } else {
363 return factory
364 }
365}