blob: 21e9c095bd7c95deadf7f614447148a752af9f8a [file] [log] [blame]
Liz Kammer2dd9ca42020-11-25 16:06:39 -08001package bp2build
2
3import (
Cole Fausteb644cf2023-04-11 13:48:17 -07004 "android/soong/starlark_fmt"
Jingwen Chen0ee88a62022-01-07 14:55:29 +00005 "encoding/json"
Cole Fausteb644cf2023-04-11 13:48:17 -07006 "fmt"
Liz Kammer2dd9ca42020-11-25 16:06:39 -08007 "reflect"
Liz Kammer2dd9ca42020-11-25 16:06:39 -08008 "strings"
9
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux447f6c92021-08-31 20:30:36 +000010 "android/soong/android"
Trevor Radcliffe4f95ee92023-01-19 16:02:47 +000011 "android/soong/cc"
Sam Delmerico932c01c2022-03-25 16:33:26 +000012 cc_config "android/soong/cc/config"
13 java_config "android/soong/java/config"
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux447f6c92021-08-31 20:30:36 +000014
Jingwen Chen7810e172022-07-29 02:25:34 +000015 "android/soong/apex"
16
Liz Kammer2dd9ca42020-11-25 16:06:39 -080017 "github.com/google/blueprint/proptools"
18)
19
20type BazelFile struct {
21 Dir string
22 Basename string
23 Contents string
24}
25
Spandan Das83e787e2023-01-11 02:50:00 +000026// PRIVATE: Use CreateSoongInjectionDirFiles instead
Cole Faust9e384e22023-02-08 17:43:09 -080027func soongInjectionFiles(cfg android.Config, metrics CodegenMetrics) ([]BazelFile, error) {
Jingwen Chenbf61afb2021-05-06 13:31:18 +000028 var files []BazelFile
29
Sam Delmerico46d08b42022-11-15 15:51:04 -050030 files = append(files, newFile("android", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
31 files = append(files, newFile("android", "constants.bzl", android.BazelCcToolchainVars(cfg)))
32
Jingwen Chenc63677b2021-06-17 05:43:19 +000033 files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
Trevor Radcliffe4f95ee92023-01-19 16:02:47 +000034 files = append(files, newFile("cc_toolchain", "config_constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
35 files = append(files, newFile("cc_toolchain", "sanitizer_constants.bzl", cc.BazelCcSanitizerToolchainVars(cfg)))
Sam Delmerico932c01c2022-03-25 16:33:26 +000036
37 files = append(files, newFile("java_toolchain", GeneratedBuildFileName, "")) // Creates a //java_toolchain package.
38 files = append(files, newFile("java_toolchain", "constants.bzl", java_config.BazelJavaToolchainVars(cfg)))
Jingwen Chenbf61afb2021-05-06 13:31:18 +000039
Jingwen Chen7810e172022-07-29 02:25:34 +000040 files = append(files, newFile("apex_toolchain", GeneratedBuildFileName, "")) // Creates a //apex_toolchain package.
Cole Faust9e384e22023-02-08 17:43:09 -080041 apexToolchainVars, err := apex.BazelApexToolchainVars()
42 if err != nil {
43 return nil, err
44 }
45 files = append(files, newFile("apex_toolchain", "constants.bzl", apexToolchainVars))
Jingwen Chen7810e172022-07-29 02:25:34 +000046
usta4f5d2c12022-10-28 23:32:01 -040047 files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.Serialize().ConvertedModules, "\n")))
Jingwen Chenc63677b2021-06-17 05:43:19 +000048
Kevin Dagostino60f562a2022-09-20 03:54:47 +000049 convertedModulePathMap, err := json.MarshalIndent(metrics.convertedModulePathMap, "", "\t")
50 if err != nil {
51 panic(err)
52 }
53 files = append(files, newFile("metrics", "converted_modules_path_map.json", string(convertedModulePathMap)))
54
Jingwen Chen01812022021-11-19 14:29:43 +000055 files = append(files, newFile("product_config", "soong_config_variables.bzl", cfg.Bp2buildSoongConfigDefinitions.String()))
56
Liz Kammere8303bd2022-02-16 09:02:48 -050057 files = append(files, newFile("product_config", "arch_configuration.bzl", android.StarlarkArchConfigurations()))
58
Jingwen Chen0ee88a62022-01-07 14:55:29 +000059 apiLevelsContent, err := json.Marshal(android.GetApiLevelsMap(cfg))
60 if err != nil {
Cole Faust9e384e22023-02-08 17:43:09 -080061 return nil, err
Jingwen Chen0ee88a62022-01-07 14:55:29 +000062 }
63 files = append(files, newFile("api_levels", GeneratedBuildFileName, `exports_files(["api_levels.json"])`))
Alix Espino4fd7e742023-02-24 14:46:43 +000064 // TODO(b/269691302) value of apiLevelsContent is product variable dependent and should be avoided for soong injection
Jingwen Chen0ee88a62022-01-07 14:55:29 +000065 files = append(files, newFile("api_levels", "api_levels.json", string(apiLevelsContent)))
Yu Liufc603162022-03-01 15:44:08 -080066 files = append(files, newFile("api_levels", "api_levels.bzl", android.StarlarkApiLevelConfigs(cfg)))
Cole Fausteb644cf2023-04-11 13:48:17 -070067 files = append(files, newFile("api_levels", "platform_versions.bzl", platformVersionContents(cfg)))
Jingwen Chen0ee88a62022-01-07 14:55:29 +000068
Sam Delmericocb3c52c2023-02-03 17:40:08 -050069 files = append(files, newFile("allowlists", GeneratedBuildFileName, ""))
70 files = append(files, newFile("allowlists", "env.bzl", android.EnvironmentVarsFile(cfg)))
Cole Faust705968d2022-12-14 11:32:05 -080071 // TODO(b/262781701): Create an alternate soong_build entrypoint for writing out these files only when requested
72 files = append(files, newFile("allowlists", "mixed_build_prod_allowlist.txt", strings.Join(android.GetBazelEnabledModules(android.BazelProdMode), "\n")+"\n"))
73 files = append(files, newFile("allowlists", "mixed_build_staging_allowlist.txt", strings.Join(android.GetBazelEnabledModules(android.BazelStagingMode), "\n")+"\n"))
74
Cole Faust9e384e22023-02-08 17:43:09 -080075 return files, nil
Jingwen Chenbf61afb2021-05-06 13:31:18 +000076}
77
Cole Fausteb644cf2023-04-11 13:48:17 -070078func platformVersionContents(cfg android.Config) string {
79 // Despite these coming from cfg.productVariables, they are actually hardcoded in global
80 // makefiles, not set in individual product config makesfiles, so they're safe to just export
81 // and load() directly.
82
83 platformVersionActiveCodenames := make([]string, 0, len(cfg.PlatformVersionActiveCodenames()))
84 for _, codename := range cfg.PlatformVersionActiveCodenames() {
85 platformVersionActiveCodenames = append(platformVersionActiveCodenames, fmt.Sprintf("%q", codename))
86 }
87
88 return fmt.Sprintf(`
89platform_versions = struct(
90 platform_sdk_final = %s,
91 platform_sdk_version = %d,
92 platform_sdk_codename = %q,
93 platform_version_active_codenames = [%s],
94)
95`, starlark_fmt.PrintBool(cfg.PlatformSdkFinal()), cfg.PlatformSdkVersion().FinalInt(), cfg.PlatformSdkCodename(), strings.Join(platformVersionActiveCodenames, ", "))
96}
97
Liz Kammer2dd9ca42020-11-25 16:06:39 -080098func CreateBazelFiles(
Sasha Smundak0fd93e02022-05-19 19:34:31 -070099 cfg android.Config,
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800100 ruleShims map[string]RuleShim,
Jingwen Chen40067de2021-01-26 21:58:43 -0500101 buildToTargets map[string]BazelTargets,
Jingwen Chen33832f92021-01-24 22:55:54 -0500102 mode CodegenMode) []BazelFile {
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800103
Jingwen Chen6c309cd2021-04-01 07:11:11 +0000104 var files []BazelFile
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800105
Jingwen Chen33832f92021-01-24 22:55:54 -0500106 if mode == QueryView {
Jingwen Chen6c309cd2021-04-01 07:11:11 +0000107 // Write top level WORKSPACE.
108 files = append(files, newFile("", "WORKSPACE", ""))
109
Jingwen Chen12b4c272021-03-10 02:05:59 -0500110 // Used to denote that the top level directory is a package.
111 files = append(files, newFile("", GeneratedBuildFileName, ""))
112
113 files = append(files, newFile(bazelRulesSubDir, GeneratedBuildFileName, ""))
114
Jingwen Chen73850672020-12-14 08:25:34 -0500115 // These files are only used for queryview.
116 files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
117
118 for bzlFileName, ruleShim := range ruleShims {
119 files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content))
120 }
121 files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800122 }
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800123
Cole Faust324a92e2022-08-23 15:29:05 -0700124 files = append(files, createBuildFiles(buildToTargets, mode)...)
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800125
126 return files
127}
128
Cole Faust324a92e2022-08-23 15:29:05 -0700129func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800130 files := make([]BazelFile, 0, len(buildToTargets))
Cole Faust18994c72023-02-28 16:02:16 -0800131 for _, dir := range android.SortedKeys(buildToTargets) {
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800132 targets := buildToTargets[dir]
Jingwen Chen49109762021-05-25 05:16:48 +0000133 targets.sort()
134
135 var content string
Spandan Das5af0bd32022-09-28 20:43:08 +0000136 if mode == Bp2Build || mode == ApiBp2build {
Jingwen Chen49109762021-05-25 05:16:48 +0000137 content = `# READ THIS FIRST:
138# This file was automatically generated by bp2build for the Bazel migration project.
139# Feel free to edit or test it, but do *not* check it into your version control system.
140`
Jingwen Chen1c231732021-02-05 09:38:15 -0500141 content += targets.LoadStatements()
Sasha Smundak8bea2672022-08-04 13:31:14 -0700142 content += "\n\n"
143 // Get package rule from the handcrafted BUILD file, otherwise emit the default one.
144 prText := "package(default_visibility = [\"//visibility:public\"])\n"
145 if pr := targets.packageRule(); pr != nil {
146 prText = pr.content
147 }
148 content += prText
Jingwen Chen49109762021-05-25 05:16:48 +0000149 } else if mode == QueryView {
150 content = soongModuleLoad
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800151 }
Jingwen Chen40067de2021-01-26 21:58:43 -0500152 if content != "" {
153 // If there are load statements, add a couple of newlines.
154 content += "\n\n"
155 }
156 content += targets.String()
Liz Kammerba3ea162021-02-17 13:22:03 -0500157 files = append(files, newFile(dir, GeneratedBuildFileName, content))
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800158 }
159 return files
160}
161
162func newFile(dir, basename, content string) BazelFile {
163 return BazelFile{
164 Dir: dir,
165 Basename: basename,
166 Contents: content,
167 }
168}
169
170const (
171 bazelRulesSubDir = "build/bazel/queryview_rules"
172
173 // additional files:
174 // * workspace file
175 // * base BUILD file
176 // * rules BUILD file
177 // * rules providers.bzl file
178 // * rules soong_module.bzl file
179 numAdditionalFiles = 5
180)
181
182var (
183 // Certain module property names are blocklisted/ignored here, for the reasons commented.
184 ignoredPropNames = map[string]bool{
Sam Delmerico263efde2022-09-08 10:43:42 -0400185 "name": true, // redundant, since this is explicitly generated for every target
186 "from": true, // reserved keyword
187 "in": true, // reserved keyword
188 "size": true, // reserved for tests
189 "arch": true, // interface prop type is not supported yet.
190 "multilib": true, // interface prop type is not supported yet.
191 "target": true, // interface prop type is not supported yet.
192 "visibility": true, // Bazel has native visibility semantics. Handle later.
193 "features": true, // There is already a built-in attribute 'features' which cannot be overridden.
194 "for": true, // reserved keyword, b/233579439
195 "versions_with_info": true, // TODO(b/245730552) struct properties not fully supported
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800196 }
197)
198
199func shouldGenerateAttribute(prop string) bool {
200 return !ignoredPropNames[prop]
201}
202
203func shouldSkipStructField(field reflect.StructField) bool {
Liz Kammer7a210ac2021-09-22 15:52:58 -0400204 if field.PkgPath != "" && !field.Anonymous {
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800205 // Skip unexported fields. Some properties are
206 // internal to Soong only, and these fields do not have PkgPath.
207 return true
208 }
Sasha Smundak8bea2672022-08-04 13:31:14 -0700209 // fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc.
Liz Kammer2dd9ca42020-11-25 16:06:39 -0800210 // but cannot be set in a .bp file
211 if proptools.HasTag(field, "blueprint", "mutated") {
212 return true
213 }
214 return false
215}
216
217// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
218// testonly = True, forcing other rules that depend on _test rules to also be
219// marked as testonly = True. This semantic constraint is not present in Soong.
220// To work around, rename "*_test" rules to "*_test_".
221func canonicalizeModuleType(moduleName string) string {
222 if strings.HasSuffix(moduleName, "_test") {
223 return moduleName + "_"
224 }
225
226 return moduleName
227}