• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1package bp2build
2
3import (
4	"encoding/json"
5	"fmt"
6	"reflect"
7	"strings"
8
9	"android/soong/android"
10	cc_config "android/soong/cc/config"
11	java_config "android/soong/java/config"
12
13	"github.com/google/blueprint/proptools"
14)
15
16type BazelFile struct {
17	Dir      string
18	Basename string
19	Contents string
20}
21
22func CreateSoongInjectionFiles(cfg android.Config, metrics CodegenMetrics) []BazelFile {
23	var files []BazelFile
24
25	files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
26	files = append(files, newFile("cc_toolchain", "constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
27
28	files = append(files, newFile("java_toolchain", GeneratedBuildFileName, "")) // Creates a //java_toolchain package.
29	files = append(files, newFile("java_toolchain", "constants.bzl", java_config.BazelJavaToolchainVars(cfg)))
30
31	files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n")))
32
33	files = append(files, newFile("product_config", "soong_config_variables.bzl", cfg.Bp2buildSoongConfigDefinitions.String()))
34
35	files = append(files, newFile("product_config", "arch_configuration.bzl", android.StarlarkArchConfigurations()))
36
37	apiLevelsContent, err := json.Marshal(android.GetApiLevelsMap(cfg))
38	if err != nil {
39		panic(err)
40	}
41	files = append(files, newFile("api_levels", GeneratedBuildFileName, `exports_files(["api_levels.json"])`))
42	files = append(files, newFile("api_levels", "api_levels.json", string(apiLevelsContent)))
43	files = append(files, newFile("api_levels", "api_levels.bzl", android.StarlarkApiLevelConfigs(cfg)))
44
45	return files
46}
47
48func convertedModules(convertedModules []string) string {
49	return strings.Join(convertedModules, "\n")
50}
51
52func CreateBazelFiles(
53	ruleShims map[string]RuleShim,
54	buildToTargets map[string]BazelTargets,
55	mode CodegenMode) []BazelFile {
56
57	var files []BazelFile
58
59	if mode == QueryView {
60		// Write top level WORKSPACE.
61		files = append(files, newFile("", "WORKSPACE", ""))
62
63		// Used to denote that the top level directory is a package.
64		files = append(files, newFile("", GeneratedBuildFileName, ""))
65
66		files = append(files, newFile(bazelRulesSubDir, GeneratedBuildFileName, ""))
67
68		// These files are only used for queryview.
69		files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
70
71		for bzlFileName, ruleShim := range ruleShims {
72			files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content))
73		}
74		files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
75	}
76
77	files = append(files, createBuildFiles(buildToTargets, mode)...)
78
79	return files
80}
81
82func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
83	files := make([]BazelFile, 0, len(buildToTargets))
84	for _, dir := range android.SortedStringKeys(buildToTargets) {
85		if mode == Bp2Build && android.ShouldKeepExistingBuildFileForDir(dir) {
86			fmt.Printf("[bp2build] Not writing generated BUILD file for dir: '%s'\n", dir)
87			continue
88		}
89		targets := buildToTargets[dir]
90		targets.sort()
91
92		var content string
93		if mode == Bp2Build {
94			content = `# READ THIS FIRST:
95# This file was automatically generated by bp2build for the Bazel migration project.
96# Feel free to edit or test it, but do *not* check it into your version control system.
97`
98			if targets.hasHandcraftedTargets() {
99				// For BUILD files with both handcrafted and generated targets,
100				// don't hardcode actual content, like package() declarations.
101				// Leave that responsibility to the checked-in BUILD file
102				// instead.
103				content += `# This file contains generated targets and handcrafted targets that are manually managed in the source tree.`
104			} else {
105				// For fully-generated BUILD files, hardcode the default visibility.
106				content += "package(default_visibility = [\"//visibility:public\"])"
107			}
108			content += "\n"
109			content += targets.LoadStatements()
110		} else if mode == QueryView {
111			content = soongModuleLoad
112		}
113		if content != "" {
114			// If there are load statements, add a couple of newlines.
115			content += "\n\n"
116		}
117		content += targets.String()
118		files = append(files, newFile(dir, GeneratedBuildFileName, content))
119	}
120	return files
121}
122
123func newFile(dir, basename, content string) BazelFile {
124	return BazelFile{
125		Dir:      dir,
126		Basename: basename,
127		Contents: content,
128	}
129}
130
131const (
132	bazelRulesSubDir = "build/bazel/queryview_rules"
133
134	// additional files:
135	//  * workspace file
136	//  * base BUILD file
137	//  * rules BUILD file
138	//  * rules providers.bzl file
139	//  * rules soong_module.bzl file
140	numAdditionalFiles = 5
141)
142
143var (
144	// Certain module property names are blocklisted/ignored here, for the reasons commented.
145	ignoredPropNames = map[string]bool{
146		"name":       true, // redundant, since this is explicitly generated for every target
147		"from":       true, // reserved keyword
148		"in":         true, // reserved keyword
149		"size":       true, // reserved for tests
150		"arch":       true, // interface prop type is not supported yet.
151		"multilib":   true, // interface prop type is not supported yet.
152		"target":     true, // interface prop type is not supported yet.
153		"visibility": true, // Bazel has native visibility semantics. Handle later.
154		"features":   true, // There is already a built-in attribute 'features' which cannot be overridden.
155	}
156)
157
158func shouldGenerateAttribute(prop string) bool {
159	return !ignoredPropNames[prop]
160}
161
162func shouldSkipStructField(field reflect.StructField) bool {
163	if field.PkgPath != "" && !field.Anonymous {
164		// Skip unexported fields. Some properties are
165		// internal to Soong only, and these fields do not have PkgPath.
166		return true
167	}
168	// fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc
169	// but cannot be set in a .bp file
170	if proptools.HasTag(field, "blueprint", "mutated") {
171		return true
172	}
173	return false
174}
175
176// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
177// testonly = True, forcing other rules that depend on _test rules to also be
178// marked as testonly = True. This semantic constraint is not present in Soong.
179// To work around, rename "*_test" rules to "*_test_".
180func canonicalizeModuleType(moduleName string) string {
181	if strings.HasSuffix(moduleName, "_test") {
182		return moduleName + "_"
183	}
184
185	return moduleName
186}
187