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