From 620dea67209e48576b0f20d357a360c8c8b2d94c Mon Sep 17 00:00:00 2001 From: Liz Kammer Date: Wed, 14 Apr 2021 17:36:10 -0400 Subject: [PATCH] Split bazel -path functions and clarify docs Test: go test Change-Id: I62f58998fc7d52c67ed5acfdb8230d404b2a5472 --- android/Android.bp | 1 + android/bazel_paths.go | 353 +++++++++++++++++++++++++++++++++++++++++ android/paths.go | 334 +++++--------------------------------- 3 files changed, 397 insertions(+), 291 deletions(-) create mode 100644 android/bazel_paths.go diff --git a/android/Android.bp b/android/Android.bp index f5e5606ef..62d5e207d 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -26,6 +26,7 @@ bootstrap_go_package { "arch_list.go", "bazel.go", "bazel_handler.go", + "bazel_paths.go", "config.go", "csuite_config.go", "deapexer.go", diff --git a/android/bazel_paths.go b/android/bazel_paths.go new file mode 100644 index 000000000..13f494922 --- /dev/null +++ b/android/bazel_paths.go @@ -0,0 +1,353 @@ +// Copyright 2015 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 ( + "android/soong/bazel" + "fmt" + "path/filepath" + "strings" + + "github.com/google/blueprint" + "github.com/google/blueprint/pathtools" +) + +// bazel_paths contains methods to: +// * resolve Soong path and module references into bazel.LabelList +// * resolve Bazel path references into Soong-compatible paths +// +// There is often a similar method for Bazel as there is for Soong path handling and should be used +// in similar circumstances +// +// Bazel Soong +// +// BazelLabelForModuleSrc PathForModuleSrc +// BazelLabelForModuleSrcExcludes PathForModuleSrcExcludes +// BazelLabelForModuleDeps n/a +// tbd PathForSource +// tbd ExistentPathsForSources +// PathForBazelOut PathForModuleOut +// +// Use cases: +// * Module contains a property (often tagged `android:"path"`) that expects paths *relative to the +// module directory*: +// * BazelLabelForModuleSrcExcludes, if the module also contains an excludes_ property +// * BazelLabelForModuleSrc, otherwise +// * Converting references to other modules to Bazel Labels: +// BazelLabelForModuleDeps +// * Converting a path obtained from bazel_handler cquery results: +// PathForBazelOut +// +// NOTE: all Soong globs are expanded within Soong rather than being converted to a Bazel glob +// syntax. This occurs because Soong does not have a concept of crossing package boundaries, +// so the glob as computed by Soong may contain paths that cross package-boundaries. These +// would be unknowingly omitted if the glob were handled by Bazel. By expanding globs within +// Soong, we support identification and detection (within Bazel) use of paths that cross +// package boundaries. +// +// Path resolution: +// * filepath/globs: resolves as itself or is converted to an absolute Bazel label (e.g. +// //path/to/dir:) if path exists in a separate package or subpackage. +// * references to other modules (using the ":name{.tag}" syntax). These resolve as a Bazel label +// for a target. If the Bazel target is in the local module directory, it will be returned +// relative to the current package (e.g. ":"). Otherwise, it will be returned as an +// absolute Bazel label (e.g. "//path/to/dir:"). If the reference to another module +// cannot be resolved,the function will panic. This is often due to the dependency not being added +// via an AddDependency* method. + +// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in +// order to form a Bazel-compatible label for conversion. +type BazelConversionPathContext interface { + EarlyModulePathContext + + GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) + Module() Module + ModuleType() string + OtherModuleName(m blueprint.Module) string + OtherModuleDir(m blueprint.Module) string +} + +// BazelLabelForModuleDeps expects a list of reference to other modules, ("" +// or ":") and returns a Bazel-compatible label which corresponds to dependencies on the +// module within the given ctx. +func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList { + var labels bazel.LabelList + for _, module := range modules { + bpText := module + if m := SrcIsModule(module); m == "" { + module = ":" + module + } + if m, t := SrcIsModuleWithTag(module); m != "" { + l := getOtherModuleLabel(ctx, m, t) + l.Bp_text = bpText + labels.Includes = append(labels.Includes, l) + } else { + ctx.ModuleErrorf("%q, is not a module reference", module) + } + } + return labels +} + +// BazelLabelForModuleSrc expects a list of path (relative to local module directory) and module +// references (":") and returns a bazel.LabelList{} containing the resolved references in +// paths, relative to the local module, or Bazel-labels (absolute if in a different package or +// relative if within the same package). +// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules +// will have already been handled by the path_deps mutator. +func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList { + return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil)) +} + +// BazelLabelForModuleSrc expects lists of path and excludes (relative to local module directory) +// and module references (":") and returns a bazel.LabelList{} containing the resolved +// references in paths, minus those in excludes, relative to the local module, or Bazel-labels +// (absolute if in a different package or relative if within the same package). +// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules +// will have already been handled by the path_deps mutator. +func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList { + excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil)) + excluded := make([]string, 0, len(excludeLabels.Includes)) + for _, e := range excludeLabels.Includes { + excluded = append(excluded, e.Label) + } + labels := expandSrcsForBazel(ctx, paths, excluded) + labels.Excludes = excludeLabels.Includes + labels = transformSubpackagePaths(ctx, labels) + return labels +} + +// Returns true if a prefix + components[:i] + /Android.bp exists +// TODO(b/185358476) Could check for BUILD file instead of checking for Android.bp file, or ensure BUILD is always generated? +func directoryHasBlueprint(fs pathtools.FileSystem, prefix string, components []string, componentIndex int) bool { + blueprintPath := prefix + if blueprintPath != "" { + blueprintPath = blueprintPath + "/" + } + blueprintPath = blueprintPath + strings.Join(components[:componentIndex+1], "/") + blueprintPath = blueprintPath + "/Android.bp" + if exists, _, _ := fs.Exists(blueprintPath); exists { + return true + } else { + return false + } +} + +// Transform a path (if necessary) to acknowledge package boundaries +// +// e.g. something like +// async_safe/include/async_safe/CHECK.h +// might become +// //bionic/libc/async_safe:include/async_safe/CHECK.h +// if the "async_safe" directory is actually a package and not just a directory. +// +// In particular, paths that extend into packages are transformed into absolute labels beginning with //. +func transformSubpackagePath(ctx BazelConversionPathContext, path bazel.Label) bazel.Label { + var newPath bazel.Label + + // Don't transform Bp_text + newPath.Bp_text = path.Bp_text + + if strings.HasPrefix(path.Label, "//") { + // Assume absolute labels are already correct (e.g. //path/to/some/package:foo.h) + newPath.Label = path.Label + return newPath + } + + newLabel := "" + pathComponents := strings.Split(path.Label, "/") + foundBlueprint := false + // Check the deepest subdirectory first and work upwards + for i := len(pathComponents) - 1; i >= 0; i-- { + pathComponent := pathComponents[i] + var sep string + if !foundBlueprint && directoryHasBlueprint(ctx.Config().fs, ctx.ModuleDir(), pathComponents, i) { + sep = ":" + foundBlueprint = true + } else { + sep = "/" + } + if newLabel == "" { + newLabel = pathComponent + } else { + newLabel = pathComponent + sep + newLabel + } + } + if foundBlueprint { + // Ensure paths end up looking like //bionic/... instead of //./bionic/... + moduleDir := ctx.ModuleDir() + if strings.HasPrefix(moduleDir, ".") { + moduleDir = moduleDir[1:] + } + // Make the path into an absolute label (e.g. //bionic/libc/foo:bar.h instead of just foo:bar.h) + if moduleDir == "" { + newLabel = "//" + newLabel + } else { + newLabel = "//" + moduleDir + "/" + newLabel + } + } + newPath.Label = newLabel + + return newPath +} + +// Transform paths to acknowledge package boundaries +// See transformSubpackagePath() for more information +func transformSubpackagePaths(ctx BazelConversionPathContext, paths bazel.LabelList) bazel.LabelList { + var newPaths bazel.LabelList + for _, include := range paths.Includes { + newPaths.Includes = append(newPaths.Includes, transformSubpackagePath(ctx, include)) + } + for _, exclude := range paths.Excludes { + newPaths.Excludes = append(newPaths.Excludes, transformSubpackagePath(ctx, exclude)) + } + return newPaths +} + +// expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local source +// directory and Bazel target labels, excluding those included in the excludes argument (which +// should already be expanded to resolve references to Soong-modules). Valid elements of paths +// include: +// * filepath, relative to local module directory, resolves as a filepath relative to the local +// source directory +// * glob, relative to the local module directory, resolves as filepath(s), relative to the local +// module directory. Because Soong does not have a concept of crossing package boundaries, the +// glob as computed by Soong may contain paths that cross package-boundaries that would be +// unknowingly omitted if the glob were handled by Bazel. To allow identification and detect +// (within Bazel) use of paths that cross package boundaries, we expand globs within Soong rather +// than converting Soong glob syntax to Bazel glob syntax. **Invalid for excludes.** +// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer +// or OutputFileProducer. These resolve as a Bazel label for a target. If the Bazel target is in +// the local module directory, it will be returned relative to the current package (e.g. +// ":"). Otherwise, it will be returned as an absolute Bazel label (e.g. +// "//path/to/dir:"). If the reference to another module cannot be resolved,the function +// will panic. +// Properties passed as the paths or excludes argument must have been annotated with struct tag +// `android:"path"` so that dependencies on other modules will have already been handled by the +// path_deps mutator. +func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList { + if paths == nil { + return bazel.LabelList{} + } + labels := bazel.LabelList{ + Includes: []bazel.Label{}, + } + for _, p := range paths { + if m, tag := SrcIsModuleWithTag(p); m != "" { + l := getOtherModuleLabel(ctx, m, tag) + if !InList(l.Label, expandedExcludes) { + l.Bp_text = fmt.Sprintf(":%s", m) + labels.Includes = append(labels.Includes, l) + } + } else { + var expandedPaths []bazel.Label + if pathtools.IsGlob(p) { + globbedPaths := GlobFiles(ctx, pathForModuleSrc(ctx, p).String(), expandedExcludes) + globbedPaths = PathsWithModuleSrcSubDir(ctx, globbedPaths, "") + for _, path := range globbedPaths { + s := path.Rel() + expandedPaths = append(expandedPaths, bazel.Label{Label: s}) + } + } else { + if !InList(p, expandedExcludes) { + expandedPaths = append(expandedPaths, bazel.Label{Label: p}) + } + } + labels.Includes = append(labels.Includes, expandedPaths...) + } + } + return labels +} + +// getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the +// module. The label will be relative to the current directory if appropriate. The dependency must +// already be resolved by either deps mutator or path deps mutator. +func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel.Label { + m, _ := ctx.GetDirectDep(dep) + if m == nil { + panic(fmt.Errorf(`Cannot get direct dep %q of %q. + This is likely because it was not added via AddDependency(). + This may be due a mutator skipped during bp2build.`, dep, ctx.Module().Name())) + } + otherLabel := bazelModuleLabel(ctx, m, tag) + label := bazelModuleLabel(ctx, ctx.Module(), "") + if samePackage(label, otherLabel) { + otherLabel = bazelShortLabel(otherLabel) + } + + return bazel.Label{ + Label: otherLabel, + } +} + +func bazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module, tag string) string { + // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets. + b, ok := module.(Bazelable) + // TODO(b/181155349): perhaps return an error here if the module can't be/isn't being converted + if !ok || !b.ConvertedToBazel(ctx) { + return bp2buildModuleLabel(ctx, module) + } + return b.GetBazelLabel(ctx, module) +} + +func bazelShortLabel(label string) string { + i := strings.Index(label, ":") + return label[i:] +} + +func bazelPackage(label string) string { + i := strings.Index(label, ":") + return label[0:i] +} + +func samePackage(label1, label2 string) bool { + return bazelPackage(label1) == bazelPackage(label2) +} + +func bp2buildModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string { + moduleName := ctx.OtherModuleName(module) + moduleDir := ctx.OtherModuleDir(module) + return fmt.Sprintf("//%s:%s", moduleDir, moduleName) +} + +// BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja. +type BazelOutPath struct { + OutputPath +} + +var _ Path = BazelOutPath{} +var _ objPathProvider = BazelOutPath{} + +func (p BazelOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath { + return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) +} + +// PathForBazelOut returns a Path representing the paths... under an output directory dedicated to +// bazel-owned outputs. +func PathForBazelOut(ctx PathContext, paths ...string) BazelOutPath { + execRootPathComponents := append([]string{"execroot", "__main__"}, paths...) + execRootPath := filepath.Join(execRootPathComponents...) + validatedExecRootPath, err := validatePath(execRootPath) + if err != nil { + reportPathError(ctx, err) + } + + outputPath := OutputPath{basePath{"", ""}, + ctx.Config().buildDir, + ctx.Config().BazelContext.OutputBase()} + + return BazelOutPath{ + OutputPath: outputPath.withRel(validatedExecRootPath), + } +} diff --git a/android/paths.go b/android/paths.go index c303c38f5..026cb8747 100644 --- a/android/paths.go +++ b/android/paths.go @@ -15,7 +15,6 @@ package android import ( - "android/soong/bazel" "fmt" "io/ioutil" "os" @@ -356,23 +355,42 @@ func ExistentPathsForSources(ctx PathContext, paths []string) Paths { return ret } -// PathsForModuleSrc returns Paths rooted from the module's local source directory. It expands globs, references to -// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the -// ":name{.tag}" syntax. Properties passed as the paths argument must have been annotated with struct tag +// PathsForModuleSrc returns a Paths{} containing the resolved references in paths: +// * filepath, relative to local module directory, resolves as a filepath relative to the local +// source directory +// * glob, relative to the local module directory, resolves as filepath(s), relative to the local +// source directory. +// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer +// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source +// filepath. +// Properties passed as the paths argument must have been annotated with struct tag // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the -// path_properties mutator. If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or -// OutputFileProducer dependencies will cause the module to be marked as having missing dependencies. +// path_deps mutator. +// If a requested module is not found as a dependency: +// * if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having +// missing dependencies +// * otherwise, a ModuleError is thrown. func PathsForModuleSrc(ctx ModuleMissingDepsPathContext, paths []string) Paths { return PathsForModuleSrcExcludes(ctx, paths, nil) } -// PathsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding paths listed in -// the excludes arguments. It expands globs, references to SourceFileProducer modules using the ":name" syntax, and -// references to OutputFileProducer modules using the ":name{.tag}" syntax. Properties passed as the paths or excludes -// argument must have been annotated with struct tag `android:"path"` so that dependencies on SourceFileProducer modules -// will have already been handled by the path_properties mutator. If ctx.Config().AllowMissingDependencies() is -// true then any missing SourceFileProducer or OutputFileProducer dependencies will cause the module to be marked as -// having missing dependencies. +// PathsForModuleSrcExcludes returns a Paths{} containing the resolved references in paths, minus +// those listed in excludes. Elements of paths and excludes are resolved as: +// * filepath, relative to local module directory, resolves as a filepath relative to the local +// source directory +// * glob, relative to the local module directory, resolves as filepath(s), relative to the local +// source directory. Not valid in excludes. +// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer +// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source +// filepath. +// excluding the items (similarly resolved +// Properties passed as the paths argument must have been annotated with struct tag +// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the +// path_deps mutator. +// If a requested module is not found as a dependency: +// * if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having +// missing dependencies +// * otherwise, a ModuleError is thrown. func PathsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) Paths { ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes) if ctx.Config().AllowMissingDependencies() { @@ -385,247 +403,6 @@ func PathsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes return ret } -// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in -// order to form a Bazel-compatible label for conversion. -type BazelConversionPathContext interface { - EarlyModulePathContext - - GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) - Module() Module - ModuleType() string - OtherModuleName(m blueprint.Module) string - OtherModuleDir(m blueprint.Module) string -} - -// BazelLabelForModuleDeps returns a Bazel-compatible label for the requested modules which -// correspond to dependencies on the module within the given ctx. -func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList { - var labels bazel.LabelList - for _, module := range modules { - bpText := module - if m := SrcIsModule(module); m == "" { - module = ":" + module - } - if m, t := SrcIsModuleWithTag(module); m != "" { - l := getOtherModuleLabel(ctx, m, t) - l.Bp_text = bpText - labels.Includes = append(labels.Includes, l) - } else { - ctx.ModuleErrorf("%q, is not a module reference", module) - } - } - return labels -} - -// Returns true if a prefix + components[:i] + /Android.bp exists -// TODO(b/185358476) Could check for BUILD file instead of checking for Android.bp file, or ensure BUILD is always generated? -func directoryHasBlueprint(fs pathtools.FileSystem, prefix string, components []string, componentIndex int) bool { - blueprintPath := prefix - if blueprintPath != "" { - blueprintPath = blueprintPath + "/" - } - blueprintPath = blueprintPath + strings.Join(components[:componentIndex+1], "/") - blueprintPath = blueprintPath + "/Android.bp" - if exists, _, _ := fs.Exists(blueprintPath); exists { - return true - } else { - return false - } -} - -// Transform a path (if necessary) to acknowledge package boundaries -// -// e.g. something like -// async_safe/include/async_safe/CHECK.h -// might become -// //bionic/libc/async_safe:include/async_safe/CHECK.h -// if the "async_safe" directory is actually a package and not just a directory. -// -// In particular, paths that extend into packages are transformed into absolute labels beginning with //. -func transformSubpackagePath(ctx BazelConversionPathContext, path bazel.Label) bazel.Label { - var newPath bazel.Label - - // Don't transform Bp_text - newPath.Bp_text = path.Bp_text - - if strings.HasPrefix(path.Label, "//") { - // Assume absolute labels are already correct (e.g. //path/to/some/package:foo.h) - newPath.Label = path.Label - return newPath - } - - newLabel := "" - pathComponents := strings.Split(path.Label, "/") - foundBlueprint := false - // Check the deepest subdirectory first and work upwards - for i := len(pathComponents) - 1; i >= 0; i-- { - pathComponent := pathComponents[i] - var sep string - if !foundBlueprint && directoryHasBlueprint(ctx.Config().fs, ctx.ModuleDir(), pathComponents, i) { - sep = ":" - foundBlueprint = true - } else { - sep = "/" - } - if newLabel == "" { - newLabel = pathComponent - } else { - newLabel = pathComponent + sep + newLabel - } - } - if foundBlueprint { - // Ensure paths end up looking like //bionic/... instead of //./bionic/... - moduleDir := ctx.ModuleDir() - if strings.HasPrefix(moduleDir, ".") { - moduleDir = moduleDir[1:] - } - // Make the path into an absolute label (e.g. //bionic/libc/foo:bar.h instead of just foo:bar.h) - if moduleDir == "" { - newLabel = "//" + newLabel - } else { - newLabel = "//" + moduleDir + "/" + newLabel - } - } - newPath.Label = newLabel - - return newPath -} - -// Transform paths to acknowledge package boundaries -// See transformSubpackagePath() for more information -func transformSubpackagePaths(ctx BazelConversionPathContext, paths bazel.LabelList) bazel.LabelList { - var newPaths bazel.LabelList - for _, include := range paths.Includes { - newPaths.Includes = append(newPaths.Includes, transformSubpackagePath(ctx, include)) - } - for _, exclude := range paths.Excludes { - newPaths.Excludes = append(newPaths.Excludes, transformSubpackagePath(ctx, exclude)) - } - return newPaths -} - -// BazelLabelForModuleSrc returns bazel.LabelList with paths rooted from the module's local source -// directory. It expands globs, and resolves references to modules using the ":name" syntax to -// bazel-compatible labels. Properties passed as the paths or excludes argument must have been -// annotated with struct tag `android:"path"` so that dependencies on other modules will have -// already been handled by the path_properties mutator. -// -// With expanded globs, we can catch package boundaries problem instead of -// silently failing to potentially missing files from Bazel's globs. -func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList { - return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil)) -} - -// BazelLabelForModuleSrcExcludes returns bazel.LabelList with paths rooted from the module's local -// source directory, excluding labels included in the excludes argument. It expands globs, and -// resolves references to modules using the ":name" syntax to bazel-compatible labels. Properties -// passed as the paths or excludes argument must have been annotated with struct tag -// `android:"path"` so that dependencies on other modules will have already been handled by the -// path_properties mutator. -// -// With expanded globs, we can catch package boundaries problem instead of -// silently failing to potentially missing files from Bazel's globs. -func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList { - excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil)) - excluded := make([]string, 0, len(excludeLabels.Includes)) - for _, e := range excludeLabels.Includes { - excluded = append(excluded, e.Label) - } - labels := expandSrcsForBazel(ctx, paths, excluded) - labels.Excludes = excludeLabels.Includes - labels = transformSubpackagePaths(ctx, labels) - return labels -} - -// expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local -// source directory, excluding labels included in the excludes argument. It expands globs, and -// resolves references to modules using the ":name" syntax to bazel-compatible labels. Properties -// passed as the paths or excludes argument must have been annotated with struct tag -// `android:"path"` so that dependencies on other modules will have already been handled by the -// path_properties mutator. -func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList { - if paths == nil { - return bazel.LabelList{} - } - labels := bazel.LabelList{ - Includes: []bazel.Label{}, - } - for _, p := range paths { - if m, tag := SrcIsModuleWithTag(p); m != "" { - l := getOtherModuleLabel(ctx, m, tag) - if !InList(l.Label, expandedExcludes) { - l.Bp_text = fmt.Sprintf(":%s", m) - labels.Includes = append(labels.Includes, l) - } - } else { - var expandedPaths []bazel.Label - if pathtools.IsGlob(p) { - globbedPaths := GlobFiles(ctx, pathForModuleSrc(ctx, p).String(), expandedExcludes) - globbedPaths = PathsWithModuleSrcSubDir(ctx, globbedPaths, "") - for _, path := range globbedPaths { - s := path.Rel() - expandedPaths = append(expandedPaths, bazel.Label{Label: s}) - } - } else { - if !InList(p, expandedExcludes) { - expandedPaths = append(expandedPaths, bazel.Label{Label: p}) - } - } - labels.Includes = append(labels.Includes, expandedPaths...) - } - } - return labels -} - -// getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the -// module. The label will be relative to the current directory if appropriate. The dependency must -// already be resolved by either deps mutator or path deps mutator. -func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel.Label { - m, _ := ctx.GetDirectDep(dep) - if m == nil { - panic(fmt.Errorf("cannot get direct dep %s of %s", dep, ctx.Module().Name())) - } - otherLabel := bazelModuleLabel(ctx, m, tag) - label := bazelModuleLabel(ctx, ctx.Module(), "") - if samePackage(label, otherLabel) { - otherLabel = bazelShortLabel(otherLabel) - } - - return bazel.Label{ - Label: otherLabel, - } -} - -func bazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module, tag string) string { - // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets. - b, ok := module.(Bazelable) - // TODO(b/181155349): perhaps return an error here if the module can't be/isn't being converted - if !ok || !b.ConvertedToBazel(ctx) { - return bp2buildModuleLabel(ctx, module) - } - return b.GetBazelLabel(ctx, module) -} - -func bazelShortLabel(label string) string { - i := strings.Index(label, ":") - return label[i:] -} - -func bazelPackage(label string) string { - i := strings.Index(label, ":") - return label[0:i] -} - -func samePackage(label1, label2 string) bool { - return bazelPackage(label1) == bazelPackage(label2) -} - -func bp2buildModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string { - moduleName := ctx.OtherModuleName(module) - moduleDir := ctx.OtherModuleDir(module) - return fmt.Sprintf("//%s:%s", moduleDir, moduleName) -} - // OutputPaths is a slice of OutputPath objects, with helpers to operate on the collection. type OutputPaths []OutputPath @@ -679,14 +456,19 @@ func getPathsFromModuleDep(ctx ModuleWithDepsPathContext, path, moduleName, tag } } -// PathsAndMissingDepsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding -// paths listed in the excludes arguments, and a list of missing dependencies. It expands globs, references to -// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the -// ":name{.tag}" syntax. Properties passed as the paths or excludes argument must have been annotated with struct tag +// PathsAndMissingDepsForModuleSrcExcludes returns a Paths{} containing the resolved references in +// paths, minus those listed in excludes. Elements of paths and excludes are resolved as: +// * filepath, relative to local module directory, resolves as a filepath relative to the local +// source directory +// * glob, relative to the local module directory, resolves as filepath(s), relative to the local +// source directory. Not valid in excludes. +// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer +// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source +// filepath. +// and a list of the module names of missing module dependencies are returned as the second return. +// Properties passed as the paths argument must have been annotated with struct tag // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the -// path_properties mutator. If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or -// OutputFileProducer dependencies will be returned, and they will NOT cause the module to be marked as having missing -// dependencies. +// path_deps mutator. func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleWithDepsPathContext, paths, excludes []string) (Paths, []string) { prefix := pathForModuleSrc(ctx).String() @@ -1566,17 +1348,6 @@ func pathForModuleOut(ctx ModuleOutPathContext) OutputPath { return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir()) } -type BazelOutPath struct { - OutputPath -} - -var _ Path = BazelOutPath{} -var _ objPathProvider = BazelOutPath{} - -func (p BazelOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath { - return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) -} - // PathForVndkRefAbiDump returns an OptionalPath representing the path of the // reference abi dump for the given module. This is not guaranteed to be valid. func PathForVndkRefAbiDump(ctx ModuleInstallPathContext, version, fileName string, @@ -1615,25 +1386,6 @@ func PathForVndkRefAbiDump(ctx ModuleInstallPathContext, version, fileName strin fileName+ext) } -// PathForBazelOut returns a Path representing the paths... under an output directory dedicated to -// bazel-owned outputs. -func PathForBazelOut(ctx PathContext, paths ...string) BazelOutPath { - execRootPathComponents := append([]string{"execroot", "__main__"}, paths...) - execRootPath := filepath.Join(execRootPathComponents...) - validatedExecRootPath, err := validatePath(execRootPath) - if err != nil { - reportPathError(ctx, err) - } - - outputPath := OutputPath{basePath{"", ""}, - ctx.Config().buildDir, - ctx.Config().BazelContext.OutputBase()} - - return BazelOutPath{ - OutputPath: outputPath.withRel(validatedExecRootPath), - } -} - // PathForModuleOut returns a Path representing the paths... under the module's // output directory. func PathForModuleOut(ctx ModuleOutPathContext, paths ...string) ModuleOutPath {