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