diff options
-rw-r--r-- | android/Android.bp | 2 | ||||
-rw-r--r-- | android/apex.go | 18 | ||||
-rw-r--r-- | android/mutator.go | 242 | ||||
-rw-r--r-- | android/mutator_test.go | 145 | ||||
-rw-r--r-- | android/transition.go | 259 | ||||
-rw-r--r-- | android/transition_test.go | 162 | ||||
-rw-r--r-- | apex/apex.go | 17 | ||||
-rw-r--r-- | cc/afdo.go | 4 | ||||
-rw-r--r-- | cc/cc.go | 14 | ||||
-rw-r--r-- | cc/coverage.go | 2 | ||||
-rw-r--r-- | cc/library.go | 9 | ||||
-rw-r--r-- | cc/orderfile.go | 2 | ||||
-rw-r--r-- | java/app.go | 2 |
13 files changed, 453 insertions, 425 deletions
diff --git a/android/Android.bp b/android/Android.bp index dfea8f999..20cd28be5 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -109,6 +109,7 @@ bootstrap_go_package { "test_asserts.go", "test_suites.go", "testing.go", + "transition.go", "util.go", "variable.go", "vendor_api_levels.go", @@ -154,6 +155,7 @@ bootstrap_go_package { "singleton_module_test.go", "soong_config_modules_test.go", "test_suites_test.go", + "transition_test.go", "util_test.go", "variable_test.go", "vintf_fragment_test.go", diff --git a/android/apex.go b/android/apex.go index db9391204..f625bafcc 100644 --- a/android/apex.go +++ b/android/apex.go @@ -264,9 +264,6 @@ type ApexProperties struct { // Marker interface that identifies dependencies that are excluded from APEX contents. // -// Unless the tag also implements the AlwaysRequireApexVariantTag this will prevent an apex variant -// from being created for the module. -// // At the moment the sdk.sdkRequirementsMutator relies on the fact that the existing tags which // implement this interface do not define dependencies onto members of an sdk_snapshot. If that // changes then sdk.sdkRequirementsMutator will need fixing. @@ -277,17 +274,6 @@ type ExcludeFromApexContentsTag interface { ExcludeFromApexContents() } -// Marker interface that identifies dependencies that always requires an APEX variant to be created. -// -// It is possible for a dependency to require an apex variant but exclude the module from the APEX -// contents. See sdk.sdkMemberDependencyTag. -type AlwaysRequireApexVariantTag interface { - blueprint.DependencyTag - - // Return true if this tag requires that the target dependency has an apex variant. - AlwaysRequireApexVariant() bool -} - // Interface that identifies dependencies to skip Apex dependency check type SkipApexAllowedDependenciesCheck interface { // Returns true to skip the Apex dependency check, which limits the allowed dependency in build. @@ -778,7 +764,7 @@ func (d *ApexBundleDepsInfo) BuildDepsInfoLists(ctx ModuleContext, minSdkVersion // Function called while walking an APEX's payload dependencies. // // Return true if the `to` module should be visited, false otherwise. -type PayloadDepsCallback func(ctx BaseModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool +type PayloadDepsCallback func(ctx BaseModuleContext, from Module, to ApexModule, externalDep bool) bool type WalkPayloadDepsFunc func(ctx BaseModuleContext, do PayloadDepsCallback) // ModuleWithMinSdkVersionCheck represents a module that implements min_sdk_version checks @@ -806,7 +792,7 @@ func CheckMinSdkVersion(ctx ModuleContext, minSdkVersion ApiLevel, walk WalkPayl return } - walk(ctx, func(ctx BaseModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool { + walk(ctx, func(ctx BaseModuleContext, from Module, to ApexModule, externalDep bool) bool { if externalDep { // external deps are outside the payload boundary, which is "stable" // interface. We don't have to check min_sdk_version for external diff --git a/android/mutator.go b/android/mutator.go index fdd16a889..152379448 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -337,248 +337,6 @@ func (x *registerMutatorsContext) BottomUpBlueprint(name string, m blueprint.Bot return mutator } -type IncomingTransitionContext interface { - ArchModuleContext - ModuleProviderContext - ModuleErrorContext - - // Module returns the target of the dependency edge for which the transition - // is being computed - Module() Module - - // Config returns the configuration for the build. - Config() Config - - DeviceConfig() DeviceConfig - - // IsAddingDependency returns true if the transition is being called while adding a dependency - // after the transition mutator has already run, or false if it is being called when the transition - // mutator is running. This should be used sparingly, all uses will have to be removed in order - // to support creating variants on demand. - IsAddingDependency() bool -} - -type OutgoingTransitionContext interface { - ArchModuleContext - ModuleProviderContext - - // Module returns the target of the dependency edge for which the transition - // is being computed - Module() Module - - // DepTag() Returns the dependency tag through which this dependency is - // reached - DepTag() blueprint.DependencyTag - - // Config returns the configuration for the build. - Config() Config - - DeviceConfig() DeviceConfig -} - -// TransitionMutator implements a top-down mechanism where a module tells its -// direct dependencies what variation they should be built in but the dependency -// has the final say. -// -// When implementing a transition mutator, one needs to implement four methods: -// - Split() that tells what variations a module has by itself -// - OutgoingTransition() where a module tells what it wants from its -// dependency -// - IncomingTransition() where a module has the final say about its own -// variation -// - Mutate() that changes the state of a module depending on its variation -// -// That the effective variation of module B when depended on by module A is the -// composition the outgoing transition of module A and the incoming transition -// of module B. -// -// the outgoing transition should not take the properties of the dependency into -// account, only those of the module that depends on it. For this reason, the -// dependency is not even passed into it as an argument. Likewise, the incoming -// transition should not take the properties of the depending module into -// account and is thus not informed about it. This makes for a nice -// decomposition of the decision logic. -// -// A given transition mutator only affects its own variation; other variations -// stay unchanged along the dependency edges. -// -// Soong makes sure that all modules are created in the desired variations and -// that dependency edges are set up correctly. This ensures that "missing -// variation" errors do not happen and allows for more flexible changes in the -// value of the variation among dependency edges (as oppposed to bottom-up -// mutators where if module A in variation X depends on module B and module B -// has that variation X, A must depend on variation X of B) -// -// The limited power of the context objects passed to individual mutators -// methods also makes it more difficult to shoot oneself in the foot. Complete -// safety is not guaranteed because no one prevents individual transition -// mutators from mutating modules in illegal ways and for e.g. Split() or -// Mutate() to run their own visitations of the transitive dependency of the -// module and both of these are bad ideas, but it's better than no guardrails at -// all. -// -// This model is pretty close to Bazel's configuration transitions. The mapping -// between concepts in Soong and Bazel is as follows: -// - Module == configured target -// - Variant == configuration -// - Variation name == configuration flag -// - Variation == configuration flag value -// - Outgoing transition == attribute transition -// - Incoming transition == rule transition -// -// The Split() method does not have a Bazel equivalent and Bazel split -// transitions do not have a Soong equivalent. -// -// Mutate() does not make sense in Bazel due to the different models of the -// two systems: when creating new variations, Soong clones the old module and -// thus some way is needed to change it state whereas Bazel creates each -// configuration of a given configured target anew. -type TransitionMutator interface { - // Split returns the set of variations that should be created for a module no - // matter who depends on it. Used when Make depends on a particular variation - // or when the module knows its variations just based on information given to - // it in the Blueprint file. This method should not mutate the module it is - // called on. - Split(ctx BaseModuleContext) []string - - // OutgoingTransition is called on a module to determine which variation it wants - // from its direct dependencies. The dependency itself can override this decision. - // This method should not mutate the module itself. - OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string - - // IncomingTransition is called on a module to determine which variation it should - // be in based on the variation modules that depend on it want. This gives the module - // a final say about its own variations. This method should not mutate the module - // itself. - IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string - - // Mutate is called after a module was split into multiple variations on each variation. - // It should not split the module any further but adding new dependencies is - // fine. Unlike all the other methods on TransitionMutator, this method is - // allowed to mutate the module. - Mutate(ctx BottomUpMutatorContext, variation string) -} - -type androidTransitionMutator struct { - finalPhase bool - mutator TransitionMutator - name string -} - -func (a *androidTransitionMutator) Split(ctx blueprint.BaseModuleContext) []string { - if a.finalPhase { - panic("TransitionMutator not allowed in FinalDepsMutators") - } - if m, ok := ctx.Module().(Module); ok { - moduleContext := m.base().baseModuleContextFactory(ctx) - return a.mutator.Split(&moduleContext) - } else { - return []string{""} - } -} - -type outgoingTransitionContextImpl struct { - archModuleContext - bp blueprint.OutgoingTransitionContext -} - -func (c *outgoingTransitionContextImpl) Module() Module { - return c.bp.Module().(Module) -} - -func (c *outgoingTransitionContextImpl) DepTag() blueprint.DependencyTag { - return c.bp.DepTag() -} - -func (c *outgoingTransitionContextImpl) Config() Config { - return c.bp.Config().(Config) -} - -func (c *outgoingTransitionContextImpl) DeviceConfig() DeviceConfig { - return DeviceConfig{c.bp.Config().(Config).deviceConfig} -} - -func (c *outgoingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) { - return c.bp.Provider(provider) -} - -func (a *androidTransitionMutator) OutgoingTransition(bpctx blueprint.OutgoingTransitionContext, sourceVariation string) string { - if m, ok := bpctx.Module().(Module); ok { - ctx := outgoingTransitionContextPool.Get().(*outgoingTransitionContextImpl) - defer outgoingTransitionContextPool.Put(ctx) - *ctx = outgoingTransitionContextImpl{ - archModuleContext: m.base().archModuleContextFactory(bpctx), - bp: bpctx, - } - return a.mutator.OutgoingTransition(ctx, sourceVariation) - } else { - return "" - } -} - -type incomingTransitionContextImpl struct { - archModuleContext - bp blueprint.IncomingTransitionContext -} - -func (c *incomingTransitionContextImpl) Module() Module { - return c.bp.Module().(Module) -} - -func (c *incomingTransitionContextImpl) Config() Config { - return c.bp.Config().(Config) -} - -func (c *incomingTransitionContextImpl) DeviceConfig() DeviceConfig { - return DeviceConfig{c.bp.Config().(Config).deviceConfig} -} - -func (c *incomingTransitionContextImpl) IsAddingDependency() bool { - return c.bp.IsAddingDependency() -} - -func (c *incomingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) { - return c.bp.Provider(provider) -} - -func (c *incomingTransitionContextImpl) ModuleErrorf(fmt string, args ...interface{}) { - c.bp.ModuleErrorf(fmt, args) -} - -func (c *incomingTransitionContextImpl) PropertyErrorf(property, fmt string, args ...interface{}) { - c.bp.PropertyErrorf(property, fmt, args) -} - -func (a *androidTransitionMutator) IncomingTransition(bpctx blueprint.IncomingTransitionContext, incomingVariation string) string { - if m, ok := bpctx.Module().(Module); ok { - ctx := incomingTransitionContextPool.Get().(*incomingTransitionContextImpl) - defer incomingTransitionContextPool.Put(ctx) - *ctx = incomingTransitionContextImpl{ - archModuleContext: m.base().archModuleContextFactory(bpctx), - bp: bpctx, - } - return a.mutator.IncomingTransition(ctx, incomingVariation) - } else { - return "" - } -} - -func (a *androidTransitionMutator) Mutate(ctx blueprint.BottomUpMutatorContext, variation string) { - if am, ok := ctx.Module().(Module); ok { - if variation != "" { - // TODO: this should really be checking whether the TransitionMutator affected this module, not - // the empty variant, but TransitionMutator has no concept of skipping a module. - base := am.base() - base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, a.name) - base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variation) - } - - mctx := bottomUpMutatorContextFactory(ctx, am, a.finalPhase) - defer bottomUpMutatorContextPool.Put(mctx) - a.mutator.Mutate(mctx, variation) - } -} - func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) TransitionMutatorHandle { atm := &androidTransitionMutator{ finalPhase: x.finalPhase, diff --git a/android/mutator_test.go b/android/mutator_test.go index 1d5f89042..60a61199f 100644 --- a/android/mutator_test.go +++ b/android/mutator_test.go @@ -83,132 +83,6 @@ func TestMutatorAddMissingDependencies(t *testing.T) { AssertDeepEquals(t, "foo missing deps", []string{"added_missing_dep", "regular_missing_dep"}, foo.missingDeps) } -type testTransitionMutator struct { - split func(ctx BaseModuleContext) []string - outgoingTransition func(ctx OutgoingTransitionContext, sourceVariation string) string - incomingTransition func(ctx IncomingTransitionContext, incomingVariation string) string - mutate func(ctx BottomUpMutatorContext, variation string) -} - -func (t *testTransitionMutator) Split(ctx BaseModuleContext) []string { - if t.split != nil { - return t.split(ctx) - } - return []string{""} -} - -func (t *testTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string { - if t.outgoingTransition != nil { - return t.outgoingTransition(ctx, sourceVariation) - } - return sourceVariation -} - -func (t *testTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string { - if t.incomingTransition != nil { - return t.incomingTransition(ctx, incomingVariation) - } - return incomingVariation -} - -func (t *testTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) { - if t.mutate != nil { - t.mutate(ctx, variation) - } -} - -func TestModuleString(t *testing.T) { - bp := ` - test { - name: "foo", - } - ` - - var moduleStrings []string - - GroupFixturePreparers( - FixtureRegisterWithContext(func(ctx RegistrationContext) { - - ctx.PreArchMutators(func(ctx RegisterMutatorsContext) { - ctx.Transition("pre_arch", &testTransitionMutator{ - split: func(ctx BaseModuleContext) []string { - moduleStrings = append(moduleStrings, ctx.Module().String()) - return []string{"a", "b"} - }, - }) - }) - - ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.Transition("pre_deps", &testTransitionMutator{ - split: func(ctx BaseModuleContext) []string { - moduleStrings = append(moduleStrings, ctx.Module().String()) - return []string{"c", "d"} - }, - }) - }) - - ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.Transition("post_deps", &testTransitionMutator{ - split: func(ctx BaseModuleContext) []string { - moduleStrings = append(moduleStrings, ctx.Module().String()) - return []string{"e", "f"} - }, - outgoingTransition: func(ctx OutgoingTransitionContext, sourceVariation string) string { - return "" - }, - }) - ctx.BottomUp("rename_bottom_up", func(ctx BottomUpMutatorContext) { - moduleStrings = append(moduleStrings, ctx.Module().String()) - ctx.Rename(ctx.Module().base().Name() + "_renamed1") - }).UsesRename() - ctx.BottomUp("final", func(ctx BottomUpMutatorContext) { - moduleStrings = append(moduleStrings, ctx.Module().String()) - }) - }) - - ctx.RegisterModuleType("test", mutatorTestModuleFactory) - }), - FixtureWithRootAndroidBp(bp), - ).RunTest(t) - - want := []string{ - // Initial name. - "foo{}", - - // After pre_arch (reversed because rename_top_down is TopDown so it visits in reverse order). - "foo{pre_arch:b}", - "foo{pre_arch:a}", - - // After pre_deps (reversed because post_deps TransitionMutator.Split is TopDown). - "foo{pre_arch:b,pre_deps:d}", - "foo{pre_arch:b,pre_deps:c}", - "foo{pre_arch:a,pre_deps:d}", - "foo{pre_arch:a,pre_deps:c}", - - // After post_deps. - "foo{pre_arch:a,pre_deps:c,post_deps:e}", - "foo{pre_arch:a,pre_deps:c,post_deps:f}", - "foo{pre_arch:a,pre_deps:d,post_deps:e}", - "foo{pre_arch:a,pre_deps:d,post_deps:f}", - "foo{pre_arch:b,pre_deps:c,post_deps:e}", - "foo{pre_arch:b,pre_deps:c,post_deps:f}", - "foo{pre_arch:b,pre_deps:d,post_deps:e}", - "foo{pre_arch:b,pre_deps:d,post_deps:f}", - - // After rename_bottom_up. - "foo_renamed1{pre_arch:a,pre_deps:c,post_deps:e}", - "foo_renamed1{pre_arch:a,pre_deps:c,post_deps:f}", - "foo_renamed1{pre_arch:a,pre_deps:d,post_deps:e}", - "foo_renamed1{pre_arch:a,pre_deps:d,post_deps:f}", - "foo_renamed1{pre_arch:b,pre_deps:c,post_deps:e}", - "foo_renamed1{pre_arch:b,pre_deps:c,post_deps:f}", - "foo_renamed1{pre_arch:b,pre_deps:d,post_deps:e}", - "foo_renamed1{pre_arch:b,pre_deps:d,post_deps:f}", - } - - AssertDeepEquals(t, "module String() values", want, moduleStrings) -} - func TestFinalDepsPhase(t *testing.T) { bp := ` test { @@ -288,22 +162,3 @@ func TestFinalDepsPhase(t *testing.T) { AssertDeepEquals(t, "final", finalWant, finalGotMap) } - -func TestTransitionMutatorInFinalDeps(t *testing.T) { - GroupFixturePreparers( - FixtureRegisterWithContext(func(ctx RegistrationContext) { - ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.Transition("vars", &testTransitionMutator{ - split: func(ctx BaseModuleContext) []string { - return []string{"a", "b"} - }, - }) - }) - - ctx.RegisterModuleType("test", mutatorTestModuleFactory) - }), - FixtureWithRootAndroidBp(`test {name: "foo"}`), - ). - ExtendWithErrorHandler(FixtureExpectsOneErrorPattern("not allowed in FinalDepsMutators")). - RunTest(t) -} diff --git a/android/transition.go b/android/transition.go new file mode 100644 index 000000000..7c04effc3 --- /dev/null +++ b/android/transition.go @@ -0,0 +1,259 @@ +// 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 android + +import "github.com/google/blueprint" + +// TransitionMutator implements a top-down mechanism where a module tells its +// direct dependencies what variation they should be built in but the dependency +// has the final say. +// +// When implementing a transition mutator, one needs to implement four methods: +// - Split() that tells what variations a module has by itself +// - OutgoingTransition() where a module tells what it wants from its +// dependency +// - IncomingTransition() where a module has the final say about its own +// variation +// - Mutate() that changes the state of a module depending on its variation +// +// That the effective variation of module B when depended on by module A is the +// composition the outgoing transition of module A and the incoming transition +// of module B. +// +// the outgoing transition should not take the properties of the dependency into +// account, only those of the module that depends on it. For this reason, the +// dependency is not even passed into it as an argument. Likewise, the incoming +// transition should not take the properties of the depending module into +// account and is thus not informed about it. This makes for a nice +// decomposition of the decision logic. +// +// A given transition mutator only affects its own variation; other variations +// stay unchanged along the dependency edges. +// +// Soong makes sure that all modules are created in the desired variations and +// that dependency edges are set up correctly. This ensures that "missing +// variation" errors do not happen and allows for more flexible changes in the +// value of the variation among dependency edges (as oppposed to bottom-up +// mutators where if module A in variation X depends on module B and module B +// has that variation X, A must depend on variation X of B) +// +// The limited power of the context objects passed to individual mutators +// methods also makes it more difficult to shoot oneself in the foot. Complete +// safety is not guaranteed because no one prevents individual transition +// mutators from mutating modules in illegal ways and for e.g. Split() or +// Mutate() to run their own visitations of the transitive dependency of the +// module and both of these are bad ideas, but it's better than no guardrails at +// all. +// +// This model is pretty close to Bazel's configuration transitions. The mapping +// between concepts in Soong and Bazel is as follows: +// - Module == configured target +// - Variant == configuration +// - Variation name == configuration flag +// - Variation == configuration flag value +// - Outgoing transition == attribute transition +// - Incoming transition == rule transition +// +// The Split() method does not have a Bazel equivalent and Bazel split +// transitions do not have a Soong equivalent. +// +// Mutate() does not make sense in Bazel due to the different models of the +// two systems: when creating new variations, Soong clones the old module and +// thus some way is needed to change it state whereas Bazel creates each +// configuration of a given configured target anew. +type TransitionMutator interface { + // Split returns the set of variations that should be created for a module no + // matter who depends on it. Used when Make depends on a particular variation + // or when the module knows its variations just based on information given to + // it in the Blueprint file. This method should not mutate the module it is + // called on. + Split(ctx BaseModuleContext) []string + + // OutgoingTransition is called on a module to determine which variation it wants + // from its direct dependencies. The dependency itself can override this decision. + // This method should not mutate the module itself. + OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string + + // IncomingTransition is called on a module to determine which variation it should + // be in based on the variation modules that depend on it want. This gives the module + // a final say about its own variations. This method should not mutate the module + // itself. + IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string + + // Mutate is called after a module was split into multiple variations on each variation. + // It should not split the module any further but adding new dependencies is + // fine. Unlike all the other methods on TransitionMutator, this method is + // allowed to mutate the module. + Mutate(ctx BottomUpMutatorContext, variation string) +} + +type IncomingTransitionContext interface { + ArchModuleContext + ModuleProviderContext + ModuleErrorContext + + // Module returns the target of the dependency edge for which the transition + // is being computed + Module() Module + + // Config returns the configuration for the build. + Config() Config + + DeviceConfig() DeviceConfig + + // IsAddingDependency returns true if the transition is being called while adding a dependency + // after the transition mutator has already run, or false if it is being called when the transition + // mutator is running. This should be used sparingly, all uses will have to be removed in order + // to support creating variants on demand. + IsAddingDependency() bool +} + +type OutgoingTransitionContext interface { + ArchModuleContext + ModuleProviderContext + + // Module returns the target of the dependency edge for which the transition + // is being computed + Module() Module + + // DepTag() Returns the dependency tag through which this dependency is + // reached + DepTag() blueprint.DependencyTag + + // Config returns the configuration for the build. + Config() Config + + DeviceConfig() DeviceConfig +} + +type androidTransitionMutator struct { + finalPhase bool + mutator TransitionMutator + name string +} + +func (a *androidTransitionMutator) Split(ctx blueprint.BaseModuleContext) []string { + if a.finalPhase { + panic("TransitionMutator not allowed in FinalDepsMutators") + } + if m, ok := ctx.Module().(Module); ok { + moduleContext := m.base().baseModuleContextFactory(ctx) + return a.mutator.Split(&moduleContext) + } else { + return []string{""} + } +} + +func (a *androidTransitionMutator) OutgoingTransition(bpctx blueprint.OutgoingTransitionContext, sourceVariation string) string { + if m, ok := bpctx.Module().(Module); ok { + ctx := outgoingTransitionContextPool.Get().(*outgoingTransitionContextImpl) + defer outgoingTransitionContextPool.Put(ctx) + *ctx = outgoingTransitionContextImpl{ + archModuleContext: m.base().archModuleContextFactory(bpctx), + bp: bpctx, + } + return a.mutator.OutgoingTransition(ctx, sourceVariation) + } else { + return "" + } +} + +func (a *androidTransitionMutator) IncomingTransition(bpctx blueprint.IncomingTransitionContext, incomingVariation string) string { + if m, ok := bpctx.Module().(Module); ok { + ctx := incomingTransitionContextPool.Get().(*incomingTransitionContextImpl) + defer incomingTransitionContextPool.Put(ctx) + *ctx = incomingTransitionContextImpl{ + archModuleContext: m.base().archModuleContextFactory(bpctx), + bp: bpctx, + } + return a.mutator.IncomingTransition(ctx, incomingVariation) + } else { + return "" + } +} + +func (a *androidTransitionMutator) Mutate(ctx blueprint.BottomUpMutatorContext, variation string) { + if am, ok := ctx.Module().(Module); ok { + if variation != "" { + // TODO: this should really be checking whether the TransitionMutator affected this module, not + // the empty variant, but TransitionMutator has no concept of skipping a module. + base := am.base() + base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, a.name) + base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variation) + } + + mctx := bottomUpMutatorContextFactory(ctx, am, a.finalPhase) + defer bottomUpMutatorContextPool.Put(mctx) + a.mutator.Mutate(mctx, variation) + } +} + +type incomingTransitionContextImpl struct { + archModuleContext + bp blueprint.IncomingTransitionContext +} + +func (c *incomingTransitionContextImpl) Module() Module { + return c.bp.Module().(Module) +} + +func (c *incomingTransitionContextImpl) Config() Config { + return c.bp.Config().(Config) +} + +func (c *incomingTransitionContextImpl) DeviceConfig() DeviceConfig { + return DeviceConfig{c.bp.Config().(Config).deviceConfig} +} + +func (c *incomingTransitionContextImpl) IsAddingDependency() bool { + return c.bp.IsAddingDependency() +} + +func (c *incomingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) { + return c.bp.Provider(provider) +} + +func (c *incomingTransitionContextImpl) ModuleErrorf(fmt string, args ...interface{}) { + c.bp.ModuleErrorf(fmt, args) +} + +func (c *incomingTransitionContextImpl) PropertyErrorf(property, fmt string, args ...interface{}) { + c.bp.PropertyErrorf(property, fmt, args) +} + +type outgoingTransitionContextImpl struct { + archModuleContext + bp blueprint.OutgoingTransitionContext +} + +func (c *outgoingTransitionContextImpl) Module() Module { + return c.bp.Module().(Module) +} + +func (c *outgoingTransitionContextImpl) DepTag() blueprint.DependencyTag { + return c.bp.DepTag() +} + +func (c *outgoingTransitionContextImpl) Config() Config { + return c.bp.Config().(Config) +} + +func (c *outgoingTransitionContextImpl) DeviceConfig() DeviceConfig { + return DeviceConfig{c.bp.Config().(Config).deviceConfig} +} + +func (c *outgoingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) { + return c.bp.Provider(provider) +} diff --git a/android/transition_test.go b/android/transition_test.go new file mode 100644 index 000000000..f7618f3fe --- /dev/null +++ b/android/transition_test.go @@ -0,0 +1,162 @@ +// 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 android + +import "testing" + +type testTransitionMutator struct { + split func(ctx BaseModuleContext) []string + outgoingTransition func(ctx OutgoingTransitionContext, sourceVariation string) string + incomingTransition func(ctx IncomingTransitionContext, incomingVariation string) string + mutate func(ctx BottomUpMutatorContext, variation string) +} + +func (t *testTransitionMutator) Split(ctx BaseModuleContext) []string { + if t.split != nil { + return t.split(ctx) + } + return []string{""} +} + +func (t *testTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string { + if t.outgoingTransition != nil { + return t.outgoingTransition(ctx, sourceVariation) + } + return sourceVariation +} + +func (t *testTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string { + if t.incomingTransition != nil { + return t.incomingTransition(ctx, incomingVariation) + } + return incomingVariation +} + +func (t *testTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) { + if t.mutate != nil { + t.mutate(ctx, variation) + } +} + +func TestModuleString(t *testing.T) { + bp := ` + test { + name: "foo", + } + ` + + var moduleStrings []string + + GroupFixturePreparers( + FixtureRegisterWithContext(func(ctx RegistrationContext) { + + ctx.PreArchMutators(func(ctx RegisterMutatorsContext) { + ctx.Transition("pre_arch", &testTransitionMutator{ + split: func(ctx BaseModuleContext) []string { + moduleStrings = append(moduleStrings, ctx.Module().String()) + return []string{"a", "b"} + }, + }) + }) + + ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.Transition("pre_deps", &testTransitionMutator{ + split: func(ctx BaseModuleContext) []string { + moduleStrings = append(moduleStrings, ctx.Module().String()) + return []string{"c", "d"} + }, + }) + }) + + ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.Transition("post_deps", &testTransitionMutator{ + split: func(ctx BaseModuleContext) []string { + moduleStrings = append(moduleStrings, ctx.Module().String()) + return []string{"e", "f"} + }, + outgoingTransition: func(ctx OutgoingTransitionContext, sourceVariation string) string { + return "" + }, + }) + ctx.BottomUp("rename_bottom_up", func(ctx BottomUpMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + ctx.Rename(ctx.Module().base().Name() + "_renamed1") + }).UsesRename() + ctx.BottomUp("final", func(ctx BottomUpMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + }) + }) + + ctx.RegisterModuleType("test", mutatorTestModuleFactory) + }), + FixtureWithRootAndroidBp(bp), + ).RunTest(t) + + want := []string{ + // Initial name. + "foo{}", + + // After pre_arch (reversed because rename_top_down is TopDown so it visits in reverse order). + "foo{pre_arch:b}", + "foo{pre_arch:a}", + + // After pre_deps (reversed because post_deps TransitionMutator.Split is TopDown). + "foo{pre_arch:b,pre_deps:d}", + "foo{pre_arch:b,pre_deps:c}", + "foo{pre_arch:a,pre_deps:d}", + "foo{pre_arch:a,pre_deps:c}", + + // After post_deps. + "foo{pre_arch:a,pre_deps:c,post_deps:e}", + "foo{pre_arch:a,pre_deps:c,post_deps:f}", + "foo{pre_arch:a,pre_deps:d,post_deps:e}", + "foo{pre_arch:a,pre_deps:d,post_deps:f}", + "foo{pre_arch:b,pre_deps:c,post_deps:e}", + "foo{pre_arch:b,pre_deps:c,post_deps:f}", + "foo{pre_arch:b,pre_deps:d,post_deps:e}", + "foo{pre_arch:b,pre_deps:d,post_deps:f}", + + // After rename_bottom_up. + "foo_renamed1{pre_arch:a,pre_deps:c,post_deps:e}", + "foo_renamed1{pre_arch:a,pre_deps:c,post_deps:f}", + "foo_renamed1{pre_arch:a,pre_deps:d,post_deps:e}", + "foo_renamed1{pre_arch:a,pre_deps:d,post_deps:f}", + "foo_renamed1{pre_arch:b,pre_deps:c,post_deps:e}", + "foo_renamed1{pre_arch:b,pre_deps:c,post_deps:f}", + "foo_renamed1{pre_arch:b,pre_deps:d,post_deps:e}", + "foo_renamed1{pre_arch:b,pre_deps:d,post_deps:f}", + } + + AssertDeepEquals(t, "module String() values", want, moduleStrings) +} + +func TestTransitionMutatorInFinalDeps(t *testing.T) { + GroupFixturePreparers( + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.Transition("vars", &testTransitionMutator{ + split: func(ctx BaseModuleContext) []string { + return []string{"a", "b"} + }, + }) + }) + + ctx.RegisterModuleType("test", mutatorTestModuleFactory) + }), + FixtureWithRootAndroidBp(`test {name: "foo"}`), + ). + ExtendWithErrorHandler(FixtureExpectsOneErrorPattern("not allowed in FinalDepsMutators")). + RunTest(t) +} diff --git a/apex/apex.go b/apex/apex.go index 9fdb2a2a5..c6b62b58c 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -966,21 +966,8 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { if !ok || !am.CanHaveApexVariants() { return false } - depTag := mctx.OtherModuleDependencyTag(child) - // Check to see if the tag always requires that the child module has an apex variant for every - // apex variant of the parent module. If it does not then it is still possible for something - // else, e.g. the DepIsInSameApex(...) method to decide that a variant is required. - if required, ok := depTag.(android.AlwaysRequireApexVariantTag); ok && required.AlwaysRequireApexVariant() { - return true - } - if !android.IsDepInSameApex(mctx, parent, child) { - return false - } - - // By default, all the transitive dependencies are collected, unless filtered out - // above. - return true + return android.IsDepInSameApex(mctx, parent, child) } android.SetProvider(mctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{}) @@ -2695,7 +2682,7 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { return } - a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { + a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from android.Module, to android.ApexModule, externalDep bool) bool { // As soon as the dependency graph crosses the APEX boundary, don't go further. if externalDep { return false diff --git a/cc/afdo.go b/cc/afdo.go index 14d105e99..03dc2713e 100644 --- a/cc/afdo.go +++ b/cc/afdo.go @@ -163,7 +163,7 @@ func (a *afdoTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitio } // TODO(b/324141705): this is designed to prevent propagating AFDO from static libraries that have afdo: true set, but - // it should be m.static() && !m.staticBinary() so that static binaries use AFDO variants of dependencies. + // it should be m.staticLibrary() so that static binaries use AFDO variants of dependencies. if m.static() { return "" } @@ -188,7 +188,7 @@ func (a *afdoTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, varia if variation == "" { // The empty variation is either a module that has enabled AFDO for itself, or the non-AFDO // variant of a dependency. - if m.afdo.afdoEnabled() && !(m.static() && !m.staticBinary()) && !m.Host() { + if m.afdo.afdoEnabled() && !m.staticLibrary() && !m.Host() { m.afdo.addDep(ctx, ctx.ModuleName()) } } else { @@ -519,6 +519,7 @@ type VendorProperties struct { type ModuleContextIntf interface { static() bool staticBinary() bool + staticLibrary() bool testBinary() bool testLibrary() bool header() bool @@ -1523,6 +1524,10 @@ func (ctx *moduleContextImpl) staticBinary() bool { return ctx.mod.staticBinary() } +func (ctx *moduleContextImpl) staticLibrary() bool { + return ctx.mod.staticLibrary() +} + func (ctx *moduleContextImpl) testBinary() bool { return ctx.mod.testBinary() } @@ -3550,6 +3555,15 @@ func (c *Module) static() bool { return false } +func (c *Module) staticLibrary() bool { + if static, ok := c.linker.(interface { + staticLibrary() bool + }); ok { + return static.staticLibrary() + } + return false +} + func (c *Module) staticBinary() bool { if static, ok := c.linker.(interface { staticBinary() bool diff --git a/cc/coverage.go b/cc/coverage.go index a7618dd96..dbb424f59 100644 --- a/cc/coverage.go +++ b/cc/coverage.go @@ -145,7 +145,7 @@ func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags // Even if we don't have coverage enabled, if any of our object files were compiled // with coverage, then we need to add --coverage to our ldflags. if !cov.linkCoverage { - if ctx.static() && !ctx.staticBinary() { + if ctx.staticLibrary() { // For static libraries, the only thing that changes our object files // are included whole static libraries, so check to see if any of // those have coverage enabled. diff --git a/cc/library.go b/cc/library.go index ea8794644..6485ea364 100644 --- a/cc/library.go +++ b/cc/library.go @@ -1800,12 +1800,17 @@ func (library *libraryDecorator) everInstallable() bool { return library.shared() || library.static() } -// static returns true if this library is for a "static' variant. +// static returns true if this library is for a "static" variant. func (library *libraryDecorator) static() bool { return library.MutatedProperties.VariantIsStatic } -// shared returns true if this library is for a "shared' variant. +// staticLibrary returns true if this library is for a "static"" variant. +func (library *libraryDecorator) staticLibrary() bool { + return library.static() +} + +// shared returns true if this library is for a "shared" variant. func (library *libraryDecorator) shared() bool { return library.MutatedProperties.VariantIsShared } diff --git a/cc/orderfile.go b/cc/orderfile.go index 6e08da7a0..c07a09851 100644 --- a/cc/orderfile.go +++ b/cc/orderfile.go @@ -153,7 +153,7 @@ func (orderfile *orderfile) begin(ctx BaseModuleContext) { } // Currently, we are not enabling orderfiles to begin from static libraries - if ctx.static() && !ctx.staticBinary() { + if ctx.staticLibrary() { return } diff --git a/java/app.go b/java/app.go index bedb45c5e..276e9606d 100644 --- a/java/app.go +++ b/java/app.go @@ -1225,7 +1225,7 @@ func (a *AndroidApp) buildAppDependencyInfo(ctx android.ModuleContext) { } depsInfo := android.DepNameToDepInfoMap{} - a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { + a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from android.Module, to android.ApexModule, externalDep bool) bool { depName := to.Name() // Skip dependencies that are only available to APEXes; they are developed with updatability |