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 "unicode" 22) 23 24// This file exposes the logic of locating a module via a query string, to enable 25// other projects to override it if desired. 26// The default name resolution implementation, SimpleNameInterface, 27// just treats the query string as a module name, and does a simple map lookup. 28 29// A ModuleGroup just points to a moduleGroup to allow external packages to refer 30// to a moduleGroup but not use it 31type ModuleGroup struct { 32 *moduleGroup 33} 34 35func (h *ModuleGroup) String() string { 36 return h.moduleGroup.name 37} 38 39// The Namespace interface is just a marker interface for usage by the NameInterface, 40// to allow a NameInterface to specify that a certain parameter should be a Namespace. 41// In practice, a specific NameInterface will expect to only give and receive structs of 42// the same concrete type, but because Go doesn't support generics, we use a marker interface 43// for a little bit of clarity, and expect implementers to do typecasting instead. 44type Namespace interface { 45 namespace(Namespace) 46} 47type NamespaceMarker struct { 48} 49 50func (m *NamespaceMarker) namespace(Namespace) { 51} 52 53// A NameInterface tells how to locate modules by name. 54// There should only be one name interface per Context, but potentially many namespaces 55type NameInterface interface { 56 // Gets called when a new module is created 57 NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error) 58 59 // Gets called when a module was pruned from the build tree by SourceRootDirs 60 NewSkippedModule(ctx NamespaceContext, name string, skipInfo SkippedModuleInfo) 61 62 // Finds the module with the given name 63 ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) 64 65 // Finds if the module with the given name was skipped 66 SkippedModuleFromName(moduleName string, namespace Namespace) (skipInfos []SkippedModuleInfo, skipped bool) 67 68 // Returns an error indicating that the given module could not be found. 69 // The error contains some diagnostic information about where the dependency can be found. 70 MissingDependencyError(depender string, dependerNamespace Namespace, depName string, guess []string) (err error) 71 72 // Rename 73 Rename(oldName string, newName string, namespace Namespace) []error 74 75 // Returns all modules in a deterministic order. 76 AllModules() []ModuleGroup 77 78 // gets the namespace for a given path 79 GetNamespace(ctx NamespaceContext) (namespace Namespace) 80 81 // returns a deterministic, unique, arbitrary string for the given name in the given namespace 82 UniqueName(ctx NamespaceContext, name string) (unique string) 83} 84 85// A NamespaceContext stores the information given to a NameInterface to enable the NameInterface 86// to choose the namespace for any given module 87type NamespaceContext interface { 88 ModulePath() string 89} 90 91type namespaceContextImpl struct { 92 modulePath string 93} 94 95func newNamespaceContext(moduleInfo *moduleInfo) (ctx NamespaceContext) { 96 return &namespaceContextImpl{moduleInfo.pos.Filename} 97} 98 99func newNamespaceContextFromFilename(filename string) NamespaceContext { 100 return &namespaceContextImpl{filename} 101} 102 103func (ctx *namespaceContextImpl) ModulePath() string { 104 return ctx.modulePath 105} 106 107type SkippedModuleInfo struct { 108 filename string 109 reason string 110} 111 112// a SimpleNameInterface just stores all modules in a map based on name 113type SimpleNameInterface struct { 114 modules map[string]ModuleGroup 115 skippedModules map[string][]SkippedModuleInfo 116} 117 118func NewSimpleNameInterface() *SimpleNameInterface { 119 return &SimpleNameInterface{ 120 modules: make(map[string]ModuleGroup), 121 skippedModules: make(map[string][]SkippedModuleInfo), 122 } 123} 124 125func (s *SimpleNameInterface) NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error) { 126 name := group.name 127 if group, present := s.modules[name]; present { 128 return nil, []error{ 129 // seven characters at the start of the second line to align with the string "error: " 130 fmt.Errorf("module %q already defined\n"+ 131 " %s <-- previous definition here", name, group.modules.firstModule().pos), 132 } 133 } 134 135 if !isValidModuleName(name) { 136 return nil, []error{ 137 // seven characters at the start of the second line to align with the string "error: " 138 fmt.Errorf("module %q should use a valid name.\n"+ 139 " Special chars like spaces are not allowed.", name), 140 } 141 } 142 143 s.modules[name] = group 144 145 return nil, []error{} 146} 147 148// Leters, Digits, Underscore, `+` (libc++), `.` are valid chars for module names. 149// Additional chars like `-` were added to the list to account for module names 150// that predate the enforcement of this check. 151var allowedSpecialCharsInModuleNames = map[rune]bool{ 152 '_': true, 153 '-': true, 154 '.': true, 155 '/': true, 156 '@': true, 157 '+': true, 158 '&': true, 159} 160 161func isValidModuleName(name string) bool { 162 for _, c := range name { 163 _, allowedSpecialChar := allowedSpecialCharsInModuleNames[c] 164 valid := unicode.IsLetter(c) || unicode.IsDigit(c) || allowedSpecialChar 165 if !valid { 166 return false 167 } 168 } 169 return len(name) > 0 170} 171 172func (s *SimpleNameInterface) NewSkippedModule(ctx NamespaceContext, name string, info SkippedModuleInfo) { 173 if name == "" { 174 return 175 } 176 s.skippedModules[name] = append(s.skippedModules[name], info) 177} 178 179func (s *SimpleNameInterface) ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) { 180 group, found = s.modules[moduleName] 181 return group, found 182} 183 184func (s *SimpleNameInterface) SkippedModuleFromName(moduleName string, namespace Namespace) (skipInfos []SkippedModuleInfo, skipped bool) { 185 skipInfos, skipped = s.skippedModules[moduleName] 186 return 187} 188 189func (s *SimpleNameInterface) Rename(oldName string, newName string, namespace Namespace) (errs []error) { 190 existingGroup, exists := s.modules[newName] 191 if exists { 192 return []error{ 193 // seven characters at the start of the second line to align with the string "error: " 194 fmt.Errorf("renaming module %q to %q conflicts with existing module\n"+ 195 " %s <-- existing module defined here", 196 oldName, newName, existingGroup.modules.firstModule().pos), 197 } 198 } 199 200 group, exists := s.modules[oldName] 201 if !exists { 202 return []error{fmt.Errorf("module %q to renamed to %q doesn't exist", oldName, newName)} 203 } 204 s.modules[newName] = group 205 delete(s.modules, group.name) 206 group.name = newName 207 return nil 208} 209 210func (s *SimpleNameInterface) AllModules() []ModuleGroup { 211 groups := make([]ModuleGroup, 0, len(s.modules)) 212 for _, group := range s.modules { 213 groups = append(groups, group) 214 } 215 216 duplicateName := "" 217 less := func(i, j int) bool { 218 if groups[i].name == groups[j].name { 219 duplicateName = groups[i].name 220 } 221 return groups[i].name < groups[j].name 222 } 223 sort.Slice(groups, less) 224 if duplicateName != "" { 225 // It is permitted to have two moduleGroup's with the same name, but not within the same 226 // Namespace. The SimpleNameInterface should catch this in NewModule, however, so this 227 // should never happen. 228 panic(fmt.Sprintf("Duplicate moduleGroup name %q", duplicateName)) 229 } 230 return groups 231} 232 233func (s *SimpleNameInterface) MissingDependencyError(depender string, dependerNamespace Namespace, dependency string, guess []string) (err error) { 234 skipInfos, skipped := s.SkippedModuleFromName(dependency, dependerNamespace) 235 if skipped { 236 filesFound := make([]string, 0, len(skipInfos)) 237 reasons := make([]string, 0, len(skipInfos)) 238 for _, info := range skipInfos { 239 filesFound = append(filesFound, info.filename) 240 reasons = append(reasons, info.reason) 241 } 242 return fmt.Errorf( 243 "module %q depends on skipped module %q; %q was defined in files(s) [%v], but was skipped for reason(s) [%v]", 244 depender, 245 dependency, 246 dependency, 247 strings.Join(filesFound, ", "), 248 strings.Join(reasons, "; "), 249 ) 250 } 251 252 guessString := "" 253 if len(guess) > 0 { 254 guessString = fmt.Sprintf(" Did you mean %q?", guess) 255 } 256 return fmt.Errorf("%q depends on undefined module %q.%s", depender, dependency, guessString) 257} 258 259func (s *SimpleNameInterface) GetNamespace(ctx NamespaceContext) Namespace { 260 return nil 261} 262 263func (s *SimpleNameInterface) UniqueName(ctx NamespaceContext, name string) (unique string) { 264 return name 265} 266