d457f11884
Bug: N/A Test: updated Change-Id: Iac9218aa7621dd6223dc26a9f2ebec5d68211328
232 lines
7.7 KiB
Go
232 lines
7.7 KiB
Go
// 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"
|
|
"strings"
|
|
)
|
|
|
|
// 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)
|
|
|
|
// Gets called when a module was pruned from the build tree by SourceRootDirs
|
|
NewSkippedModule(ctx NamespaceContext, name string, skipInfo SkippedModuleInfo)
|
|
|
|
// Finds the module with the given name
|
|
ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool)
|
|
|
|
// Finds if the module with the given name was skipped
|
|
SkippedModuleFromName(moduleName string, namespace Namespace) (skipInfos []SkippedModuleInfo, skipped 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, guess []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)
|
|
|
|
// returns a deterministic, unique, arbitrary string for the given name in the given namespace
|
|
UniqueName(ctx NamespaceContext, name string) (unique string)
|
|
}
|
|
|
|
// A NamespaceContext stores the information given to a NameInterface to enable the NameInterface
|
|
// to choose the namespace for any given module
|
|
type NamespaceContext interface {
|
|
ModulePath() string
|
|
}
|
|
|
|
type namespaceContextImpl struct {
|
|
modulePath string
|
|
}
|
|
|
|
func newNamespaceContext(moduleInfo *moduleInfo) (ctx NamespaceContext) {
|
|
return &namespaceContextImpl{moduleInfo.pos.Filename}
|
|
}
|
|
|
|
func newNamespaceContextFromFilename(filename string) NamespaceContext {
|
|
return &namespaceContextImpl{filename}
|
|
}
|
|
|
|
func (ctx *namespaceContextImpl) ModulePath() string {
|
|
return ctx.modulePath
|
|
}
|
|
|
|
type SkippedModuleInfo struct {
|
|
filename string
|
|
reason string
|
|
}
|
|
|
|
// a SimpleNameInterface just stores all modules in a map based on name
|
|
type SimpleNameInterface struct {
|
|
modules map[string]ModuleGroup
|
|
skippedModules map[string][]SkippedModuleInfo
|
|
}
|
|
|
|
func NewSimpleNameInterface() *SimpleNameInterface {
|
|
return &SimpleNameInterface{
|
|
modules: make(map[string]ModuleGroup),
|
|
skippedModules: make(map[string][]SkippedModuleInfo),
|
|
}
|
|
}
|
|
|
|
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.firstModule().pos),
|
|
}
|
|
}
|
|
|
|
s.modules[name] = group
|
|
|
|
return nil, []error{}
|
|
}
|
|
|
|
func (s *SimpleNameInterface) NewSkippedModule(ctx NamespaceContext, name string, info SkippedModuleInfo) {
|
|
if name == "" {
|
|
return
|
|
}
|
|
s.skippedModules[name] = append(s.skippedModules[name], info)
|
|
}
|
|
|
|
func (s *SimpleNameInterface) ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) {
|
|
group, found = s.modules[moduleName]
|
|
return group, found
|
|
}
|
|
|
|
func (s *SimpleNameInterface) SkippedModuleFromName(moduleName string, namespace Namespace) (skipInfos []SkippedModuleInfo, skipped bool) {
|
|
skipInfos, skipped = s.skippedModules[moduleName]
|
|
return
|
|
}
|
|
|
|
func (s *SimpleNameInterface) Rename(oldName string, newName string, namespace Namespace) (errs []error) {
|
|
existingGroup, exists := s.modules[newName]
|
|
if exists {
|
|
return []error{
|
|
// 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.firstModule().pos),
|
|
}
|
|
}
|
|
|
|
group, exists := s.modules[oldName]
|
|
if !exists {
|
|
return []error{fmt.Errorf("module %q to renamed to %q doesn't exist", oldName, newName)}
|
|
}
|
|
s.modules[newName] = group
|
|
delete(s.modules, group.name)
|
|
group.name = newName
|
|
return nil
|
|
}
|
|
|
|
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, guess []string) (err error) {
|
|
skipInfos, skipped := s.SkippedModuleFromName(dependency, dependerNamespace)
|
|
if skipped {
|
|
filesFound := make([]string, 0, len(skipInfos))
|
|
reasons := make([]string, 0, len(skipInfos))
|
|
for _, info := range skipInfos {
|
|
filesFound = append(filesFound, info.filename)
|
|
reasons = append(reasons, info.reason)
|
|
}
|
|
return fmt.Errorf(
|
|
"module %q depends on skipped module %q; %q was defined in files(s) [%v], but was skipped for reason(s) [%v]",
|
|
depender,
|
|
dependency,
|
|
dependency,
|
|
strings.Join(filesFound, ", "),
|
|
strings.Join(reasons, "; "),
|
|
)
|
|
}
|
|
|
|
guessString := ""
|
|
if len(guess) > 0 {
|
|
guessString = fmt.Sprintf(" Did you mean %q?", guess)
|
|
}
|
|
return fmt.Errorf("%q depends on undefined module %q.%s", depender, dependency, guessString)
|
|
}
|
|
|
|
func (s *SimpleNameInterface) GetNamespace(ctx NamespaceContext) Namespace {
|
|
return nil
|
|
}
|
|
|
|
func (s *SimpleNameInterface) UniqueName(ctx NamespaceContext, name string) (unique string) {
|
|
return name
|
|
}
|