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