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