Merge changes from topic "soong-namespaces"
* changes: Make ninja file deterministic even with dup module names require namespaces to be declared only in files named Android.bp Document Soong namespaces Revert "Revert "Soong support for namespaces""
This commit is contained in:
@@ -50,6 +50,7 @@ bootstrap_go_package {
|
||||
"android/makevars.go",
|
||||
"android/module.go",
|
||||
"android/mutator.go",
|
||||
"android/namespace.go",
|
||||
"android/onceper.go",
|
||||
"android/package_ctx.go",
|
||||
"android/paths.go",
|
||||
@@ -67,6 +68,7 @@ bootstrap_go_package {
|
||||
testSrcs: [
|
||||
"android/config_test.go",
|
||||
"android/expand_test.go",
|
||||
"android/namespace_test.go",
|
||||
"android/paths_test.go",
|
||||
"android/prebuilt_test.go",
|
||||
"android/util_test.go",
|
||||
|
32
README.md
32
README.md
@@ -95,6 +95,38 @@ cc_binary {
|
||||
}
|
||||
```
|
||||
|
||||
### Name resolution
|
||||
|
||||
Soong provides the ability for modules in different directories to specify
|
||||
the same name, as long as each module is declared within a separate namespace.
|
||||
A namespace can be declared like this:
|
||||
|
||||
```
|
||||
soong_namespace {
|
||||
imports: ["path/to/otherNamespace1", "path/to/otherNamespace2"],
|
||||
}
|
||||
```
|
||||
|
||||
Each Soong module is assigned a namespace based on its location in the tree.
|
||||
Each Soong module is considered to be in the namespace defined by the
|
||||
soong_namespace found in an Android.bp in the current directory or closest
|
||||
ancestor directory, unless no such soong_namespace module is found, in which
|
||||
case the module is considered to be in the implicit root namespace.
|
||||
|
||||
When Soong attempts to resolve dependency D declared my module M in namespace
|
||||
N which imports namespaces I1, I2, I3..., then if D is a fully-qualified name
|
||||
of the form "//namespace:module", only the specified namespace will be searched
|
||||
for the specified module name. Otherwise, Soong will first look for a module
|
||||
named D declared in namespace N. If that module does not exist, Soong will look
|
||||
for a module named D in namespaces I1, I2, I3... Lastly, Soong will look in the
|
||||
root namespace.
|
||||
|
||||
Until we have fully converted from Make to Soong, it will be necessary for the
|
||||
Make product config to specify a value of PRODUCT_SOONG_NAMESPACES. Its value
|
||||
should be a space-separated list of namespaces that Soong export to Make to be
|
||||
built by the `m` command. After we have fully converted from Make to Soong, the
|
||||
details of enabling namespaces could potentially change.
|
||||
|
||||
### Formatter
|
||||
|
||||
Soong includes a canonical formatter for blueprint files, similar to
|
||||
|
@@ -157,6 +157,12 @@ func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.M
|
||||
return nil
|
||||
}
|
||||
|
||||
if !amod.commonProperties.NamespaceExportedToMake {
|
||||
// TODO(jeffrygaston) do we want to validate that there are no modules being
|
||||
// exported to Kati that depend on this module?
|
||||
return nil
|
||||
}
|
||||
|
||||
data := provider.AndroidMk()
|
||||
|
||||
if data.Include == "" {
|
||||
|
@@ -151,6 +151,7 @@ type ModuleContext interface {
|
||||
VisitAllModuleVariants(visit func(Module))
|
||||
|
||||
GetMissingDependencies() []string
|
||||
Namespace() blueprint.Namespace
|
||||
}
|
||||
|
||||
type Module interface {
|
||||
@@ -235,6 +236,8 @@ type commonProperties struct {
|
||||
ArchSpecific bool `blueprint:"mutated"`
|
||||
|
||||
SkipInstall bool `blueprint:"mutated"`
|
||||
|
||||
NamespaceExportedToMake bool `blueprint:"mutated"`
|
||||
}
|
||||
|
||||
type hostAndDeviceProperties struct {
|
||||
@@ -500,8 +503,13 @@ func (a *ModuleBase) generateModuleTarget(ctx ModuleContext) {
|
||||
|
||||
var deps Paths
|
||||
|
||||
namespacePrefix := ctx.Namespace().(*Namespace).id
|
||||
if namespacePrefix != "" {
|
||||
namespacePrefix = namespacePrefix + "-"
|
||||
}
|
||||
|
||||
if len(allInstalledFiles) > 0 {
|
||||
name := PathForPhony(ctx, ctx.ModuleName()+"-install")
|
||||
name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+"-install")
|
||||
ctx.Build(pctx, BuildParams{
|
||||
Rule: blueprint.Phony,
|
||||
Output: name,
|
||||
@@ -513,7 +521,7 @@ func (a *ModuleBase) generateModuleTarget(ctx ModuleContext) {
|
||||
}
|
||||
|
||||
if len(allCheckbuildFiles) > 0 {
|
||||
name := PathForPhony(ctx, ctx.ModuleName()+"-checkbuild")
|
||||
name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+"-checkbuild")
|
||||
ctx.Build(pctx, BuildParams{
|
||||
Rule: blueprint.Phony,
|
||||
Output: name,
|
||||
@@ -529,9 +537,10 @@ func (a *ModuleBase) generateModuleTarget(ctx ModuleContext) {
|
||||
suffix = "-soong"
|
||||
}
|
||||
|
||||
name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+suffix)
|
||||
ctx.Build(pctx, BuildParams{
|
||||
Rule: blueprint.Phony,
|
||||
Output: PathForPhony(ctx, ctx.ModuleName()+suffix),
|
||||
Outputs: []WritablePath{name},
|
||||
Implicits: deps,
|
||||
})
|
||||
|
||||
|
@@ -86,6 +86,7 @@ func registerArchMutator(ctx RegisterMutatorsContext) {
|
||||
}
|
||||
|
||||
var preDeps = []RegisterMutatorFunc{
|
||||
RegisterNamespaceMutator,
|
||||
registerArchMutator,
|
||||
}
|
||||
|
||||
|
423
android/namespace.go
Normal file
423
android/namespace.go
Normal file
@@ -0,0 +1,423 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package android
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/google/blueprint"
|
||||
)
|
||||
|
||||
// This file implements namespaces
|
||||
const (
|
||||
namespacePrefix = "//"
|
||||
modulePrefix = ":"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterModuleType("soong_namespace", NamespaceFactory)
|
||||
}
|
||||
|
||||
// threadsafe sorted list
|
||||
type sortedNamespaces struct {
|
||||
lock sync.Mutex
|
||||
items []*Namespace
|
||||
sorted bool
|
||||
}
|
||||
|
||||
func (s *sortedNamespaces) add(namespace *Namespace) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
if s.sorted {
|
||||
panic("It is not supported to call sortedNamespaces.add() after sortedNamespaces.sortedItems()")
|
||||
}
|
||||
s.items = append(s.items, namespace)
|
||||
}
|
||||
|
||||
func (s *sortedNamespaces) sortedItems() []*Namespace {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
if !s.sorted {
|
||||
less := func(i int, j int) bool {
|
||||
return s.items[i].Path < s.items[j].Path
|
||||
}
|
||||
sort.Slice(s.items, less)
|
||||
s.sorted = true
|
||||
}
|
||||
return s.items
|
||||
}
|
||||
|
||||
func (s *sortedNamespaces) index(namespace *Namespace) int {
|
||||
for i, candidate := range s.sortedItems() {
|
||||
if namespace == candidate {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// A NameResolver implements blueprint.NameInterface, and implements the logic to
|
||||
// find a module from namespaces based on a query string.
|
||||
// A query string can be a module name or can be be "//namespace_path:module_path"
|
||||
type NameResolver struct {
|
||||
rootNamespace *Namespace
|
||||
|
||||
// id counter for atomic.AddInt32
|
||||
nextNamespaceId int32
|
||||
|
||||
// All namespaces, without duplicates.
|
||||
sortedNamespaces sortedNamespaces
|
||||
|
||||
// Map from dir to namespace. Will have duplicates if two dirs are part of the same namespace.
|
||||
namespacesByDir sync.Map // if generics were supported, this would be sync.Map[string]*Namespace
|
||||
|
||||
// func telling whether to export a namespace to Kati
|
||||
namespaceExportFilter func(*Namespace) bool
|
||||
}
|
||||
|
||||
func NewNameResolver(namespaceExportFilter func(*Namespace) bool) *NameResolver {
|
||||
namespacesByDir := sync.Map{}
|
||||
|
||||
r := &NameResolver{
|
||||
namespacesByDir: namespacesByDir,
|
||||
namespaceExportFilter: namespaceExportFilter,
|
||||
}
|
||||
r.rootNamespace = r.newNamespace(".")
|
||||
r.rootNamespace.visibleNamespaces = []*Namespace{r.rootNamespace}
|
||||
r.addNamespace(r.rootNamespace)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *NameResolver) newNamespace(path string) *Namespace {
|
||||
namespace := NewNamespace(path)
|
||||
|
||||
namespace.exportToKati = r.namespaceExportFilter(namespace)
|
||||
|
||||
return namespace
|
||||
}
|
||||
|
||||
func (r *NameResolver) addNewNamespaceForModule(module *NamespaceModule, path string) error {
|
||||
fileName := filepath.Base(path)
|
||||
if fileName != "Android.bp" {
|
||||
return errors.New("A namespace may only be declared in a file named Android.bp")
|
||||
}
|
||||
dir := filepath.Dir(path)
|
||||
|
||||
namespace := r.newNamespace(dir)
|
||||
module.namespace = namespace
|
||||
module.resolver = r
|
||||
namespace.importedNamespaceNames = module.properties.Imports
|
||||
return r.addNamespace(namespace)
|
||||
}
|
||||
|
||||
func (r *NameResolver) addNamespace(namespace *Namespace) (err error) {
|
||||
existingNamespace, exists := r.namespaceAt(namespace.Path)
|
||||
if exists {
|
||||
if existingNamespace.Path == namespace.Path {
|
||||
return fmt.Errorf("namespace %v already exists", namespace.Path)
|
||||
} else {
|
||||
// It would probably confuse readers if namespaces were declared anywhere but
|
||||
// the top of the file, so we forbid declaring namespaces after anything else.
|
||||
return fmt.Errorf("a namespace must be the first module in the file")
|
||||
}
|
||||
}
|
||||
r.sortedNamespaces.add(namespace)
|
||||
|
||||
r.namespacesByDir.Store(namespace.Path, namespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
// non-recursive check for namespace
|
||||
func (r *NameResolver) namespaceAt(path string) (namespace *Namespace, found bool) {
|
||||
mapVal, found := r.namespacesByDir.Load(path)
|
||||
if !found {
|
||||
return nil, false
|
||||
}
|
||||
return mapVal.(*Namespace), true
|
||||
}
|
||||
|
||||
// recursive search upward for a namespace
|
||||
func (r *NameResolver) findNamespace(path string) (namespace *Namespace) {
|
||||
namespace, found := r.namespaceAt(path)
|
||||
if found {
|
||||
return namespace
|
||||
}
|
||||
parentDir := filepath.Dir(path)
|
||||
if parentDir == path {
|
||||
return nil
|
||||
}
|
||||
namespace = r.findNamespace(parentDir)
|
||||
r.namespacesByDir.Store(path, namespace)
|
||||
return namespace
|
||||
}
|
||||
|
||||
func (r *NameResolver) NewModule(ctx blueprint.NamespaceContext, moduleGroup blueprint.ModuleGroup, module blueprint.Module) (namespace blueprint.Namespace, errs []error) {
|
||||
// if this module is a namespace, then save it to our list of namespaces
|
||||
newNamespace, ok := module.(*NamespaceModule)
|
||||
if ok {
|
||||
err := r.addNewNamespaceForModule(newNamespace, ctx.ModulePath())
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// if this module is not a namespace, then save it into the appropriate namespace
|
||||
ns := r.findNamespaceFromCtx(ctx)
|
||||
|
||||
_, errs = ns.moduleContainer.NewModule(ctx, moduleGroup, module)
|
||||
if len(errs) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
amod, ok := module.(Module)
|
||||
if ok {
|
||||
// inform the module whether its namespace is one that we want to export to Make
|
||||
amod.base().commonProperties.NamespaceExportedToMake = ns.exportToKati
|
||||
}
|
||||
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
func (r *NameResolver) AllModules() []blueprint.ModuleGroup {
|
||||
childLists := [][]blueprint.ModuleGroup{}
|
||||
totalCount := 0
|
||||
for _, namespace := range r.sortedNamespaces.sortedItems() {
|
||||
newModules := namespace.moduleContainer.AllModules()
|
||||
totalCount += len(newModules)
|
||||
childLists = append(childLists, newModules)
|
||||
}
|
||||
|
||||
allModules := make([]blueprint.ModuleGroup, 0, totalCount)
|
||||
for _, childList := range childLists {
|
||||
allModules = append(allModules, childList...)
|
||||
}
|
||||
return allModules
|
||||
}
|
||||
|
||||
// parses a fully-qualified path (like "//namespace_path:module_name") into a namespace name and a
|
||||
// module name
|
||||
func (r *NameResolver) parseFullyQualifiedName(name string) (namespaceName string, moduleName string, ok bool) {
|
||||
if !strings.HasPrefix(name, namespacePrefix) {
|
||||
return "", "", false
|
||||
}
|
||||
name = strings.TrimPrefix(name, namespacePrefix)
|
||||
components := strings.Split(name, modulePrefix)
|
||||
if len(components) != 2 {
|
||||
return "", "", false
|
||||
}
|
||||
return components[0], components[1], true
|
||||
|
||||
}
|
||||
|
||||
func (r *NameResolver) getNamespacesToSearchForModule(sourceNamespace *Namespace) (searchOrder []*Namespace) {
|
||||
return sourceNamespace.visibleNamespaces
|
||||
}
|
||||
|
||||
func (r *NameResolver) ModuleFromName(name string, namespace blueprint.Namespace) (group blueprint.ModuleGroup, found bool) {
|
||||
// handle fully qualified references like "//namespace_path:module_name"
|
||||
nsName, moduleName, isAbs := r.parseFullyQualifiedName(name)
|
||||
if isAbs {
|
||||
namespace, found := r.namespaceAt(nsName)
|
||||
if !found {
|
||||
return blueprint.ModuleGroup{}, false
|
||||
}
|
||||
container := namespace.moduleContainer
|
||||
return container.ModuleFromName(moduleName, nil)
|
||||
}
|
||||
for _, candidate := range r.getNamespacesToSearchForModule(namespace.(*Namespace)) {
|
||||
group, found = candidate.moduleContainer.ModuleFromName(name, nil)
|
||||
if found {
|
||||
return group, true
|
||||
}
|
||||
}
|
||||
return blueprint.ModuleGroup{}, false
|
||||
|
||||
}
|
||||
|
||||
func (r *NameResolver) Rename(oldName string, newName string, namespace blueprint.Namespace) []error {
|
||||
oldNs := r.findNamespace(oldName)
|
||||
newNs := r.findNamespace(newName)
|
||||
if oldNs != newNs {
|
||||
return []error{fmt.Errorf("cannot rename %v to %v because the destination is outside namespace %v", oldName, newName, oldNs.Path)}
|
||||
}
|
||||
|
||||
oldName, err := filepath.Rel(oldNs.Path, oldName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
newName, err = filepath.Rel(newNs.Path, newName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return oldNs.moduleContainer.Rename(oldName, newName, nil)
|
||||
}
|
||||
|
||||
// resolve each element of namespace.importedNamespaceNames and put the result in namespace.visibleNamespaces
|
||||
func (r *NameResolver) FindNamespaceImports(namespace *Namespace) (err error) {
|
||||
namespace.visibleNamespaces = make([]*Namespace, 0, 2+len(namespace.importedNamespaceNames))
|
||||
// search itself first
|
||||
namespace.visibleNamespaces = append(namespace.visibleNamespaces, namespace)
|
||||
// search its imports next
|
||||
for _, name := range namespace.importedNamespaceNames {
|
||||
imp, ok := r.namespaceAt(name)
|
||||
if !ok {
|
||||
return fmt.Errorf("namespace %v does not exist", name)
|
||||
}
|
||||
namespace.visibleNamespaces = append(namespace.visibleNamespaces, imp)
|
||||
}
|
||||
// search the root namespace last
|
||||
namespace.visibleNamespaces = append(namespace.visibleNamespaces, r.rootNamespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *NameResolver) chooseId(namespace *Namespace) {
|
||||
id := r.sortedNamespaces.index(namespace)
|
||||
if id < 0 {
|
||||
panic(fmt.Sprintf("Namespace not found: %v\n", namespace.id))
|
||||
}
|
||||
namespace.id = strconv.Itoa(id)
|
||||
}
|
||||
|
||||
func (r *NameResolver) MissingDependencyError(depender string, dependerNamespace blueprint.Namespace, depName string) (err error) {
|
||||
text := fmt.Sprintf("%q depends on undefined module %q", depender, depName)
|
||||
|
||||
_, _, isAbs := r.parseFullyQualifiedName(depName)
|
||||
if isAbs {
|
||||
// if the user gave a fully-qualified name, we don't need to look for other
|
||||
// modules that they might have been referring to
|
||||
return fmt.Errorf(text)
|
||||
}
|
||||
|
||||
// determine which namespaces the module can be found in
|
||||
foundInNamespaces := []string{}
|
||||
for _, namespace := range r.sortedNamespaces.sortedItems() {
|
||||
_, found := namespace.moduleContainer.ModuleFromName(depName, nil)
|
||||
if found {
|
||||
foundInNamespaces = append(foundInNamespaces, namespace.Path)
|
||||
}
|
||||
}
|
||||
if len(foundInNamespaces) > 0 {
|
||||
// determine which namespaces are visible to dependerNamespace
|
||||
dependerNs := dependerNamespace.(*Namespace)
|
||||
searched := r.getNamespacesToSearchForModule(dependerNs)
|
||||
importedNames := []string{}
|
||||
for _, ns := range searched {
|
||||
importedNames = append(importedNames, ns.Path)
|
||||
}
|
||||
text += fmt.Sprintf("\nModule %q is defined in namespace %q which can read these %v namespaces: %q", depender, dependerNs.Path, len(importedNames), importedNames)
|
||||
text += fmt.Sprintf("\nModule %q can be found in these namespaces: %q", depName, foundInNamespaces)
|
||||
}
|
||||
|
||||
return fmt.Errorf(text)
|
||||
}
|
||||
|
||||
func (r *NameResolver) GetNamespace(ctx blueprint.NamespaceContext) blueprint.Namespace {
|
||||
return r.findNamespaceFromCtx(ctx)
|
||||
}
|
||||
|
||||
func (r *NameResolver) findNamespaceFromCtx(ctx blueprint.NamespaceContext) *Namespace {
|
||||
return r.findNamespace(filepath.Dir(ctx.ModulePath()))
|
||||
}
|
||||
|
||||
func (r *NameResolver) UniqueName(ctx blueprint.NamespaceContext, name string) (unique string) {
|
||||
prefix := r.findNamespaceFromCtx(ctx).id
|
||||
if prefix != "" {
|
||||
prefix = prefix + "-"
|
||||
}
|
||||
return prefix + name
|
||||
}
|
||||
|
||||
var _ blueprint.NameInterface = (*NameResolver)(nil)
|
||||
|
||||
type Namespace struct {
|
||||
blueprint.NamespaceMarker
|
||||
Path string
|
||||
|
||||
// names of namespaces listed as imports by this namespace
|
||||
importedNamespaceNames []string
|
||||
// all namespaces that should be searched when a module in this namespace declares a dependency
|
||||
visibleNamespaces []*Namespace
|
||||
|
||||
id string
|
||||
|
||||
exportToKati bool
|
||||
|
||||
moduleContainer blueprint.NameInterface
|
||||
}
|
||||
|
||||
func NewNamespace(path string) *Namespace {
|
||||
return &Namespace{Path: path, moduleContainer: blueprint.NewSimpleNameInterface()}
|
||||
}
|
||||
|
||||
var _ blueprint.Namespace = (*Namespace)(nil)
|
||||
|
||||
type NamespaceModule struct {
|
||||
ModuleBase
|
||||
|
||||
namespace *Namespace
|
||||
resolver *NameResolver
|
||||
|
||||
properties struct {
|
||||
Imports []string
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NamespaceModule) DepsMutator(context BottomUpMutatorContext) {
|
||||
}
|
||||
|
||||
func (n *NamespaceModule) GenerateAndroidBuildActions(ctx ModuleContext) {
|
||||
}
|
||||
|
||||
func (n *NamespaceModule) GenerateBuildActions(ctx blueprint.ModuleContext) {
|
||||
}
|
||||
|
||||
func (n *NamespaceModule) Name() (name string) {
|
||||
return *n.nameProperties.Name
|
||||
}
|
||||
|
||||
func NamespaceFactory() Module {
|
||||
module := &NamespaceModule{}
|
||||
|
||||
name := "soong_namespace"
|
||||
module.nameProperties.Name = &name
|
||||
|
||||
module.AddProperties(&module.properties)
|
||||
return module
|
||||
}
|
||||
|
||||
func RegisterNamespaceMutator(ctx RegisterMutatorsContext) {
|
||||
ctx.BottomUp("namespace_deps", namespaceMutator).Parallel()
|
||||
}
|
||||
|
||||
func namespaceMutator(ctx BottomUpMutatorContext) {
|
||||
module, ok := ctx.Module().(*NamespaceModule)
|
||||
if ok {
|
||||
err := module.resolver.FindNamespaceImports(module.namespace)
|
||||
if err != nil {
|
||||
ctx.ModuleErrorf(err.Error())
|
||||
}
|
||||
|
||||
module.resolver.chooseId(module.namespace)
|
||||
}
|
||||
}
|
703
android/namespace_test.go
Normal file
703
android/namespace_test.go
Normal file
@@ -0,0 +1,703 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package android
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/blueprint"
|
||||
)
|
||||
|
||||
func TestDependingOnModuleInSameNamespace(t *testing.T) {
|
||||
ctx := setupTest(t,
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
}
|
||||
test_module {
|
||||
name: "b",
|
||||
deps: ["a"],
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
a := getModule(ctx, "a")
|
||||
b := getModule(ctx, "b")
|
||||
if !dependsOn(ctx, b, a) {
|
||||
t.Errorf("module b does not depend on module a in the same namespace")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDependingOnModuleInRootNamespace(t *testing.T) {
|
||||
ctx := setupTest(t,
|
||||
map[string]string{
|
||||
".": `
|
||||
test_module {
|
||||
name: "b",
|
||||
deps: ["a"],
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
a := getModule(ctx, "a")
|
||||
b := getModule(ctx, "b")
|
||||
if !dependsOn(ctx, b, a) {
|
||||
t.Errorf("module b in root namespace does not depend on module a in the root namespace")
|
||||
}
|
||||
}
|
||||
|
||||
func TestImplicitlyImportRootNamespace(t *testing.T) {
|
||||
_ = setupTest(t,
|
||||
map[string]string{
|
||||
".": `
|
||||
test_module {
|
||||
name: "a",
|
||||
}
|
||||
`,
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "b",
|
||||
deps: ["a"],
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
// setupTest will report any errors
|
||||
}
|
||||
|
||||
func TestDependingOnModuleInImportedNamespace(t *testing.T) {
|
||||
ctx := setupTest(t,
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
}
|
||||
`,
|
||||
"dir2": `
|
||||
soong_namespace {
|
||||
imports: ["dir1"],
|
||||
}
|
||||
test_module {
|
||||
name: "b",
|
||||
deps: ["a"],
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
a := getModule(ctx, "a")
|
||||
b := getModule(ctx, "b")
|
||||
if !dependsOn(ctx, b, a) {
|
||||
t.Errorf("module b does not depend on module a in the same namespace")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDependingOnModuleInNonImportedNamespace(t *testing.T) {
|
||||
_, errs := setupTestExpectErrs(
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
}
|
||||
`,
|
||||
"dir2": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
}
|
||||
`,
|
||||
"dir3": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "b",
|
||||
deps: ["a"],
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
expectedErrors := []error{
|
||||
errors.New(
|
||||
`dir3/Android.bp:4:4: "b" depends on undefined module "a"
|
||||
Module "b" is defined in namespace "dir3" which can read these 2 namespaces: ["dir3" "."]
|
||||
Module "a" can be found in these namespaces: ["dir1" "dir2"]`),
|
||||
}
|
||||
|
||||
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
|
||||
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDependingOnModuleByFullyQualifiedReference(t *testing.T) {
|
||||
ctx := setupTest(t,
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
}
|
||||
`,
|
||||
"dir2": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "b",
|
||||
deps: ["//dir1:a"],
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
a := getModule(ctx, "a")
|
||||
b := getModule(ctx, "b")
|
||||
if !dependsOn(ctx, b, a) {
|
||||
t.Errorf("module b does not depend on module a")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSameNameInTwoNamespaces(t *testing.T) {
|
||||
ctx := setupTest(t,
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
id: "1",
|
||||
}
|
||||
test_module {
|
||||
name: "b",
|
||||
deps: ["a"],
|
||||
id: "2",
|
||||
}
|
||||
`,
|
||||
"dir2": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
id:"3",
|
||||
}
|
||||
test_module {
|
||||
name: "b",
|
||||
deps: ["a"],
|
||||
id:"4",
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
one := findModuleById(ctx, "1")
|
||||
two := findModuleById(ctx, "2")
|
||||
three := findModuleById(ctx, "3")
|
||||
four := findModuleById(ctx, "4")
|
||||
if !dependsOn(ctx, two, one) {
|
||||
t.Fatalf("Module 2 does not depend on module 1 in its namespace")
|
||||
}
|
||||
if dependsOn(ctx, two, three) {
|
||||
t.Fatalf("Module 2 depends on module 3 in another namespace")
|
||||
}
|
||||
if !dependsOn(ctx, four, three) {
|
||||
t.Fatalf("Module 4 does not depend on module 3 in its namespace")
|
||||
}
|
||||
if dependsOn(ctx, four, one) {
|
||||
t.Fatalf("Module 4 depends on module 1 in another namespace")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchOrder(t *testing.T) {
|
||||
ctx := setupTest(t,
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
id: "1",
|
||||
}
|
||||
`,
|
||||
"dir2": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
id:"2",
|
||||
}
|
||||
test_module {
|
||||
name: "b",
|
||||
id:"3",
|
||||
}
|
||||
`,
|
||||
"dir3": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
id:"4",
|
||||
}
|
||||
test_module {
|
||||
name: "b",
|
||||
id:"5",
|
||||
}
|
||||
test_module {
|
||||
name: "c",
|
||||
id:"6",
|
||||
}
|
||||
`,
|
||||
".": `
|
||||
test_module {
|
||||
name: "a",
|
||||
id: "7",
|
||||
}
|
||||
test_module {
|
||||
name: "b",
|
||||
id: "8",
|
||||
}
|
||||
test_module {
|
||||
name: "c",
|
||||
id: "9",
|
||||
}
|
||||
test_module {
|
||||
name: "d",
|
||||
id: "10",
|
||||
}
|
||||
`,
|
||||
"dir4": `
|
||||
soong_namespace {
|
||||
imports: ["dir1", "dir2", "dir3"]
|
||||
}
|
||||
test_module {
|
||||
name: "test_me",
|
||||
id:"0",
|
||||
deps: ["a", "b", "c", "d"],
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
testMe := findModuleById(ctx, "0")
|
||||
if !dependsOn(ctx, testMe, findModuleById(ctx, "1")) {
|
||||
t.Errorf("test_me doesn't depend on id 1")
|
||||
}
|
||||
if !dependsOn(ctx, testMe, findModuleById(ctx, "3")) {
|
||||
t.Errorf("test_me doesn't depend on id 3")
|
||||
}
|
||||
if !dependsOn(ctx, testMe, findModuleById(ctx, "6")) {
|
||||
t.Errorf("test_me doesn't depend on id 6")
|
||||
}
|
||||
if !dependsOn(ctx, testMe, findModuleById(ctx, "10")) {
|
||||
t.Errorf("test_me doesn't depend on id 10")
|
||||
}
|
||||
if numDeps(ctx, testMe) != 4 {
|
||||
t.Errorf("num dependencies of test_me = %v, not 4\n", numDeps(ctx, testMe))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTwoNamespacesCanImportEachOther(t *testing.T) {
|
||||
_ = setupTest(t,
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
imports: ["dir2"]
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
}
|
||||
test_module {
|
||||
name: "c",
|
||||
deps: ["b"],
|
||||
}
|
||||
`,
|
||||
"dir2": `
|
||||
soong_namespace {
|
||||
imports: ["dir1"],
|
||||
}
|
||||
test_module {
|
||||
name: "b",
|
||||
deps: ["a"],
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
// setupTest will report any errors
|
||||
}
|
||||
|
||||
func TestImportingNonexistentNamespace(t *testing.T) {
|
||||
_, errs := setupTestExpectErrs(
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
imports: ["a_nonexistent_namespace"]
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
deps: ["a_nonexistent_module"]
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
// should complain about the missing namespace and not complain about the unresolvable dependency
|
||||
expectedErrors := []error{
|
||||
errors.New(`dir1/Android.bp:2:4: module "soong_namespace": namespace a_nonexistent_namespace does not exist`),
|
||||
}
|
||||
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
|
||||
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamespacesDontInheritParentNamespaces(t *testing.T) {
|
||||
_, errs := setupTestExpectErrs(
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
}
|
||||
`,
|
||||
"dir1/subdir1": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "b",
|
||||
deps: ["a"],
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
expectedErrors := []error{
|
||||
errors.New(`dir1/subdir1/Android.bp:4:4: "b" depends on undefined module "a"
|
||||
Module "b" is defined in namespace "dir1/subdir1" which can read these 2 namespaces: ["dir1/subdir1" "."]
|
||||
Module "a" can be found in these namespaces: ["dir1"]`),
|
||||
}
|
||||
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
|
||||
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModulesDoReceiveParentNamespace(t *testing.T) {
|
||||
_ = setupTest(t,
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
}
|
||||
`,
|
||||
"dir1/subdir": `
|
||||
test_module {
|
||||
name: "b",
|
||||
deps: ["a"],
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
// setupTest will report any errors
|
||||
}
|
||||
|
||||
func TestNamespaceImportsNotTransitive(t *testing.T) {
|
||||
_, errs := setupTestExpectErrs(
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a",
|
||||
}
|
||||
`,
|
||||
"dir2": `
|
||||
soong_namespace {
|
||||
imports: ["dir1"],
|
||||
}
|
||||
test_module {
|
||||
name: "b",
|
||||
deps: ["a"],
|
||||
}
|
||||
`,
|
||||
"dir3": `
|
||||
soong_namespace {
|
||||
imports: ["dir2"],
|
||||
}
|
||||
test_module {
|
||||
name: "c",
|
||||
deps: ["a"],
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
expectedErrors := []error{
|
||||
errors.New(`dir3/Android.bp:5:4: "c" depends on undefined module "a"
|
||||
Module "c" is defined in namespace "dir3" which can read these 3 namespaces: ["dir3" "dir2" "."]
|
||||
Module "a" can be found in these namespaces: ["dir1"]`),
|
||||
}
|
||||
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
|
||||
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTwoNamepacesInSameDir(t *testing.T) {
|
||||
_, errs := setupTestExpectErrs(
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
}
|
||||
soong_namespace {
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
expectedErrors := []error{
|
||||
errors.New(`dir1/Android.bp:4:4: namespace dir1 already exists`),
|
||||
}
|
||||
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
|
||||
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamespaceNotAtTopOfFile(t *testing.T) {
|
||||
_, errs := setupTestExpectErrs(
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
test_module {
|
||||
name: "a"
|
||||
}
|
||||
soong_namespace {
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
expectedErrors := []error{
|
||||
errors.New(`dir1/Android.bp:5:4: a namespace must be the first module in the file`),
|
||||
}
|
||||
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
|
||||
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTwoModulesWithSameNameInSameNamespace(t *testing.T) {
|
||||
_, errs := setupTestExpectErrs(
|
||||
map[string]string{
|
||||
"dir1": `
|
||||
soong_namespace {
|
||||
}
|
||||
test_module {
|
||||
name: "a"
|
||||
}
|
||||
test_module {
|
||||
name: "a"
|
||||
}
|
||||
`,
|
||||
},
|
||||
)
|
||||
|
||||
expectedErrors := []error{
|
||||
errors.New(`dir1/Android.bp:7:4: module "a" already defined
|
||||
dir1/Android.bp:4:4 <-- previous definition here`),
|
||||
}
|
||||
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
|
||||
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeclaringNamespaceInNonAndroidBpFile(t *testing.T) {
|
||||
_, errs := setupTestFromFiles(
|
||||
map[string][]byte{
|
||||
"Android.bp": []byte(`
|
||||
build = ["include.bp"]
|
||||
`),
|
||||
"include.bp": []byte(`
|
||||
soong_namespace {
|
||||
}
|
||||
`),
|
||||
},
|
||||
)
|
||||
|
||||
expectedErrors := []error{
|
||||
errors.New(`include.bp:2:5: A namespace may only be declared in a file named Android.bp`),
|
||||
}
|
||||
|
||||
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
|
||||
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
|
||||
}
|
||||
}
|
||||
|
||||
// so that the generated .ninja file will have consistent names
|
||||
func TestConsistentNamespaceNames(t *testing.T) {
|
||||
ctx := setupTest(t,
|
||||
map[string]string{
|
||||
"dir1": "soong_namespace{}",
|
||||
"dir2": "soong_namespace{}",
|
||||
"dir3": "soong_namespace{}",
|
||||
})
|
||||
|
||||
ns1, _ := ctx.NameResolver.namespaceAt("dir1")
|
||||
ns2, _ := ctx.NameResolver.namespaceAt("dir2")
|
||||
ns3, _ := ctx.NameResolver.namespaceAt("dir3")
|
||||
actualIds := []string{ns1.id, ns2.id, ns3.id}
|
||||
expectedIds := []string{"1", "2", "3"}
|
||||
if !reflect.DeepEqual(actualIds, expectedIds) {
|
||||
t.Errorf("Incorrect namespace ids.\nactual: %s\nexpected: %s\n", actualIds, expectedIds)
|
||||
}
|
||||
}
|
||||
|
||||
// some utils to support the tests
|
||||
|
||||
func mockFiles(bps map[string]string) (files map[string][]byte) {
|
||||
files = make(map[string][]byte, len(bps))
|
||||
files["Android.bp"] = []byte("")
|
||||
for dir, text := range bps {
|
||||
files[filepath.Join(dir, "Android.bp")] = []byte(text)
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
func setupTestFromFiles(bps map[string][]byte) (ctx *TestContext, errs []error) {
|
||||
buildDir, err := ioutil.TempDir("", "soong_namespace_test")
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
defer os.RemoveAll(buildDir)
|
||||
|
||||
config := TestConfig(buildDir, nil)
|
||||
|
||||
ctx = NewTestContext()
|
||||
ctx.MockFileSystem(bps)
|
||||
ctx.RegisterModuleType("test_module", ModuleFactoryAdaptor(newTestModule))
|
||||
ctx.RegisterModuleType("soong_namespace", ModuleFactoryAdaptor(NamespaceFactory))
|
||||
ctx.PreDepsMutators(RegisterNamespaceMutator)
|
||||
ctx.Register()
|
||||
|
||||
_, errs = ctx.ParseBlueprintsFiles("Android.bp")
|
||||
if len(errs) > 0 {
|
||||
return ctx, errs
|
||||
}
|
||||
_, errs = ctx.PrepareBuildActions(config)
|
||||
return ctx, errs
|
||||
}
|
||||
|
||||
func setupTestExpectErrs(bps map[string]string) (ctx *TestContext, errs []error) {
|
||||
files := make(map[string][]byte, len(bps))
|
||||
files["Android.bp"] = []byte("")
|
||||
for dir, text := range bps {
|
||||
files[filepath.Join(dir, "Android.bp")] = []byte(text)
|
||||
}
|
||||
return setupTestFromFiles(files)
|
||||
}
|
||||
|
||||
func setupTest(t *testing.T, bps map[string]string) (ctx *TestContext) {
|
||||
ctx, errs := setupTestExpectErrs(bps)
|
||||
failIfErrored(t, errs)
|
||||
return ctx
|
||||
}
|
||||
|
||||
func dependsOn(ctx *TestContext, module TestingModule, possibleDependency TestingModule) bool {
|
||||
depends := false
|
||||
visit := func(dependency blueprint.Module) {
|
||||
if dependency == possibleDependency.module {
|
||||
depends = true
|
||||
}
|
||||
}
|
||||
ctx.VisitDirectDeps(module.module, visit)
|
||||
return depends
|
||||
}
|
||||
|
||||
func numDeps(ctx *TestContext, module TestingModule) int {
|
||||
count := 0
|
||||
visit := func(dependency blueprint.Module) {
|
||||
count++
|
||||
}
|
||||
ctx.VisitDirectDeps(module.module, visit)
|
||||
return count
|
||||
}
|
||||
|
||||
func getModule(ctx *TestContext, moduleName string) TestingModule {
|
||||
return ctx.ModuleForTests(moduleName, "")
|
||||
}
|
||||
|
||||
func findModuleById(ctx *TestContext, id string) (module TestingModule) {
|
||||
visit := func(candidate blueprint.Module) {
|
||||
testModule, ok := candidate.(*testModule)
|
||||
if ok {
|
||||
if testModule.properties.Id == id {
|
||||
module = TestingModule{testModule}
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.VisitAllModules(visit)
|
||||
return module
|
||||
}
|
||||
|
||||
type testModule struct {
|
||||
ModuleBase
|
||||
properties struct {
|
||||
Deps []string
|
||||
Id string
|
||||
}
|
||||
}
|
||||
|
||||
func (m *testModule) DepsMutator(ctx BottomUpMutatorContext) {
|
||||
for _, d := range m.properties.Deps {
|
||||
ctx.AddDependency(ctx.Module(), nil, d)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *testModule) GenerateAndroidBuildActions(ModuleContext) {
|
||||
}
|
||||
|
||||
func newTestModule() Module {
|
||||
m := &testModule{}
|
||||
m.AddProperties(&m.properties)
|
||||
InitAndroidModule(m)
|
||||
return m
|
||||
}
|
||||
|
||||
func failIfErrored(t *testing.T, errs []error) {
|
||||
if len(errs) > 0 {
|
||||
for _, err := range errs {
|
||||
t.Error(err)
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
@@ -23,9 +23,19 @@ import (
|
||||
)
|
||||
|
||||
func NewTestContext() *TestContext {
|
||||
return &TestContext{
|
||||
Context: blueprint.NewContext(),
|
||||
namespaceExportFilter := func(namespace *Namespace) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
nameResolver := NewNameResolver(namespaceExportFilter)
|
||||
ctx := &TestContext{
|
||||
Context: blueprint.NewContext(),
|
||||
NameResolver: nameResolver,
|
||||
}
|
||||
|
||||
ctx.SetNameInterface(nameResolver)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func NewTestArchContext() *TestContext {
|
||||
@@ -37,6 +47,7 @@ func NewTestArchContext() *TestContext {
|
||||
type TestContext struct {
|
||||
*blueprint.Context
|
||||
preArch, preDeps, postDeps []RegisterMutatorFunc
|
||||
NameResolver *NameResolver
|
||||
}
|
||||
|
||||
func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
|
||||
|
@@ -194,6 +194,8 @@ type productVariables struct {
|
||||
DistDir *string `json:",omitempty"`
|
||||
|
||||
ExtraVndkVersions []string `json:",omitempty"`
|
||||
|
||||
NamespacesToExport []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func boolPtr(v bool) *bool {
|
||||
|
@@ -25,6 +25,22 @@ import (
|
||||
"android/soong/android"
|
||||
)
|
||||
|
||||
func newNameResolver(config android.Config) *android.NameResolver {
|
||||
namespacePathsToExport := make(map[string]bool)
|
||||
|
||||
for _, namespaceName := range config.ProductVariables.NamespacesToExport {
|
||||
namespacePathsToExport[namespaceName] = true
|
||||
}
|
||||
|
||||
namespacePathsToExport["."] = true // always export the root namespace
|
||||
|
||||
exportFilter := func(namespace *android.Namespace) bool {
|
||||
return namespacePathsToExport[namespace.Path]
|
||||
}
|
||||
|
||||
return android.NewNameResolver(exportFilter)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
@@ -40,8 +56,7 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Temporary hack
|
||||
//ctx.SetIgnoreUnknownModuleTypes(true)
|
||||
ctx.SetNameInterface(newNameResolver(configuration))
|
||||
|
||||
ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
|
||||
|
||||
|
@@ -106,6 +106,7 @@ var BannerVars = []string{
|
||||
"AUX_OS_VARIANT_LIST",
|
||||
"TARGET_BUILD_PDK",
|
||||
"PDK_FUSION_PLATFORM_ZIP",
|
||||
"PRODUCT_SOONG_NAMESPACES",
|
||||
}
|
||||
|
||||
func Banner(make_vars map[string]string) string {
|
||||
|
Reference in New Issue
Block a user