platform_build_soong/android/bazel_test.go
Liz Kammer c13f785061 Handle missing dependencies in mixed builds
Bazel will fail on queries with missing dependencies. Instead, we check
for missing dependencies in mixed builds and we fall back to Soong when
we are aware of missing dependencies in a module.

Test: go test soong tests
Change-Id: I4f83752704970d8b43650d5b55ff35799c7bc625
2023-05-23 12:14:02 +00:00

585 lines
16 KiB
Go

// 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 (
"fmt"
"testing"
"android/soong/android/allowlists"
"android/soong/bazel"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
func TestConvertAllModulesInPackage(t *testing.T) {
testCases := []struct {
prefixes allowlists.Bp2BuildConfig
packageDir string
}{
{
prefixes: allowlists.Bp2BuildConfig{
"a": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a/b": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a/b",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a/b": allowlists.Bp2BuildDefaultTrueRecursively,
"a/b/c": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a/b",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a": allowlists.Bp2BuildDefaultTrueRecursively,
"d/e/f": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a/b",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a": allowlists.Bp2BuildDefaultFalse,
"a/b": allowlists.Bp2BuildDefaultTrueRecursively,
"a/b/c": allowlists.Bp2BuildDefaultFalse,
},
packageDir: "a/b",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a": allowlists.Bp2BuildDefaultTrueRecursively,
"a/b": allowlists.Bp2BuildDefaultFalse,
"a/b/c": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a": allowlists.Bp2BuildDefaultFalseRecursively,
"a/b": allowlists.Bp2BuildDefaultTrue,
},
packageDir: "a/b",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a": allowlists.Bp2BuildDefaultFalseRecursively,
"a/b": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a/b/c",
},
}
for _, test := range testCases {
if ok, _ := bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes); !ok {
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 allowlists.Bp2BuildConfig
packageDir string
}{
{
prefixes: allowlists.Bp2BuildConfig{
"a/b": allowlists.Bp2BuildDefaultFalse,
},
packageDir: "a/b",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a": allowlists.Bp2BuildDefaultFalse,
"a/b": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a/b": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a", // opt-in by default
},
{
prefixes: allowlists.Bp2BuildConfig{
"a/b/c": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a/b",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a": allowlists.Bp2BuildDefaultTrueRecursively,
"d/e/f": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "foo/bar",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a": allowlists.Bp2BuildDefaultTrueRecursively,
"a/b": allowlists.Bp2BuildDefaultFalse,
"a/b/c": allowlists.Bp2BuildDefaultTrueRecursively,
},
packageDir: "a/b",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a": allowlists.Bp2BuildDefaultFalse,
"a/b": allowlists.Bp2BuildDefaultTrueRecursively,
"a/b/c": allowlists.Bp2BuildDefaultFalse,
},
packageDir: "a",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a": allowlists.Bp2BuildDefaultFalseRecursively,
"a/b": allowlists.Bp2BuildDefaultTrue,
},
packageDir: "a/b/c",
},
{
prefixes: allowlists.Bp2BuildConfig{
"a": allowlists.Bp2BuildDefaultTrueRecursively,
"a/b": allowlists.Bp2BuildDefaultFalseRecursively,
},
packageDir: "a/b/c",
},
}
for _, test := range testCases {
if ok, _ := bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes); ok {
t.Errorf("Expected to allow module opt-in in %s based on %v, but failed.", test.packageDir, test.prefixes)
}
}
}
type TestBazelModule struct {
bazel.TestModuleInfo
BazelModuleBase
}
var _ blueprint.Module = TestBazelModule{}
func (m TestBazelModule) Name() string {
return m.TestModuleInfo.ModuleName
}
func (m TestBazelModule) GenerateBuildActions(blueprint.ModuleContext) {
}
type TestBazelConversionContext struct {
omc bazel.OtherModuleTestContext
allowlist Bp2BuildConversionAllowlist
errors []string
}
var _ bazelOtherModuleContext = &TestBazelConversionContext{}
func (bcc *TestBazelConversionContext) OtherModuleType(m blueprint.Module) string {
return bcc.omc.OtherModuleType(m)
}
func (bcc *TestBazelConversionContext) OtherModuleName(m blueprint.Module) string {
return bcc.omc.OtherModuleName(m)
}
func (bcc *TestBazelConversionContext) OtherModuleDir(m blueprint.Module) string {
return bcc.omc.OtherModuleDir(m)
}
func (bcc *TestBazelConversionContext) ModuleErrorf(format string, args ...interface{}) {
bcc.errors = append(bcc.errors, fmt.Sprintf(format, args...))
}
func (bcc *TestBazelConversionContext) Config() Config {
return Config{
&config{
Bp2buildPackageConfig: bcc.allowlist,
},
}
}
var bazelableBazelModuleBase = BazelModuleBase{
bazelProperties: properties{
Bazel_module: BazelModuleProperties{
CanConvertToBazel: true,
},
},
}
func TestBp2BuildAllowlist(t *testing.T) {
testCases := []struct {
description string
shouldConvert bool
expectedErrors []string
module TestBazelModule
allowlist Bp2BuildConversionAllowlist
}{
{
description: "allowlist enables module",
shouldConvert: true,
module: TestBazelModule{
TestModuleInfo: bazel.TestModuleInfo{
ModuleName: "foo",
Typ: "rule1",
Dir: "dir1",
},
BazelModuleBase: bazelableBazelModuleBase,
},
allowlist: Bp2BuildConversionAllowlist{
moduleAlwaysConvert: map[string]bool{
"foo": true,
},
},
},
{
description: "module in name allowlist and type allowlist fails",
shouldConvert: false,
expectedErrors: []string{"A module cannot be in moduleAlwaysConvert and also be in moduleTypeAlwaysConvert"},
module: TestBazelModule{
TestModuleInfo: bazel.TestModuleInfo{
ModuleName: "foo",
Typ: "rule1",
Dir: "dir1",
},
BazelModuleBase: bazelableBazelModuleBase,
},
allowlist: Bp2BuildConversionAllowlist{
moduleAlwaysConvert: map[string]bool{
"foo": true,
},
moduleTypeAlwaysConvert: map[string]bool{
"rule1": true,
},
},
},
{
description: "module in allowlist and denylist fails",
shouldConvert: false,
expectedErrors: []string{"a module cannot be in moduleDoNotConvert and also be in moduleAlwaysConvert"},
module: TestBazelModule{
TestModuleInfo: bazel.TestModuleInfo{
ModuleName: "foo",
Typ: "rule1",
Dir: "dir1",
},
BazelModuleBase: bazelableBazelModuleBase,
},
allowlist: Bp2BuildConversionAllowlist{
moduleAlwaysConvert: map[string]bool{
"foo": true,
},
moduleDoNotConvert: map[string]bool{
"foo": true,
},
},
},
{
description: "module allowlist and enabled directory",
shouldConvert: false,
expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir' Module: 'foo'"},
module: TestBazelModule{
TestModuleInfo: bazel.TestModuleInfo{
ModuleName: "foo",
Typ: "rule1",
Dir: "existing/build/dir",
},
BazelModuleBase: bazelableBazelModuleBase,
},
allowlist: Bp2BuildConversionAllowlist{
moduleAlwaysConvert: map[string]bool{
"foo": true,
},
defaultConfig: allowlists.Bp2BuildConfig{
"existing/build/dir": allowlists.Bp2BuildDefaultTrue,
},
},
},
{
description: "module allowlist and enabled subdirectory",
shouldConvert: false,
expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir' Module: 'foo'"},
module: TestBazelModule{
TestModuleInfo: bazel.TestModuleInfo{
ModuleName: "foo",
Typ: "rule1",
Dir: "existing/build/dir/subdir",
},
BazelModuleBase: bazelableBazelModuleBase,
},
allowlist: Bp2BuildConversionAllowlist{
moduleAlwaysConvert: map[string]bool{
"foo": true,
},
defaultConfig: allowlists.Bp2BuildConfig{
"existing/build/dir": allowlists.Bp2BuildDefaultTrueRecursively,
},
},
},
{
description: "module enabled in unit test short-circuits other allowlists",
shouldConvert: true,
module: TestBazelModule{
TestModuleInfo: bazel.TestModuleInfo{
ModuleName: "foo",
Typ: "rule1",
Dir: ".",
},
BazelModuleBase: BazelModuleBase{
bazelProperties: properties{
Bazel_module: BazelModuleProperties{
CanConvertToBazel: true,
Bp2build_available: proptools.BoolPtr(true),
},
},
},
},
allowlist: Bp2BuildConversionAllowlist{
moduleAlwaysConvert: map[string]bool{
"foo": true,
},
moduleDoNotConvert: map[string]bool{
"foo": true,
},
},
},
}
for _, test := range testCases {
t.Run(test.description, func(t *testing.T) {
bcc := &TestBazelConversionContext{
omc: bazel.OtherModuleTestContext{
Modules: []bazel.TestModuleInfo{
test.module.TestModuleInfo,
},
},
allowlist: test.allowlist,
}
shouldConvert := test.module.shouldConvertWithBp2build(bcc, test.module.TestModuleInfo)
if test.shouldConvert != shouldConvert {
t.Errorf("Module shouldConvert expected to be: %v, but was: %v", test.shouldConvert, shouldConvert)
}
errorsMatch := true
if len(test.expectedErrors) != len(bcc.errors) {
errorsMatch = false
} else {
for i, err := range test.expectedErrors {
if err != bcc.errors[i] {
errorsMatch = false
}
}
}
if !errorsMatch {
t.Errorf("Expected errors to be: %v, but were: %v", test.expectedErrors, bcc.errors)
}
})
}
}
func TestBp2buildAllowList(t *testing.T) {
allowlist := GetBp2BuildAllowList()
for k, v := range allowlists.Bp2buildDefaultConfig {
if allowlist.defaultConfig[k] != v {
t.Errorf("bp2build default config of %s: expected: %v, got: %v", k, v, allowlist.defaultConfig[k])
}
}
for k, v := range allowlists.Bp2buildKeepExistingBuildFile {
if allowlist.keepExistingBuildFile[k] != v {
t.Errorf("bp2build keep existing build file of %s: expected: %v, got: %v", k, v, allowlist.keepExistingBuildFile[k])
}
}
for _, k := range allowlists.Bp2buildModuleTypeAlwaysConvertList {
if !allowlist.moduleTypeAlwaysConvert[k] {
t.Errorf("bp2build module type always convert of %s: expected: true, got: %v", k, allowlist.moduleTypeAlwaysConvert[k])
}
}
for _, k := range allowlists.Bp2buildModuleDoNotConvertList {
if !allowlist.moduleDoNotConvert[k] {
t.Errorf("bp2build module do not convert of %s: expected: true, got: %v", k, allowlist.moduleDoNotConvert[k])
}
}
}
func TestShouldKeepExistingBuildFileForDir(t *testing.T) {
allowlist := NewBp2BuildAllowlist()
// entry "a/b2/c2" is moot because of its parent "a/b2"
allowlist.SetKeepExistingBuildFile(map[string]bool{"a": false, "a/b1": false, "a/b2": true, "a/b1/c1": true, "a/b2/c2": false})
truths := []string{"a", "a/b1", "a/b2", "a/b1/c1", "a/b2/c", "a/b2/c2", "a/b2/c2/d"}
falsities := []string{"a1", "a/b", "a/b1/c"}
for _, dir := range truths {
if !allowlist.ShouldKeepExistingBuildFileForDir(dir) {
t.Errorf("%s expected TRUE but was FALSE", dir)
}
}
for _, dir := range falsities {
if allowlist.ShouldKeepExistingBuildFileForDir(dir) {
t.Errorf("%s expected FALSE but was TRUE", dir)
}
}
}
type mixedBuildModule struct {
ModuleBase
BazelModuleBase
props struct {
Deps []string
Mixed_build_incompatible *bool
QueuedBazelCall bool `blueprint:"mutated"`
}
}
type mixedBuildModuleInfo struct {
QueuedBazelCall bool
}
var mixedBuildModuleProvider = blueprint.NewProvider(mixedBuildModuleInfo{})
func mixedBuildModuleFactory() Module {
m := &mixedBuildModule{}
m.AddProperties(&m.props)
InitAndroidArchModule(m, HostAndDeviceDefault, MultilibBoth)
InitBazelModule(m)
return m
}
func (m *mixedBuildModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
}
func (m *mixedBuildModule) DepsMutator(ctx BottomUpMutatorContext) {
ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
}
func (m *mixedBuildModule) GenerateAndroidBuildActions(ctx ModuleContext) {
}
func (m *mixedBuildModule) IsMixedBuildSupported(ctx BaseModuleContext) bool {
return !proptools.Bool(m.props.Mixed_build_incompatible)
}
func (m *mixedBuildModule) QueueBazelCall(ctx BaseModuleContext) {
m.props.QueuedBazelCall = true
}
func (m *mixedBuildModule) ProcessBazelQueryResponse(ctx ModuleContext) {
ctx.SetProvider(mixedBuildModuleProvider, mixedBuildModuleInfo{
QueuedBazelCall: m.props.QueuedBazelCall,
})
}
var prepareForMixedBuildTests = FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("deps", mixedBuildModuleFactory)
RegisterMixedBuildsMutator(ctx)
})
func TestMixedBuildsEnabledForType(t *testing.T) {
baseBp := `
deps {
name: "foo",
deps: ["bar"],
target: { windows: { enabled: true } },
%s
}
`
depBp := `
deps {
name: "bar",
target: {
windows: {
enabled: true,
},
},
}
`
testCases := []struct {
desc string
variant *string
missingDeps bool
extraBpInfo string
mixedBuildsEnabled bool
}{
{
desc: "mixed builds works",
mixedBuildsEnabled: true,
extraBpInfo: `bazel_module: { bp2build_available: true },`,
},
{
desc: "missing deps",
missingDeps: true,
mixedBuildsEnabled: false,
extraBpInfo: `bazel_module: { bp2build_available: true },`,
},
{
desc: "windows no mixed builds",
mixedBuildsEnabled: false,
variant: proptools.StringPtr("windows_x86"),
extraBpInfo: `bazel_module: { bp2build_available: true },`,
},
{
desc: "mixed builds disabled by type",
mixedBuildsEnabled: false,
extraBpInfo: `mixed_build_incompatible: true,
bazel_module: { bp2build_available: true },`,
},
{
desc: "mixed builds not bp2build available",
mixedBuildsEnabled: false,
extraBpInfo: `bazel_module: { bp2build_available: false },`,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
handlers := GroupFixturePreparers(
prepareForMixedBuildTests,
PrepareForTestWithArchMutator,
FixtureModifyConfig(func(config Config) {
config.BazelContext = MockBazelContext{
OutputBaseDir: "base",
}
config.Targets[Windows] = []Target{
{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
{Windows, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", true},
}
}),
)
bp := fmt.Sprintf(baseBp, tc.extraBpInfo)
if tc.missingDeps {
handlers = GroupFixturePreparers(
handlers,
PrepareForTestWithAllowMissingDependencies,
)
} else {
bp += depBp
}
result := handlers.RunTestWithBp(t, bp)
variant := proptools.StringDefault(tc.variant, "android_arm64_armv8-a")
m := result.ModuleForTests("foo", variant)
mixedBuildModuleInfo := result.TestContext.ModuleProvider(m.Module(), mixedBuildModuleProvider).(mixedBuildModuleInfo)
if w, g := tc.mixedBuildsEnabled, mixedBuildModuleInfo.QueuedBazelCall; w != g {
t.Errorf("Expected mixed builds enabled %t, got mixed builds enabled %t", w, g)
}
})
}
}