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