123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- package bp2build
- import (
- "android/soong/starlark_fmt"
- "encoding/json"
- "fmt"
- "reflect"
- "strconv"
- "strings"
- "android/soong/android"
- "android/soong/cc"
- cc_config "android/soong/cc/config"
- java_config "android/soong/java/config"
- "android/soong/apex"
- "github.com/google/blueprint/proptools"
- )
- type BazelFile struct {
- Dir string
- Basename string
- Contents string
- }
- // PRIVATE: Use CreateSoongInjectionDirFiles instead
- func soongInjectionFiles(cfg android.Config, metrics CodegenMetrics) ([]BazelFile, error) {
- var files []BazelFile
- files = append(files, newFile("android", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
- files = append(files, newFile("android", "constants.bzl", android.BazelCcToolchainVars(cfg)))
- files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
- files = append(files, newFile("cc_toolchain", "config_constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
- files = append(files, newFile("cc_toolchain", "sanitizer_constants.bzl", cc.BazelCcSanitizerToolchainVars(cfg)))
- files = append(files, newFile("java_toolchain", GeneratedBuildFileName, "")) // Creates a //java_toolchain package.
- files = append(files, newFile("java_toolchain", "constants.bzl", java_config.BazelJavaToolchainVars(cfg)))
- files = append(files, newFile("apex_toolchain", GeneratedBuildFileName, "")) // Creates a //apex_toolchain package.
- apexToolchainVars, err := apex.BazelApexToolchainVars()
- if err != nil {
- return nil, err
- }
- files = append(files, newFile("apex_toolchain", "constants.bzl", apexToolchainVars))
- files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.Serialize().ConvertedModules, "\n")))
- convertedModulePathMap, err := json.MarshalIndent(metrics.convertedModulePathMap, "", "\t")
- if err != nil {
- panic(err)
- }
- files = append(files, newFile("metrics", GeneratedBuildFileName, "")) // Creates a //metrics package.
- files = append(files, newFile("metrics", "converted_modules_path_map.json", string(convertedModulePathMap)))
- files = append(files, newFile("metrics", "converted_modules_path_map.bzl", "modules = "+strings.ReplaceAll(string(convertedModulePathMap), "\\", "\\\\")))
- files = append(files, newFile("product_config", "soong_config_variables.bzl", cfg.Bp2buildSoongConfigDefinitions.String()))
- files = append(files, newFile("product_config", "arch_configuration.bzl", android.StarlarkArchConfigurations()))
- apiLevelsMap, err := android.GetApiLevelsMap(cfg)
- if err != nil {
- return nil, err
- }
- apiLevelsContent, err := json.Marshal(apiLevelsMap)
- if err != nil {
- return nil, err
- }
- files = append(files, newFile("api_levels", GeneratedBuildFileName, `exports_files(["api_levels.json"])`))
- // TODO(b/269691302) value of apiLevelsContent is product variable dependent and should be avoided for soong injection
- files = append(files, newFile("api_levels", "api_levels.json", string(apiLevelsContent)))
- files = append(files, newFile("api_levels", "platform_versions.bzl", platformVersionContents(cfg)))
- files = append(files, newFile("allowlists", GeneratedBuildFileName, ""))
- // TODO(b/262781701): Create an alternate soong_build entrypoint for writing out these files only when requested
- files = append(files, newFile("allowlists", "mixed_build_prod_allowlist.txt", strings.Join(android.GetBazelEnabledModules(android.BazelProdMode), "\n")+"\n"))
- files = append(files, newFile("allowlists", "mixed_build_staging_allowlist.txt", strings.Join(android.GetBazelEnabledModules(android.BazelStagingMode), "\n")+"\n"))
- return files, nil
- }
- func platformVersionContents(cfg android.Config) string {
- // Despite these coming from cfg.productVariables, they are actually hardcoded in global
- // makefiles, not set in individual product config makesfiles, so they're safe to just export
- // and load() directly.
- platformVersionActiveCodenames := make([]string, 0, len(cfg.PlatformVersionActiveCodenames()))
- for _, codename := range cfg.PlatformVersionActiveCodenames() {
- platformVersionActiveCodenames = append(platformVersionActiveCodenames, fmt.Sprintf("%q", codename))
- }
- platformSdkVersion := "None"
- if cfg.RawPlatformSdkVersion() != nil {
- platformSdkVersion = strconv.Itoa(*cfg.RawPlatformSdkVersion())
- }
- return fmt.Sprintf(`
- platform_versions = struct(
- platform_sdk_final = %s,
- platform_sdk_version = %s,
- platform_sdk_codename = %q,
- platform_version_active_codenames = [%s],
- )
- `, starlark_fmt.PrintBool(cfg.PlatformSdkFinal()), platformSdkVersion, cfg.PlatformSdkCodename(), strings.Join(platformVersionActiveCodenames, ", "))
- }
- func CreateBazelFiles(
- cfg android.Config,
- 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.SortedKeys(buildToTargets) {
- targets := buildToTargets[dir]
- targets.sort()
- var content string
- if mode == Bp2Build || mode == ApiBp2build {
- content = `# READ THIS FIRST:
- # 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 += targets.LoadStatements()
- content += "\n\n"
- // Get package rule from the handcrafted BUILD file, otherwise emit the default one.
- prText := "package(default_visibility = [\"//visibility:public\"])\n"
- if pr := targets.packageRule(); pr != nil {
- prText = pr.content
- }
- content += prText
- } else if mode == QueryView {
- content = soongModuleLoad
- }
- 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.
- "for": true, // reserved keyword, b/233579439
- "versions_with_info": true, // TODO(b/245730552) struct properties not fully supported
- }
- )
- func shouldGenerateAttribute(prop string) bool {
- return !ignoredPropNames[prop]
- }
- func shouldSkipStructField(field reflect.StructField) bool {
- if field.PkgPath != "" && !field.Anonymous {
- // 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
- }
|