Extract module naming into an interface

in facilitate moving name resolution to Soong

Bug: 65683273
Test: build/soong/scripts/diff_build_graphs.sh \
      --products=aosp_arm \
      'build/blueprint:work^' 'build/blueprint:work'
      # and see that the only changes were:
      # 1. adding the name_interface.go file
      # 2. changing some line numbers

Change-Id: Ifa7603cca59b3b3d592f2f146fdafe57012bd4b9
This commit is contained in:
Jeff Gaston 2017-11-10 15:12:08 -08:00
parent 90d258833a
commit d70bf75491
6 changed files with 254 additions and 90 deletions

View file

@ -12,6 +12,7 @@ bootstrap_go_package {
"live_tracker.go",
"mangle.go",
"module_ctx.go",
"name_interface.go",
"ninja_defs.go",
"ninja_strings.go",
"ninja_writer.go",

View file

@ -69,7 +69,7 @@ const MockModuleListFile = "bplist"
type Context struct {
// set at instantiation
moduleFactories map[string]ModuleFactory
moduleNames map[string]*moduleGroup
nameInterface NameInterface
moduleGroups []*moduleGroup
moduleInfo map[Module]*moduleInfo
modulesSorted []*moduleInfo
@ -104,8 +104,8 @@ type Context struct {
requiredNinjaMinor int // For the ninja_required_version variable
requiredNinjaMicro int // For the ninja_required_version variable
// set lazily by sortedModuleNames
cachedSortedModuleNames []string
// set lazily by sortedModuleGroups
cachedSortedModuleGroups []*moduleGroup
globs map[string]GlobPath
globLock sync.Mutex
@ -158,6 +158,8 @@ type moduleGroup struct {
ninjaName string
modules []*moduleInfo
namespace Namespace
}
type moduleInfo struct {
@ -211,6 +213,10 @@ func (module *moduleInfo) String() string {
return s
}
func (module *moduleInfo) namespace() Namespace {
return module.group.namespace
}
// A Variation is a way that a variant of a module differs from other variants of the same module.
// For example, two variants of the same module might have Variation{"arch","arm"} and
// Variation{"arch","arm64"}
@ -270,7 +276,7 @@ type mutatorInfo struct {
func newContext() *Context {
return &Context{
moduleFactories: make(map[string]ModuleFactory),
moduleNames: make(map[string]*moduleGroup),
nameInterface: NewSimpleNameInterface(),
moduleInfo: make(map[Module]*moduleInfo),
moduleNinjaNames: make(map[string]*moduleGroup),
globs: make(map[string]GlobPath),
@ -416,6 +422,10 @@ func (c *Context) RegisterPreSingletonType(name string, factory SingletonFactory
})
}
func (c *Context) SetNameInterface(i NameInterface) {
c.nameInterface = i
}
func singletonPkgPath(singleton Singleton) string {
typ := reflect.TypeOf(singleton)
for typ.Kind() == reflect.Ptr {
@ -1233,21 +1243,7 @@ func (c *Context) addModule(module *moduleInfo) []error {
name := module.logicModule.Name()
c.moduleInfo[module.logicModule] = module
if group, present := c.moduleNames[name]; present {
return []error{
&BlueprintError{
Err: fmt.Errorf("module %q already defined", name),
Pos: module.pos,
},
&BlueprintError{
Err: fmt.Errorf("<-- previous definition here"),
Pos: group.modules[0].pos,
},
}
}
ninjaName := toNinjaName(name)
// The sanitizing in toNinjaName can result in collisions, uniquify the name if it
// already exists
for i := 0; c.moduleNinjaNames[ninjaName] != nil; i++ {
@ -1260,7 +1256,18 @@ func (c *Context) addModule(module *moduleInfo) []error {
modules: []*moduleInfo{module},
}
module.group = group
c.moduleNames[name] = group
namespace, errs := c.nameInterface.NewModule(
&moduleCreationContextImpl{c.ModuleDir(module.logicModule)},
ModuleGroup{moduleGroup: group},
module.logicModule)
if len(errs) > 0 {
for i := range errs {
errs[i] = &BlueprintError{Err: errs[i], Pos: module.pos}
}
return errs
}
group.namespace = namespace
c.moduleNinjaNames[ninjaName] = group
c.moduleGroups = append(c.moduleGroups, group)
@ -1347,17 +1354,9 @@ func (c *Context) addDependency(module *moduleInfo, tag DependencyTag, depName s
}}
}
possibleDeps := c.modulesFromName(depName)
possibleDeps := c.modulesFromName(depName, module.namespace())
if possibleDeps == nil {
if c.allowMissingDependencies {
module.missingDeps = append(module.missingDeps, depName)
return nil
}
return []error{&BlueprintError{
Err: fmt.Errorf("%q depends on undefined module %q",
module.Name(), depName),
Pos: module.pos,
}}
return c.discoveredMissingDependencies(module, depName)
}
if m := c.findMatchingVariant(module, possibleDeps); m != nil {
@ -1395,7 +1394,7 @@ func (c *Context) findReverseDependency(module *moduleInfo, destName string) (*m
}}
}
possibleDeps := c.modulesFromName(destName)
possibleDeps := c.modulesFromName(destName, module.namespace())
if possibleDeps == nil {
return nil, []error{&BlueprintError{
Err: fmt.Errorf("%q has a reverse dependency on undefined module %q",
@ -1429,17 +1428,9 @@ func (c *Context) addVariationDependency(module *moduleInfo, variations []Variat
panic("BaseDependencyTag is not allowed to be used directly!")
}
possibleDeps := c.modulesFromName(depName)
possibleDeps := c.modulesFromName(depName, module.namespace())
if possibleDeps == nil {
if c.allowMissingDependencies {
module.missingDeps = append(module.missingDeps, depName)
return nil
}
return []error{&BlueprintError{
Err: fmt.Errorf("%q depends on undefined module %q",
module.Name(), depName),
Pos: module.pos,
}}
return c.discoveredMissingDependencies(module, depName)
}
// We can't just append variant.Variant to module.dependencyVariants.variantName and
@ -2190,11 +2181,7 @@ func (c *Context) generateModuleBuildActions(config interface{},
if module.missingDeps != nil && !mctx.handledMissingDeps {
var errs []error
for _, depName := range module.missingDeps {
errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q depends on undefined module %q",
module.Name(), depName),
Pos: module.pos,
})
errs = append(errs, c.missingDependencyError(module, depName))
}
errsCh <- errs
return true
@ -2359,7 +2346,7 @@ type rename struct {
}
func (c *Context) moduleMatchingVariant(module *moduleInfo, name string) *moduleInfo {
targets := c.modulesFromName(name)
targets := c.modulesFromName(name, module.namespace())
if targets == nil {
return nil
@ -2378,29 +2365,11 @@ func (c *Context) handleRenames(renames []rename) []error {
var errs []error
for _, rename := range renames {
group, name := rename.group, rename.name
if name == group.name {
if name == group.name || len(group.modules) < 1 {
continue
}
existing := c.moduleNames[name]
if existing != nil {
errs = append(errs,
&BlueprintError{
Err: fmt.Errorf("renaming module %q to %q conflicts with existing module",
group.name, name),
Pos: group.modules[0].pos,
},
&BlueprintError{
Err: fmt.Errorf("<-- existing module defined here"),
Pos: existing.modules[0].pos,
},
)
continue
}
c.moduleNames[name] = group
delete(c.moduleNames, group.name)
group.name = name
errs = append(errs, c.nameInterface.Rename(group.name, rename.name, group.namespace)...)
}
return errs
@ -2423,24 +2392,45 @@ func (c *Context) handleReplacements(replacements []replace) []error {
return errs
}
func (c *Context) modulesFromName(name string) []*moduleInfo {
if group := c.moduleNames[name]; group != nil {
func (c *Context) discoveredMissingDependencies(module *moduleInfo, depName string) (errs []error) {
if c.allowMissingDependencies {
module.missingDeps = append(module.missingDeps, depName)
return nil
}
return []error{c.missingDependencyError(module, depName)}
}
func (c *Context) missingDependencyError(module *moduleInfo, depName string) (errs error) {
err := c.nameInterface.MissingDependencyError(module.Name(), module.namespace(), depName)
return &BlueprintError{
Err: err,
Pos: module.pos,
}
}
func (c *Context) modulesFromName(name string, namespace Namespace) []*moduleInfo {
group, exists := c.nameInterface.ModuleFromName(name, namespace)
if exists {
return group.modules
}
return nil
}
func (c *Context) sortedModuleNames() []string {
if c.cachedSortedModuleNames == nil {
c.cachedSortedModuleNames = make([]string, 0, len(c.moduleNames))
for moduleName := range c.moduleNames {
c.cachedSortedModuleNames = append(c.cachedSortedModuleNames,
moduleName)
func (c *Context) sortedModuleGroups() []*moduleGroup {
if c.cachedSortedModuleGroups == nil {
unwrap := func(wrappers []ModuleGroup) []*moduleGroup {
result := make([]*moduleGroup, 0, len(wrappers))
for _, group := range wrappers {
result = append(result, group.moduleGroup)
}
return result
}
sort.Strings(c.cachedSortedModuleNames)
c.cachedSortedModuleGroups = unwrap(c.nameInterface.AllModules())
}
return c.cachedSortedModuleNames
return c.cachedSortedModuleGroups
}
func (c *Context) visitAllModules(visit func(Module)) {
@ -2453,9 +2443,8 @@ func (c *Context) visitAllModules(visit func(Module)) {
}
}()
for _, moduleName := range c.sortedModuleNames() {
modules := c.modulesFromName(moduleName)
for _, module = range modules {
for _, moduleGroup := range c.sortedModuleGroups() {
for _, module = range moduleGroup.modules {
visit(module.logicModule)
}
}
@ -2473,9 +2462,8 @@ func (c *Context) visitAllModulesIf(pred func(Module) bool,
}
}()
for _, moduleName := range c.sortedModuleNames() {
modules := c.modulesFromName(moduleName)
for _, module := range modules {
for _, moduleGroup := range c.sortedModuleGroups() {
for _, module := range moduleGroup.modules {
if pred(module.logicModule) {
visit(module.logicModule)
}

View file

@ -183,7 +183,7 @@ func TestWalkDeps(t *testing.T) {
var outputDown string
var outputUp string
topModule := ctx.modulesFromName("A")[0]
topModule := ctx.modulesFromName("A", nil)[0]
ctx.walkDeps(topModule,
func(dep depInfo, parent *moduleInfo) bool {
if dep.module.logicModule.(Walker).Walk() {
@ -239,10 +239,10 @@ func TestCreateModule(t *testing.T) {
t.FailNow()
}
a := ctx.modulesFromName("A")[0].logicModule.(*fooModule)
b := ctx.modulesFromName("B")[0].logicModule.(*barModule)
c := ctx.modulesFromName("C")[0].logicModule.(*barModule)
d := ctx.modulesFromName("D")[0].logicModule.(*fooModule)
a := ctx.modulesFromName("A", nil)[0].logicModule.(*fooModule)
b := ctx.modulesFromName("B", nil)[0].logicModule.(*barModule)
c := ctx.modulesFromName("C", nil)[0].logicModule.(*barModule)
d := ctx.modulesFromName("D", nil)[0].logicModule.(*fooModule)
checkDeps := func(m Module, expected string) {
var deps []string

View file

@ -142,6 +142,8 @@ type BaseModuleContext interface {
moduleInfo() *moduleInfo
error(err error)
Namespace() Namespace
}
type DynamicDependerModuleContext BottomUpMutatorContext
@ -269,6 +271,10 @@ func (d *baseModuleContext) Fs() pathtools.FileSystem {
return d.context.fs
}
func (d *baseModuleContext) Namespace() Namespace {
return d.context.nameInterface.GetNamespace(d)
}
var _ ModuleContext = (*moduleContext)(nil)
type moduleContext struct {
@ -650,7 +656,8 @@ func (mctx *mutatorContext) Module() Module {
// correctly for all future mutator passes.
func (mctx *mutatorContext) AddDependency(module Module, tag DependencyTag, deps ...string) {
for _, dep := range deps {
errs := mctx.context.addDependency(mctx.context.moduleInfo[module], tag, dep)
modInfo := mctx.context.moduleInfo[module]
errs := mctx.context.addDependency(modInfo, tag, dep)
if len(errs) > 0 {
mctx.errs = append(mctx.errs, errs...)
}
@ -731,7 +738,8 @@ func (mctx *mutatorContext) ReplaceDependencies(name string) {
}
func (mctx *mutatorContext) OtherModuleExists(name string) bool {
return mctx.context.moduleNames[name] != nil
_, exists := mctx.context.nameInterface.ModuleFromName(name, mctx.module.namespace())
return exists
}
// Rename all variants of a module. The new name is not visible to calls to ModuleName,

167
name_interface.go Normal file
View file

@ -0,0 +1,167 @@
// Copyright 2017 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 (
"fmt"
"sort"
)
// This file exposes the logic of locating a module via a query string, to enable
// other projects to override it if desired.
// The default name resolution implementation, SimpleNameInterface,
// just treats the query string as a module name, and does a simple map lookup.
// A ModuleGroup just points to a moduleGroup to allow external packages to refer
// to a moduleGroup but not use it
type ModuleGroup struct {
*moduleGroup
}
func (h *ModuleGroup) String() string {
return h.moduleGroup.name
}
// The Namespace interface is just a marker interface for usage by the NameInterface,
// to allow a NameInterface to specify that a certain parameter should be a Namespace.
// In practice, a specific NameInterface will expect to only give and receive structs of
// the same concrete type, but because Go doesn't support generics, we use a marker interface
// for a little bit of clarity, and expect implementers to do typecasting instead.
type Namespace interface {
namespace(Namespace)
}
type NamespaceMarker struct {
}
func (m *NamespaceMarker) namespace(Namespace) {
}
// A NameInterface tells how to locate modules by name.
// There should only be one name interface per Context, but potentially many namespaces
type NameInterface interface {
// Gets called when a new module is created
NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error)
// Finds the module with the given name
ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool)
// Returns an error indicating that the given module could not be found.
// The error contains some diagnostic information about where the dependency can be found.
MissingDependencyError(depender string, dependerNamespace Namespace, depName string) (err error)
// Rename
Rename(oldName string, newName string, namespace Namespace) []error
// Returns all modules in a deterministic order.
AllModules() []ModuleGroup
// gets the namespace for a given path
GetNamespace(ctx NamespaceContext) (namespace Namespace)
}
// A NamespaceContext stores the information given to a NameInterface to enable the NameInterface
// to choose the namespace for any given module
type NamespaceContext interface {
ModuleDir() string
}
type moduleCreationContextImpl struct {
moduleDir string
}
func (ctx *moduleCreationContextImpl) ModuleDir() string {
return ctx.moduleDir
}
// a SimpleNameInterface just stores all modules in a map based on name
type SimpleNameInterface struct {
modules map[string]ModuleGroup
}
func NewSimpleNameInterface() *SimpleNameInterface {
return &SimpleNameInterface{
modules: make(map[string]ModuleGroup),
}
}
func (s *SimpleNameInterface) NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error) {
name := group.name
if group, present := s.modules[name]; present {
return nil, []error{
// seven characters at the start of the second line to align with the string "error: "
fmt.Errorf("module %q already defined\n"+
" %s <-- previous definition here", name, group.modules[0].pos),
}
}
s.modules[name] = group
return nil, []error{}
}
func (s *SimpleNameInterface) ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) {
group, found = s.modules[moduleName]
return group, found
}
func (s *SimpleNameInterface) Rename(oldName string, newName string, namespace Namespace) (errs []error) {
existingGroup, exists := s.modules[newName]
if exists {
errs = append(errs,
// seven characters at the start of the second line to align with the string "error: "
fmt.Errorf("renaming module %q to %q conflicts with existing module\n"+
" %s <-- existing module defined here",
oldName, newName, existingGroup.modules[0].pos),
)
return errs
}
group := s.modules[oldName]
s.modules[newName] = group
delete(s.modules, group.name)
group.name = newName
return []error{}
}
func (s *SimpleNameInterface) AllModules() []ModuleGroup {
groups := make([]ModuleGroup, 0, len(s.modules))
for _, group := range s.modules {
groups = append(groups, group)
}
duplicateName := ""
less := func(i, j int) bool {
if groups[i].name == groups[j].name {
duplicateName = groups[i].name
}
return groups[i].name < groups[j].name
}
sort.Slice(groups, less)
if duplicateName != "" {
// It is permitted to have two moduleGroup's with the same name, but not within the same
// Namespace. The SimpleNameInterface should catch this in NewModule, however, so this
// should never happen.
panic(fmt.Sprintf("Duplicate moduleGroup name %q", duplicateName))
}
return groups
}
func (s *SimpleNameInterface) MissingDependencyError(depender string, dependerNamespace Namespace, dependency string) (err error) {
return fmt.Errorf("%q depends on undefined module %q", depender, dependency)
}
func (s *SimpleNameInterface) GetNamespace(ctx NamespaceContext) Namespace {
return nil
}

View file

@ -141,7 +141,7 @@ func setupVisitTest(t *testing.T) *Context {
func TestVisit(t *testing.T) {
ctx := setupVisitTest(t)
topModule := ctx.modulesFromName("A")[0].logicModule.(*visitModule)
topModule := ctx.modulesFromName("A", nil)[0].logicModule.(*visitModule)
assertString(t, topModule.properties.VisitDepsDepthFirst, "EDCB")
assertString(t, topModule.properties.VisitDepsDepthFirstIf, "EDC")
assertString(t, topModule.properties.VisitDirectDeps, "B")