// Copyright 2021 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package android import ( "bufio" "errors" "fmt" "strings" "android/soong/ui/metrics/bp2build_metrics_proto" "github.com/google/blueprint" "github.com/google/blueprint/bootstrap" "github.com/google/blueprint/proptools" "android/soong/android/allowlists" ) const ( // A sentinel value to be used as a key in Bp2BuildConfig for modules with // no package path. This is also the module dir for top level Android.bp // modules. Bp2BuildTopLevel = "." ) type MixedBuildEnabledStatus int const ( // This module can be mixed_built. MixedBuildEnabled = iota // There is a technical incompatibility preventing this module from being // bazel-analyzed. Note: the module might also be incompatible. TechnicalIncompatibility // This module cannot be mixed_built due to some incompatibility with it // that is not a platform incompatibility. Example: the module-type is not // enabled, or is not bp2build-converted. ModuleIncompatibility // Missing dependencies. We can't query Bazel for modules if it has missing dependencies, there // will be failures. ModuleMissingDeps ) // FileGroupAsLibrary describes a filegroup module that is converted to some library // such as aidl_library or proto_library. type FileGroupAsLibrary interface { ShouldConvertToAidlLibrary(ctx BazelConversionPathContext) bool ShouldConvertToProtoLibrary(ctx BazelConversionPathContext) bool GetAidlLibraryLabel(ctx BazelConversionPathContext) string GetProtoLibraryLabel(ctx BazelConversionPathContext) string } type BazelConversionStatus struct { // Information about _all_ bp2build targets generated by this module. Multiple targets are // supported as Soong handles some things within a single target that we may choose to split into // multiple targets, e.g. renderscript, protos, yacc within a cc module. Bp2buildInfo []bp2buildInfo `blueprint:"mutated"` // UnconvertedBp2buildDep stores the module names of direct dependency that were not converted to // Bazel UnconvertedDeps []string `blueprint:"mutated"` // MissingBp2buildDep stores the module names of direct dependency that were not found MissingDeps []string `blueprint:"mutated"` // If non-nil, indicates that the module could not be converted successfully // with bp2build. This will describe the reason the module could not be converted. UnconvertedReason *UnconvertedReason } // The reason a module could not be converted to a BUILD target via bp2build. // This should match bp2build_metrics_proto.UnconvertedReason, but omits private // proto-related fields that prevent copying this struct. type UnconvertedReason struct { // Should correspond to a valid value in bp2build_metrics_proto.UnconvertedReasonType. // A raw int is used here instead, because blueprint logic requires that all transitive // fields of module definitions be primitives. ReasonType int Detail string } type BazelModuleProperties struct { // The label of the Bazel target replacing this Soong module. When run in conversion mode, this // will import the handcrafted build target into the autogenerated file. Note: this may result in // a conflict due to duplicate targets if bp2build_available is also set. Label *string // If true, bp2build will generate the converted Bazel target for this module. Note: this may // cause a conflict due to the duplicate targets if label is also set. // // This is a bool pointer to support tristates: true, false, not set. // // To opt in a module, set bazel_module: { bp2build_available: true } // To opt out a module, set bazel_module: { bp2build_available: false } // To defer the default setting for the directory, do not set the value. Bp2build_available *bool // CanConvertToBazel is set via InitBazelModule to indicate that a module type can be converted to // Bazel with Bp2build. CanConvertToBazel bool `blueprint:"mutated"` } // Properties contains common module properties for Bazel migration purposes. type properties struct { // In "Bazel mixed build" mode, this represents the Bazel target replacing // this Soong module. Bazel_module BazelModuleProperties } // namespacedVariableProperties is a map from a string representing a Soong // config variable namespace, like "android" or "vendor_name" to a slice of // pointer to a struct containing a single field called Soong_config_variables // whose value mirrors the structure in the Blueprint file. type namespacedVariableProperties map[string][]interface{} // BazelModuleBase contains the property structs with metadata for modules which can be converted to // Bazel. type BazelModuleBase struct { bazelProperties properties // namespacedVariableProperties is used for soong_config_module_type support // in bp2build. Soong config modules allow users to set module properties // based on custom product variables defined in Android.bp files. These // variables are namespaced to prevent clobbering, especially when set from // Makefiles. namespacedVariableProperties namespacedVariableProperties // baseModuleType is set when this module was created from a module type // defined by a soong_config_module_type. Every soong_config_module_type // "wraps" another module type, e.g. a soong_config_module_type can wrap a // cc_defaults to a custom_cc_defaults, or cc_binary to a custom_cc_binary. // This baseModuleType is set to the wrapped module type. baseModuleType string } // Bazelable is specifies the interface for modules that can be converted to Bazel. type Bazelable interface { bazelProps() *properties HasHandcraftedLabel() bool HandcraftedLabel() string GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string ShouldConvertWithBp2build(ctx ShouldConvertWithBazelContext) bool shouldConvertWithBp2build(shouldConvertModuleContext, shouldConvertParams) bool // ConvertWithBp2build either converts the module to a Bazel build target or // declares the module as unconvertible (for logging and metrics). // Modules must implement this function to be bp2build convertible. The function // must either create at least one Bazel target module (using ctx.CreateBazelTargetModule or // its related functions), or declare itself unconvertible using ctx.MarkBp2buildUnconvertible. ConvertWithBp2build(ctx Bp2buildMutatorContext) // namespacedVariableProps is a map from a soong config variable namespace // (e.g. acme, android) to a map of interfaces{}, which are really // reflect.Struct pointers, representing the value of the // soong_config_variables property of a module. The struct pointer is the // one with the single member called Soong_config_variables, which itself is // a struct containing fields for each supported feature in that namespace. // // The reason for using a slice of interface{} is to support defaults // propagation of the struct pointers. namespacedVariableProps() namespacedVariableProperties setNamespacedVariableProps(props namespacedVariableProperties) BaseModuleType() string SetBaseModuleType(baseModuleType string) } // ApiProvider is implemented by modules that contribute to an API surface type ApiProvider interface { ConvertWithApiBp2build(ctx TopDownMutatorContext) } // MixedBuildBuildable is an interface that module types should implement in order // to be "handled by Bazel" in a mixed build. type MixedBuildBuildable interface { // IsMixedBuildSupported returns true if and only if this module should be // "handled by Bazel" in a mixed build. // This "escape hatch" allows modules with corner-case scenarios to opt out // of being built with Bazel. IsMixedBuildSupported(ctx BaseModuleContext) bool // QueueBazelCall invokes request-queueing functions on the BazelContext // so that these requests are handled when Bazel's cquery is invoked. QueueBazelCall(ctx BaseModuleContext) // ProcessBazelQueryResponse uses Bazel information (obtained from the BazelContext) // to set module fields and providers to propagate this module's metadata upstream. // This effectively "bridges the gap" between Bazel and Soong in a mixed build. // Soong modules depending on this module should be oblivious to the fact that // this module was handled by Bazel. ProcessBazelQueryResponse(ctx ModuleContext) } // BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules. type BazelModule interface { Module Bazelable } // InitBazelModule is a wrapper function that decorates a BazelModule with Bazel-conversion // properties. func InitBazelModule(module BazelModule) { module.AddProperties(module.bazelProps()) module.bazelProps().Bazel_module.CanConvertToBazel = true } // BazelHandcraftedHook is a load hook to possibly register the current module as // a "handcrafted" Bazel target of a given name. If the current module should be // registered in this way, the hook function should return the target name. If // it should not be registered in this way, this function should return the empty string. type BazelHandcraftedHook func(ctx LoadHookContext) string // AddBazelHandcraftedHook adds a load hook to (maybe) mark the given module so that // it is treated by bp2build as if it has a handcrafted Bazel target. func AddBazelHandcraftedHook(module BazelModule, hook BazelHandcraftedHook) { AddLoadHook(module, func(ctx LoadHookContext) { var targetName string = hook(ctx) if len(targetName) > 0 { moduleDir := ctx.ModuleDir() if moduleDir == Bp2BuildTopLevel { moduleDir = "" } label := fmt.Sprintf("//%s:%s", moduleDir, targetName) module.bazelProps().Bazel_module.Label = &label } }) } // bazelProps returns the Bazel properties for the given BazelModuleBase. func (b *BazelModuleBase) bazelProps() *properties { return &b.bazelProperties } func (b *BazelModuleBase) namespacedVariableProps() namespacedVariableProperties { return b.namespacedVariableProperties } func (b *BazelModuleBase) setNamespacedVariableProps(props namespacedVariableProperties) { b.namespacedVariableProperties = props } func (b *BazelModuleBase) BaseModuleType() string { return b.baseModuleType } func (b *BazelModuleBase) SetBaseModuleType(baseModuleType string) { b.baseModuleType = baseModuleType } // HasHandcraftedLabel returns whether this module has a handcrafted Bazel label. func (b *BazelModuleBase) HasHandcraftedLabel() bool { return b.bazelProperties.Bazel_module.Label != nil } // HandcraftedLabel returns the handcrafted label for this module, or empty string if there is none func (b *BazelModuleBase) HandcraftedLabel() string { return proptools.String(b.bazelProperties.Bazel_module.Label) } // GetBazelLabel returns the Bazel label for the given BazelModuleBase. func (b *BazelModuleBase) GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string { if b.HasHandcraftedLabel() { return b.HandcraftedLabel() } if b.ShouldConvertWithBp2build(ctx) { return bp2buildModuleLabel(ctx, module) } panic(fmt.Errorf("requested non-existent label for module %s", module.Name())) } type Bp2BuildConversionAllowlist struct { // Configure modules in these directories to enable bp2build_available: true or false by default. defaultConfig allowlists.Bp2BuildConfig // Keep any existing BUILD files (and do not generate new BUILD files) for these directories // in the synthetic Bazel workspace. keepExistingBuildFile map[string]bool // Per-module allowlist to always opt modules into both bp2build and Bazel Dev Mode mixed // builds. These modules are usually in directories with many other modules that are not ready // for conversion. // // A module can either be in this list or its directory allowlisted entirely // in bp2buildDefaultConfig, but not both at the same time. moduleAlwaysConvert map[string]bool // Per-module-type allowlist to always opt modules in to both bp2build and // Bazel Dev Mode mixed builds when they have the same type as one listed. moduleTypeAlwaysConvert map[string]bool // Per-module denylist to always opt modules out of bp2build conversion. moduleDoNotConvert map[string]bool } // NewBp2BuildAllowlist creates a new, empty Bp2BuildConversionAllowlist // which can be populated using builder pattern Set* methods func NewBp2BuildAllowlist() Bp2BuildConversionAllowlist { return Bp2BuildConversionAllowlist{ allowlists.Bp2BuildConfig{}, map[string]bool{}, map[string]bool{}, map[string]bool{}, map[string]bool{}, } } // SetDefaultConfig copies the entries from defaultConfig into the allowlist func (a Bp2BuildConversionAllowlist) SetDefaultConfig(defaultConfig allowlists.Bp2BuildConfig) Bp2BuildConversionAllowlist { if a.defaultConfig == nil { a.defaultConfig = allowlists.Bp2BuildConfig{} } for k, v := range defaultConfig { a.defaultConfig[k] = v } return a } // SetKeepExistingBuildFile copies the entries from keepExistingBuildFile into the allowlist func (a Bp2BuildConversionAllowlist) SetKeepExistingBuildFile(keepExistingBuildFile map[string]bool) Bp2BuildConversionAllowlist { if a.keepExistingBuildFile == nil { a.keepExistingBuildFile = map[string]bool{} } for k, v := range keepExistingBuildFile { a.keepExistingBuildFile[k] = v } return a } // SetModuleAlwaysConvertList copies the entries from moduleAlwaysConvert into the allowlist func (a Bp2BuildConversionAllowlist) SetModuleAlwaysConvertList(moduleAlwaysConvert []string) Bp2BuildConversionAllowlist { if a.moduleAlwaysConvert == nil { a.moduleAlwaysConvert = map[string]bool{} } for _, m := range moduleAlwaysConvert { a.moduleAlwaysConvert[m] = true } return a } // SetModuleTypeAlwaysConvertList copies the entries from moduleTypeAlwaysConvert into the allowlist func (a Bp2BuildConversionAllowlist) SetModuleTypeAlwaysConvertList(moduleTypeAlwaysConvert []string) Bp2BuildConversionAllowlist { if a.moduleTypeAlwaysConvert == nil { a.moduleTypeAlwaysConvert = map[string]bool{} } for _, m := range moduleTypeAlwaysConvert { a.moduleTypeAlwaysConvert[m] = true } return a } // SetModuleDoNotConvertList copies the entries from moduleDoNotConvert into the allowlist func (a Bp2BuildConversionAllowlist) SetModuleDoNotConvertList(moduleDoNotConvert []string) Bp2BuildConversionAllowlist { if a.moduleDoNotConvert == nil { a.moduleDoNotConvert = map[string]bool{} } for _, m := range moduleDoNotConvert { a.moduleDoNotConvert[m] = true } return a } // ShouldKeepExistingBuildFileForDir returns whether an existing BUILD file should be // added to the build symlink forest based on the current global configuration. func (a Bp2BuildConversionAllowlist) ShouldKeepExistingBuildFileForDir(dir string) bool { if _, ok := a.keepExistingBuildFile[dir]; ok { // Exact dir match return true } var i int // Check if subtree match for { j := strings.Index(dir[i:], "/") if j == -1 { return false //default } prefix := dir[0 : i+j] i = i + j + 1 // skip the "/" if recursive, ok := a.keepExistingBuildFile[prefix]; ok && recursive { return true } } } var bp2BuildAllowListKey = NewOnceKey("Bp2BuildAllowlist") var bp2buildAllowlist OncePer func GetBp2BuildAllowList() Bp2BuildConversionAllowlist { return bp2buildAllowlist.Once(bp2BuildAllowListKey, func() interface{} { return NewBp2BuildAllowlist().SetDefaultConfig(allowlists.Bp2buildDefaultConfig). SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile). SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList). SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList). SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList) }).(Bp2BuildConversionAllowlist) } // MixedBuildsEnabled returns a MixedBuildEnabledStatus regarding whether // a module is ready to be replaced by a converted or handcrafted Bazel target. // As a side effect, calling this method will also log whether this module is // mixed build enabled for metrics reporting. func MixedBuildsEnabled(ctx BaseModuleContext) MixedBuildEnabledStatus { platformIncompatible := isPlatformIncompatible(ctx.Os(), ctx.Arch().ArchType) if platformIncompatible { ctx.Config().LogMixedBuild(ctx, false) return TechnicalIncompatibility } if ctx.Config().AllowMissingDependencies() { missingDeps := ctx.getMissingDependencies() // If there are missing dependencies, querying Bazel will fail. Soong instead fails at execution // time, not loading/analysis. disable mixed builds and fall back to Soong to maintain that // behavior. if len(missingDeps) > 0 { ctx.Config().LogMixedBuild(ctx, false) return ModuleMissingDeps } } module := ctx.Module() apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo) withinApex := !apexInfo.IsForPlatform() mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() && module.Enabled() && convertedToBazel(ctx, module) && ctx.Config().BazelContext.IsModuleNameAllowed(module.Name(), withinApex) ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled) if mixedBuildEnabled { return MixedBuildEnabled } return ModuleIncompatibility } func isGoModule(module blueprint.Module) bool { if _, ok := module.(*bootstrap.GoPackage); ok { return true } if _, ok := module.(*bootstrap.GoBinary); ok { return true } return false } // ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel. func convertedToBazel(ctx BazelConversionContext, module blueprint.Module) bool { // Special-case bootstrap_go_package and bootstrap_go_binary // These do not implement Bazelable, but have been converted if isGoModule(module) { return true } b, ok := module.(Bazelable) if !ok { return false } return b.HasHandcraftedLabel() || b.shouldConvertWithBp2build(ctx, shouldConvertParams{ module: module, moduleDir: ctx.OtherModuleDir(module), moduleName: ctx.OtherModuleName(module), moduleType: ctx.OtherModuleType(module), }) } type ShouldConvertWithBazelContext interface { ModuleErrorf(format string, args ...interface{}) Module() Module Config() Config ModuleType() string ModuleName() string ModuleDir() string } // ShouldConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build func (b *BazelModuleBase) ShouldConvertWithBp2build(ctx ShouldConvertWithBazelContext) bool { return b.shouldConvertWithBp2build(ctx, shouldConvertParams{ module: ctx.Module(), moduleDir: ctx.ModuleDir(), moduleName: ctx.ModuleName(), moduleType: ctx.ModuleType(), }) } type bazelOtherModuleContext interface { ModuleErrorf(format string, args ...interface{}) Config() Config OtherModuleType(m blueprint.Module) string OtherModuleName(m blueprint.Module) string OtherModuleDir(m blueprint.Module) string } func isPlatformIncompatible(osType OsType, arch ArchType) bool { return osType == Windows || // Windows toolchains are not currently supported. osType == LinuxBionic || // Linux Bionic toolchains are not currently supported. osType == LinuxMusl || // Linux musl toolchains are not currently supported (b/259266326). arch == Riscv64 // TODO(b/262192655) Riscv64 toolchains are not currently supported. } type shouldConvertModuleContext interface { ModuleErrorf(format string, args ...interface{}) Config() Config } type shouldConvertParams struct { module blueprint.Module moduleType string moduleDir string moduleName string } func (b *BazelModuleBase) shouldConvertWithBp2build(ctx shouldConvertModuleContext, p shouldConvertParams) bool { if !b.bazelProps().Bazel_module.CanConvertToBazel { return false } module := p.module propValue := b.bazelProperties.Bazel_module.Bp2build_available packagePath := moduleDirWithPossibleOverride(ctx, module, p.moduleDir) // Modules in unit tests which are enabled in the allowlist by type or name // trigger this conditional because unit tests run under the "." package path isTestModule := packagePath == Bp2BuildTopLevel && proptools.BoolDefault(propValue, false) if isTestModule { return true } moduleName := moduleNameWithPossibleOverride(ctx, module, p.moduleName) allowlist := ctx.Config().Bp2buildPackageConfig moduleNameAllowed := allowlist.moduleAlwaysConvert[moduleName] moduleTypeAllowed := allowlist.moduleTypeAlwaysConvert[p.moduleType] allowlistConvert := moduleNameAllowed || moduleTypeAllowed if moduleNameAllowed && moduleTypeAllowed { ctx.ModuleErrorf("A module %q of type %q cannot be in moduleAlwaysConvert and also be in moduleTypeAlwaysConvert", moduleName, p.moduleType) return false } if allowlist.moduleDoNotConvert[moduleName] { if moduleNameAllowed { ctx.ModuleErrorf("a module %q cannot be in moduleDoNotConvert and also be in moduleAlwaysConvert", moduleName) } return false } // This is a tristate value: true, false, or unset. if ok, directoryPath := bp2buildDefaultTrueRecursively(packagePath, allowlist.defaultConfig); ok { if moduleNameAllowed { ctx.ModuleErrorf("A module cannot be in a directory marked Bp2BuildDefaultTrue"+ " or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: '%s'"+ " Module: '%s'", directoryPath, moduleName) return false } // Allow modules to explicitly opt-out. return proptools.BoolDefault(propValue, true) } // Allow modules to explicitly opt-in. return proptools.BoolDefault(propValue, allowlistConvert) } // bp2buildDefaultTrueRecursively checks that the package contains a prefix from the // set of package prefixes where all modules must be converted. That is, if the // package is x/y/z, and the list contains either x, x/y, or x/y/z, this function will // return true. // // However, if the package is x/y, and it matches a Bp2BuildDefaultFalse "x/y" entry // exactly, this module will return false early. // // This function will also return false if the package doesn't match anything in // the config. // // This function will also return the allowlist entry which caused a particular // package to be enabled. Since packages can be enabled via a recursive declaration, // the path returned will not always be the same as the one provided. func bp2buildDefaultTrueRecursively(packagePath string, config allowlists.Bp2BuildConfig) (bool, string) { // Check if the package path has an exact match in the config. if config[packagePath] == allowlists.Bp2BuildDefaultTrue || config[packagePath] == allowlists.Bp2BuildDefaultTrueRecursively { return true, packagePath } else if config[packagePath] == allowlists.Bp2BuildDefaultFalse || config[packagePath] == allowlists.Bp2BuildDefaultFalseRecursively { return false, packagePath } // If not, check for the config recursively. packagePrefix := packagePath // e.g. for x/y/z, iterate over x/y, then x, taking the most-specific value from the allowlist. for strings.Contains(packagePrefix, "/") { dirIndex := strings.LastIndex(packagePrefix, "/") packagePrefix = packagePrefix[:dirIndex] switch value := config[packagePrefix]; value { case allowlists.Bp2BuildDefaultTrueRecursively: // package contains this prefix and this prefix should convert all modules return true, packagePrefix case allowlists.Bp2BuildDefaultFalseRecursively: //package contains this prefix and this prefix should NOT convert any modules return false, packagePrefix } // Continue to the next part of the package dir. } return false, packagePath } func registerBp2buildConversionMutator(ctx RegisterMutatorsContext) { ctx.BottomUp("bp2build_conversion", bp2buildConversionMutator).Parallel() } func registerBp2buildDepsMutator(ctx RegisterMutatorsContext) { ctx.BottomUp("bp2build_deps", bp2buildDepsMutator).Parallel() } func bp2buildConversionMutator(ctx BottomUpMutatorContext) { // If an existing BUILD file in the module directory has a target defined // with this same name as this module, assume that this is an existing // definition for this target. if ctx.Config().HasBazelBuildTargetInSource(ctx.ModuleDir(), ctx.ModuleName()) { ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_DEFINED_IN_BUILD_FILE, ctx.ModuleName()) return } bModule, ok := ctx.Module().(Bazelable) if !ok { ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_TYPE_UNSUPPORTED, "") return } // There may be cases where the target is created by a macro rather than in a BUILD file, those // should be captured as well. if bModule.HasHandcraftedLabel() { // Defer to the BUILD target. Generating an additional target would // cause a BUILD file conflict. ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_DEFINED_IN_BUILD_FILE, "") return } // TODO: b/285631638 - Differentiate between denylisted modules and missing bp2build capabilities. if !bModule.shouldConvertWithBp2build(ctx, shouldConvertParams{ module: ctx.Module(), moduleDir: ctx.ModuleDir(), moduleName: ctx.ModuleName(), moduleType: ctx.ModuleType(), }) { ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_UNSUPPORTED, "") return } if ctx.Module().base().GetUnconvertedReason() != nil { return } bModule.ConvertWithBp2build(ctx) if len(ctx.Module().base().Bp2buildTargets()) == 0 && ctx.Module().base().GetUnconvertedReason() == nil { panic(fmt.Errorf("illegal bp2build invariant: module '%s' was neither converted nor marked unconvertible", ctx.ModuleName())) } // If an existing BUILD file in the module directory has a target defined // with the same name as any target generated by this module, assume that this // is an existing definition for this target. (These generated target names // may be different than the module name, as checked at the beginning of this function!) for _, targetInfo := range ctx.Module().base().Bp2buildTargets() { if ctx.Config().HasBazelBuildTargetInSource(targetInfo.TargetPackage(), targetInfo.TargetName()) { // Defer to the BUILD target. Generating an additional target would // cause a BUILD file conflict. ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_DEFINED_IN_BUILD_FILE, targetInfo.TargetName()) return } } } // TODO: b/285631638 - Add this as a new mutator to the bp2build conversion mutators. // Currently, this only exists to prepare test coverage for the launch of this feature. func bp2buildDepsMutator(ctx BottomUpMutatorContext) { if ctx.Module().base().GetUnconvertedReason() != nil { return } if len(ctx.Module().GetMissingBp2buildDeps()) > 0 { exampleDep := ctx.Module().GetMissingBp2buildDeps()[0] ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_UNCONVERTED_DEP, exampleDep) } ctx.VisitDirectDeps(func(dep Module) { if dep.base().GetUnconvertedReason() != nil && dep.base().GetUnconvertedReason().ReasonType != int(bp2build_metrics_proto.UnconvertedReasonType_DEFINED_IN_BUILD_FILE) && ctx.OtherModuleDependencyTag(dep) == Bp2buildDepTag { ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_UNCONVERTED_DEP, dep.Name()) } }) } // GetMainClassInManifest scans the manifest file specified in filepath and returns // the value of attribute Main-Class in the manifest file if it exists, or returns error. // WARNING: this is for bp2build converters of java_* modules only. func GetMainClassInManifest(c Config, filepath string) (string, error) { file, err := c.fs.Open(filepath) if err != nil { return "", err } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "Main-Class:") { return strings.TrimSpace(line[len("Main-Class:"):]), nil } } return "", errors.New("Main-Class is not found.") } func AttachValidationActions(ctx ModuleContext, outputFilePath Path, validations Paths) ModuleOutPath { validatedOutputFilePath := PathForModuleOut(ctx, "validated", outputFilePath.Base()) ctx.Build(pctx, BuildParams{ Rule: CpNoPreserveSymlink, Description: "run validations " + outputFilePath.Base(), Output: validatedOutputFilePath, Input: outputFilePath, Validations: validations, }) return validatedOutputFilePath } func RunsOn(hostSupported bool, deviceSupported bool, unitTest bool) []string { var runsOn []string if hostSupported && deviceSupported { runsOn = []string{"host_without_device", "device"} } else if hostSupported { if unitTest { runsOn = []string{"host_without_device"} } else { runsOn = []string{"host_with_device"} } } else if deviceSupported { runsOn = []string{"device"} } return runsOn }