638 lines
26 KiB
Go
638 lines
26 KiB
Go
// 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 (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"android/soong/bazel"
|
|
|
|
"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_<propname> 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:<filepath>) 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. ":<target>"). Otherwise, it will be returned as an
|
|
// absolute Bazel label (e.g. "//path/to/dir:<target>"). 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.
|
|
|
|
// BazelConversionContext is a minimal context interface to check if a module should be converted by bp2build,
|
|
// with functions containing information to match against allowlists and denylists.
|
|
// If a module is deemed to be convertible by bp2build, then it should rely on a
|
|
// BazelConversionPathContext for more functions for dep/path features.
|
|
type BazelConversionContext interface {
|
|
Config() Config
|
|
|
|
Module() Module
|
|
OtherModuleType(m blueprint.Module) string
|
|
OtherModuleName(m blueprint.Module) string
|
|
OtherModuleDir(m blueprint.Module) string
|
|
ModuleErrorf(format string, args ...interface{})
|
|
}
|
|
|
|
// 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
|
|
BazelConversionContext
|
|
|
|
ModuleName() string
|
|
ModuleType() string
|
|
ModuleErrorf(fmt string, args ...interface{})
|
|
PropertyErrorf(property, fmt string, args ...interface{})
|
|
GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
|
|
ModuleFromName(name string) (blueprint.Module, bool)
|
|
AddUnconvertedBp2buildDep(string)
|
|
AddMissingBp2buildDep(dep string)
|
|
}
|
|
|
|
// BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>"
|
|
// or ":<module>") 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 {
|
|
return BazelLabelForModuleDepsWithFn(ctx, modules, BazelModuleLabel)
|
|
}
|
|
|
|
// BazelLabelForModuleWholeDepsExcludes expects two lists: modules (containing modules to include in
|
|
// the list), and excludes (modules to exclude from the list). Both of these should contain
|
|
// references to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label
|
|
// list which corresponds to dependencies on the module within the given ctx, and the excluded
|
|
// dependencies. Prebuilt dependencies will be appended with _alwayslink so they can be handled as
|
|
// whole static libraries.
|
|
func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
|
|
return BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, BazelModuleLabel)
|
|
}
|
|
|
|
// BazelLabelForModuleDepsWithFn expects a list of reference to other modules, ("<module>"
|
|
// or ":<module>") and applies moduleToLabelFn to determine and return a Bazel-compatible label
|
|
// which corresponds to dependencies on the module within the given ctx.
|
|
func BazelLabelForModuleDepsWithFn(ctx BazelConversionPathContext, modules []string,
|
|
moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList {
|
|
var labels bazel.LabelList
|
|
// In some cases, a nil string list is different than an explicitly empty list.
|
|
if len(modules) == 0 && modules != nil {
|
|
labels.Includes = []bazel.Label{}
|
|
return labels
|
|
}
|
|
modules = FirstUniqueStrings(modules)
|
|
for _, module := range modules {
|
|
bpText := module
|
|
if m := SrcIsModule(module); m == "" {
|
|
module = ":" + module
|
|
}
|
|
if m, t := SrcIsModuleWithTag(module); m != "" {
|
|
l := getOtherModuleLabel(ctx, m, t, moduleToLabelFn)
|
|
if l != nil {
|
|
l.OriginalModuleName = bpText
|
|
labels.Includes = append(labels.Includes, *l)
|
|
}
|
|
} else {
|
|
ctx.ModuleErrorf("%q, is not a module reference", module)
|
|
}
|
|
}
|
|
return labels
|
|
}
|
|
|
|
// BazelLabelForModuleDepsExcludesWithFn expects two lists: modules (containing modules to include in the
|
|
// list), and excludes (modules to exclude from the list). Both of these should contain references
|
|
// to other modules, ("<module>" or ":<module>"). It applies moduleToLabelFn to determine and return a
|
|
// Bazel-compatible label list which corresponds to dependencies on the module within the given ctx, and
|
|
// the excluded dependencies.
|
|
func BazelLabelForModuleDepsExcludesWithFn(ctx BazelConversionPathContext, modules, excludes []string,
|
|
moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList {
|
|
moduleLabels := BazelLabelForModuleDepsWithFn(ctx, RemoveListFromList(modules, excludes), moduleToLabelFn)
|
|
if len(excludes) == 0 {
|
|
return moduleLabels
|
|
}
|
|
excludeLabels := BazelLabelForModuleDepsWithFn(ctx, excludes, moduleToLabelFn)
|
|
return bazel.LabelList{
|
|
Includes: moduleLabels.Includes,
|
|
Excludes: excludeLabels.Includes,
|
|
}
|
|
}
|
|
|
|
func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label {
|
|
if srcs := BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
|
|
return srcs[0]
|
|
}
|
|
return bazel.Label{}
|
|
}
|
|
|
|
func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) bazel.Label {
|
|
if srcs := BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
|
|
return srcs[0]
|
|
}
|
|
return bazel.Label{}
|
|
}
|
|
|
|
// BazelLabelForModuleSrc expects a list of path (relative to local module directory) and module
|
|
// references (":<module>") 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 pathdeps 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 (":<module>") 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 pathdeps 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.Config(), ctx.ModuleDir(), labels)
|
|
return labels
|
|
}
|
|
|
|
func BazelLabelForSrcPatternExcludes(ctx BazelConversionPathContext, dir, pattern string, excludes []string) bazel.LabelList {
|
|
topRelPaths, err := ctx.GlobWithDeps(filepath.Join(dir, pattern), excludes)
|
|
if err != nil {
|
|
ctx.ModuleErrorf("Could not search dir: %s for pattern %s due to %v\n", dir, pattern, err)
|
|
}
|
|
// An intermediate list of labels relative to `dir` that assumes that there no subpacakges beneath `dir`
|
|
dirRelLabels := []bazel.Label{}
|
|
for _, topRelPath := range topRelPaths {
|
|
dirRelPath := Rel(ctx, dir, topRelPath)
|
|
dirRelLabels = append(dirRelLabels, bazel.Label{Label: "./" + dirRelPath})
|
|
}
|
|
// Return the package boudary resolved labels
|
|
return TransformSubpackagePaths(ctx.Config(), dir, bazel.MakeLabelList(dirRelLabels))
|
|
}
|
|
|
|
// Returns true if a prefix + components[:i] is a package boundary.
|
|
//
|
|
// A package boundary is determined by a BUILD file in the directory. This can happen in 2 cases:
|
|
//
|
|
// 1. An Android.bp exists, which bp2build will always convert to a sibling BUILD file.
|
|
// 2. An Android.bp doesn't exist, but a checked-in BUILD/BUILD.bazel file exists, and that file
|
|
// is allowlisted by the bp2build configuration to be merged into the symlink forest workspace.
|
|
func isPackageBoundary(config Config, prefix string, components []string, componentIndex int) bool {
|
|
isSymlink := func(c Config, path string) bool {
|
|
f, err := c.fs.Lstat(path)
|
|
if err != nil {
|
|
// The file does not exist
|
|
return false
|
|
}
|
|
return f.Mode()&os.ModeSymlink == os.ModeSymlink
|
|
}
|
|
prefix = filepath.Join(prefix, filepath.Join(components[:componentIndex+1]...))
|
|
if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "Android.bp")); exists {
|
|
return true
|
|
} else if config.Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(prefix) || isSymlink(config, prefix) {
|
|
if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD")); exists {
|
|
return true
|
|
} else if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD.bazel")); exists {
|
|
return true
|
|
}
|
|
}
|
|
|
|
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(cfg Config, dir string, path bazel.Label) bazel.Label {
|
|
var newPath bazel.Label
|
|
|
|
// Don't transform OriginalModuleName
|
|
newPath.OriginalModuleName = path.OriginalModuleName
|
|
// if it wasn't a module, store the original path. We may need the original path to replace
|
|
// references if it is actually in another package
|
|
if path.OriginalModuleName == "" {
|
|
newPath.OriginalModuleName = path.Label
|
|
}
|
|
|
|
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
|
|
}
|
|
if strings.HasPrefix(path.Label, "./") {
|
|
// Drop "./" for consistent handling of paths.
|
|
// Specifically, to not let "." be considered a package boundary.
|
|
// Say `inputPath` is `x/Android.bp` and that file has some module
|
|
// with `srcs=["y/a.c", "z/b.c"]`.
|
|
// And say the directory tree is:
|
|
// x
|
|
// ├── Android.bp
|
|
// ├── y
|
|
// │ ├── a.c
|
|
// │ └── Android.bp
|
|
// └── z
|
|
// └── b.c
|
|
// Then bazel equivalent labels in srcs should be:
|
|
// //x/y:a.c, x/z/b.c
|
|
// The above should still be the case if `x/Android.bp` had
|
|
// srcs=["./y/a.c", "./z/b.c"]
|
|
// However, if we didn't strip "./", we'd get
|
|
// //x/./y:a.c, //x/.:z/b.c
|
|
path.Label = strings.TrimPrefix(path.Label, "./")
|
|
}
|
|
pathComponents := strings.Split(path.Label, "/")
|
|
newLabel := ""
|
|
foundPackageBoundary := false
|
|
// Check the deepest subdirectory first and work upwards
|
|
for i := len(pathComponents) - 1; i >= 0; i-- {
|
|
pathComponent := pathComponents[i]
|
|
var sep string
|
|
if !foundPackageBoundary && isPackageBoundary(cfg, dir, pathComponents, i) {
|
|
sep = ":"
|
|
foundPackageBoundary = true
|
|
} else {
|
|
sep = "/"
|
|
}
|
|
if newLabel == "" {
|
|
newLabel = pathComponent
|
|
} else {
|
|
newLabel = pathComponent + sep + newLabel
|
|
}
|
|
}
|
|
if foundPackageBoundary {
|
|
// Ensure paths end up looking like //bionic/... instead of //./bionic/...
|
|
moduleDir := dir
|
|
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(cfg Config, dir string, paths bazel.LabelList) bazel.LabelList {
|
|
var newPaths bazel.LabelList
|
|
for _, include := range paths.Includes {
|
|
newPaths.Includes = append(newPaths.Includes, transformSubpackagePath(cfg, dir, include))
|
|
}
|
|
for _, exclude := range paths.Excludes {
|
|
newPaths.Excludes = append(newPaths.Excludes, transformSubpackagePath(cfg, dir, exclude))
|
|
}
|
|
return newPaths
|
|
}
|
|
|
|
// Converts root-relative Paths to a list of bazel.Label relative to the module in ctx.
|
|
func RootToModuleRelativePaths(ctx BazelConversionPathContext, paths Paths) []bazel.Label {
|
|
var newPaths []bazel.Label
|
|
for _, path := range PathsWithModuleSrcSubDir(ctx, paths, "") {
|
|
s := path.Rel()
|
|
newPaths = append(newPaths, bazel.Label{Label: s})
|
|
}
|
|
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.
|
|
// ":<target>"). Otherwise, it will be returned as an absolute Bazel label (e.g.
|
|
// "//path/to/dir:<target>"). 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
|
|
// pathdeps mutator.
|
|
func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList {
|
|
if paths == nil {
|
|
return bazel.LabelList{}
|
|
}
|
|
labels := bazel.LabelList{
|
|
Includes: []bazel.Label{},
|
|
}
|
|
|
|
// expandedExcludes contain module-dir relative paths, but root-relative paths
|
|
// are needed for GlobFiles later.
|
|
var rootRelativeExpandedExcludes []string
|
|
for _, e := range expandedExcludes {
|
|
rootRelativeExpandedExcludes = append(rootRelativeExpandedExcludes, filepath.Join(ctx.ModuleDir(), e))
|
|
}
|
|
|
|
for _, p := range paths {
|
|
if m, tag := SrcIsModuleWithTag(p); m != "" {
|
|
l := getOtherModuleLabel(ctx, m, tag, BazelModuleLabel)
|
|
if l != nil && !InList(l.Label, expandedExcludes) {
|
|
if strings.HasPrefix(m, "//") {
|
|
// this is a module in a soong namespace
|
|
// It appears as //<namespace>:<module_name> in srcs, and not ://<namespace>:<module_name>
|
|
l.OriginalModuleName = m
|
|
} else {
|
|
l.OriginalModuleName = fmt.Sprintf(":%s", m)
|
|
}
|
|
labels.Includes = append(labels.Includes, *l)
|
|
}
|
|
} else {
|
|
var expandedPaths []bazel.Label
|
|
if pathtools.IsGlob(p) {
|
|
// e.g. turn "math/*.c" in
|
|
// external/arm-optimized-routines to external/arm-optimized-routines/math/*.c
|
|
rootRelativeGlobPath := pathForModuleSrc(ctx, p).String()
|
|
expandedPaths = RootToModuleRelativePaths(ctx, GlobFiles(ctx, rootRelativeGlobPath, rootRelativeExpandedExcludes))
|
|
} 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,
|
|
labelFromModule func(BazelConversionPathContext, blueprint.Module) string) *bazel.Label {
|
|
m, _ := ctx.ModuleFromName(dep)
|
|
// The module was not found in an Android.bp file, this is often due to:
|
|
// * a limited manifest
|
|
// * a required module not being converted from Android.mk
|
|
if m == nil {
|
|
ctx.AddMissingBp2buildDep(dep)
|
|
return &bazel.Label{
|
|
Label: ":" + dep + "__BP2BUILD__MISSING__DEP",
|
|
}
|
|
}
|
|
if !convertedToBazel(ctx, m) {
|
|
ctx.AddUnconvertedBp2buildDep(dep)
|
|
}
|
|
label := BazelModuleLabel(ctx, ctx.Module())
|
|
otherLabel := labelFromModule(ctx, m)
|
|
|
|
// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
|
|
if (tag != "" && m.Name() == "framework-res") ||
|
|
(tag == ".generated_srcjars" && ctx.OtherModuleType(m) == "java_aconfig_library") {
|
|
otherLabel += tag
|
|
}
|
|
|
|
if samePackage(label, otherLabel) {
|
|
otherLabel = bazelShortLabel(otherLabel)
|
|
}
|
|
|
|
return &bazel.Label{
|
|
Label: otherLabel,
|
|
}
|
|
}
|
|
|
|
func BazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
|
|
// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
|
|
if !convertedToBazel(ctx, module) || isGoModule(module) {
|
|
return bp2buildModuleLabel(ctx, module)
|
|
}
|
|
b, _ := module.(Bazelable)
|
|
return b.GetBazelLabel(ctx, module)
|
|
}
|
|
|
|
func bazelShortLabel(label string) string {
|
|
i := strings.Index(label, ":")
|
|
if i == -1 {
|
|
panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label))
|
|
}
|
|
return label[i:]
|
|
}
|
|
|
|
func bazelPackage(label string) string {
|
|
i := strings.Index(label, ":")
|
|
if i == -1 {
|
|
panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label))
|
|
}
|
|
return label[0:i]
|
|
}
|
|
|
|
func samePackage(label1, label2 string) bool {
|
|
return bazelPackage(label1) == bazelPackage(label2)
|
|
}
|
|
|
|
func bp2buildModuleLabel(ctx BazelConversionContext, module blueprint.Module) string {
|
|
moduleName := moduleNameWithPossibleOverride(ctx, module, ctx.OtherModuleName(module))
|
|
moduleDir := moduleDirWithPossibleOverride(ctx, module, ctx.OtherModuleDir(module))
|
|
if moduleDir == Bp2BuildTopLevel {
|
|
moduleDir = ""
|
|
}
|
|
if a, ok := module.(Module); ok && IsModulePrebuilt(a) {
|
|
moduleName = RemoveOptionalPrebuiltPrefix(moduleName)
|
|
}
|
|
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
|
|
}
|
|
|
|
// ensure BazelOutPath implements Path
|
|
var _ Path = BazelOutPath{}
|
|
|
|
// ensure BazelOutPath implements genPathProvider
|
|
var _ genPathProvider = BazelOutPath{}
|
|
|
|
// ensure BazelOutPath implements objPathProvider
|
|
var _ objPathProvider = BazelOutPath{}
|
|
|
|
func (p BazelOutPath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath {
|
|
return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
|
|
}
|
|
|
|
func (p BazelOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
|
|
return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
|
|
}
|
|
|
|
// PathForBazelOutRelative returns a BazelOutPath representing the path under an output directory dedicated to
|
|
// bazel-owned outputs. Calling .Rel() on the result will give the input path as relative to the given
|
|
// relativeRoot.
|
|
func PathForBazelOutRelative(ctx PathContext, relativeRoot string, path string) BazelOutPath {
|
|
validatedPath, err := validatePath(filepath.Join("execroot", "__main__", path))
|
|
if err != nil {
|
|
reportPathError(ctx, err)
|
|
}
|
|
var relativeRootPath string
|
|
if pathComponents := strings.SplitN(path, "/", 4); len(pathComponents) >= 3 &&
|
|
pathComponents[0] == "bazel-out" && pathComponents[2] == "bin" {
|
|
// If the path starts with something like: bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/
|
|
// make it relative to that folder. bazel-out/volatile-status.txt is an example
|
|
// of something that starts with bazel-out but is not relative to the bin folder
|
|
relativeRootPath = filepath.Join("execroot", "__main__", pathComponents[0], pathComponents[1], pathComponents[2], relativeRoot)
|
|
} else {
|
|
relativeRootPath = filepath.Join("execroot", "__main__", relativeRoot)
|
|
}
|
|
|
|
var relPath string
|
|
if relPath, err = filepath.Rel(relativeRootPath, validatedPath); err != nil || strings.HasPrefix(relPath, "../") {
|
|
// We failed to make this path relative to execroot/__main__, fall back to a non-relative path
|
|
// One case where this happens is when path is ../bazel_tools/something
|
|
relativeRootPath = ""
|
|
relPath = validatedPath
|
|
}
|
|
|
|
outputPath := OutputPath{
|
|
basePath{"", ""},
|
|
ctx.Config().soongOutDir,
|
|
ctx.Config().BazelContext.OutputBase(),
|
|
}
|
|
|
|
return BazelOutPath{
|
|
// .withRel() appends its argument onto the current path, and only the most
|
|
// recently appended part is returned by outputPath.rel().
|
|
// So outputPath.rel() will return relPath.
|
|
OutputPath: outputPath.withRel(relativeRootPath).withRel(relPath),
|
|
}
|
|
}
|
|
|
|
// PathForBazelOut returns a BazelOutPath representing the path under an output directory dedicated to
|
|
// bazel-owned outputs.
|
|
func PathForBazelOut(ctx PathContext, path string) BazelOutPath {
|
|
return PathForBazelOutRelative(ctx, "", path)
|
|
}
|
|
|
|
// PathsForBazelOut returns a list of paths representing the paths under an output directory
|
|
// dedicated to Bazel-owned outputs.
|
|
func PathsForBazelOut(ctx PathContext, paths []string) Paths {
|
|
outs := make(Paths, 0, len(paths))
|
|
for _, p := range paths {
|
|
outs = append(outs, PathForBazelOut(ctx, p))
|
|
}
|
|
return outs
|
|
}
|
|
|
|
// BazelStringOrLabelFromProp splits a Soong module property that can be
|
|
// either a string literal, path (with android:path tag) or a module reference
|
|
// into separate bazel string or label attributes. Bazel treats string and label
|
|
// attributes as distinct types, so this function categorizes a string property
|
|
// into either one of them.
|
|
//
|
|
// e.g. apex.private_key = "foo.pem" can either refer to:
|
|
//
|
|
// 1. "foo.pem" in the current directory -> file target
|
|
// 2. "foo.pem" module -> rule target
|
|
// 3. "foo.pem" file in a different directory, prefixed by a product variable handled
|
|
// in a bazel macro. -> string literal
|
|
//
|
|
// For the first two cases, they are defined using the label attribute. For the third case,
|
|
// it's defined with the string attribute.
|
|
func BazelStringOrLabelFromProp(
|
|
ctx Bp2buildMutatorContext,
|
|
propToDistinguish *string) (bazel.LabelAttribute, bazel.StringAttribute) {
|
|
|
|
var labelAttr bazel.LabelAttribute
|
|
var strAttr bazel.StringAttribute
|
|
|
|
if propToDistinguish == nil {
|
|
// nil pointer
|
|
return labelAttr, strAttr
|
|
}
|
|
|
|
prop := String(propToDistinguish)
|
|
if SrcIsModule(prop) != "" {
|
|
// If it's a module (SrcIsModule will return the module name), set the
|
|
// resolved label to the label attribute.
|
|
labelAttr.SetValue(BazelLabelForModuleDepSingle(ctx, prop))
|
|
} else {
|
|
// Not a module name. This could be a string literal or a file target in
|
|
// the current dir. Check if the path exists:
|
|
path := ExistentPathForSource(ctx, ctx.ModuleDir(), prop)
|
|
|
|
if path.Valid() && parentDir(path.String()) == ctx.ModuleDir() {
|
|
// If it exists and the path is relative to the current dir, resolve the bazel label
|
|
// for the _file target_ and set it to the label attribute.
|
|
//
|
|
// Resolution is necessary because this could be a file in a subpackage.
|
|
labelAttr.SetValue(BazelLabelForModuleSrcSingle(ctx, prop))
|
|
} else {
|
|
// Otherwise, treat it as a string literal and assign to the string attribute.
|
|
strAttr.Value = propToDistinguish
|
|
}
|
|
}
|
|
|
|
return labelAttr, strAttr
|
|
}
|