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
This commit is contained in:
Liz Kammer
2023-05-17 13:01:48 -04:00
parent 038a597b0e
commit c13f785061
5 changed files with 209 additions and 10 deletions

View File

@@ -46,6 +46,10 @@ const (
// 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
@@ -367,16 +371,26 @@ func GetBp2BuildAllowList() Bp2BuildConversionAllowlist {
// 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 {
module := ctx.Module()
apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo)
withinApex := !apexInfo.IsForPlatform()
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) &&

View File

@@ -29,6 +29,7 @@ import (
"android/soong/bazel/cquery"
"android/soong/shared"
"android/soong/starlark_fmt"
"github.com/google/blueprint"
"github.com/google/blueprint/metrics"
@@ -73,14 +74,12 @@ var (
}
)
func init() {
RegisterMixedBuildsMutator(InitRegistrationContext)
func registerMixedBuildsMutator(ctx RegisterMutatorsContext) {
ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
}
func RegisterMixedBuildsMutator(ctx RegistrationContext) {
ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
})
ctx.FinalDepsMutators(registerMixedBuildsMutator)
}
func mixedBuildsPrepareMutator(ctx BottomUpMutatorContext) {

View File

@@ -436,3 +436,150 @@ func TestShouldKeepExistingBuildFileForDir(t *testing.T) {
}
}
}
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)
}
})
}
}

View File

@@ -354,6 +354,10 @@ type BaseModuleContext interface {
AddMissingDependencies(missingDeps []string)
// getMissingDependencies returns the list of missing dependencies.
// Calling this function prevents adding new dependencies.
getMissingDependencies() []string
// AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build
AddUnconvertedBp2buildDep(dep string)
@@ -939,7 +943,8 @@ type commonProperties struct {
NamespaceExportedToMake bool `blueprint:"mutated"`
MissingDeps []string `blueprint:"mutated"`
MissingDeps []string `blueprint:"mutated"`
CheckedMissingDeps bool `blueprint:"mutated"`
// Name and variant strings stored by mutators to enable Module.String()
DebugName string `blueprint:"mutated"`
@@ -2862,6 +2867,20 @@ func (b *baseModuleContext) AddMissingDependencies(deps []string) {
}
}
func (b *baseModuleContext) checkedMissingDeps() bool {
return b.Module().base().commonProperties.CheckedMissingDeps
}
func (b *baseModuleContext) getMissingDependencies() []string {
checked := &b.Module().base().commonProperties.CheckedMissingDeps
*checked = true
var missingDeps []string
missingDeps = append(missingDeps, b.Module().base().commonProperties.MissingDeps...)
missingDeps = append(missingDeps, b.bp.EarlyGetMissingDependencies()...)
missingDeps = FirstUniqueStrings(missingDeps)
return missingDeps
}
type AllowDisabledModuleDependency interface {
blueprint.DependencyTag
AllowDisabledModuleDependency(target Module) bool

View File

@@ -67,6 +67,8 @@ func registerMutatorsForBazelConversion(ctx *Context, bp2buildMutators []Registe
// collateGloballyRegisteredMutators constructs the list of mutators that have been registered
// with the InitRegistrationContext and will be used at runtime.
func collateGloballyRegisteredMutators() sortableComponents {
// ensure mixed builds mutator is the last mutator
finalDeps = append(finalDeps, registerMixedBuildsMutator)
return collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps)
}
@@ -885,10 +887,16 @@ func (b *bottomUpMutatorContext) Rename(name string) {
}
func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module {
if b.baseModuleContext.checkedMissingDeps() {
panic("Adding deps not allowed after checking for missing deps")
}
return b.bp.AddDependency(module, tag, name...)
}
func (b *bottomUpMutatorContext) AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string) {
if b.baseModuleContext.checkedMissingDeps() {
panic("Adding deps not allowed after checking for missing deps")
}
b.bp.AddReverseDependency(module, tag, name)
}
@@ -938,11 +946,17 @@ func (b *bottomUpMutatorContext) SetDefaultDependencyVariation(variation *string
func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
names ...string) []blueprint.Module {
if b.baseModuleContext.checkedMissingDeps() {
panic("Adding deps not allowed after checking for missing deps")
}
return b.bp.AddVariationDependencies(variations, tag, names...)
}
func (b *bottomUpMutatorContext) AddFarVariationDependencies(variations []blueprint.Variation,
tag blueprint.DependencyTag, names ...string) []blueprint.Module {
if b.baseModuleContext.checkedMissingDeps() {
panic("Adding deps not allowed after checking for missing deps")
}
return b.bp.AddFarVariationDependencies(variations, tag, names...)
}
@@ -952,10 +966,16 @@ func (b *bottomUpMutatorContext) AddInterVariantDependency(tag blueprint.Depende
}
func (b *bottomUpMutatorContext) ReplaceDependencies(name string) {
if b.baseModuleContext.checkedMissingDeps() {
panic("Adding deps not allowed after checking for missing deps")
}
b.bp.ReplaceDependencies(name)
}
func (b *bottomUpMutatorContext) ReplaceDependenciesIf(name string, predicate blueprint.ReplaceDependencyPredicate) {
if b.baseModuleContext.checkedMissingDeps() {
panic("Adding deps not allowed after checking for missing deps")
}
b.bp.ReplaceDependenciesIf(name, predicate)
}