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