1// Copyright 2017 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package blueprint 16 17import ( 18 "fmt" 19 "sort" 20 "strings" 21) 22 23// This file exposes the logic of locating a module via a query string, to enable 24// other projects to override it if desired. 25// The default name resolution implementation, SimpleNameInterface, 26// just treats the query string as a module name, and does a simple map lookup. 27 28// A ModuleGroup just points to a moduleGroup to allow external packages to refer 29// to a moduleGroup but not use it 30type ModuleGroup struct { 31 *moduleGroup 32} 33 34func (h *ModuleGroup) String() string { 35 return h.moduleGroup.name 36} 37 38// The Namespace interface is just a marker interface for usage by the NameInterface, 39// to allow a NameInterface to specify that a certain parameter should be a Namespace. 40// In practice, a specific NameInterface will expect to only give and receive structs of 41// the same concrete type, but because Go doesn't support generics, we use a marker interface 42// for a little bit of clarity, and expect implementers to do typecasting instead. 43type Namespace interface { 44 namespace(Namespace) 45} 46type NamespaceMarker struct { 47} 48 49func (m *NamespaceMarker) namespace(Namespace) { 50} 51 52// A NameInterface tells how to locate modules by name. 53// There should only be one name interface per Context, but potentially many namespaces 54type NameInterface interface { 55 // Gets called when a new module is created 56 NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error) 57 58 // Gets called when a module was pruned from the build tree by SourceRootDirs 59 NewSkippedModule(ctx NamespaceContext, name string, skipInfo SkippedModuleInfo) 60 61 // Finds the module with the given name 62 ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) 63 64 // Finds if the module with the given name was skipped 65 SkippedModuleFromName(moduleName string, namespace Namespace) (skipInfos []SkippedModuleInfo, skipped bool) 66 67 // Returns an error indicating that the given module could not be found. 68 // The error contains some diagnostic information about where the dependency can be found. 69 MissingDependencyError(depender string, dependerNamespace Namespace, depName string, guess []string) (err error) 70 71 // Rename 72 Rename(oldName string, newName string, namespace Namespace) []error 73 74 // Returns all modules in a deterministic order. 75 AllModules() []ModuleGroup 76 77 // gets the namespace for a given path 78 GetNamespace(ctx NamespaceContext) (namespace Namespace) 79 80 // returns a deterministic, unique, arbitrary string for the given name in the given namespace 81 UniqueName(ctx NamespaceContext, name string) (unique string) 82} 83 84// A NamespaceContext stores the information given to a NameInterface to enable the NameInterface 85// to choose the namespace for any given module 86type NamespaceContext interface { 87 ModulePath() string 88} 89 90type namespaceContextImpl struct { 91 modulePath string 92} 93 94func newNamespaceContext(moduleInfo *moduleInfo) (ctx NamespaceContext) { 95 return &namespaceContextImpl{moduleInfo.pos.Filename} 96} 97 98func newNamespaceContextFromFilename(filename string) NamespaceContext { 99 return &namespaceContextImpl{filename} 100} 101 102func (ctx *namespaceContextImpl) ModulePath() string { 103 return ctx.modulePath 104} 105 106type SkippedModuleInfo struct { 107 filename string 108 reason string 109} 110 111// a SimpleNameInterface just stores all modules in a map based on name 112type SimpleNameInterface struct { 113 modules map[string]ModuleGroup 114 skippedModules map[string][]SkippedModuleInfo 115} 116 117func NewSimpleNameInterface() *SimpleNameInterface { 118 return &SimpleNameInterface{ 119 modules: make(map[string]ModuleGroup), 120 skippedModules: make(map[string][]SkippedModuleInfo), 121 } 122} 123 124func (s *SimpleNameInterface) NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error) { 125 name := group.name 126 if group, present := s.modules[name]; present { 127 return nil, []error{ 128 // seven characters at the start of the second line to align with the string "error: " 129 fmt.Errorf("module %q already defined\n"+ 130 " %s <-- previous definition here", name, group.modules.firstModule().pos), 131 } 132 } 133 134 s.modules[name] = group 135 136 return nil, []error{} 137} 138 139func (s *SimpleNameInterface) NewSkippedModule(ctx NamespaceContext, name string, info SkippedModuleInfo) { 140 if name == "" { 141 return 142 } 143 s.skippedModules[name] = append(s.skippedModules[name], info) 144} 145 146func (s *SimpleNameInterface) ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) { 147 group, found = s.modules[moduleName] 148 return group, found 149} 150 151func (s *SimpleNameInterface) SkippedModuleFromName(moduleName string, namespace Namespace) (skipInfos []SkippedModuleInfo, skipped bool) { 152 skipInfos, skipped = s.skippedModules[moduleName] 153 return 154} 155 156func (s *SimpleNameInterface) Rename(oldName string, newName string, namespace Namespace) (errs []error) { 157 existingGroup, exists := s.modules[newName] 158 if exists { 159 return []error{ 160 // seven characters at the start of the second line to align with the string "error: " 161 fmt.Errorf("renaming module %q to %q conflicts with existing module\n"+ 162 " %s <-- existing module defined here", 163 oldName, newName, existingGroup.modules.firstModule().pos), 164 } 165 } 166 167 group, exists := s.modules[oldName] 168 if !exists { 169 return []error{fmt.Errorf("module %q to renamed to %q doesn't exist", oldName, newName)} 170 } 171 s.modules[newName] = group 172 delete(s.modules, group.name) 173 group.name = newName 174 return nil 175} 176 177func (s *SimpleNameInterface) AllModules() []ModuleGroup { 178 groups := make([]ModuleGroup, 0, len(s.modules)) 179 for _, group := range s.modules { 180 groups = append(groups, group) 181 } 182 183 duplicateName := "" 184 less := func(i, j int) bool { 185 if groups[i].name == groups[j].name { 186 duplicateName = groups[i].name 187 } 188 return groups[i].name < groups[j].name 189 } 190 sort.Slice(groups, less) 191 if duplicateName != "" { 192 // It is permitted to have two moduleGroup's with the same name, but not within the same 193 // Namespace. The SimpleNameInterface should catch this in NewModule, however, so this 194 // should never happen. 195 panic(fmt.Sprintf("Duplicate moduleGroup name %q", duplicateName)) 196 } 197 return groups 198} 199 200func (s *SimpleNameInterface) MissingDependencyError(depender string, dependerNamespace Namespace, dependency string, guess []string) (err error) { 201 skipInfos, skipped := s.SkippedModuleFromName(dependency, dependerNamespace) 202 if skipped { 203 filesFound := make([]string, 0, len(skipInfos)) 204 reasons := make([]string, 0, len(skipInfos)) 205 for _, info := range skipInfos { 206 filesFound = append(filesFound, info.filename) 207 reasons = append(reasons, info.reason) 208 } 209 return fmt.Errorf( 210 "module %q depends on skipped module %q; %q was defined in files(s) [%v], but was skipped for reason(s) [%v]", 211 depender, 212 dependency, 213 dependency, 214 strings.Join(filesFound, ", "), 215 strings.Join(reasons, "; "), 216 ) 217 } 218 219 guessString := "" 220 if len(guess) > 0 { 221 guessString = fmt.Sprintf(" Did you mean %q?", guess) 222 } 223 return fmt.Errorf("%q depends on undefined module %q.%s", depender, dependency, guessString) 224} 225 226func (s *SimpleNameInterface) GetNamespace(ctx NamespaceContext) Namespace { 227 return nil 228} 229 230func (s *SimpleNameInterface) UniqueName(ctx NamespaceContext, name string) (unique string) { 231 return name 232} 233