Snap for 8782634 from e76d4122ee550cf1f7da8e64bcd6f77fc5542e00 to sdk-release
Change-Id: I665dc6e68887dcbd5054fb49be77f164173aee59
diff --git a/bootstrap/command.go b/bootstrap/command.go
index 8c045b4..3342bbe 100644
--- a/bootstrap/command.go
+++ b/bootstrap/command.go
@@ -113,6 +113,13 @@
return ninjaDeps
}
+ if ctx.BeforePrepareBuildActionsHook != nil {
+ err := ctx.BeforePrepareBuildActionsHook()
+ if err != nil {
+ fatalErrors([]error{err})
+ }
+ }
+
extraDeps, errs = ctx.PrepareBuildActions(config)
if len(errs) > 0 {
fatalErrors(errs)
diff --git a/bpmodify/bpmodify.go b/bpmodify/bpmodify.go
index 431eb83..aecda73 100644
--- a/bpmodify/bpmodify.go
+++ b/bpmodify/bpmodify.go
@@ -15,6 +15,7 @@
"os/exec"
"path/filepath"
"strings"
+ "syscall"
"unicode"
"github.com/google/blueprint/parser"
@@ -68,6 +69,11 @@
return err
}
defer f.Close()
+
+ if *write {
+ syscall.Flock(int(f.Fd()), syscall.LOCK_EX)
+ }
+
in = f
}
diff --git a/context.go b/context.go
index 47b31ae..5448f1e 100644
--- a/context.go
+++ b/context.go
@@ -75,6 +75,8 @@
// Used for metrics-related event logging.
EventHandler *metrics.EventHandler
+ BeforePrepareBuildActionsHook func() error
+
moduleFactories map[string]ModuleFactory
nameInterface NameInterface
moduleGroups []*moduleGroup
@@ -270,6 +272,11 @@
// set during each runMutator
splitModules modulesOrAliases
+ // Used by TransitionMutator implementations
+ transitionVariations []string
+ currentTransitionMutator string
+ requiredVariationsLock sync.Mutex
+
// set during PrepareBuildActions
actionDefs localBuildActions
@@ -617,6 +624,230 @@
return info
}
+type IncomingTransitionContext interface {
+ // Module returns the target of the dependency edge for which the transition
+ // is being computed
+ Module() Module
+
+ // Config returns the config object that was passed to
+ // Context.PrepareBuildActions.
+ Config() interface{}
+}
+
+type OutgoingTransitionContext interface {
+ // 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() DependencyTag
+}
+
+// Transition mutators implement 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 {
+ // 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
+
+ // 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
+
+ // 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
+
+ // 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 transitionMutatorImpl struct {
+ name string
+ mutator TransitionMutator
+}
+
+// Adds each argument in items to l if it's not already there.
+func addToStringListIfNotPresent(l []string, items ...string) []string {
+OUTER:
+ for _, i := range items {
+ for _, existing := range l {
+ if existing == i {
+ continue OUTER
+ }
+ }
+
+ l = append(l, i)
+ }
+
+ return l
+}
+
+func (t *transitionMutatorImpl) addRequiredVariation(m *moduleInfo, variation string) {
+ m.requiredVariationsLock.Lock()
+ defer m.requiredVariationsLock.Unlock()
+
+ // This is only a consistency check. Leaking the variations of a transition
+ // mutator to another one could well lead to issues that are difficult to
+ // track down.
+ if m.currentTransitionMutator != "" && m.currentTransitionMutator != t.name {
+ panic(fmt.Errorf("transition mutator is %s in mutator %s", m.currentTransitionMutator, t.name))
+ }
+
+ m.currentTransitionMutator = t.name
+ m.transitionVariations = addToStringListIfNotPresent(m.transitionVariations, variation)
+}
+
+func (t *transitionMutatorImpl) topDownMutator(mctx TopDownMutatorContext) {
+ module := mctx.(*mutatorContext).module
+ mutatorSplits := t.mutator.Split(mctx)
+ if mutatorSplits == nil || len(mutatorSplits) == 0 {
+ panic(fmt.Errorf("transition mutator %s returned no splits for module %s", t.name, mctx.ModuleName()))
+ }
+
+ // transitionVariations for given a module can be mutated by the module itself
+ // and modules that directly depend on it. Since this is a top-down mutator,
+ // all modules that directly depend on this module have already been processed
+ // so no locking is necessary.
+ module.transitionVariations = addToStringListIfNotPresent(module.transitionVariations, mutatorSplits...)
+ sort.Strings(module.transitionVariations)
+
+ for _, srcVariation := range module.transitionVariations {
+ for _, dep := range module.directDeps {
+ finalVariation := t.transition(mctx)(mctx.Module(), srcVariation, dep.module.logicModule, dep.tag)
+ t.addRequiredVariation(dep.module, finalVariation)
+ }
+ }
+}
+
+type transitionContextImpl struct {
+ module Module
+ depTag DependencyTag
+ config interface{}
+}
+
+func (c *transitionContextImpl) Module() Module {
+ return c.module
+}
+
+func (c *transitionContextImpl) DepTag() DependencyTag {
+ return c.depTag
+}
+
+func (c *transitionContextImpl) Config() interface{} {
+ return c.config
+}
+
+func (t *transitionMutatorImpl) transition(mctx BaseMutatorContext) Transition {
+ return func(source Module, sourceVariation string, dep Module, depTag DependencyTag) string {
+ tc := &transitionContextImpl{module: dep, depTag: depTag, config: mctx.Config()}
+ outgoingVariation := t.mutator.OutgoingTransition(tc, sourceVariation)
+ finalVariation := t.mutator.IncomingTransition(tc, outgoingVariation)
+ return finalVariation
+ }
+}
+
+func (t *transitionMutatorImpl) bottomUpMutator(mctx BottomUpMutatorContext) {
+ mc := mctx.(*mutatorContext)
+ // Fetch and clean up transition mutator state. No locking needed since the
+ // only time interaction between multiple modules is required is during the
+ // computation of the variations required by a given module.
+ variations := mc.module.transitionVariations
+ mc.module.transitionVariations = nil
+ mc.module.currentTransitionMutator = ""
+
+ if len(variations) < 1 {
+ panic(fmt.Errorf("no variations found for module %s by mutator %s",
+ mctx.ModuleName(), t.name))
+ }
+
+ if len(variations) == 1 && variations[0] == "" {
+ // Module is not split, just apply the transition
+ mc.applyTransition(t.transition(mctx))
+ } else {
+ mc.createVariationsWithTransition(t.transition(mctx), variations...)
+ }
+}
+
+func (t *transitionMutatorImpl) mutateMutator(mctx BottomUpMutatorContext) {
+ module := mctx.(*mutatorContext).module
+ currentVariation := module.variant.variations[t.name]
+ t.mutator.Mutate(mctx, currentVariation)
+}
+
+func (c *Context) RegisterTransitionMutator(name string, mutator TransitionMutator) {
+ impl := &transitionMutatorImpl{name: name, mutator: mutator}
+
+ c.RegisterTopDownMutator(name+"_deps", impl.topDownMutator).Parallel()
+ c.RegisterBottomUpMutator(name, impl.bottomUpMutator).Parallel()
+ c.RegisterBottomUpMutator(name+"_mutate", impl.mutateMutator).Parallel()
+}
+
type MutatorHandle interface {
// Set the mutator to visit modules in parallel while maintaining ordering. Calling any
// method on the mutator context is thread-safe, but the mutator must handle synchronization
@@ -1301,7 +1532,7 @@
}
func (c *Context) createVariations(origModule *moduleInfo, mutatorName string,
- defaultVariationName *string, variationNames []string, local bool) (modulesOrAliases, []error) {
+ depChooser depChooser, variationNames []string, local bool) (modulesOrAliases, []error) {
if len(variationNames) == 0 {
panic(fmt.Errorf("mutator %q passed zero-length variation list for module %q",
@@ -1337,7 +1568,7 @@
newModules = append(newModules, newModule)
- newErrs := c.convertDepsToVariation(newModule, mutatorName, variationName, defaultVariationName)
+ newErrs := c.convertDepsToVariation(newModule, depChooser)
if len(newErrs) > 0 {
errs = append(errs, newErrs...)
}
@@ -1353,31 +1584,79 @@
return newModules, errs
}
-func (c *Context) convertDepsToVariation(module *moduleInfo,
- mutatorName, variationName string, defaultVariationName *string) (errs []error) {
+type depChooser func(source *moduleInfo, dep depInfo) (*moduleInfo, string)
+// This function is called for every dependency edge to determine which
+// variation of the dependency is needed. Its inputs are the depending module,
+// its variation, the dependency and the dependency tag.
+type Transition func(source Module, sourceVariation string, dep Module, depTag DependencyTag) string
+
+func chooseDepByTransition(mutatorName string, transition Transition) depChooser {
+ return func(source *moduleInfo, dep depInfo) (*moduleInfo, string) {
+ sourceVariation := source.variant.variations[mutatorName]
+ depLogicModule := dep.module.logicModule
+ if depLogicModule == nil {
+ // This is really a lie because the original dependency before the split
+ // went away when it was split. We choose an arbitrary split module
+ // instead and hope that whatever information the transition wants from it
+ // is the same as in the original one
+ // TODO(lberki): this can be fixed by calling transition() once and saving
+ // its results somewhere
+ depLogicModule = dep.module.splitModules[0].moduleOrAliasTarget().logicModule
+ }
+
+ desiredVariation := transition(source.logicModule, sourceVariation, depLogicModule, dep.tag)
+ for _, m := range dep.module.splitModules {
+ if m.moduleOrAliasVariant().variations[mutatorName] == desiredVariation {
+ return m.moduleOrAliasTarget(), ""
+ }
+ }
+
+ return nil, desiredVariation
+ }
+}
+
+func chooseDep(candidates modulesOrAliases, mutatorName, variationName string, defaultVariationName *string) (*moduleInfo, string) {
+ for _, m := range candidates {
+ if m.moduleOrAliasVariant().variations[mutatorName] == variationName {
+ return m.moduleOrAliasTarget(), ""
+ }
+ }
+
+ if defaultVariationName != nil {
+ // give it a second chance; match with defaultVariationName
+ for _, m := range candidates {
+ if m.moduleOrAliasVariant().variations[mutatorName] == *defaultVariationName {
+ return m.moduleOrAliasTarget(), ""
+ }
+ }
+ }
+
+ return nil, variationName
+}
+
+func chooseDepExplicit(mutatorName string,
+ variationName string, defaultVariationName *string) depChooser {
+ return func(source *moduleInfo, dep depInfo) (*moduleInfo, string) {
+ return chooseDep(dep.module.splitModules, mutatorName, variationName, defaultVariationName)
+ }
+}
+
+func chooseDepInherit(mutatorName string, defaultVariationName *string) depChooser {
+ return func(source *moduleInfo, dep depInfo) (*moduleInfo, string) {
+ sourceVariation := source.variant.variations[mutatorName]
+ return chooseDep(dep.module.splitModules, mutatorName, sourceVariation, defaultVariationName)
+ }
+}
+
+func (c *Context) convertDepsToVariation(module *moduleInfo, depChooser depChooser) (errs []error) {
for i, dep := range module.directDeps {
if dep.module.logicModule == nil {
- var newDep *moduleInfo
- for _, m := range dep.module.splitModules {
- if m.moduleOrAliasVariant().variations[mutatorName] == variationName {
- newDep = m.moduleOrAliasTarget()
- break
- }
- }
- if newDep == nil && defaultVariationName != nil {
- // give it a second chance; match with defaultVariationName
- for _, m := range dep.module.splitModules {
- if m.moduleOrAliasVariant().variations[mutatorName] == *defaultVariationName {
- newDep = m.moduleOrAliasTarget()
- break
- }
- }
- }
+ newDep, missingVariation := depChooser(module, dep)
if newDep == nil {
errs = append(errs, &BlueprintError{
Err: fmt.Errorf("failed to find variation %q for module %q needed by %q",
- variationName, dep.module.Name(), module.Name()),
+ missingVariation, dep.module.Name(), module.Name()),
Pos: module.pos,
})
continue
@@ -2253,6 +2532,7 @@
Deps []jsonDep
Type string
Blueprint string
+ CreatedBy *string
Module map[string]interface{}
}
@@ -2282,6 +2562,18 @@
AddJSONData(d *map[string]interface{})
}
+// JSONAction contains the action-related info we expose to json module graph
+type JSONAction struct {
+ Inputs []string
+ Outputs []string
+}
+
+// JSONActionSupplier allows JSON representation of additional actions that are not registered in
+// Ninja
+type JSONActionSupplier interface {
+ JSONActions() []JSONAction
+}
+
func jsonModuleFromModuleInfo(m *moduleInfo) *JsonModule {
result := &JsonModule{
jsonModuleName: *jsonModuleNameFromModuleInfo(m),
@@ -2290,6 +2582,10 @@
Blueprint: m.relBlueprintsFile,
Module: make(map[string]interface{}),
}
+ if m.createdBy != nil {
+ n := m.createdBy.Name()
+ result.CreatedBy = &n
+ }
if j, ok := m.logicModule.(JSONDataSupplier); ok {
j.AddJSONData(&result.Module)
}
@@ -2311,17 +2607,27 @@
Blueprint: m.relBlueprintsFile,
Module: make(map[string]interface{}),
}
- var actions []map[string]interface{}
+ var actions []JSONAction
for _, bDef := range m.actionDefs.buildDefs {
- actions = append(actions, map[string]interface{}{
- "Inputs": append(
+ actions = append(actions, JSONAction{
+ Inputs: append(
getNinjaStringsWithNilPkgNames(bDef.Inputs),
getNinjaStringsWithNilPkgNames(bDef.Implicits)...),
- "Outputs": append(
+ Outputs: append(
getNinjaStringsWithNilPkgNames(bDef.Outputs),
getNinjaStringsWithNilPkgNames(bDef.ImplicitOutputs)...),
})
}
+
+ if j, ok := m.logicModule.(JSONActionSupplier); ok {
+ actions = append(actions, j.JSONActions()...)
+ }
+ for _, p := range m.providers {
+ if j, ok := p.(JSONActionSupplier); ok {
+ actions = append(actions, j.JSONActions()...)
+ }
+ }
+
result.Module["Actions"] = actions
return result
}
@@ -3619,7 +3925,7 @@
return ""
}
-// WriteBuildFile writes the Ninja manifeset text for the generated build
+// WriteBuildFile writes the Ninja manifest text for the generated build
// actions to w. If this is called before PrepareBuildActions successfully
// completes then ErrBuildActionsNotReady is returned.
func (c *Context) WriteBuildFile(w io.StringWriter) error {
@@ -4095,6 +4401,10 @@
c.EventHandler.End(name)
}
+func (c *Context) SetBeforePrepareBuildActionsHook(hookFn func() error) {
+ c.BeforePrepareBuildActionsHook = hookFn
+}
+
func (c *Context) writeLocalBuildActions(nw *ninjaWriter,
defs *localBuildActions) error {
diff --git a/metrics/event_handler.go b/metrics/event_handler.go
index c19d039..f6bcaf1 100644
--- a/metrics/event_handler.go
+++ b/metrics/event_handler.go
@@ -62,6 +62,13 @@
h.scopeStartTimes = append(h.scopeStartTimes, _now())
}
+// Do wraps a function with calls to Begin() and End().
+func (h *EventHandler) Do(name string, f func()) {
+ h.Begin(name)
+ defer h.End(name)
+ f()
+}
+
// End logs the end of an event. All events nested within this event must have
// themselves been marked completed.
func (h *EventHandler) End(name string) {
diff --git a/module_ctx.go b/module_ctx.go
index b7246c1..f2a04c8 100644
--- a/module_ctx.go
+++ b/module_ctx.go
@@ -991,11 +991,17 @@
}
func (mctx *mutatorContext) CreateVariations(variationNames ...string) []Module {
- return mctx.createVariations(variationNames, false)
+ depChooser := chooseDepInherit(mctx.name, mctx.defaultVariation)
+ return mctx.createVariations(variationNames, depChooser, false)
+}
+
+func (mctx *mutatorContext) createVariationsWithTransition(transition Transition, variationNames ...string) []Module {
+ return mctx.createVariations(variationNames, chooseDepByTransition(mctx.name, transition), false)
}
func (mctx *mutatorContext) CreateLocalVariations(variationNames ...string) []Module {
- return mctx.createVariations(variationNames, true)
+ depChooser := chooseDepInherit(mctx.name, mctx.defaultVariation)
+ return mctx.createVariations(variationNames, depChooser, true)
}
func (mctx *mutatorContext) SetVariationProvider(module Module, provider ProviderKey, value interface{}) {
@@ -1008,9 +1014,9 @@
panic(fmt.Errorf("module %q is not a newly created variant of %q", module, mctx.module))
}
-func (mctx *mutatorContext) createVariations(variationNames []string, local bool) []Module {
+func (mctx *mutatorContext) createVariations(variationNames []string, depChooser depChooser, local bool) []Module {
var ret []Module
- modules, errs := mctx.context.createVariations(mctx.module, mctx.name, mctx.defaultVariation, variationNames, local)
+ modules, errs := mctx.context.createVariations(mctx.module, mctx.name, depChooser, variationNames, local)
if len(errs) > 0 {
mctx.errs = append(mctx.errs, errs...)
}
@@ -1091,8 +1097,13 @@
panic(fmt.Errorf("no %q variation in module variations %q", targetVariationName, foundVariations))
}
+func (mctx *mutatorContext) applyTransition(transition Transition) {
+ mctx.context.convertDepsToVariation(mctx.module, chooseDepByTransition(mctx.name, transition))
+}
+
func (mctx *mutatorContext) SetDependencyVariation(variationName string) {
- mctx.context.convertDepsToVariation(mctx.module, mctx.name, variationName, nil)
+ mctx.context.convertDepsToVariation(mctx.module, chooseDepExplicit(
+ mctx.name, variationName, nil))
}
func (mctx *mutatorContext) SetDefaultDependencyVariation(variationName *string) {
diff --git a/proptools/escape.go b/proptools/escape.go
index 4ef10f0..9443a95 100644
--- a/proptools/escape.go
+++ b/proptools/escape.go
@@ -122,4 +122,8 @@
return ShellEscape(NinjaEscape(s))
}
+func NinjaAndShellEscapeIncludingSpaces(s string) string {
+ return ShellEscapeIncludingSpaces(NinjaEscape(s))
+}
+
var singleQuoteReplacer = strings.NewReplacer(`'`, `'\''`)
diff --git a/singleton_ctx.go b/singleton_ctx.go
index 455f6fc..d579b8e 100644
--- a/singleton_ctx.go
+++ b/singleton_ctx.go
@@ -157,6 +157,10 @@
// Fs returns a pathtools.Filesystem that can be used to interact with files. Using the Filesystem interface allows
// the singleton to be used in build system tests that run against a mock filesystem.
Fs() pathtools.FileSystem
+
+ // ModuleVariantsFromName returns the list of module variants named `name` in the same namespace as `referer`.
+ // Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context.
+ ModuleVariantsFromName(referer Module, name string) []Module
}
var _ SingletonContext = (*singletonContext)(nil)
@@ -369,3 +373,26 @@
func (s *singletonContext) Fs() pathtools.FileSystem {
return s.context.fs
}
+
+func (s *singletonContext) ModuleVariantsFromName(referer Module, name string) []Module {
+ c := s.context
+
+ refererInfo := c.moduleInfo[referer]
+ if refererInfo == nil {
+ s.ModuleErrorf(referer, "could not find module %q", referer.Name())
+ return nil
+ }
+
+ moduleGroup, exists := c.nameInterface.ModuleFromName(name, refererInfo.namespace())
+ if !exists {
+ return nil
+ }
+ result := make([]Module, 0, len(moduleGroup.modules))
+ for _, module := range moduleGroup.modules {
+ moduleInfo := module.module()
+ if moduleInfo != nil {
+ result = append(result, moduleInfo.logicModule)
+ }
+ }
+ return result
+}