platform_build_blueprint/name_interface.go
Sam Delmerico 08bd504b62 ignore bp files from PRODUCT_SOURCE_ROOT_DIRS
Soong analyzes the entire source tree even though not every lunch target
needs to know about every module. For example, OEM sources can be
ignored for cuttlefish products. This functionality allows blueprint to
ignore a list of undesired directories.

Bug: 269457150
Change-Id: Icbbf8f3b66813ad639a7ebd27b1a3ec153cbf269
2023-03-27 14:43:07 -04:00

219 lines
7.1 KiB
Go

// 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 blueprint
import (
"fmt"
"os"
"sort"
"strings"
)
// This file exposes the logic of locating a module via a query string, to enable
// other projects to override it if desired.
// The default name resolution implementation, SimpleNameInterface,
// just treats the query string as a module name, and does a simple map lookup.
// A ModuleGroup just points to a moduleGroup to allow external packages to refer
// to a moduleGroup but not use it
type ModuleGroup struct {
*moduleGroup
}
func (h *ModuleGroup) String() string {
return h.moduleGroup.name
}
// The Namespace interface is just a marker interface for usage by the NameInterface,
// to allow a NameInterface to specify that a certain parameter should be a Namespace.
// In practice, a specific NameInterface will expect to only give and receive structs of
// the same concrete type, but because Go doesn't support generics, we use a marker interface
// for a little bit of clarity, and expect implementers to do typecasting instead.
type Namespace interface {
namespace(Namespace)
}
type NamespaceMarker struct {
}
func (m *NamespaceMarker) namespace(Namespace) {
}
// A NameInterface tells how to locate modules by name.
// There should only be one name interface per Context, but potentially many namespaces
type NameInterface interface {
// Gets called when a new module is created
NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error)
// Gets called when a module was pruned from the build tree by SourceRootDirs
NewSkippedModule(ctx NamespaceContext, name string, skipInfo SkippedModuleInfo)
// Finds the module with the given name
ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool)
// Returns an error indicating that the given module could not be found.
// The error contains some diagnostic information about where the dependency can be found.
MissingDependencyError(depender string, dependerNamespace Namespace, depName string) (err error)
// Rename
Rename(oldName string, newName string, namespace Namespace) []error
// Returns all modules in a deterministic order.
AllModules() []ModuleGroup
// gets the namespace for a given path
GetNamespace(ctx NamespaceContext) (namespace Namespace)
// returns a deterministic, unique, arbitrary string for the given name in the given namespace
UniqueName(ctx NamespaceContext, name string) (unique string)
}
// A NamespaceContext stores the information given to a NameInterface to enable the NameInterface
// to choose the namespace for any given module
type NamespaceContext interface {
ModulePath() string
}
type namespaceContextImpl struct {
modulePath string
}
func newNamespaceContext(moduleInfo *moduleInfo) (ctx NamespaceContext) {
return &namespaceContextImpl{moduleInfo.pos.Filename}
}
func newNamespaceContextFromFilename(filename string) NamespaceContext {
return &namespaceContextImpl{filename}
}
func (ctx *namespaceContextImpl) ModulePath() string {
return ctx.modulePath
}
type SkippedModuleInfo struct {
filename string
reason string
}
// a SimpleNameInterface just stores all modules in a map based on name
type SimpleNameInterface struct {
modules map[string]ModuleGroup
skippedModules map[string][]SkippedModuleInfo
}
func NewSimpleNameInterface() *SimpleNameInterface {
return &SimpleNameInterface{
modules: make(map[string]ModuleGroup),
skippedModules: make(map[string][]SkippedModuleInfo),
}
}
func (s *SimpleNameInterface) NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error) {
name := group.name
if group, present := s.modules[name]; present {
return nil, []error{
// seven characters at the start of the second line to align with the string "error: "
fmt.Errorf("module %q already defined\n"+
" %s <-- previous definition here", name, group.modules.firstModule().pos),
}
}
s.modules[name] = group
return nil, []error{}
}
func (s *SimpleNameInterface) NewSkippedModule(ctx NamespaceContext, name string, info SkippedModuleInfo) {
if name == "" {
return
}
s.skippedModules[name] = append(s.skippedModules[name], info)
}
func (s *SimpleNameInterface) ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) {
group, found = s.modules[moduleName]
skipInfos, skipped := s.skippedModules[moduleName]
if skipped {
filesFound := make([]string, 0, len(skipInfos))
reasons := make([]string, 0, len(skipInfos))
for _, info := range skipInfos {
filesFound = append(filesFound, info.filename)
reasons = append(reasons, info.reason)
}
fmt.Fprintf(
os.Stderr,
"module %q was defined in files(s) [%v], but was skipped for reason(s) [%v]\n",
moduleName,
strings.Join(filesFound, ", "),
strings.Join(reasons, "; "),
)
}
return group, found
}
func (s *SimpleNameInterface) Rename(oldName string, newName string, namespace Namespace) (errs []error) {
existingGroup, exists := s.modules[newName]
if exists {
return []error{
// seven characters at the start of the second line to align with the string "error: "
fmt.Errorf("renaming module %q to %q conflicts with existing module\n"+
" %s <-- existing module defined here",
oldName, newName, existingGroup.modules.firstModule().pos),
}
}
group, exists := s.modules[oldName]
if !exists {
return []error{fmt.Errorf("module %q to renamed to %q doesn't exist", oldName, newName)}
}
s.modules[newName] = group
delete(s.modules, group.name)
group.name = newName
return nil
}
func (s *SimpleNameInterface) AllModules() []ModuleGroup {
groups := make([]ModuleGroup, 0, len(s.modules))
for _, group := range s.modules {
groups = append(groups, group)
}
duplicateName := ""
less := func(i, j int) bool {
if groups[i].name == groups[j].name {
duplicateName = groups[i].name
}
return groups[i].name < groups[j].name
}
sort.Slice(groups, less)
if duplicateName != "" {
// It is permitted to have two moduleGroup's with the same name, but not within the same
// Namespace. The SimpleNameInterface should catch this in NewModule, however, so this
// should never happen.
panic(fmt.Sprintf("Duplicate moduleGroup name %q", duplicateName))
}
return groups
}
func (s *SimpleNameInterface) MissingDependencyError(depender string, dependerNamespace Namespace, dependency string) (err error) {
return fmt.Errorf("%q depends on undefined module %q", depender, dependency)
}
func (s *SimpleNameInterface) GetNamespace(ctx NamespaceContext) Namespace {
return nil
}
func (s *SimpleNameInterface) UniqueName(ctx NamespaceContext, name string) (unique string) {
return name
}