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