blob: a2ff1a692ccb1a750c57a0ed5d1194e0fa46b9bf [file] [log] [blame]
Jeff Gaston088e29e2017-11-29 16:47:17 -08001// 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 android
16
17import (
18 "fmt"
19 "path/filepath"
20 "sort"
21 "strconv"
22 "strings"
23 "sync"
24 "sync/atomic"
25
26 "github.com/google/blueprint"
27)
28
29// This file implements namespaces
30const (
31 namespacePrefix = "//"
32 modulePrefix = ":"
33)
34
35func init() {
36 RegisterModuleType("soong_namespace", NamespaceFactory)
37}
38
39// threadsafe sorted list
40type sortedNamespaces struct {
41 lock sync.Mutex
42 items []*Namespace
43 sorted bool
44}
45
46func (s *sortedNamespaces) add(namespace *Namespace) {
47 s.lock.Lock()
48 defer s.lock.Unlock()
49 if s.sorted {
50 panic("It is not supported to call sortedNamespaces.add() after sortedNamespaces.sortedItems()")
51 }
52 s.items = append(s.items, namespace)
53}
54
55func (s *sortedNamespaces) sortedItems() []*Namespace {
56 s.lock.Lock()
57 defer s.lock.Unlock()
58 if !s.sorted {
59 less := func(i int, j int) bool {
60 return s.items[i].Path < s.items[j].Path
61 }
62 sort.Slice(s.items, less)
63 s.sorted = true
64 }
65 return s.items
66}
67
68// A NameResolver implements blueprint.NameInterface, and implements the logic to
69// find a module from namespaces based on a query string.
70// A query string can be a module name or can be be "//namespace_path:module_path"
71type NameResolver struct {
72 rootNamespace *Namespace
73
74 // id counter for atomic.AddInt32
75 numNamespaces int32
76
77 // All namespaces, without duplicates.
78 sortedNamespaces sortedNamespaces
79
80 // Map from dir to namespace. Will have duplicates if two dirs are part of the same namespace.
81 namespacesByDir sync.Map // if generics were supported, this would be sync.Map[string]*Namespace
82
83 // func telling whether to export a namespace to Kati
84 namespaceExportFilter func(*Namespace) bool
85}
86
87func NewNameResolver(namespaceExportFilter func(*Namespace) bool) *NameResolver {
88 namespacesByDir := sync.Map{}
89
90 r := &NameResolver{
91 namespacesByDir: namespacesByDir,
92 namespaceExportFilter: namespaceExportFilter,
93 }
94 r.rootNamespace = r.newNamespace(".")
95 r.rootNamespace.visibleNamespaces = []*Namespace{r.rootNamespace}
96 r.addNamespace(r.rootNamespace)
97
98 return r
99}
100
101func (r *NameResolver) newNamespace(path string) *Namespace {
102 namespace := NewNamespace(path)
103
104 namespace.exportToKati = r.namespaceExportFilter(namespace)
105
106 nextId := atomic.AddInt32(&r.numNamespaces, 1)
107 id := nextId - 1
108 stringId := ""
109 if id > 0 {
110 stringId = strconv.Itoa(int(id))
111 }
112 namespace.id = stringId
113
114 return namespace
115}
116
117func (r *NameResolver) addNewNamespaceForModule(module *NamespaceModule, dir string) error {
118 namespace := r.newNamespace(dir)
119 module.namespace = namespace
120 module.resolver = r
121 namespace.importedNamespaceNames = module.properties.Imports
122 return r.addNamespace(namespace)
123}
124
125func (r *NameResolver) addNamespace(namespace *Namespace) (err error) {
126 existingNamespace, exists := r.namespaceAt(namespace.Path)
127 if exists {
128 if existingNamespace.Path == namespace.Path {
129 return fmt.Errorf("namespace %v already exists", namespace.Path)
130 } else {
131 // It would probably confuse readers if namespaces were declared anywhere but
132 // the top of the file, so we forbid declaring namespaces after anything else.
133 return fmt.Errorf("a namespace must be the first module in the file")
134 }
135 }
136 r.sortedNamespaces.add(namespace)
137
138 r.namespacesByDir.Store(namespace.Path, namespace)
139 return nil
140}
141
142// non-recursive check for namespace
143func (r *NameResolver) namespaceAt(path string) (namespace *Namespace, found bool) {
144 mapVal, found := r.namespacesByDir.Load(path)
145 if !found {
146 return nil, false
147 }
148 return mapVal.(*Namespace), true
149}
150
151// recursive search upward for a namespace
152func (r *NameResolver) findNamespace(path string) (namespace *Namespace) {
153 namespace, found := r.namespaceAt(path)
154 if found {
155 return namespace
156 }
157 parentDir := filepath.Dir(path)
158 if parentDir == path {
159 return nil
160 }
161 namespace = r.findNamespace(parentDir)
162 r.namespacesByDir.Store(path, namespace)
163 return namespace
164}
165
166func (r *NameResolver) NewModule(ctx blueprint.NamespaceContext, moduleGroup blueprint.ModuleGroup, module blueprint.Module) (namespace blueprint.Namespace, errs []error) {
167 // if this module is a namespace, then save it to our list of namespaces
168 newNamespace, ok := module.(*NamespaceModule)
169 if ok {
170 err := r.addNewNamespaceForModule(newNamespace, ctx.ModuleDir())
171 if err != nil {
172 return nil, []error{err}
173 }
174 return nil, nil
175 }
176
177 // if this module is not a namespace, then save it into the appropriate namespace
178 ns := r.findNamespaceFromCtx(ctx)
179
180 _, errs = ns.moduleContainer.NewModule(ctx, moduleGroup, module)
181 if len(errs) > 0 {
182 return nil, errs
183 }
184
185 amod, ok := module.(Module)
186 if ok {
187 // inform the module whether its namespace is one that we want to export to Make
188 amod.base().commonProperties.NamespaceExportedToMake = ns.exportToKati
189 }
190
191 return ns, nil
192}
193
194func (r *NameResolver) AllModules() []blueprint.ModuleGroup {
195 childLists := [][]blueprint.ModuleGroup{}
196 totalCount := 0
197 for _, namespace := range r.sortedNamespaces.sortedItems() {
198 newModules := namespace.moduleContainer.AllModules()
199 totalCount += len(newModules)
200 childLists = append(childLists, newModules)
201 }
202
203 allModules := make([]blueprint.ModuleGroup, 0, totalCount)
204 for _, childList := range childLists {
205 allModules = append(allModules, childList...)
206 }
207 return allModules
208}
209
210// parses a fully-qualified path (like "//namespace_path:module_name") into a namespace name and a
211// module name
212func (r *NameResolver) parseFullyQualifiedName(name string) (namespaceName string, moduleName string, ok bool) {
213 if !strings.HasPrefix(name, namespacePrefix) {
214 return "", "", false
215 }
216 name = strings.TrimPrefix(name, namespacePrefix)
217 components := strings.Split(name, modulePrefix)
218 if len(components) != 2 {
219 return "", "", false
220 }
221 return components[0], components[1], true
222
223}
224
225func (r *NameResolver) getNamespacesToSearchForModule(sourceNamespace *Namespace) (searchOrder []*Namespace) {
226 return sourceNamespace.visibleNamespaces
227}
228
229func (r *NameResolver) ModuleFromName(name string, namespace blueprint.Namespace) (group blueprint.ModuleGroup, found bool) {
230 // handle fully qualified references like "//namespace_path:module_name"
231 nsName, moduleName, isAbs := r.parseFullyQualifiedName(name)
232 if isAbs {
233 namespace, found := r.namespaceAt(nsName)
234 if !found {
235 return blueprint.ModuleGroup{}, false
236 }
237 container := namespace.moduleContainer
238 return container.ModuleFromName(moduleName, nil)
239 }
240 for _, candidate := range r.getNamespacesToSearchForModule(namespace.(*Namespace)) {
241 group, found = candidate.moduleContainer.ModuleFromName(name, nil)
242 if found {
243 return group, true
244 }
245 }
246 return blueprint.ModuleGroup{}, false
247
248}
249
250func (r *NameResolver) Rename(oldName string, newName string, namespace blueprint.Namespace) []error {
251 oldNs := r.findNamespace(oldName)
252 newNs := r.findNamespace(newName)
253 if oldNs != newNs {
254 return []error{fmt.Errorf("cannot rename %v to %v because the destination is outside namespace %v", oldName, newName, oldNs.Path)}
255 }
256
257 oldName, err := filepath.Rel(oldNs.Path, oldName)
258 if err != nil {
259 panic(err)
260 }
261 newName, err = filepath.Rel(newNs.Path, newName)
262 if err != nil {
263 panic(err)
264 }
265
266 return oldNs.moduleContainer.Rename(oldName, newName, nil)
267}
268
269// resolve each element of namespace.importedNamespaceNames and put the result in namespace.visibleNamespaces
270func (r *NameResolver) FindNamespaceImports(namespace *Namespace) (err error) {
271 namespace.visibleNamespaces = make([]*Namespace, 0, 2+len(namespace.importedNamespaceNames))
272 // search itself first
273 namespace.visibleNamespaces = append(namespace.visibleNamespaces, namespace)
274 // search its imports next
275 for _, name := range namespace.importedNamespaceNames {
276 imp, ok := r.namespaceAt(name)
277 if !ok {
278 return fmt.Errorf("namespace %v does not exist", name)
279 }
280 namespace.visibleNamespaces = append(namespace.visibleNamespaces, imp)
281 }
282 // search the root namespace last
283 namespace.visibleNamespaces = append(namespace.visibleNamespaces, r.rootNamespace)
284 return nil
285}
286
287func (r *NameResolver) MissingDependencyError(depender string, dependerNamespace blueprint.Namespace, depName string) (err error) {
288 text := fmt.Sprintf("%q depends on undefined module %q", depender, depName)
289
290 _, _, isAbs := r.parseFullyQualifiedName(depName)
291 if isAbs {
292 // if the user gave a fully-qualified name, we don't need to look for other
293 // modules that they might have been referring to
294 return fmt.Errorf(text)
295 }
296
297 // determine which namespaces the module can be found in
298 foundInNamespaces := []string{}
299 for _, namespace := range r.sortedNamespaces.sortedItems() {
300 _, found := namespace.moduleContainer.ModuleFromName(depName, nil)
301 if found {
302 foundInNamespaces = append(foundInNamespaces, namespace.Path)
303 }
304 }
305 if len(foundInNamespaces) > 0 {
306 // determine which namespaces are visible to dependerNamespace
307 dependerNs := dependerNamespace.(*Namespace)
308 searched := r.getNamespacesToSearchForModule(dependerNs)
309 importedNames := []string{}
310 for _, ns := range searched {
311 importedNames = append(importedNames, ns.Path)
312 }
313 text += fmt.Sprintf("\nModule %q is defined in namespace %q which can read these %v namespaces: %q", depender, dependerNs.Path, len(importedNames), importedNames)
314 text += fmt.Sprintf("\nModule %q can be found in these namespaces: %q", depName, foundInNamespaces)
315 }
316
317 return fmt.Errorf(text)
318}
319
320func (r *NameResolver) GetNamespace(ctx blueprint.NamespaceContext) blueprint.Namespace {
321 return r.findNamespaceFromCtx(ctx)
322}
323
324func (r *NameResolver) findNamespaceFromCtx(ctx blueprint.NamespaceContext) *Namespace {
325 return r.findNamespace(ctx.ModuleDir())
326}
327
328var _ blueprint.NameInterface = (*NameResolver)(nil)
329
330type Namespace struct {
331 blueprint.NamespaceMarker
332 Path string
333
334 // names of namespaces listed as imports by this namespace
335 importedNamespaceNames []string
336 // all namespaces that should be searched when a module in this namespace declares a dependency
337 visibleNamespaces []*Namespace
338
339 id string
340
341 exportToKati bool
342
343 moduleContainer blueprint.NameInterface
344}
345
346func NewNamespace(path string) *Namespace {
347 return &Namespace{Path: path, moduleContainer: blueprint.NewSimpleNameInterface()}
348}
349
350var _ blueprint.Namespace = (*Namespace)(nil)
351
352type NamespaceModule struct {
353 ModuleBase
354
355 namespace *Namespace
356 resolver *NameResolver
357
358 properties struct {
359 Imports []string
360 }
361}
362
363func (n *NamespaceModule) DepsMutator(context BottomUpMutatorContext) {
364}
365
366func (n *NamespaceModule) GenerateAndroidBuildActions(ctx ModuleContext) {
367}
368
369func (n *NamespaceModule) GenerateBuildActions(ctx blueprint.ModuleContext) {
370}
371
372func (n *NamespaceModule) Name() (name string) {
373 return *n.nameProperties.Name
374}
375
376func NamespaceFactory() Module {
377 module := &NamespaceModule{}
378
379 name := "soong_namespace"
380 module.nameProperties.Name = &name
381
382 module.AddProperties(&module.properties)
383 return module
384}
385
386func RegisterNamespaceMutator(ctx RegisterMutatorsContext) {
387 ctx.BottomUp("namespace_deps", namespaceDeps)
388}
389
390func namespaceDeps(ctx BottomUpMutatorContext) {
391 module, ok := ctx.Module().(*NamespaceModule)
392 if ok {
393 err := module.resolver.FindNamespaceImports(module.namespace)
394 if err != nil {
395 ctx.ModuleErrorf(err.Error())
396 }
397 }
398}