• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1package bp2build
2
3import (
4	"android/soong/starlark_fmt"
5	"encoding/json"
6	"fmt"
7	"reflect"
8	"strconv"
9	"strings"
10
11	"android/soong/android"
12	"android/soong/cc"
13	cc_config "android/soong/cc/config"
14	java_config "android/soong/java/config"
15
16	"android/soong/apex"
17
18	"github.com/google/blueprint/proptools"
19)
20
21type BazelFile struct {
22	Dir      string
23	Basename string
24	Contents string
25}
26
27// PRIVATE: Use CreateSoongInjectionDirFiles instead
28func soongInjectionFiles(cfg android.Config, metrics CodegenMetrics) ([]BazelFile, error) {
29	var files []BazelFile
30
31	files = append(files, newFile("android", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
32	files = append(files, newFile("android", "constants.bzl", android.BazelCcToolchainVars(cfg)))
33
34	files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
35	files = append(files, newFile("cc_toolchain", "config_constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
36	files = append(files, newFile("cc_toolchain", "sanitizer_constants.bzl", cc.BazelCcSanitizerToolchainVars(cfg)))
37
38	files = append(files, newFile("java_toolchain", GeneratedBuildFileName, "")) // Creates a //java_toolchain package.
39	files = append(files, newFile("java_toolchain", "constants.bzl", java_config.BazelJavaToolchainVars(cfg)))
40
41	files = append(files, newFile("apex_toolchain", GeneratedBuildFileName, "")) // Creates a //apex_toolchain package.
42	apexToolchainVars, err := apex.BazelApexToolchainVars()
43	if err != nil {
44		return nil, err
45	}
46	files = append(files, newFile("apex_toolchain", "constants.bzl", apexToolchainVars))
47
48	files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.Serialize().ConvertedModules, "\n")))
49
50	convertedModulePathMap, err := json.MarshalIndent(metrics.convertedModulePathMap, "", "\t")
51	if err != nil {
52		panic(err)
53	}
54	files = append(files, newFile("metrics", GeneratedBuildFileName, "")) // Creates a //metrics package.
55	files = append(files, newFile("metrics", "converted_modules_path_map.json", string(convertedModulePathMap)))
56	files = append(files, newFile("metrics", "converted_modules_path_map.bzl", "modules = "+strings.ReplaceAll(string(convertedModulePathMap), "\\", "\\\\")))
57
58	files = append(files, newFile("product_config", "soong_config_variables.bzl", cfg.Bp2buildSoongConfigDefinitions.String()))
59
60	files = append(files, newFile("product_config", "arch_configuration.bzl", android.StarlarkArchConfigurations()))
61
62	apiLevelsContent, err := json.Marshal(android.GetApiLevelsMap(cfg))
63	if err != nil {
64		return nil, err
65	}
66	files = append(files, newFile("api_levels", GeneratedBuildFileName, `exports_files(["api_levels.json"])`))
67	// TODO(b/269691302)  value of apiLevelsContent is product variable dependent and should be avoided for soong injection
68	files = append(files, newFile("api_levels", "api_levels.json", string(apiLevelsContent)))
69	files = append(files, newFile("api_levels", "api_levels.bzl", android.StarlarkApiLevelConfigs(cfg)))
70	files = append(files, newFile("api_levels", "platform_versions.bzl", platformVersionContents(cfg)))
71
72	files = append(files, newFile("allowlists", GeneratedBuildFileName, ""))
73	files = append(files, newFile("allowlists", "env.bzl", android.EnvironmentVarsFile(cfg)))
74	// TODO(b/262781701): Create an alternate soong_build entrypoint for writing out these files only when requested
75	files = append(files, newFile("allowlists", "mixed_build_prod_allowlist.txt", strings.Join(android.GetBazelEnabledModules(android.BazelProdMode), "\n")+"\n"))
76	files = append(files, newFile("allowlists", "mixed_build_staging_allowlist.txt", strings.Join(android.GetBazelEnabledModules(android.BazelStagingMode), "\n")+"\n"))
77
78	return files, nil
79}
80
81func platformVersionContents(cfg android.Config) string {
82	// Despite these coming from cfg.productVariables, they are actually hardcoded in global
83	// makefiles, not set in individual product config makesfiles, so they're safe to just export
84	// and load() directly.
85
86	platformVersionActiveCodenames := make([]string, 0, len(cfg.PlatformVersionActiveCodenames()))
87	for _, codename := range cfg.PlatformVersionActiveCodenames() {
88		platformVersionActiveCodenames = append(platformVersionActiveCodenames, fmt.Sprintf("%q", codename))
89	}
90
91	platformSdkVersion := "None"
92	if cfg.RawPlatformSdkVersion() != nil {
93		platformSdkVersion = strconv.Itoa(*cfg.RawPlatformSdkVersion())
94	}
95
96	return fmt.Sprintf(`
97platform_versions = struct(
98    platform_sdk_final = %s,
99    platform_sdk_version = %s,
100    platform_sdk_codename = %q,
101    platform_version_active_codenames = [%s],
102)
103`, starlark_fmt.PrintBool(cfg.PlatformSdkFinal()), platformSdkVersion, cfg.PlatformSdkCodename(), strings.Join(platformVersionActiveCodenames, ", "))
104}
105
106func CreateBazelFiles(
107	cfg android.Config,
108	ruleShims map[string]RuleShim,
109	buildToTargets map[string]BazelTargets,
110	mode CodegenMode) []BazelFile {
111
112	var files []BazelFile
113
114	if mode == QueryView {
115		// Write top level WORKSPACE.
116		files = append(files, newFile("", "WORKSPACE", ""))
117
118		// Used to denote that the top level directory is a package.
119		files = append(files, newFile("", GeneratedBuildFileName, ""))
120
121		files = append(files, newFile(bazelRulesSubDir, GeneratedBuildFileName, ""))
122
123		// These files are only used for queryview.
124		files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
125
126		for bzlFileName, ruleShim := range ruleShims {
127			files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content))
128		}
129		files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
130	}
131
132	files = append(files, createBuildFiles(buildToTargets, mode)...)
133
134	return files
135}
136
137func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
138	files := make([]BazelFile, 0, len(buildToTargets))
139	for _, dir := range android.SortedKeys(buildToTargets) {
140		targets := buildToTargets[dir]
141		targets.sort()
142
143		var content string
144		if mode == Bp2Build || mode == ApiBp2build {
145			content = `# READ THIS FIRST:
146# This file was automatically generated by bp2build for the Bazel migration project.
147# Feel free to edit or test it, but do *not* check it into your version control system.
148`
149			content += targets.LoadStatements()
150			content += "\n\n"
151			// Get package rule from the handcrafted BUILD file, otherwise emit the default one.
152			prText := "package(default_visibility = [\"//visibility:public\"])\n"
153			if pr := targets.packageRule(); pr != nil {
154				prText = pr.content
155			}
156			content += prText
157		} else if mode == QueryView {
158			content = soongModuleLoad
159		}
160		if content != "" {
161			// If there are load statements, add a couple of newlines.
162			content += "\n\n"
163		}
164		content += targets.String()
165		files = append(files, newFile(dir, GeneratedBuildFileName, content))
166	}
167	return files
168}
169
170func newFile(dir, basename, content string) BazelFile {
171	return BazelFile{
172		Dir:      dir,
173		Basename: basename,
174		Contents: content,
175	}
176}
177
178const (
179	bazelRulesSubDir = "build/bazel/queryview_rules"
180
181	// additional files:
182	//  * workspace file
183	//  * base BUILD file
184	//  * rules BUILD file
185	//  * rules providers.bzl file
186	//  * rules soong_module.bzl file
187	numAdditionalFiles = 5
188)
189
190var (
191	// Certain module property names are blocklisted/ignored here, for the reasons commented.
192	ignoredPropNames = map[string]bool{
193		"name":               true, // redundant, since this is explicitly generated for every target
194		"from":               true, // reserved keyword
195		"in":                 true, // reserved keyword
196		"size":               true, // reserved for tests
197		"arch":               true, // interface prop type is not supported yet.
198		"multilib":           true, // interface prop type is not supported yet.
199		"target":             true, // interface prop type is not supported yet.
200		"visibility":         true, // Bazel has native visibility semantics. Handle later.
201		"features":           true, // There is already a built-in attribute 'features' which cannot be overridden.
202		"for":                true, // reserved keyword, b/233579439
203		"versions_with_info": true, // TODO(b/245730552) struct properties not fully supported
204	}
205)
206
207func shouldGenerateAttribute(prop string) bool {
208	return !ignoredPropNames[prop]
209}
210
211func shouldSkipStructField(field reflect.StructField) bool {
212	if field.PkgPath != "" && !field.Anonymous {
213		// Skip unexported fields. Some properties are
214		// internal to Soong only, and these fields do not have PkgPath.
215		return true
216	}
217	// fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc.
218	// but cannot be set in a .bp file
219	if proptools.HasTag(field, "blueprint", "mutated") {
220		return true
221	}
222	return false
223}
224
225// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
226// testonly = True, forcing other rules that depend on _test rules to also be
227// marked as testonly = True. This semantic constraint is not present in Soong.
228// To work around, rename "*_test" rules to "*_test_".
229func canonicalizeModuleType(moduleName string) string {
230	if strings.HasSuffix(moduleName, "_test") {
231		return moduleName + "_"
232	}
233
234	return moduleName
235}
236