blob: 72e0fbdf5a9c43dd9c4969cf8d9357476e570ddf [file] [log] [blame]
Jingwen Chen5ba7e472020-07-15 10:06:41 +00001// Copyright 2020 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 main
16
17import (
18 "android/soong/android"
19 "fmt"
20 "io/ioutil"
21 "os"
22 "path/filepath"
Jingwen Chen69d4cbe2020-08-07 14:16:34 +000023 "reflect"
Jingwen Chen5ba7e472020-07-15 10:06:41 +000024 "strings"
25
26 "github.com/google/blueprint"
Jingwen Chend8004ef2020-08-27 09:40:43 +000027 "github.com/google/blueprint/bootstrap/bpdoc"
Jingwen Chen69d4cbe2020-08-07 14:16:34 +000028 "github.com/google/blueprint/proptools"
Jingwen Chen5ba7e472020-07-15 10:06:41 +000029)
30
31const (
Jingwen Chend8004ef2020-08-27 09:40:43 +000032 // The default `load` preamble for every generated BUILD file.
Jingwen Chen5ba7e472020-07-15 10:06:41 +000033 soongModuleLoad = `package(default_visibility = ["//visibility:public"])
34load("//:soong_module.bzl", "soong_module")
Jingwen Chen69d4cbe2020-08-07 14:16:34 +000035
Jingwen Chen5ba7e472020-07-15 10:06:41 +000036`
37
Jingwen Chend8004ef2020-08-27 09:40:43 +000038 // A macro call in the BUILD file representing a Soong module, with space
39 // for expanding more attributes.
Jingwen Chen5ba7e472020-07-15 10:06:41 +000040 soongModuleTarget = `soong_module(
41 name = "%s",
42 module_name = "%s",
43 module_type = "%s",
44 module_variant = "%s",
Jingwen Chence3d46f2020-08-25 05:34:43 +000045 module_deps = %s,
Jingwen Chen69d4cbe2020-08-07 14:16:34 +000046%s)`
Jingwen Chen5ba7e472020-07-15 10:06:41 +000047
Jingwen Chend8004ef2020-08-27 09:40:43 +000048 // A simple provider to mark and differentiate Soong module rule shims from
49 // regular Bazel rules. Every Soong module rule shim returns a
50 // SoongModuleInfo provider, and can only depend on rules returning
51 // SoongModuleInfo in the `module_deps` attribute.
52 providersBzl = `SoongModuleInfo = provider(
Jingwen Chen5ba7e472020-07-15 10:06:41 +000053 fields = {
54 "name": "Name of module",
55 "type": "Type of module",
56 "variant": "Variant of module",
57 },
58)
Jingwen Chend8004ef2020-08-27 09:40:43 +000059`
Jingwen Chen5ba7e472020-07-15 10:06:41 +000060
Jingwen Chend8004ef2020-08-27 09:40:43 +000061 // The soong_module rule implementation in a .bzl file.
62 soongModuleBzl = `
63%s
Jingwen Chen69d4cbe2020-08-07 14:16:34 +000064
Jingwen Chend8004ef2020-08-27 09:40:43 +000065load(":providers.bzl", "SoongModuleInfo")
Jingwen Chen69d4cbe2020-08-07 14:16:34 +000066
67def _generic_soong_module_impl(ctx):
Jingwen Chen5ba7e472020-07-15 10:06:41 +000068 return [
69 SoongModuleInfo(
70 name = ctx.attr.module_name,
71 type = ctx.attr.module_type,
72 variant = ctx.attr.module_variant,
73 ),
74 ]
75
Jingwen Chen69d4cbe2020-08-07 14:16:34 +000076generic_soong_module = rule(
77 implementation = _generic_soong_module_impl,
Jingwen Chend8004ef2020-08-27 09:40:43 +000078 attrs = {
79 "module_name": attr.string(mandatory = True),
80 "module_type": attr.string(mandatory = True),
81 "module_variant": attr.string(),
82 "module_deps": attr.label_list(providers = [SoongModuleInfo]),
83 },
Jingwen Chen69d4cbe2020-08-07 14:16:34 +000084)
85
86soong_module_rule_map = {
Jingwen Chend8004ef2020-08-27 09:40:43 +000087%s}
88
89_SUPPORTED_TYPES = ["bool", "int", "string"]
90
91def _is_supported_type(value):
92 if type(value) in _SUPPORTED_TYPES:
93 return True
94 elif type(value) == "list":
95 supported = True
96 for v in value:
97 supported = supported and type(v) in _SUPPORTED_TYPES
98 return supported
99 else:
100 return False
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000101
102# soong_module is a macro that supports arbitrary kwargs, and uses module_type to
103# expand to the right underlying shim.
104def soong_module(name, module_type, **kwargs):
105 soong_module_rule = soong_module_rule_map.get(module_type)
106
107 if soong_module_rule == None:
108 # This module type does not have an existing rule to map to, so use the
109 # generic_soong_module rule instead.
110 generic_soong_module(
111 name = name,
112 module_type = module_type,
113 module_name = kwargs.pop("module_name", ""),
114 module_variant = kwargs.pop("module_variant", ""),
Jingwen Chence3d46f2020-08-25 05:34:43 +0000115 module_deps = kwargs.pop("module_deps", []),
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000116 )
117 else:
Jingwen Chend8004ef2020-08-27 09:40:43 +0000118 supported_kwargs = dict()
119 for key, value in kwargs.items():
120 if _is_supported_type(value):
121 supported_kwargs[key] = value
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000122 soong_module_rule(
123 name = name,
Jingwen Chend8004ef2020-08-27 09:40:43 +0000124 **supported_kwargs,
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000125 )
Jingwen Chen5ba7e472020-07-15 10:06:41 +0000126`
Jingwen Chend8004ef2020-08-27 09:40:43 +0000127
128 // A rule shim for representing a Soong module type and its properties.
129 moduleRuleShim = `
130def _%[1]s_impl(ctx):
131 return [SoongModuleInfo()]
132
133%[1]s = rule(
134 implementation = _%[1]s_impl,
135 attrs = %[2]s
136)
137`
138)
139
140var (
141 // An allowlist of prop types that are surfaced from module props to rule
142 // attributes. (nested) dictionaries are notably absent here, because while
143 // Soong supports multi value typed and nested dictionaries, Bazel's rule
144 // attr() API supports only single-level string_dicts.
145 allowedPropTypes = map[string]bool{
146 "int": true, // e.g. 42
147 "bool": true, // e.g. True
148 "string_list": true, // e.g. ["a", "b"]
149 "string": true, // e.g. "a"
150 }
151
152 // TODO(b/166563303): Specific properties of some module types aren't
153 // recognized by the documentation generator. As a workaround, hardcode a
154 // mapping of the module type to prop name to prop type here, and ultimately
155 // fix the documentation generator to also parse these properties correctly.
156 additionalPropTypes = map[string]map[string]string{
157 // sdk and module_exports props are created at runtime using reflection.
158 // bpdocs isn't wired up to read runtime generated structs.
159 "sdk": {
160 "java_header_libs": "string_list",
161 "java_sdk_libs": "string_list",
162 "java_system_modules": "string_list",
163 "native_header_libs": "string_list",
164 "native_libs": "string_list",
165 "native_objects": "string_list",
166 "native_shared_libs": "string_list",
167 "native_static_libs": "string_list",
168 },
169 "module_exports": {
170 "java_libs": "string_list",
171 "java_tests": "string_list",
172 "native_binaries": "string_list",
173 "native_shared_libs": "string_list",
174 },
175 }
176
177 // Certain module property names are blocklisted/ignored here, for the reasons commented.
178 ignoredPropNames = map[string]bool{
179 "name": true, // redundant, since this is explicitly generated for every target
180 "from": true, // reserved keyword
181 "in": true, // reserved keyword
182 "arch": true, // interface prop type is not supported yet.
183 "multilib": true, // interface prop type is not supported yet.
184 "target": true, // interface prop type is not supported yet.
185 "visibility": true, // Bazel has native visibility semantics. Handle later.
186 "features": true, // There is already a built-in attribute 'features' which cannot be overridden.
187 }
Jingwen Chen5ba7e472020-07-15 10:06:41 +0000188)
189
190func targetNameWithVariant(c *blueprint.Context, logicModule blueprint.Module) string {
191 name := ""
192 if c.ModuleSubDir(logicModule) != "" {
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000193 // TODO(b/162720883): Figure out a way to drop the "--" variant suffixes.
Jingwen Chen5ba7e472020-07-15 10:06:41 +0000194 name = c.ModuleName(logicModule) + "--" + c.ModuleSubDir(logicModule)
195 } else {
196 name = c.ModuleName(logicModule)
197 }
198
199 return strings.Replace(name, "//", "", 1)
200}
201
202func qualifiedTargetLabel(c *blueprint.Context, logicModule blueprint.Module) string {
203 return "//" +
204 packagePath(c, logicModule) +
205 ":" +
206 targetNameWithVariant(c, logicModule)
207}
208
209func packagePath(c *blueprint.Context, logicModule blueprint.Module) string {
210 return filepath.Dir(c.BlueprintFile(logicModule))
211}
212
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000213func escapeString(s string) string {
214 s = strings.ReplaceAll(s, "\\", "\\\\")
215 return strings.ReplaceAll(s, "\"", "\\\"")
216}
217
218func makeIndent(indent int) string {
219 if indent < 0 {
220 panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
221 }
222 return strings.Repeat(" ", indent)
223}
224
225// prettyPrint a property value into the equivalent Starlark representation
226// recursively.
227func prettyPrint(propertyValue reflect.Value, indent int) (string, error) {
228 if isZero(propertyValue) {
229 // A property value being set or unset actually matters -- Soong does set default
230 // values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
231 // https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
232 //
233 // In Bazel-parlance, we would use "attr.<type>(default = <default value>)" to set the default
234 // value of unset attributes.
235 return "", nil
236 }
237
238 var ret string
239 switch propertyValue.Kind() {
240 case reflect.String:
241 ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
242 case reflect.Bool:
243 ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
244 case reflect.Int, reflect.Uint, reflect.Int64:
245 ret = fmt.Sprintf("%v", propertyValue.Interface())
246 case reflect.Ptr:
247 return prettyPrint(propertyValue.Elem(), indent)
248 case reflect.Slice:
249 ret = "[\n"
250 for i := 0; i < propertyValue.Len(); i++ {
251 indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
252 if err != nil {
253 return "", err
254 }
255
256 if indexedValue != "" {
257 ret += makeIndent(indent + 1)
258 ret += indexedValue
259 ret += ",\n"
260 }
261 }
262 ret += makeIndent(indent)
263 ret += "]"
264 case reflect.Struct:
265 ret = "{\n"
266 // Sort and print the struct props by the key.
267 structProps := extractStructProperties(propertyValue, indent)
268 for _, k := range android.SortedStringKeys(structProps) {
269 ret += makeIndent(indent + 1)
Jingwen Chend8004ef2020-08-27 09:40:43 +0000270 ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000271 }
272 ret += makeIndent(indent)
273 ret += "}"
274 case reflect.Interface:
275 // TODO(b/164227191): implement pretty print for interfaces.
276 // Interfaces are used for for arch, multilib and target properties.
277 return "", nil
278 default:
279 return "", fmt.Errorf(
280 "unexpected kind for property struct field: %s", propertyValue.Kind())
281 }
282 return ret, nil
283}
284
Jingwen Chend8004ef2020-08-27 09:40:43 +0000285// Converts a reflected property struct value into a map of property names and property values,
286// which each property value correctly pretty-printed and indented at the right nest level,
287// since property structs can be nested. In Starlark, nested structs are represented as nested
288// dicts: https://docs.bazel.build/skylark/lib/dict.html
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000289func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
290 if structValue.Kind() != reflect.Struct {
291 panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
292 }
293
294 ret := map[string]string{}
295 structType := structValue.Type()
296 for i := 0; i < structValue.NumField(); i++ {
297 field := structType.Field(i)
298 if field.PkgPath != "" {
299 // Skip unexported fields. Some properties are
300 // internal to Soong only, and these fields do not have PkgPath.
301 continue
302 }
303 if proptools.HasTag(field, "blueprint", "mutated") {
304 continue
305 }
306
307 fieldValue := structValue.Field(i)
308 if isZero(fieldValue) {
309 // Ignore zero-valued fields
310 continue
311 }
312
313 propertyName := proptools.PropertyNameForField(field.Name)
314 prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
315 if err != nil {
316 panic(
317 fmt.Errorf(
318 "Error while parsing property: %q. %s",
319 propertyName,
320 err))
321 }
322 if prettyPrintedValue != "" {
323 ret[propertyName] = prettyPrintedValue
324 }
325 }
326
327 return ret
328}
329
330func isStructPtr(t reflect.Type) bool {
331 return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
332}
333
334// Generically extract module properties and types into a map, keyed by the module property name.
335func extractModuleProperties(aModule android.Module) map[string]string {
336 ret := map[string]string{}
337
338 // Iterate over this android.Module's property structs.
339 for _, properties := range aModule.GetProperties() {
340 propertiesValue := reflect.ValueOf(properties)
341 // Check that propertiesValue is a pointer to the Properties struct, like
342 // *cc.BaseLinkerProperties or *java.CompilerProperties.
343 //
344 // propertiesValue can also be type-asserted to the structs to
345 // manipulate internal props, if needed.
346 if isStructPtr(propertiesValue.Type()) {
347 structValue := propertiesValue.Elem()
348 for k, v := range extractStructProperties(structValue, 0) {
349 ret[k] = v
350 }
351 } else {
352 panic(fmt.Errorf(
353 "properties must be a pointer to a struct, got %T",
354 propertiesValue.Interface()))
355 }
356
357 }
358
359 return ret
360}
361
Jingwen Chend8004ef2020-08-27 09:40:43 +0000362// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
363// testonly = True, forcing other rules that depend on _test rules to also be
364// marked as testonly = True. This semantic constraint is not present in Soong.
365// To work around, rename "*_test" rules to "*_test_".
366func canonicalizeModuleType(moduleName string) string {
367 if strings.HasSuffix(moduleName, "_test") {
368 return moduleName + "_"
369 }
370
371 return moduleName
372}
373
374type RuleShim struct {
375 // The rule class shims contained in a bzl file. e.g. ["cc_object", "cc_library", ..]
376 rules []string
377
378 // The generated string content of the bzl file.
379 content string
380}
381
382// Create <module>.bzl containing Bazel rule shims for every module type available in Soong and
383// user-specified Go plugins.
384//
385// This function reuses documentation generation APIs to ensure parity between modules-as-docs
386// and modules-as-code, including the names and types of module properties.
387func createRuleShims(packages []*bpdoc.Package) (map[string]RuleShim, error) {
388 var propToAttr func(prop bpdoc.Property, propName string) string
389 propToAttr = func(prop bpdoc.Property, propName string) string {
390 // dots are not allowed in Starlark attribute names. Substitute them with double underscores.
391 propName = strings.ReplaceAll(propName, ".", "__")
392 if !shouldGenerateAttribute(propName) {
393 return ""
394 }
395
396 // Canonicalize and normalize module property types to Bazel attribute types
397 starlarkAttrType := prop.Type
398 if starlarkAttrType == "list of strings" {
399 starlarkAttrType = "string_list"
400 } else if starlarkAttrType == "int64" {
401 starlarkAttrType = "int"
402 } else if starlarkAttrType == "" {
403 var attr string
404 for _, nestedProp := range prop.Properties {
405 nestedAttr := propToAttr(nestedProp, propName+"__"+nestedProp.Name)
406 if nestedAttr != "" {
407 // TODO(b/167662930): Fix nested props resulting in too many attributes.
408 // Let's still generate these, but comment them out.
409 attr += "# " + nestedAttr
410 }
411 }
412 return attr
413 }
414
415 if !allowedPropTypes[starlarkAttrType] {
416 return ""
417 }
418
419 return fmt.Sprintf(" %q: attr.%s(),\n", propName, starlarkAttrType)
420 }
421
422 ruleShims := map[string]RuleShim{}
423 for _, pkg := range packages {
424 content := "load(\":providers.bzl\", \"SoongModuleInfo\")\n"
425
426 bzlFileName := strings.ReplaceAll(pkg.Path, "android/soong/", "")
427 bzlFileName = strings.ReplaceAll(bzlFileName, ".", "_")
428 bzlFileName = strings.ReplaceAll(bzlFileName, "/", "_")
429
430 rules := []string{}
431
432 for _, moduleTypeTemplate := range moduleTypeDocsToTemplates(pkg.ModuleTypes) {
433 attrs := `{
434 "module_name": attr.string(mandatory = True),
435 "module_variant": attr.string(),
436 "module_deps": attr.label_list(providers = [SoongModuleInfo]),
437`
438 for _, prop := range moduleTypeTemplate.Properties {
439 attrs += propToAttr(prop, prop.Name)
440 }
441
442 for propName, propType := range additionalPropTypes[moduleTypeTemplate.Name] {
443 attrs += fmt.Sprintf(" %q: attr.%s(),\n", propName, propType)
444 }
445
446 attrs += " },"
447
448 rule := canonicalizeModuleType(moduleTypeTemplate.Name)
449 content += fmt.Sprintf(moduleRuleShim, rule, attrs)
450 rules = append(rules, rule)
451 }
452
453 ruleShims[bzlFileName] = RuleShim{content: content, rules: rules}
454 }
455 return ruleShims, nil
456}
457
Jingwen Chen5ba7e472020-07-15 10:06:41 +0000458func createBazelOverlay(ctx *android.Context, bazelOverlayDir string) error {
459 blueprintCtx := ctx.Context
460 blueprintCtx.VisitAllModules(func(module blueprint.Module) {
461 buildFile, err := buildFileForModule(blueprintCtx, module)
462 if err != nil {
463 panic(err)
464 }
465
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000466 buildFile.Write([]byte(generateSoongModuleTarget(blueprintCtx, module) + "\n\n"))
Jingwen Chen5ba7e472020-07-15 10:06:41 +0000467 buildFile.Close()
468 })
469
470 if err := writeReadOnlyFile(bazelOverlayDir, "WORKSPACE", ""); err != nil {
471 return err
472 }
473
474 if err := writeReadOnlyFile(bazelOverlayDir, "BUILD", ""); err != nil {
475 return err
476 }
477
Jingwen Chend8004ef2020-08-27 09:40:43 +0000478 if err := writeReadOnlyFile(bazelOverlayDir, "providers.bzl", providersBzl); err != nil {
479 return err
480 }
481
482 packages, err := getPackages(ctx)
483 if err != nil {
484 return err
485 }
486 ruleShims, err := createRuleShims(packages)
487 if err != nil {
488 return err
489 }
490
491 for bzlFileName, ruleShim := range ruleShims {
492 if err := writeReadOnlyFile(bazelOverlayDir, bzlFileName+".bzl", ruleShim.content); err != nil {
493 return err
494 }
495 }
496
497 return writeReadOnlyFile(bazelOverlayDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims))
Jingwen Chen5ba7e472020-07-15 10:06:41 +0000498}
499
Jingwen Chend8004ef2020-08-27 09:40:43 +0000500// Generate the content of soong_module.bzl with the rule shim load statements
501// and mapping of module_type to rule shim map for every module type in Soong.
502func generateSoongModuleBzl(bzlLoads map[string]RuleShim) string {
503 var loadStmts string
504 var moduleRuleMap string
505 for bzlFileName, ruleShim := range bzlLoads {
506 loadStmt := "load(\"//:"
507 loadStmt += bzlFileName
508 loadStmt += ".bzl\""
509 for _, rule := range ruleShim.rules {
510 loadStmt += fmt.Sprintf(", %q", rule)
511 moduleRuleMap += " \"" + rule + "\": " + rule + ",\n"
512 }
513 loadStmt += ")\n"
514 loadStmts += loadStmt
515 }
516
517 return fmt.Sprintf(soongModuleBzl, loadStmts, moduleRuleMap)
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000518}
519
520func shouldGenerateAttribute(prop string) bool {
Jingwen Chend8004ef2020-08-27 09:40:43 +0000521 return !ignoredPropNames[prop]
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000522}
523
524// props is an unsorted map. This function ensures that
525// the generated attributes are sorted to ensure determinism.
526func propsToAttributes(props map[string]string) string {
527 var attributes string
528 for _, propName := range android.SortedStringKeys(props) {
529 if shouldGenerateAttribute(propName) {
530 attributes += fmt.Sprintf(" %s = %s,\n", propName, props[propName])
531 }
532 }
533 return attributes
534}
535
536// Convert a module and its deps and props into a Bazel macro/rule
537// representation in the BUILD file.
538func generateSoongModuleTarget(
539 blueprintCtx *blueprint.Context,
540 module blueprint.Module) string {
541
542 var props map[string]string
543 if aModule, ok := module.(android.Module); ok {
544 props = extractModuleProperties(aModule)
545 }
546 attributes := propsToAttributes(props)
547
548 // TODO(b/163018919): DirectDeps can have duplicate (module, variant)
549 // items, if the modules are added using different DependencyTag. Figure
550 // out the implications of that.
551 depLabels := map[string]bool{}
552 blueprintCtx.VisitDirectDeps(module, func(depModule blueprint.Module) {
553 depLabels[qualifiedTargetLabel(blueprintCtx, depModule)] = true
554 })
555
556 depLabelList := "[\n"
557 for depLabel, _ := range depLabels {
Jingwen Chend8004ef2020-08-27 09:40:43 +0000558 depLabelList += fmt.Sprintf(" %q,\n", depLabel)
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000559 }
560 depLabelList += " ]"
561
562 return fmt.Sprintf(
563 soongModuleTarget,
564 targetNameWithVariant(blueprintCtx, module),
565 blueprintCtx.ModuleName(module),
Jingwen Chend8004ef2020-08-27 09:40:43 +0000566 canonicalizeModuleType(blueprintCtx.ModuleType(module)),
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000567 blueprintCtx.ModuleSubDir(module),
568 depLabelList,
569 attributes)
570}
571
Jingwen Chen5ba7e472020-07-15 10:06:41 +0000572func buildFileForModule(ctx *blueprint.Context, module blueprint.Module) (*os.File, error) {
573 // Create nested directories for the BUILD file
574 dirPath := filepath.Join(bazelOverlayDir, packagePath(ctx, module))
575 if _, err := os.Stat(dirPath); os.IsNotExist(err) {
576 os.MkdirAll(dirPath, os.ModePerm)
577 }
578 // Open the file for appending, and create it if it doesn't exist
579 f, err := os.OpenFile(
580 filepath.Join(dirPath, "BUILD.bazel"),
581 os.O_APPEND|os.O_CREATE|os.O_WRONLY,
582 0644)
583 if err != nil {
584 return nil, err
585 }
586
587 // If the file is empty, add the load statement for the `soong_module` rule
588 fi, err := f.Stat()
589 if err != nil {
590 return nil, err
591 }
592 if fi.Size() == 0 {
593 f.Write([]byte(soongModuleLoad + "\n"))
594 }
595
596 return f, nil
597}
598
Jingwen Chend8004ef2020-08-27 09:40:43 +0000599// The overlay directory should be read-only, sufficient for bazel query. The files
600// are not intended to be edited by end users.
Jingwen Chen5ba7e472020-07-15 10:06:41 +0000601func writeReadOnlyFile(dir string, baseName string, content string) error {
Jingwen Chend8004ef2020-08-27 09:40:43 +0000602 pathToFile := filepath.Join(bazelOverlayDir, baseName)
Jingwen Chen5ba7e472020-07-15 10:06:41 +0000603 // 0444 is read-only
Jingwen Chend8004ef2020-08-27 09:40:43 +0000604 return ioutil.WriteFile(pathToFile, []byte(content), 0444)
Jingwen Chen5ba7e472020-07-15 10:06:41 +0000605}
Jingwen Chen69d4cbe2020-08-07 14:16:34 +0000606
607func isZero(value reflect.Value) bool {
608 switch value.Kind() {
609 case reflect.Func, reflect.Map, reflect.Slice:
610 return value.IsNil()
611 case reflect.Array:
612 valueIsZero := true
613 for i := 0; i < value.Len(); i++ {
614 valueIsZero = valueIsZero && isZero(value.Index(i))
615 }
616 return valueIsZero
617 case reflect.Struct:
618 valueIsZero := true
619 for i := 0; i < value.NumField(); i++ {
620 if value.Field(i).CanSet() {
621 valueIsZero = valueIsZero && isZero(value.Field(i))
622 }
623 }
624 return valueIsZero
625 case reflect.Ptr:
626 if !value.IsNil() {
627 return isZero(reflect.Indirect(value))
628 } else {
629 return true
630 }
631 default:
632 zeroValue := reflect.Zero(value.Type())
633 result := value.Interface() == zeroValue.Interface()
634 return result
635 }
636}