blob: 787222d6e4e8af242a07ba9903e449b05eb38226 [file] [log] [blame]
Liz Kammer2dd9ca42020-11-25 16:06:39 -08001package bp2build
2
3import (
4 "android/soong/android"
5 "reflect"
6 "sort"
7 "strings"
8
9 "github.com/google/blueprint/proptools"
10)
11
12type BazelFile struct {
13 Dir string
14 Basename string
15 Contents string
16}
17
18func CreateBazelFiles(
19 ruleShims map[string]RuleShim,
Jingwen Chen40067de2021-01-26 21:58:43 -050020 buildToTargets map[string]BazelTargets,
Jingwen Chen33832f92021-01-24 22:55:54 -050021 mode CodegenMode) []BazelFile {
Liz Kammer2dd9ca42020-11-25 16:06:39 -080022 files := make([]BazelFile, 0, len(ruleShims)+len(buildToTargets)+numAdditionalFiles)
23
Jingwen Chen12b4c272021-03-10 02:05:59 -050024 // Write top level files: WORKSPACE. These files are empty.
Liz Kammer2dd9ca42020-11-25 16:06:39 -080025 files = append(files, newFile("", "WORKSPACE", ""))
Liz Kammer2dd9ca42020-11-25 16:06:39 -080026
Jingwen Chen33832f92021-01-24 22:55:54 -050027 if mode == QueryView {
Jingwen Chen12b4c272021-03-10 02:05:59 -050028 // Used to denote that the top level directory is a package.
29 files = append(files, newFile("", GeneratedBuildFileName, ""))
30
31 files = append(files, newFile(bazelRulesSubDir, GeneratedBuildFileName, ""))
32
Jingwen Chen73850672020-12-14 08:25:34 -050033 // These files are only used for queryview.
34 files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
35
36 for bzlFileName, ruleShim := range ruleShims {
37 files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content))
38 }
39 files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
Liz Kammer2dd9ca42020-11-25 16:06:39 -080040 }
Liz Kammer2dd9ca42020-11-25 16:06:39 -080041
Jingwen Chen33832f92021-01-24 22:55:54 -050042 files = append(files, createBuildFiles(buildToTargets, mode)...)
Liz Kammer2dd9ca42020-11-25 16:06:39 -080043
44 return files
45}
46
Jingwen Chen40067de2021-01-26 21:58:43 -050047func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
Liz Kammer2dd9ca42020-11-25 16:06:39 -080048 files := make([]BazelFile, 0, len(buildToTargets))
49 for _, dir := range android.SortedStringKeys(buildToTargets) {
Liz Kammer2dd9ca42020-11-25 16:06:39 -080050 targets := buildToTargets[dir]
Liz Kammerba3ea162021-02-17 13:22:03 -050051 sort.Slice(targets, func(i, j int) bool {
52 // this will cover all bp2build generated targets
53 if targets[i].name < targets[j].name {
54 return true
55 }
56 // give a strict ordering to content from hand-crafted targets
57 return targets[i].content < targets[j].content
58 })
Jingwen Chen40067de2021-01-26 21:58:43 -050059 content := soongModuleLoad
60 if mode == Bp2Build {
Jingwen Chen1c231732021-02-05 09:38:15 -050061 content = `# This file was automatically generated by bp2build for the Bazel migration project.
62# Feel free to edit or test it, but do *not* check it into your version control system.`
63 content += "\n\n"
64 content += "package(default_visibility = [\"//visibility:public\"])"
65 content += "\n\n"
66 content += targets.LoadStatements()
Liz Kammer2dd9ca42020-11-25 16:06:39 -080067 }
Jingwen Chen40067de2021-01-26 21:58:43 -050068 if content != "" {
69 // If there are load statements, add a couple of newlines.
70 content += "\n\n"
71 }
72 content += targets.String()
Liz Kammerba3ea162021-02-17 13:22:03 -050073 files = append(files, newFile(dir, GeneratedBuildFileName, content))
Liz Kammer2dd9ca42020-11-25 16:06:39 -080074 }
75 return files
76}
77
78func newFile(dir, basename, content string) BazelFile {
79 return BazelFile{
80 Dir: dir,
81 Basename: basename,
82 Contents: content,
83 }
84}
85
86const (
87 bazelRulesSubDir = "build/bazel/queryview_rules"
88
89 // additional files:
90 // * workspace file
91 // * base BUILD file
92 // * rules BUILD file
93 // * rules providers.bzl file
94 // * rules soong_module.bzl file
95 numAdditionalFiles = 5
96)
97
98var (
99 // Certain module property names are blocklisted/ignored here, for the reasons commented.
100 ignoredPropNames = map[string]bool{
101 "name": true, // redundant, since this is explicitly generated for every target
102 "from": true, // reserved keyword
103 "in": true, // reserved keyword
Jingwen Chen88ae4082021-02-24 19:55:50 -0500104 "size": true, // reserved for tests
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800105 "arch": true, // interface prop type is not supported yet.
106 "multilib": true, // interface prop type is not supported yet.
107 "target": true, // interface prop type is not supported yet.
108 "visibility": true, // Bazel has native visibility semantics. Handle later.
109 "features": true, // There is already a built-in attribute 'features' which cannot be overridden.
110 }
111)
112
113func shouldGenerateAttribute(prop string) bool {
114 return !ignoredPropNames[prop]
115}
116
117func shouldSkipStructField(field reflect.StructField) bool {
118 if field.PkgPath != "" {
119 // Skip unexported fields. Some properties are
120 // internal to Soong only, and these fields do not have PkgPath.
121 return true
122 }
123 // fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc
124 // but cannot be set in a .bp file
125 if proptools.HasTag(field, "blueprint", "mutated") {
126 return true
127 }
128 return false
129}
130
131// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
132// testonly = True, forcing other rules that depend on _test rules to also be
133// marked as testonly = True. This semantic constraint is not present in Soong.
134// To work around, rename "*_test" rules to "*_test_".
135func canonicalizeModuleType(moduleName string) string {
136 if strings.HasSuffix(moduleName, "_test") {
137 return moduleName + "_"
138 }
139
140 return moduleName
141}