Revert "Revert "Soong support for namespaces""
This mostly reverts commit 178d5fefc0
and mostly reapplies change I6d3e52ef62c4cabe85b9a135a54de0e1a6aab29c .
Bug: 65683273
Test: build/soong/scripts/diff_build_graphs.sh \
--products=aosp_arm \
'build/blueprint:work^ build/soong:work^' \
'build/blueprint:work build/soong:work'
# and see that the only changes were:
# 1. adding some new files
# 2. changing some line numbers
Test: m -j nothing # which runs unit tests
Change-Id: I32baae00277a547fdcdd1c2219fe6625ee0e45d7
This commit is contained in:
parent
7154928c93
commit
088e29ed38
|
@ -50,6 +50,7 @@ bootstrap_go_package {
|
||||||
"android/makevars.go",
|
"android/makevars.go",
|
||||||
"android/module.go",
|
"android/module.go",
|
||||||
"android/mutator.go",
|
"android/mutator.go",
|
||||||
|
"android/namespace.go",
|
||||||
"android/onceper.go",
|
"android/onceper.go",
|
||||||
"android/package_ctx.go",
|
"android/package_ctx.go",
|
||||||
"android/paths.go",
|
"android/paths.go",
|
||||||
|
@ -67,6 +68,7 @@ bootstrap_go_package {
|
||||||
testSrcs: [
|
testSrcs: [
|
||||||
"android/config_test.go",
|
"android/config_test.go",
|
||||||
"android/expand_test.go",
|
"android/expand_test.go",
|
||||||
|
"android/namespace_test.go",
|
||||||
"android/paths_test.go",
|
"android/paths_test.go",
|
||||||
"android/prebuilt_test.go",
|
"android/prebuilt_test.go",
|
||||||
"android/util_test.go",
|
"android/util_test.go",
|
||||||
|
|
|
@ -157,6 +157,12 @@ func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.M
|
||||||
return nil
|
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()
|
data := provider.AndroidMk()
|
||||||
|
|
||||||
if data.Include == "" {
|
if data.Include == "" {
|
||||||
|
|
|
@ -151,6 +151,7 @@ type ModuleContext interface {
|
||||||
VisitAllModuleVariants(visit func(Module))
|
VisitAllModuleVariants(visit func(Module))
|
||||||
|
|
||||||
GetMissingDependencies() []string
|
GetMissingDependencies() []string
|
||||||
|
Namespace() blueprint.Namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
type Module interface {
|
type Module interface {
|
||||||
|
@ -235,6 +236,8 @@ type commonProperties struct {
|
||||||
ArchSpecific bool `blueprint:"mutated"`
|
ArchSpecific bool `blueprint:"mutated"`
|
||||||
|
|
||||||
SkipInstall bool `blueprint:"mutated"`
|
SkipInstall bool `blueprint:"mutated"`
|
||||||
|
|
||||||
|
NamespaceExportedToMake bool `blueprint:"mutated"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type hostAndDeviceProperties struct {
|
type hostAndDeviceProperties struct {
|
||||||
|
@ -500,8 +503,13 @@ func (a *ModuleBase) generateModuleTarget(ctx ModuleContext) {
|
||||||
|
|
||||||
var deps Paths
|
var deps Paths
|
||||||
|
|
||||||
|
namespacePrefix := ctx.Namespace().(*Namespace).id
|
||||||
|
if namespacePrefix != "" {
|
||||||
|
namespacePrefix = namespacePrefix + "-"
|
||||||
|
}
|
||||||
|
|
||||||
if len(allInstalledFiles) > 0 {
|
if len(allInstalledFiles) > 0 {
|
||||||
name := PathForPhony(ctx, ctx.ModuleName()+"-install")
|
name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+"-install")
|
||||||
ctx.Build(pctx, BuildParams{
|
ctx.Build(pctx, BuildParams{
|
||||||
Rule: blueprint.Phony,
|
Rule: blueprint.Phony,
|
||||||
Output: name,
|
Output: name,
|
||||||
|
@ -513,7 +521,7 @@ func (a *ModuleBase) generateModuleTarget(ctx ModuleContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(allCheckbuildFiles) > 0 {
|
if len(allCheckbuildFiles) > 0 {
|
||||||
name := PathForPhony(ctx, ctx.ModuleName()+"-checkbuild")
|
name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+"-checkbuild")
|
||||||
ctx.Build(pctx, BuildParams{
|
ctx.Build(pctx, BuildParams{
|
||||||
Rule: blueprint.Phony,
|
Rule: blueprint.Phony,
|
||||||
Output: name,
|
Output: name,
|
||||||
|
@ -529,9 +537,10 @@ func (a *ModuleBase) generateModuleTarget(ctx ModuleContext) {
|
||||||
suffix = "-soong"
|
suffix = "-soong"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+suffix)
|
||||||
ctx.Build(pctx, BuildParams{
|
ctx.Build(pctx, BuildParams{
|
||||||
Rule: blueprint.Phony,
|
Rule: blueprint.Phony,
|
||||||
Output: PathForPhony(ctx, ctx.ModuleName()+suffix),
|
Outputs: []WritablePath{name},
|
||||||
Implicits: deps,
|
Implicits: deps,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,7 @@ func registerArchMutator(ctx RegisterMutatorsContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var preDeps = []RegisterMutatorFunc{
|
var preDeps = []RegisterMutatorFunc{
|
||||||
|
RegisterNamespaceMutator,
|
||||||
registerArchMutator,
|
registerArchMutator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,398 @@
|
||||||
|
// 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 (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
numNamespaces 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)
|
||||||
|
|
||||||
|
nextId := atomic.AddInt32(&r.numNamespaces, 1)
|
||||||
|
id := nextId - 1
|
||||||
|
stringId := ""
|
||||||
|
if id > 0 {
|
||||||
|
stringId = strconv.Itoa(int(id))
|
||||||
|
}
|
||||||
|
namespace.id = stringId
|
||||||
|
|
||||||
|
return namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *NameResolver) addNewNamespaceForModule(module *NamespaceModule, dir string) error {
|
||||||
|
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.ModuleDir())
|
||||||
|
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) 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(ctx.ModuleDir())
|
||||||
|
}
|
||||||
|
|
||||||
|
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", namespaceDeps)
|
||||||
|
}
|
||||||
|
|
||||||
|
func namespaceDeps(ctx BottomUpMutatorContext) {
|
||||||
|
module, ok := ctx.Module().(*NamespaceModule)
|
||||||
|
if ok {
|
||||||
|
err := module.resolver.FindNamespaceImports(module.namespace)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ModuleErrorf(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,652 @@
|
||||||
|
// 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"
|
||||||
|
"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/Blueprints: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/Blueprints: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/Blueprints: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/Blueprints: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/Blueprints: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/Blueprints: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/Blueprints:7:4: module "a" already defined
|
||||||
|
dir1/Blueprints: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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// some utils to support the tests
|
||||||
|
|
||||||
|
func mockFiles(bps map[string]string) (files map[string][]byte) {
|
||||||
|
files = make(map[string][]byte, len(bps))
|
||||||
|
files["Blueprints"] = []byte("")
|
||||||
|
for dir, text := range bps {
|
||||||
|
files[filepath.Join(dir, "Blueprints")] = []byte(text)
|
||||||
|
}
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupTestExpectErrs(bps map[string]string) (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(mockFiles(bps))
|
||||||
|
ctx.RegisterModuleType("test_module", ModuleFactoryAdaptor(newTestModule))
|
||||||
|
ctx.RegisterModuleType("soong_namespace", ModuleFactoryAdaptor(NamespaceFactory))
|
||||||
|
ctx.PreDepsMutators(RegisterNamespaceMutator)
|
||||||
|
ctx.Register()
|
||||||
|
|
||||||
|
_, errs = ctx.ParseBlueprintsFiles("Blueprints")
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return ctx, errs
|
||||||
|
}
|
||||||
|
_, errs = ctx.PrepareBuildActions(config)
|
||||||
|
return ctx, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
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,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewTestContext() *TestContext {
|
func NewTestContext() *TestContext {
|
||||||
return &TestContext{
|
ctx := &TestContext{
|
||||||
Context: blueprint.NewContext(),
|
Context: blueprint.NewContext(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespaceExportFilter := func(namespace *Namespace) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ctx.SetNameInterface(NewNameResolver(namespaceExportFilter))
|
||||||
|
|
||||||
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestArchContext() *TestContext {
|
func NewTestArchContext() *TestContext {
|
||||||
|
|
|
@ -194,6 +194,8 @@ type productVariables struct {
|
||||||
DistDir *string `json:",omitempty"`
|
DistDir *string `json:",omitempty"`
|
||||||
|
|
||||||
ExtraVndkVersions []string `json:",omitempty"`
|
ExtraVndkVersions []string `json:",omitempty"`
|
||||||
|
|
||||||
|
NamespacesToExport []string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func boolPtr(v bool) *bool {
|
func boolPtr(v bool) *bool {
|
||||||
|
|
|
@ -25,6 +25,22 @@ import (
|
||||||
"android/soong/android"
|
"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() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -40,8 +56,7 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporary hack
|
ctx.SetNameInterface(newNameResolver(configuration))
|
||||||
//ctx.SetIgnoreUnknownModuleTypes(true)
|
|
||||||
|
|
||||||
ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
|
ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,7 @@ var BannerVars = []string{
|
||||||
"AUX_OS_VARIANT_LIST",
|
"AUX_OS_VARIANT_LIST",
|
||||||
"TARGET_BUILD_PDK",
|
"TARGET_BUILD_PDK",
|
||||||
"PDK_FUSION_PLATFORM_ZIP",
|
"PDK_FUSION_PLATFORM_ZIP",
|
||||||
|
"PRODUCT_SOONG_NAMESPACES",
|
||||||
}
|
}
|
||||||
|
|
||||||
func Banner(make_vars map[string]string) string {
|
func Banner(make_vars map[string]string) string {
|
||||||
|
|
Loading…
Reference in New Issue