package bp2build import ( "android/soong/android" "android/soong/cc/config" "fmt" "reflect" "sort" "strings" "github.com/google/blueprint/proptools" ) type BazelFile struct { Dir string Basename string Contents string } func CreateSoongInjectionFiles() []BazelFile { var files []BazelFile files = append(files, newFile("cc_toolchain", "BUILD", "")) // Creates a //cc_toolchain package. files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars())) return files } func CreateBazelFiles( ruleShims map[string]RuleShim, buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile { var files []BazelFile if mode == QueryView { // Write top level WORKSPACE. files = append(files, newFile("", "WORKSPACE", "")) // Used to denote that the top level directory is a package. files = append(files, newFile("", GeneratedBuildFileName, "")) files = append(files, newFile(bazelRulesSubDir, GeneratedBuildFileName, "")) // These files are only used for queryview. files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl)) for bzlFileName, ruleShim := range ruleShims { files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content)) } files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims))) } files = append(files, createBuildFiles(buildToTargets, mode)...) return files } func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile { files := make([]BazelFile, 0, len(buildToTargets)) for _, dir := range android.SortedStringKeys(buildToTargets) { if mode == Bp2Build && !android.ShouldWriteBuildFileForDir(dir) { fmt.Printf("[bp2build] Not writing generated BUILD file for dir: '%s'\n", dir) continue } targets := buildToTargets[dir] sort.Slice(targets, func(i, j int) bool { // this will cover all bp2build generated targets if targets[i].name < targets[j].name { return true } // give a strict ordering to content from hand-crafted targets return targets[i].content < targets[j].content }) content := soongModuleLoad if mode == Bp2Build { content = `# This file was automatically generated by bp2build for the Bazel migration project. # Feel free to edit or test it, but do *not* check it into your version control system.` content += "\n\n" content += "package(default_visibility = [\"//visibility:public\"])" content += "\n\n" content += targets.LoadStatements() } if content != "" { // If there are load statements, add a couple of newlines. content += "\n\n" } content += targets.String() files = append(files, newFile(dir, GeneratedBuildFileName, content)) } return files } func newFile(dir, basename, content string) BazelFile { return BazelFile{ Dir: dir, Basename: basename, Contents: content, } } const ( bazelRulesSubDir = "build/bazel/queryview_rules" // additional files: // * workspace file // * base BUILD file // * rules BUILD file // * rules providers.bzl file // * rules soong_module.bzl file numAdditionalFiles = 5 ) var ( // Certain module property names are blocklisted/ignored here, for the reasons commented. ignoredPropNames = map[string]bool{ "name": true, // redundant, since this is explicitly generated for every target "from": true, // reserved keyword "in": true, // reserved keyword "size": true, // reserved for tests "arch": true, // interface prop type is not supported yet. "multilib": true, // interface prop type is not supported yet. "target": true, // interface prop type is not supported yet. "visibility": true, // Bazel has native visibility semantics. Handle later. "features": true, // There is already a built-in attribute 'features' which cannot be overridden. } ) func shouldGenerateAttribute(prop string) bool { return !ignoredPropNames[prop] } func shouldSkipStructField(field reflect.StructField) bool { if field.PkgPath != "" { // Skip unexported fields. Some properties are // internal to Soong only, and these fields do not have PkgPath. return true } // fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc // but cannot be set in a .bp file if proptools.HasTag(field, "blueprint", "mutated") { return true } return false } // FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as // testonly = True, forcing other rules that depend on _test rules to also be // marked as testonly = True. This semantic constraint is not present in Soong. // To work around, rename "*_test" rules to "*_test_". func canonicalizeModuleType(moduleName string) string { if strings.HasSuffix(moduleName, "_test") { return moduleName + "_" } return moduleName }