• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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