bp2build: add allowlist for package-level conversions.
This CL adds the support for specifying lists of directories in
build/soong/android/bazel.go, which are then written into
out/soong/bp2build/MANIFEST. Using this configuration,
modules/directories can either default to bp2build_available: true or
false, while still retaining the ability to opt-in or out at the module level.
It also ensures that ConvertWithBp2Build returns true iff the module
type has a registered bp2build converter.
Test: go tests
Test: demo.sh full
Test: TreeHugger presubmits for bp2build and mixed builds.
Change-Id: I0e0f6f4b1b2ec045f2f1c338f7084defc5d23a55
diff --git a/android/bazel.go b/android/bazel.go
index 683495b..9b06426 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -32,7 +32,13 @@
// 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.
- Bp2build_available bool
+ //
+ // 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
}
// Properties contains common module properties for Bazel migration purposes.
@@ -54,9 +60,9 @@
HasHandcraftedLabel() bool
HandcraftedLabel() string
GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
- ConvertWithBp2build() bool
+ ConvertWithBp2build(ctx BazelConversionPathContext) bool
GetBazelBuildFileContents(c Config, path, name string) (string, error)
- ConvertedToBazel() bool
+ ConvertedToBazel(ctx BazelConversionPathContext) bool
}
// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
@@ -91,15 +97,91 @@
if b.HasHandcraftedLabel() {
return b.HandcraftedLabel()
}
- if b.ConvertWithBp2build() {
+ if b.ConvertWithBp2build(ctx) {
return bp2buildModuleLabel(ctx, module)
}
return "" // no label for unconverted module
}
+// Configuration to decide if modules in a directory should default to true/false for bp2build_available
+type Bp2BuildConfig map[string]BazelConversionConfigEntry
+type BazelConversionConfigEntry int
+
+const (
+ // iota + 1 ensures that the int value is not 0 when used in the Bp2buildAllowlist map,
+ // which can also mean that the key doesn't exist in a lookup.
+
+ // all modules in this package and subpackages default to bp2build_available: true.
+ // allows modules to opt-out.
+ Bp2BuildDefaultTrueRecursively BazelConversionConfigEntry = iota + 1
+
+ // all modules in this package (not recursively) default to bp2build_available: false.
+ // allows modules to opt-in.
+ Bp2BuildDefaultFalse
+)
+
+var (
+ // Configure modules in these directories to enable bp2build_available: true or false by default.
+ bp2buildDefaultConfig = Bp2BuildConfig{
+ "bionic": Bp2BuildDefaultTrueRecursively,
+ "system/core/libcutils": Bp2BuildDefaultTrueRecursively,
+ "system/logging/liblog": Bp2BuildDefaultTrueRecursively,
+ }
+)
+
// ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
-func (b *BazelModuleBase) ConvertWithBp2build() bool {
- return b.bazelProperties.Bazel_module.Bp2build_available
+func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionPathContext) bool {
+ // Ensure that the module type of this module has a bp2build converter. This
+ // prevents mixed builds from using auto-converted modules just by matching
+ // the package dir; it also has to have a bp2build mutator as well.
+ if ctx.Config().bp2buildModuleTypeConfig[ctx.ModuleType()] == false {
+ return false
+ }
+
+ packagePath := ctx.ModuleDir()
+ config := ctx.Config().bp2buildPackageConfig
+
+ // This is a tristate value: true, false, or unset.
+ propValue := b.bazelProperties.Bazel_module.Bp2build_available
+ if bp2buildDefaultTrueRecursively(packagePath, config) {
+ // Allow modules to explicitly opt-out.
+ return proptools.BoolDefault(propValue, true)
+ }
+
+ // Allow modules to explicitly opt-in.
+ return proptools.BoolDefault(propValue, false)
+}
+
+// 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.
+func bp2buildDefaultTrueRecursively(packagePath string, config Bp2BuildConfig) bool {
+ ret := false
+
+ if config[packagePath] == Bp2BuildDefaultFalse {
+ return false
+ }
+
+ packagePrefix := ""
+ // e.g. for x/y/z, iterate over x, x/y, then x/y/z, taking the final value from the allowlist.
+ for _, part := range strings.Split(packagePath, "/") {
+ packagePrefix += part
+ if config[packagePrefix] == Bp2BuildDefaultTrueRecursively {
+ // package contains this prefix and this prefix should convert all modules
+ return true
+ }
+ // Continue to the next part of the package dir.
+ packagePrefix += "/"
+ }
+
+ return ret
}
// GetBazelBuildFileContents returns the file contents of a hand-crafted BUILD file if available or
@@ -126,6 +208,6 @@
// ConvertedToBazel returns whether this module has been converted to Bazel, whether automatically
// or manually
-func (b *BazelModuleBase) ConvertedToBazel() bool {
- return b.ConvertWithBp2build() || b.HasHandcraftedLabel()
+func (b *BazelModuleBase) ConvertedToBazel(ctx BazelConversionPathContext) bool {
+ return b.ConvertWithBp2build(ctx) || b.HasHandcraftedLabel()
}