blob: d67ab3d7746f69b32f594430c6750ac25c2bdf98 [file] [log] [blame]
Liz Kammer2dd9ca42020-11-25 16:06:39 -08001package bp2build
2
3import (
4 "android/soong/android"
Rupert Shuttleworth2a4fc3e2021-04-21 07:10:09 -04005 "fmt"
Liz Kammer2dd9ca42020-11-25 16:06:39 -08006 "reflect"
7 "sort"
8 "strings"
9
10 "github.com/google/blueprint/proptools"
11)
12
13type BazelFile struct {
14 Dir string
15 Basename string
16 Contents string
17}
18
19func CreateBazelFiles(
20 ruleShims map[string]RuleShim,
Jingwen Chen40067de2021-01-26 21:58:43 -050021 buildToTargets map[string]BazelTargets,
Jingwen Chen33832f92021-01-24 22:55:54 -050022 mode CodegenMode) []BazelFile {
Liz Kammer2dd9ca42020-11-25 16:06:39 -080023
Jingwen Chen6c309cd2021-04-01 07:11:11 +000024 var files []BazelFile
Liz Kammer2dd9ca42020-11-25 16:06:39 -080025
Jingwen Chen33832f92021-01-24 22:55:54 -050026 if mode == QueryView {
Jingwen Chen6c309cd2021-04-01 07:11:11 +000027 // Write top level WORKSPACE.
28 files = append(files, newFile("", "WORKSPACE", ""))
29
Jingwen Chen12b4c272021-03-10 02:05:59 -050030 // Used to denote that the top level directory is a package.
31 files = append(files, newFile("", GeneratedBuildFileName, ""))
32
33 files = append(files, newFile(bazelRulesSubDir, GeneratedBuildFileName, ""))
34
Jingwen Chen73850672020-12-14 08:25:34 -050035 // These files are only used for queryview.
36 files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
37
38 for bzlFileName, ruleShim := range ruleShims {
39 files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content))
40 }
41 files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
Liz Kammer2dd9ca42020-11-25 16:06:39 -080042 }
Liz Kammer2dd9ca42020-11-25 16:06:39 -080043
Jingwen Chen33832f92021-01-24 22:55:54 -050044 files = append(files, createBuildFiles(buildToTargets, mode)...)
Liz Kammer2dd9ca42020-11-25 16:06:39 -080045
46 return files
47}
48
Jingwen Chen40067de2021-01-26 21:58:43 -050049func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
Liz Kammer2dd9ca42020-11-25 16:06:39 -080050 files := make([]BazelFile, 0, len(buildToTargets))
51 for _, dir := range android.SortedStringKeys(buildToTargets) {
Liz Kammer78c68652021-04-22 14:40:17 -040052 if mode == Bp2Build && !android.ShouldWriteBuildFileForDir(dir) {
Rupert Shuttleworth2a4fc3e2021-04-21 07:10:09 -040053 fmt.Printf("[bp2build] Not writing generated BUILD file for dir: '%s'\n", dir)
54 continue
55 }
Liz Kammer2dd9ca42020-11-25 16:06:39 -080056 targets := buildToTargets[dir]
Liz Kammerba3ea162021-02-17 13:22:03 -050057 sort.Slice(targets, func(i, j int) bool {
58 // this will cover all bp2build generated targets
59 if targets[i].name < targets[j].name {
60 return true
61 }
62 // give a strict ordering to content from hand-crafted targets
63 return targets[i].content < targets[j].content
64 })
Jingwen Chen40067de2021-01-26 21:58:43 -050065 content := soongModuleLoad
66 if mode == Bp2Build {
Jingwen Chen1c231732021-02-05 09:38:15 -050067 content = `# This file was automatically generated by bp2build for the Bazel migration project.
68# Feel free to edit or test it, but do *not* check it into your version control system.`
69 content += "\n\n"
70 content += "package(default_visibility = [\"//visibility:public\"])"
71 content += "\n\n"
72 content += targets.LoadStatements()
Liz Kammer2dd9ca42020-11-25 16:06:39 -080073 }
Jingwen Chen40067de2021-01-26 21:58:43 -050074 if content != "" {
75 // If there are load statements, add a couple of newlines.
76 content += "\n\n"
77 }
78 content += targets.String()
Liz Kammerba3ea162021-02-17 13:22:03 -050079 files = append(files, newFile(dir, GeneratedBuildFileName, content))
Liz Kammer2dd9ca42020-11-25 16:06:39 -080080 }
81 return files
82}
83
84func newFile(dir, basename, content string) BazelFile {
85 return BazelFile{
86 Dir: dir,
87 Basename: basename,
88 Contents: content,
89 }
90}
91
92const (
93 bazelRulesSubDir = "build/bazel/queryview_rules"
94
95 // additional files:
96 // * workspace file
97 // * base BUILD file
98 // * rules BUILD file
99 // * rules providers.bzl file
100 // * rules soong_module.bzl file
101 numAdditionalFiles = 5
102)
103
104var (
105 // Certain module property names are blocklisted/ignored here, for the reasons commented.
106 ignoredPropNames = map[string]bool{
107 "name": true, // redundant, since this is explicitly generated for every target
108 "from": true, // reserved keyword
109 "in": true, // reserved keyword
Jingwen Chen88ae4082021-02-24 19:55:50 -0500110 "size": true, // reserved for tests
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800111 "arch": true, // interface prop type is not supported yet.
112 "multilib": true, // interface prop type is not supported yet.
113 "target": true, // interface prop type is not supported yet.
114 "visibility": true, // Bazel has native visibility semantics. Handle later.
115 "features": true, // There is already a built-in attribute 'features' which cannot be overridden.
116 }
117)
118
119func shouldGenerateAttribute(prop string) bool {
120 return !ignoredPropNames[prop]
121}
122
123func shouldSkipStructField(field reflect.StructField) bool {
124 if field.PkgPath != "" {
125 // Skip unexported fields. Some properties are
126 // internal to Soong only, and these fields do not have PkgPath.
127 return true
128 }
129 // fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc
130 // but cannot be set in a .bp file
131 if proptools.HasTag(field, "blueprint", "mutated") {
132 return true
133 }
134 return false
135}
136
137// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
138// testonly = True, forcing other rules that depend on _test rules to also be
139// marked as testonly = True. This semantic constraint is not present in Soong.
140// To work around, rename "*_test" rules to "*_test_".
141func canonicalizeModuleType(moduleName string) string {
142 if strings.HasSuffix(moduleName, "_test") {
143 return moduleName + "_"
144 }
145
146 return moduleName
147}