2024-04-12 23:48:34 +02:00
|
|
|
// Copyright 2024 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 blueprint
|
|
|
|
|
|
|
|
import (
|
2024-03-29 20:25:29 +01:00
|
|
|
"fmt"
|
2024-04-12 23:48:34 +02:00
|
|
|
"slices"
|
2024-03-29 20:25:29 +01:00
|
|
|
"strings"
|
2024-04-12 23:48:34 +02:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2024-03-29 20:25:29 +01:00
|
|
|
func testTransition(bp string) (*Context, []error) {
|
2024-04-12 23:48:34 +02:00
|
|
|
ctx := newContext()
|
|
|
|
ctx.MockFileSystem(map[string][]byte{
|
2024-03-29 20:25:29 +01:00
|
|
|
"Android.bp": []byte(bp),
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx.RegisterBottomUpMutator("deps", depsMutator)
|
|
|
|
ctx.RegisterTransitionMutator("transition", transitionTestMutator{})
|
|
|
|
ctx.RegisterBottomUpMutator("post_transition_deps", postTransitionDepsMutator)
|
|
|
|
|
|
|
|
ctx.RegisterModuleType("transition_module", newTransitionModule)
|
|
|
|
_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return nil, errs
|
|
|
|
}
|
|
|
|
|
|
|
|
_, errs = ctx.ResolveDependencies(nil)
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return nil, errs
|
|
|
|
}
|
|
|
|
|
|
|
|
return ctx, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func assertNoErrors(t *testing.T, errs []error) {
|
|
|
|
t.Helper()
|
|
|
|
if len(errs) > 0 {
|
|
|
|
t.Errorf("unexpected errors:")
|
|
|
|
for _, err := range errs {
|
|
|
|
t.Errorf(" %s", err)
|
|
|
|
}
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const testTransitionBp = `
|
2024-04-12 23:48:34 +02:00
|
|
|
transition_module {
|
|
|
|
name: "A",
|
|
|
|
deps: ["B", "C"],
|
|
|
|
split: ["a", "b"],
|
|
|
|
}
|
|
|
|
|
|
|
|
transition_module {
|
|
|
|
name: "B",
|
|
|
|
deps: ["C"],
|
|
|
|
outgoing: "c",
|
2024-03-29 20:25:29 +01:00
|
|
|
%s
|
2024-04-12 23:48:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
transition_module {
|
|
|
|
name: "C",
|
|
|
|
deps: ["D"],
|
|
|
|
}
|
|
|
|
|
|
|
|
transition_module {
|
|
|
|
name: "D",
|
|
|
|
incoming: "d",
|
2024-03-29 20:25:29 +01:00
|
|
|
deps: ["E"],
|
2024-04-12 23:48:34 +02:00
|
|
|
}
|
|
|
|
|
2024-03-29 20:25:29 +01:00
|
|
|
transition_module {
|
|
|
|
name: "E",
|
|
|
|
}
|
|
|
|
`
|
2024-04-12 23:48:34 +02:00
|
|
|
|
2024-03-29 20:25:29 +01:00
|
|
|
func getTransitionModule(ctx *Context, name, variant string) *transitionModule {
|
|
|
|
group := ctx.moduleGroupFromName(name, nil)
|
|
|
|
module := group.moduleOrAliasByVariantName(variant).module()
|
|
|
|
return module.logicModule.(*transitionModule)
|
|
|
|
}
|
2024-04-12 23:48:34 +02:00
|
|
|
|
2024-03-29 20:25:29 +01:00
|
|
|
func checkTransitionVariants(t *testing.T, ctx *Context, name string, expectedVariants []string) {
|
|
|
|
t.Helper()
|
|
|
|
group := ctx.moduleGroupFromName(name, nil)
|
|
|
|
var gotVariants []string
|
|
|
|
for _, variant := range group.modules {
|
|
|
|
gotVariants = append(gotVariants, variant.moduleOrAliasVariant().variations["transition"])
|
|
|
|
}
|
|
|
|
if !slices.Equal(expectedVariants, gotVariants) {
|
|
|
|
t.Errorf("expected variants of %q to be %q, got %q", name, expectedVariants, gotVariants)
|
2024-04-12 23:48:34 +02:00
|
|
|
}
|
2024-03-29 20:25:29 +01:00
|
|
|
}
|
2024-04-12 23:48:34 +02:00
|
|
|
|
2024-03-29 20:25:29 +01:00
|
|
|
func checkTransitionDeps(t *testing.T, ctx *Context, m Module, expected ...string) {
|
|
|
|
t.Helper()
|
|
|
|
var got []string
|
|
|
|
ctx.VisitDirectDeps(m, func(m Module) {
|
|
|
|
got = append(got, ctx.ModuleName(m)+"("+ctx.ModuleSubDir(m)+")")
|
|
|
|
})
|
|
|
|
if !slices.Equal(got, expected) {
|
|
|
|
t.Errorf("unexpected %q dependencies, got %q expected %q",
|
|
|
|
ctx.ModuleName(m), got, expected)
|
2024-04-12 23:48:34 +02:00
|
|
|
}
|
2024-03-29 20:25:29 +01:00
|
|
|
}
|
2024-04-12 23:48:34 +02:00
|
|
|
|
2024-03-29 20:25:29 +01:00
|
|
|
func checkTransitionMutate(t *testing.T, m *transitionModule, variant string) {
|
|
|
|
t.Helper()
|
|
|
|
if m.properties.Mutated != variant {
|
|
|
|
t.Errorf("unexpected mutated property in %q, expected %q got %q", m.Name(), variant, m.properties.Mutated)
|
2024-04-12 23:48:34 +02:00
|
|
|
}
|
2024-03-29 20:25:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestTransition(t *testing.T) {
|
|
|
|
ctx, errs := testTransition(fmt.Sprintf(testTransitionBp, ""))
|
|
|
|
assertNoErrors(t, errs)
|
2024-04-12 23:48:34 +02:00
|
|
|
|
|
|
|
// Module A uses Split to create a and b variants
|
2024-03-29 20:25:29 +01:00
|
|
|
checkTransitionVariants(t, ctx, "A", []string{"a", "b"})
|
2024-04-12 23:48:34 +02:00
|
|
|
// Module B inherits a and b variants from A
|
2024-03-29 20:25:29 +01:00
|
|
|
checkTransitionVariants(t, ctx, "B", []string{"", "a", "b"})
|
2024-04-12 23:48:34 +02:00
|
|
|
// Module C inherits a and b variants from A, but gets an outgoing c variant from B
|
2024-03-29 20:25:29 +01:00
|
|
|
checkTransitionVariants(t, ctx, "C", []string{"", "a", "b", "c"})
|
2024-04-12 23:48:34 +02:00
|
|
|
// Module D always has incoming variant d
|
2024-03-29 20:25:29 +01:00
|
|
|
checkTransitionVariants(t, ctx, "D", []string{"", "d"})
|
|
|
|
// Module E inherits d from D
|
|
|
|
checkTransitionVariants(t, ctx, "E", []string{"", "d"})
|
2024-04-12 23:48:34 +02:00
|
|
|
|
2024-03-29 20:25:29 +01:00
|
|
|
A_a := getTransitionModule(ctx, "A", "a")
|
|
|
|
A_b := getTransitionModule(ctx, "A", "b")
|
|
|
|
B_a := getTransitionModule(ctx, "B", "a")
|
|
|
|
B_b := getTransitionModule(ctx, "B", "b")
|
|
|
|
C_a := getTransitionModule(ctx, "C", "a")
|
|
|
|
C_b := getTransitionModule(ctx, "C", "b")
|
|
|
|
C_c := getTransitionModule(ctx, "C", "c")
|
|
|
|
D_d := getTransitionModule(ctx, "D", "d")
|
|
|
|
E_d := getTransitionModule(ctx, "E", "d")
|
|
|
|
|
|
|
|
checkTransitionDeps(t, ctx, A_a, "B(a)", "C(a)")
|
|
|
|
checkTransitionDeps(t, ctx, A_b, "B(b)", "C(b)")
|
|
|
|
checkTransitionDeps(t, ctx, B_a, "C(c)")
|
|
|
|
checkTransitionDeps(t, ctx, B_b, "C(c)")
|
|
|
|
checkTransitionDeps(t, ctx, C_a, "D(d)")
|
|
|
|
checkTransitionDeps(t, ctx, C_b, "D(d)")
|
|
|
|
checkTransitionDeps(t, ctx, C_c, "D(d)")
|
|
|
|
checkTransitionDeps(t, ctx, D_d, "E(d)")
|
|
|
|
checkTransitionDeps(t, ctx, E_d)
|
2024-04-12 23:48:34 +02:00
|
|
|
|
2024-03-29 20:25:29 +01:00
|
|
|
checkTransitionMutate(t, A_a, "a")
|
|
|
|
checkTransitionMutate(t, A_b, "b")
|
|
|
|
checkTransitionMutate(t, B_a, "a")
|
|
|
|
checkTransitionMutate(t, B_b, "b")
|
|
|
|
checkTransitionMutate(t, C_a, "a")
|
|
|
|
checkTransitionMutate(t, C_b, "b")
|
|
|
|
checkTransitionMutate(t, C_c, "c")
|
|
|
|
checkTransitionMutate(t, D_d, "d")
|
|
|
|
checkTransitionMutate(t, E_d, "d")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPostTransitionDeps(t *testing.T) {
|
|
|
|
ctx, errs := testTransition(fmt.Sprintf(testTransitionBp,
|
|
|
|
`post_transition_deps: ["D:late", "E:d"],`))
|
|
|
|
assertNoErrors(t, errs)
|
|
|
|
|
|
|
|
// Module A uses Split to create a and b variants
|
|
|
|
checkTransitionVariants(t, ctx, "A", []string{"a", "b"})
|
|
|
|
// Module B inherits a and b variants from A
|
|
|
|
checkTransitionVariants(t, ctx, "B", []string{"", "a", "b"})
|
|
|
|
// Module C inherits a and b variants from A, but gets an outgoing c variant from B
|
|
|
|
checkTransitionVariants(t, ctx, "C", []string{"", "a", "b", "c"})
|
|
|
|
// Module D always has incoming variant d
|
|
|
|
checkTransitionVariants(t, ctx, "D", []string{"", "d"})
|
|
|
|
// Module E inherits d from D
|
|
|
|
checkTransitionVariants(t, ctx, "E", []string{"", "d"})
|
|
|
|
|
|
|
|
A_a := getTransitionModule(ctx, "A", "a")
|
|
|
|
A_b := getTransitionModule(ctx, "A", "b")
|
|
|
|
B_a := getTransitionModule(ctx, "B", "a")
|
|
|
|
B_b := getTransitionModule(ctx, "B", "b")
|
|
|
|
C_a := getTransitionModule(ctx, "C", "a")
|
|
|
|
C_b := getTransitionModule(ctx, "C", "b")
|
|
|
|
C_c := getTransitionModule(ctx, "C", "c")
|
|
|
|
D_d := getTransitionModule(ctx, "D", "d")
|
|
|
|
E_d := getTransitionModule(ctx, "E", "d")
|
|
|
|
|
|
|
|
checkTransitionDeps(t, ctx, A_a, "B(a)", "C(a)")
|
|
|
|
checkTransitionDeps(t, ctx, A_b, "B(b)", "C(b)")
|
|
|
|
checkTransitionDeps(t, ctx, B_a, "C(c)", "D(d)", "E(d)")
|
|
|
|
checkTransitionDeps(t, ctx, B_b, "C(c)", "D(d)", "E(d)")
|
|
|
|
checkTransitionDeps(t, ctx, C_a, "D(d)")
|
|
|
|
checkTransitionDeps(t, ctx, C_b, "D(d)")
|
|
|
|
checkTransitionDeps(t, ctx, C_c, "D(d)")
|
|
|
|
checkTransitionDeps(t, ctx, D_d, "E(d)")
|
|
|
|
checkTransitionDeps(t, ctx, E_d)
|
|
|
|
|
|
|
|
checkTransitionMutate(t, A_a, "a")
|
|
|
|
checkTransitionMutate(t, A_b, "b")
|
|
|
|
checkTransitionMutate(t, B_a, "a")
|
|
|
|
checkTransitionMutate(t, B_b, "b")
|
|
|
|
checkTransitionMutate(t, C_a, "a")
|
|
|
|
checkTransitionMutate(t, C_b, "b")
|
|
|
|
checkTransitionMutate(t, C_c, "c")
|
|
|
|
checkTransitionMutate(t, D_d, "d")
|
|
|
|
checkTransitionMutate(t, E_d, "d")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPostTransitionDepsMissingVariant(t *testing.T) {
|
|
|
|
// TODO: eventually this will create the missing variant on demand
|
|
|
|
_, errs := testTransition(fmt.Sprintf(testTransitionBp,
|
|
|
|
`post_transition_deps: ["E:missing"],`))
|
|
|
|
expectedError := `Android.bp:8:4: dependency "E" of "B" missing variant:
|
|
|
|
transition:missing
|
|
|
|
available variants:
|
|
|
|
transition:
|
|
|
|
transition:d`
|
|
|
|
if len(errs) != 1 || errs[0].Error() != expectedError {
|
|
|
|
t.Errorf("expected error %q, got %q", expectedError, errs)
|
|
|
|
}
|
2024-04-12 23:48:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type transitionTestMutator struct{}
|
|
|
|
|
|
|
|
func (transitionTestMutator) Split(ctx BaseModuleContext) []string {
|
|
|
|
if split := ctx.Module().(*transitionModule).properties.Split; len(split) > 0 {
|
|
|
|
return split
|
|
|
|
}
|
|
|
|
return []string{""}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (transitionTestMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
|
|
|
|
if outgoing := ctx.Module().(*transitionModule).properties.Outgoing; outgoing != nil {
|
|
|
|
return *outgoing
|
|
|
|
}
|
|
|
|
return sourceVariation
|
|
|
|
}
|
|
|
|
|
|
|
|
func (transitionTestMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
|
|
|
|
if incoming := ctx.Module().(*transitionModule).properties.Incoming; incoming != nil {
|
|
|
|
return *incoming
|
|
|
|
}
|
|
|
|
return incomingVariation
|
|
|
|
}
|
|
|
|
|
|
|
|
func (transitionTestMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
|
|
|
|
ctx.Module().(*transitionModule).properties.Mutated = variation
|
|
|
|
}
|
|
|
|
|
|
|
|
type transitionModule struct {
|
|
|
|
SimpleName
|
|
|
|
properties struct {
|
2024-03-29 20:25:29 +01:00
|
|
|
Deps []string
|
|
|
|
Post_transition_deps []string
|
|
|
|
Split []string
|
|
|
|
Outgoing *string
|
|
|
|
Incoming *string
|
2024-04-12 23:48:34 +02:00
|
|
|
|
|
|
|
Mutated string `blueprint:"mutated"`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newTransitionModule() (Module, []interface{}) {
|
|
|
|
m := &transitionModule{}
|
|
|
|
return m, []interface{}{&m.properties, &m.SimpleName.Properties}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *transitionModule) GenerateBuildActions(ModuleContext) {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *transitionModule) Deps() []string {
|
|
|
|
return f.properties.Deps
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *transitionModule) IgnoreDeps() []string {
|
|
|
|
return nil
|
|
|
|
}
|
2024-03-29 20:25:29 +01:00
|
|
|
|
|
|
|
func postTransitionDepsMutator(mctx BottomUpMutatorContext) {
|
|
|
|
if m, ok := mctx.Module().(*transitionModule); ok {
|
|
|
|
for _, dep := range m.properties.Post_transition_deps {
|
|
|
|
module, variation, _ := strings.Cut(dep, ":")
|
|
|
|
variations := []Variation{{"transition", variation}}
|
|
|
|
mctx.AddVariationDependencies(variations, walkerDepsTag{follow: true}, module)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|