blob: 6b47cd1f1069c2db5ec18e6bd8a6a036593d0171 [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
Jingwen Chen6c309cd2021-04-01 07:11:11 +000023 var files []BazelFile
Liz Kammer2dd9ca42020-11-25 16:06:39 -080024
Jingwen Chen33832f92021-01-24 22:55:54 -050025 if mode == QueryView {
Jingwen Chen6c309cd2021-04-01 07:11:11 +000026 // Write top level WORKSPACE.
27 files = append(files, newFile("", "WORKSPACE", ""))
28
Jingwen Chen12b4c272021-03-10 02:05:59 -050029 // Used to denote that the top level directory is a package.
30 files = append(files, newFile("", GeneratedBuildFileName, ""))
31
32 files = append(files, newFile(bazelRulesSubDir, GeneratedBuildFileName, ""))
33
Jingwen Chen73850672020-12-14 08:25:34 -050034 // These files are only used for queryview.
35 files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
36
37 for bzlFileName, ruleShim := range ruleShims {
38 files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content))
39 }
40 files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
Liz Kammer2dd9ca42020-11-25 16:06:39 -080041 }
Liz Kammer2dd9ca42020-11-25 16:06:39 -080042
Jingwen Chen33832f92021-01-24 22:55:54 -050043 files = append(files, createBuildFiles(buildToTargets, mode)...)
Liz Kammer2dd9ca42020-11-25 16:06:39 -080044
45 return files
46}
47
Jingwen Chen40067de2021-01-26 21:58:43 -050048func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
Liz Kammer2dd9ca42020-11-25 16:06:39 -080049 files := make([]BazelFile, 0, len(buildToTargets))
50 for _, dir := range android.SortedStringKeys(buildToTargets) {
Liz Kammer2dd9ca42020-11-25 16:06:39 -080051 targets := buildToTargets[dir]
Liz Kammerba3ea162021-02-17 13:22:03 -050052 sort.Slice(targets, func(i, j int) bool {
53 // this will cover all bp2build generated targets
54 if targets[i].name < targets[j].name {
55 return true
56 }
57 // give a strict ordering to content from hand-crafted targets
58 return targets[i].content < targets[j].content
59 })
Jingwen Chen40067de2021-01-26 21:58:43 -050060 content := soongModuleLoad
61 if mode == Bp2Build {
Jingwen Chen1c231732021-02-05 09:38:15 -050062 content = `# This file was automatically generated by bp2build for the Bazel migration project.
63# Feel free to edit or test it, but do *not* check it into your version control system.`
64 content += "\n\n"
65 content += "package(default_visibility = [\"//visibility:public\"])"
66 content += "\n\n"
67 content += targets.LoadStatements()
Liz Kammer2dd9ca42020-11-25 16:06:39 -080068 }
Jingwen Chen40067de2021-01-26 21:58:43 -050069 if content != "" {
70 // If there are load statements, add a couple of newlines.
71 content += "\n\n"
72 }
73 content += targets.String()
Liz Kammerba3ea162021-02-17 13:22:03 -050074 files = append(files, newFile(dir, GeneratedBuildFileName, content))
Liz Kammer2dd9ca42020-11-25 16:06:39 -080075 }
76 return files
77}
78
79func newFile(dir, basename, content string) BazelFile {
80 return BazelFile{
81 Dir: dir,
82 Basename: basename,
83 Contents: content,
84 }
85}
86
87const (
88 bazelRulesSubDir = "build/bazel/queryview_rules"
89
90 // additional files:
91 // * workspace file
92 // * base BUILD file
93 // * rules BUILD file
94 // * rules providers.bzl file
95 // * rules soong_module.bzl file
96 numAdditionalFiles = 5
97)
98
99var (
100 // Certain module property names are blocklisted/ignored here, for the reasons commented.
101 ignoredPropNames = map[string]bool{
102 "name": true, // redundant, since this is explicitly generated for every target
103 "from": true, // reserved keyword
104 "in": true, // reserved keyword
Jingwen Chen88ae4082021-02-24 19:55:50 -0500105 "size": true, // reserved for tests
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800106 "arch": true, // interface prop type is not supported yet.
107 "multilib": true, // interface prop type is not supported yet.
108 "target": true, // interface prop type is not supported yet.
109 "visibility": true, // Bazel has native visibility semantics. Handle later.
110 "features": true, // There is already a built-in attribute 'features' which cannot be overridden.
111 }
112)
113
114func shouldGenerateAttribute(prop string) bool {
115 return !ignoredPropNames[prop]
116}
117
118func shouldSkipStructField(field reflect.StructField) bool {
119 if field.PkgPath != "" {
120 // Skip unexported fields. Some properties are
121 // internal to Soong only, and these fields do not have PkgPath.
122 return true
123 }
124 // fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc
125 // but cannot be set in a .bp file
126 if proptools.HasTag(field, "blueprint", "mutated") {
127 return true
128 }
129 return false
130}
131
132// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
133// testonly = True, forcing other rules that depend on _test rules to also be
134// marked as testonly = True. This semantic constraint is not present in Soong.
135// To work around, rename "*_test" rules to "*_test_".
136func canonicalizeModuleType(moduleName string) string {
137 if strings.HasSuffix(moduleName, "_test") {
138 return moduleName + "_"
139 }
140
141 return moduleName
142}