Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 12b4c270 authored by Jingwen Chen's avatar Jingwen Chen
Browse files

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
parent dca349a7
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ bootstrap_go_package {
        "androidmk_test.go",
        "apex_test.go",
        "arch_test.go",
        "bazel_test.go",
        "config_test.go",
        "csuite_config_test.go",
        "depset_test.go",
+90 −8
Original line number Diff line number Diff line
@@ -32,7 +32,13 @@ type bazelModuleProperties struct {

	// 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 @@ type Bazelable interface {
	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 @@ func (b *BazelModuleBase) GetBazelLabel(ctx BazelConversionPathContext, module b
	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 @@ func (b *BazelModuleBase) GetBazelBuildFileContents(c Config, path, name string)

// 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()
}

android/bazel_test.go

0 → 100644
+134 −0
Original line number Diff line number Diff line
// 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 "testing"

func TestConvertAllModulesInPackage(t *testing.T) {
	testCases := []struct {
		prefixes   Bp2BuildConfig
		packageDir string
	}{
		{
			prefixes: Bp2BuildConfig{
				"a": Bp2BuildDefaultTrueRecursively,
			},
			packageDir: "a",
		},
		{
			prefixes: Bp2BuildConfig{
				"a/b": Bp2BuildDefaultTrueRecursively,
			},
			packageDir: "a/b",
		},
		{
			prefixes: Bp2BuildConfig{
				"a/b":   Bp2BuildDefaultTrueRecursively,
				"a/b/c": Bp2BuildDefaultTrueRecursively,
			},
			packageDir: "a/b",
		},
		{
			prefixes: Bp2BuildConfig{
				"a":     Bp2BuildDefaultTrueRecursively,
				"d/e/f": Bp2BuildDefaultTrueRecursively,
			},
			packageDir: "a/b",
		},
		{
			prefixes: Bp2BuildConfig{
				"a":     Bp2BuildDefaultFalse,
				"a/b":   Bp2BuildDefaultTrueRecursively,
				"a/b/c": Bp2BuildDefaultFalse,
			},
			packageDir: "a/b",
		},
		{
			prefixes: Bp2BuildConfig{
				"a":     Bp2BuildDefaultTrueRecursively,
				"a/b":   Bp2BuildDefaultFalse,
				"a/b/c": Bp2BuildDefaultTrueRecursively,
			},
			packageDir: "a",
		},
	}

	for _, test := range testCases {
		if !bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes) {
			t.Errorf("Expected to convert all modules in %s based on %v, but failed.", test.packageDir, test.prefixes)
		}
	}
}

func TestModuleOptIn(t *testing.T) {
	testCases := []struct {
		prefixes   Bp2BuildConfig
		packageDir string
	}{
		{
			prefixes: Bp2BuildConfig{
				"a/b": Bp2BuildDefaultFalse,
			},
			packageDir: "a/b",
		},
		{
			prefixes: Bp2BuildConfig{
				"a":   Bp2BuildDefaultFalse,
				"a/b": Bp2BuildDefaultTrueRecursively,
			},
			packageDir: "a",
		},
		{
			prefixes: Bp2BuildConfig{
				"a/b": Bp2BuildDefaultTrueRecursively,
			},
			packageDir: "a", // opt-in by default
		},
		{
			prefixes: Bp2BuildConfig{
				"a/b/c": Bp2BuildDefaultTrueRecursively,
			},
			packageDir: "a/b",
		},
		{
			prefixes: Bp2BuildConfig{
				"a":     Bp2BuildDefaultTrueRecursively,
				"d/e/f": Bp2BuildDefaultTrueRecursively,
			},
			packageDir: "foo/bar",
		},
		{
			prefixes: Bp2BuildConfig{
				"a":     Bp2BuildDefaultTrueRecursively,
				"a/b":   Bp2BuildDefaultFalse,
				"a/b/c": Bp2BuildDefaultTrueRecursively,
			},
			packageDir: "a/b",
		},
		{
			prefixes: Bp2BuildConfig{
				"a":     Bp2BuildDefaultFalse,
				"a/b":   Bp2BuildDefaultTrueRecursively,
				"a/b/c": Bp2BuildDefaultFalse,
			},
			packageDir: "a",
		},
	}

	for _, test := range testCases {
		if bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes) {
			t.Errorf("Expected to allow module opt-in in %s based on %v, but failed.", test.packageDir, test.prefixes)
		}
	}
}
+7 −0
Original line number Diff line number Diff line
@@ -140,6 +140,9 @@ type config struct {
	fs         pathtools.FileSystem
	mockBpList string

	bp2buildPackageConfig    Bp2BuildConfig
	bp2buildModuleTypeConfig map[string]bool

	// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
	// in tests when a path doesn't exist.
	TestAllowNonExistentPaths bool
@@ -281,6 +284,8 @@ func TestConfig(buildDir string, env map[string]string, bp string, fs map[string

	config.mockFileSystem(bp, fs)

	config.bp2buildModuleTypeConfig = map[string]bool{}

	return Config{config}
}

@@ -452,6 +457,8 @@ func NewConfig(srcDir, buildDir string, moduleListFile string) (Config, error) {
			Bool(config.productVariables.ClangCoverage))

	config.BazelContext, err = NewBazelContext(config)
	config.bp2buildPackageConfig = bp2buildDefaultConfig
	config.bp2buildModuleTypeConfig = make(map[string]bool)

	return Config{config}, err
}
+1 −1
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ func (bfg *bazelFilegroup) GenerateAndroidBuildActions(ctx ModuleContext) {}

func FilegroupBp2Build(ctx TopDownMutatorContext) {
	fg, ok := ctx.Module().(*fileGroup)
	if !ok || !fg.ConvertWithBp2build() {
	if !ok || !fg.ConvertWithBp2build(ctx) {
		return
	}

Loading