|  | // 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" | 
|  | "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 { | 
|  | 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) { | 
|  | 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 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 = "" | 
|  | } | 
|  | 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 TopDownMutatorContext, | 
|  | 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 | 
|  | } |