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