bazel_paths.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. // Copyright 2015 Google Inc. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package android
  15. import (
  16. "fmt"
  17. "path/filepath"
  18. "strings"
  19. "android/soong/bazel"
  20. "github.com/google/blueprint"
  21. "github.com/google/blueprint/pathtools"
  22. )
  23. // bazel_paths contains methods to:
  24. // * resolve Soong path and module references into bazel.LabelList
  25. // * resolve Bazel path references into Soong-compatible paths
  26. //
  27. // There is often a similar method for Bazel as there is for Soong path handling and should be used
  28. // in similar circumstances
  29. //
  30. // Bazel Soong
  31. // ==============================================================
  32. // BazelLabelForModuleSrc PathForModuleSrc
  33. // BazelLabelForModuleSrcExcludes PathForModuleSrcExcludes
  34. // BazelLabelForModuleDeps n/a
  35. // tbd PathForSource
  36. // tbd ExistentPathsForSources
  37. // PathForBazelOut PathForModuleOut
  38. //
  39. // Use cases:
  40. // * Module contains a property (often tagged `android:"path"`) that expects paths *relative to the
  41. // module directory*:
  42. // * BazelLabelForModuleSrcExcludes, if the module also contains an excludes_<propname> property
  43. // * BazelLabelForModuleSrc, otherwise
  44. // * Converting references to other modules to Bazel Labels:
  45. // BazelLabelForModuleDeps
  46. // * Converting a path obtained from bazel_handler cquery results:
  47. // PathForBazelOut
  48. //
  49. // NOTE: all Soong globs are expanded within Soong rather than being converted to a Bazel glob
  50. // syntax. This occurs because Soong does not have a concept of crossing package boundaries,
  51. // so the glob as computed by Soong may contain paths that cross package-boundaries. These
  52. // would be unknowingly omitted if the glob were handled by Bazel. By expanding globs within
  53. // Soong, we support identification and detection (within Bazel) use of paths that cross
  54. // package boundaries.
  55. //
  56. // Path resolution:
  57. // * filepath/globs: resolves as itself or is converted to an absolute Bazel label (e.g.
  58. // //path/to/dir:<filepath>) if path exists in a separate package or subpackage.
  59. // * references to other modules (using the ":name{.tag}" syntax). These resolve as a Bazel label
  60. // for a target. If the Bazel target is in the local module directory, it will be returned
  61. // relative to the current package (e.g. ":<target>"). Otherwise, it will be returned as an
  62. // absolute Bazel label (e.g. "//path/to/dir:<target>"). If the reference to another module
  63. // cannot be resolved,the function will panic. This is often due to the dependency not being added
  64. // via an AddDependency* method.
  65. // BazelConversionContext is a minimal context interface to check if a module should be converted by bp2build,
  66. // with functions containing information to match against allowlists and denylists.
  67. // If a module is deemed to be convertible by bp2build, then it should rely on a
  68. // BazelConversionPathContext for more functions for dep/path features.
  69. type BazelConversionContext interface {
  70. Config() Config
  71. Module() Module
  72. OtherModuleType(m blueprint.Module) string
  73. OtherModuleName(m blueprint.Module) string
  74. OtherModuleDir(m blueprint.Module) string
  75. ModuleErrorf(format string, args ...interface{})
  76. }
  77. // A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in
  78. // order to form a Bazel-compatible label for conversion.
  79. type BazelConversionPathContext interface {
  80. EarlyModulePathContext
  81. BazelConversionContext
  82. ModuleErrorf(fmt string, args ...interface{})
  83. PropertyErrorf(property, fmt string, args ...interface{})
  84. GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
  85. ModuleFromName(name string) (blueprint.Module, bool)
  86. AddUnconvertedBp2buildDep(string)
  87. AddMissingBp2buildDep(dep string)
  88. }
  89. // BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>"
  90. // or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
  91. // module within the given ctx.
  92. func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
  93. return BazelLabelForModuleDepsWithFn(ctx, modules, BazelModuleLabel)
  94. }
  95. // BazelLabelForModuleWholeDepsExcludes expects two lists: modules (containing modules to include in
  96. // the list), and excludes (modules to exclude from the list). Both of these should contain
  97. // references to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label
  98. // list which corresponds to dependencies on the module within the given ctx, and the excluded
  99. // dependencies. Prebuilt dependencies will be appended with _alwayslink so they can be handled as
  100. // whole static libraries.
  101. func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
  102. return BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, BazelModuleLabel)
  103. }
  104. // BazelLabelForModuleDepsWithFn expects a list of reference to other modules, ("<module>"
  105. // or ":<module>") and applies moduleToLabelFn to determine and return a Bazel-compatible label
  106. // which corresponds to dependencies on the module within the given ctx.
  107. func BazelLabelForModuleDepsWithFn(ctx BazelConversionPathContext, modules []string,
  108. moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList {
  109. var labels bazel.LabelList
  110. // In some cases, a nil string list is different than an explicitly empty list.
  111. if len(modules) == 0 && modules != nil {
  112. labels.Includes = []bazel.Label{}
  113. return labels
  114. }
  115. for _, module := range modules {
  116. bpText := module
  117. if m := SrcIsModule(module); m == "" {
  118. module = ":" + module
  119. }
  120. if m, t := SrcIsModuleWithTag(module); m != "" {
  121. l := getOtherModuleLabel(ctx, m, t, moduleToLabelFn)
  122. if l != nil {
  123. l.OriginalModuleName = bpText
  124. labels.Includes = append(labels.Includes, *l)
  125. }
  126. } else {
  127. ctx.ModuleErrorf("%q, is not a module reference", module)
  128. }
  129. }
  130. return labels
  131. }
  132. // BazelLabelForModuleDepsExcludesWithFn expects two lists: modules (containing modules to include in the
  133. // list), and excludes (modules to exclude from the list). Both of these should contain references
  134. // to other modules, ("<module>" or ":<module>"). It applies moduleToLabelFn to determine and return a
  135. // Bazel-compatible label list which corresponds to dependencies on the module within the given ctx, and
  136. // the excluded dependencies.
  137. func BazelLabelForModuleDepsExcludesWithFn(ctx BazelConversionPathContext, modules, excludes []string,
  138. moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList {
  139. moduleLabels := BazelLabelForModuleDepsWithFn(ctx, RemoveListFromList(modules, excludes), moduleToLabelFn)
  140. if len(excludes) == 0 {
  141. return moduleLabels
  142. }
  143. excludeLabels := BazelLabelForModuleDepsWithFn(ctx, excludes, moduleToLabelFn)
  144. return bazel.LabelList{
  145. Includes: moduleLabels.Includes,
  146. Excludes: excludeLabels.Includes,
  147. }
  148. }
  149. func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label {
  150. if srcs := BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
  151. return srcs[0]
  152. }
  153. return bazel.Label{}
  154. }
  155. func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) bazel.Label {
  156. if srcs := BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
  157. return srcs[0]
  158. }
  159. return bazel.Label{}
  160. }
  161. // BazelLabelForModuleSrc expects a list of path (relative to local module directory) and module
  162. // references (":<module>") and returns a bazel.LabelList{} containing the resolved references in
  163. // paths, relative to the local module, or Bazel-labels (absolute if in a different package or
  164. // relative if within the same package).
  165. // Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
  166. // will have already been handled by the path_deps mutator.
  167. func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList {
  168. return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
  169. }
  170. // BazelLabelForModuleSrc expects lists of path and excludes (relative to local module directory)
  171. // and module references (":<module>") and returns a bazel.LabelList{} containing the resolved
  172. // references in paths, minus those in excludes, relative to the local module, or Bazel-labels
  173. // (absolute if in a different package or relative if within the same package).
  174. // Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
  175. // will have already been handled by the path_deps mutator.
  176. func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList {
  177. excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil))
  178. excluded := make([]string, 0, len(excludeLabels.Includes))
  179. for _, e := range excludeLabels.Includes {
  180. excluded = append(excluded, e.Label)
  181. }
  182. labels := expandSrcsForBazel(ctx, paths, excluded)
  183. labels.Excludes = excludeLabels.Includes
  184. labels = transformSubpackagePaths(ctx, labels)
  185. return labels
  186. }
  187. // Returns true if a prefix + components[:i] is a package boundary.
  188. //
  189. // A package boundary is determined by a BUILD file in the directory. This can happen in 2 cases:
  190. //
  191. // 1. An Android.bp exists, which bp2build will always convert to a sibling BUILD file.
  192. // 2. An Android.bp doesn't exist, but a checked-in BUILD/BUILD.bazel file exists, and that file
  193. // is allowlisted by the bp2build configuration to be merged into the symlink forest workspace.
  194. func isPackageBoundary(config Config, prefix string, components []string, componentIndex int) bool {
  195. prefix = filepath.Join(prefix, filepath.Join(components[:componentIndex+1]...))
  196. if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "Android.bp")); exists {
  197. return true
  198. } else if config.Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(prefix) {
  199. if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD")); exists {
  200. return true
  201. } else if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD.bazel")); exists {
  202. return true
  203. }
  204. }
  205. return false
  206. }
  207. // Transform a path (if necessary) to acknowledge package boundaries
  208. //
  209. // e.g. something like
  210. //
  211. // async_safe/include/async_safe/CHECK.h
  212. //
  213. // might become
  214. //
  215. // //bionic/libc/async_safe:include/async_safe/CHECK.h
  216. //
  217. // if the "async_safe" directory is actually a package and not just a directory.
  218. //
  219. // In particular, paths that extend into packages are transformed into absolute labels beginning with //.
  220. func transformSubpackagePath(ctx BazelConversionPathContext, path bazel.Label) bazel.Label {
  221. var newPath bazel.Label
  222. // Don't transform OriginalModuleName
  223. newPath.OriginalModuleName = path.OriginalModuleName
  224. if strings.HasPrefix(path.Label, "//") {
  225. // Assume absolute labels are already correct (e.g. //path/to/some/package:foo.h)
  226. newPath.Label = path.Label
  227. return newPath
  228. }
  229. if strings.HasPrefix(path.Label, "./") {
  230. // Drop "./" for consistent handling of paths.
  231. // Specifically, to not let "." be considered a package boundary.
  232. // Say `inputPath` is `x/Android.bp` and that file has some module
  233. // with `srcs=["y/a.c", "z/b.c"]`.
  234. // And say the directory tree is:
  235. // x
  236. // ├── Android.bp
  237. // ├── y
  238. // │ ├── a.c
  239. // │ └── Android.bp
  240. // └── z
  241. // └── b.c
  242. // Then bazel equivalent labels in srcs should be:
  243. // //x/y:a.c, x/z/b.c
  244. // The above should still be the case if `x/Android.bp` had
  245. // srcs=["./y/a.c", "./z/b.c"]
  246. // However, if we didn't strip "./", we'd get
  247. // //x/./y:a.c, //x/.:z/b.c
  248. path.Label = strings.TrimPrefix(path.Label, "./")
  249. }
  250. pathComponents := strings.Split(path.Label, "/")
  251. newLabel := ""
  252. foundPackageBoundary := false
  253. // Check the deepest subdirectory first and work upwards
  254. for i := len(pathComponents) - 1; i >= 0; i-- {
  255. pathComponent := pathComponents[i]
  256. var sep string
  257. if !foundPackageBoundary && isPackageBoundary(ctx.Config(), ctx.ModuleDir(), pathComponents, i) {
  258. sep = ":"
  259. foundPackageBoundary = true
  260. } else {
  261. sep = "/"
  262. }
  263. if newLabel == "" {
  264. newLabel = pathComponent
  265. } else {
  266. newLabel = pathComponent + sep + newLabel
  267. }
  268. }
  269. if foundPackageBoundary {
  270. // Ensure paths end up looking like //bionic/... instead of //./bionic/...
  271. moduleDir := ctx.ModuleDir()
  272. if strings.HasPrefix(moduleDir, ".") {
  273. moduleDir = moduleDir[1:]
  274. }
  275. // Make the path into an absolute label (e.g. //bionic/libc/foo:bar.h instead of just foo:bar.h)
  276. if moduleDir == "" {
  277. newLabel = "//" + newLabel
  278. } else {
  279. newLabel = "//" + moduleDir + "/" + newLabel
  280. }
  281. }
  282. newPath.Label = newLabel
  283. return newPath
  284. }
  285. // Transform paths to acknowledge package boundaries
  286. // See transformSubpackagePath() for more information
  287. func transformSubpackagePaths(ctx BazelConversionPathContext, paths bazel.LabelList) bazel.LabelList {
  288. var newPaths bazel.LabelList
  289. for _, include := range paths.Includes {
  290. newPaths.Includes = append(newPaths.Includes, transformSubpackagePath(ctx, include))
  291. }
  292. for _, exclude := range paths.Excludes {
  293. newPaths.Excludes = append(newPaths.Excludes, transformSubpackagePath(ctx, exclude))
  294. }
  295. return newPaths
  296. }
  297. // Converts root-relative Paths to a list of bazel.Label relative to the module in ctx.
  298. func RootToModuleRelativePaths(ctx BazelConversionPathContext, paths Paths) []bazel.Label {
  299. var newPaths []bazel.Label
  300. for _, path := range PathsWithModuleSrcSubDir(ctx, paths, "") {
  301. s := path.Rel()
  302. newPaths = append(newPaths, bazel.Label{Label: s})
  303. }
  304. return newPaths
  305. }
  306. // expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local source
  307. // directory and Bazel target labels, excluding those included in the excludes argument (which
  308. // should already be expanded to resolve references to Soong-modules). Valid elements of paths
  309. // include:
  310. // - filepath, relative to local module directory, resolves as a filepath relative to the local
  311. // source directory
  312. // - glob, relative to the local module directory, resolves as filepath(s), relative to the local
  313. // module directory. Because Soong does not have a concept of crossing package boundaries, the
  314. // glob as computed by Soong may contain paths that cross package-boundaries that would be
  315. // unknowingly omitted if the glob were handled by Bazel. To allow identification and detect
  316. // (within Bazel) use of paths that cross package boundaries, we expand globs within Soong rather
  317. // than converting Soong glob syntax to Bazel glob syntax. **Invalid for excludes.**
  318. // - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
  319. // or OutputFileProducer. These resolve as a Bazel label for a target. If the Bazel target is in
  320. // the local module directory, it will be returned relative to the current package (e.g.
  321. // ":<target>"). Otherwise, it will be returned as an absolute Bazel label (e.g.
  322. // "//path/to/dir:<target>"). If the reference to another module cannot be resolved,the function
  323. // will panic.
  324. //
  325. // Properties passed as the paths or excludes argument must have been annotated with struct tag
  326. // `android:"path"` so that dependencies on other modules will have already been handled by the
  327. // path_deps mutator.
  328. func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList {
  329. if paths == nil {
  330. return bazel.LabelList{}
  331. }
  332. labels := bazel.LabelList{
  333. Includes: []bazel.Label{},
  334. }
  335. // expandedExcludes contain module-dir relative paths, but root-relative paths
  336. // are needed for GlobFiles later.
  337. var rootRelativeExpandedExcludes []string
  338. for _, e := range expandedExcludes {
  339. rootRelativeExpandedExcludes = append(rootRelativeExpandedExcludes, filepath.Join(ctx.ModuleDir(), e))
  340. }
  341. for _, p := range paths {
  342. if m, tag := SrcIsModuleWithTag(p); m != "" {
  343. l := getOtherModuleLabel(ctx, m, tag, BazelModuleLabel)
  344. if l != nil && !InList(l.Label, expandedExcludes) {
  345. l.OriginalModuleName = fmt.Sprintf(":%s", m)
  346. labels.Includes = append(labels.Includes, *l)
  347. }
  348. } else {
  349. var expandedPaths []bazel.Label
  350. if pathtools.IsGlob(p) {
  351. // e.g. turn "math/*.c" in
  352. // external/arm-optimized-routines to external/arm-optimized-routines/math/*.c
  353. rootRelativeGlobPath := pathForModuleSrc(ctx, p).String()
  354. expandedPaths = RootToModuleRelativePaths(ctx, GlobFiles(ctx, rootRelativeGlobPath, rootRelativeExpandedExcludes))
  355. } else {
  356. if !InList(p, expandedExcludes) {
  357. expandedPaths = append(expandedPaths, bazel.Label{Label: p})
  358. }
  359. }
  360. labels.Includes = append(labels.Includes, expandedPaths...)
  361. }
  362. }
  363. return labels
  364. }
  365. // getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the
  366. // module. The label will be relative to the current directory if appropriate. The dependency must
  367. // already be resolved by either deps mutator or path deps mutator.
  368. func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string,
  369. labelFromModule func(BazelConversionPathContext, blueprint.Module) string) *bazel.Label {
  370. m, _ := ctx.ModuleFromName(dep)
  371. // The module was not found in an Android.bp file, this is often due to:
  372. // * a limited manifest
  373. // * a required module not being converted from Android.mk
  374. if m == nil {
  375. ctx.AddMissingBp2buildDep(dep)
  376. return &bazel.Label{
  377. Label: ":" + dep + "__BP2BUILD__MISSING__DEP",
  378. }
  379. }
  380. if !convertedToBazel(ctx, m) {
  381. ctx.AddUnconvertedBp2buildDep(dep)
  382. }
  383. label := BazelModuleLabel(ctx, ctx.Module())
  384. otherLabel := labelFromModule(ctx, m)
  385. // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
  386. if samePackage(label, otherLabel) {
  387. otherLabel = bazelShortLabel(otherLabel)
  388. }
  389. return &bazel.Label{
  390. Label: otherLabel,
  391. }
  392. }
  393. func BazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
  394. // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
  395. if !convertedToBazel(ctx, module) {
  396. return bp2buildModuleLabel(ctx, module)
  397. }
  398. b, _ := module.(Bazelable)
  399. return b.GetBazelLabel(ctx, module)
  400. }
  401. func bazelShortLabel(label string) string {
  402. i := strings.Index(label, ":")
  403. if i == -1 {
  404. panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label))
  405. }
  406. return label[i:]
  407. }
  408. func bazelPackage(label string) string {
  409. i := strings.Index(label, ":")
  410. if i == -1 {
  411. panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label))
  412. }
  413. return label[0:i]
  414. }
  415. func samePackage(label1, label2 string) bool {
  416. return bazelPackage(label1) == bazelPackage(label2)
  417. }
  418. func bp2buildModuleLabel(ctx BazelConversionContext, module blueprint.Module) string {
  419. moduleName := ctx.OtherModuleName(module)
  420. moduleDir := ctx.OtherModuleDir(module)
  421. return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
  422. }
  423. // BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja.
  424. type BazelOutPath struct {
  425. OutputPath
  426. }
  427. // ensure BazelOutPath implements Path
  428. var _ Path = BazelOutPath{}
  429. // ensure BazelOutPath implements genPathProvider
  430. var _ genPathProvider = BazelOutPath{}
  431. // ensure BazelOutPath implements objPathProvider
  432. var _ objPathProvider = BazelOutPath{}
  433. func (p BazelOutPath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath {
  434. return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
  435. }
  436. func (p BazelOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
  437. return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
  438. }
  439. // PathForBazelOutRelative returns a BazelOutPath representing the path under an output directory dedicated to
  440. // bazel-owned outputs. Calling .Rel() on the result will give the input path as relative to the given
  441. // relativeRoot.
  442. func PathForBazelOutRelative(ctx PathContext, relativeRoot string, path string) BazelOutPath {
  443. validatedPath, err := validatePath(filepath.Join("execroot", "__main__", path))
  444. if err != nil {
  445. reportPathError(ctx, err)
  446. }
  447. relativeRootPath := filepath.Join("execroot", "__main__", relativeRoot)
  448. if pathComponents := strings.Split(path, "/"); len(pathComponents) >= 3 &&
  449. pathComponents[0] == "bazel-out" && pathComponents[2] == "bin" {
  450. // If the path starts with something like: bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/
  451. // make it relative to that folder. bazel-out/volatile-status.txt is an example
  452. // of something that starts with bazel-out but is not relative to the bin folder
  453. relativeRootPath = filepath.Join("execroot", "__main__", pathComponents[0], pathComponents[1], pathComponents[2], relativeRoot)
  454. }
  455. var relPath string
  456. if relPath, err = filepath.Rel(relativeRootPath, validatedPath); err != nil || strings.HasPrefix(relPath, "../") {
  457. // We failed to make this path relative to execroot/__main__, fall back to a non-relative path
  458. // One case where this happens is when path is ../bazel_tools/something
  459. relativeRootPath = ""
  460. relPath = validatedPath
  461. }
  462. outputPath := OutputPath{
  463. basePath{"", ""},
  464. ctx.Config().soongOutDir,
  465. ctx.Config().BazelContext.OutputBase(),
  466. }
  467. return BazelOutPath{
  468. // .withRel() appends its argument onto the current path, and only the most
  469. // recently appended part is returned by outputPath.rel().
  470. // So outputPath.rel() will return relPath.
  471. OutputPath: outputPath.withRel(relativeRootPath).withRel(relPath),
  472. }
  473. }
  474. // PathForBazelOut returns a BazelOutPath representing the path under an output directory dedicated to
  475. // bazel-owned outputs.
  476. func PathForBazelOut(ctx PathContext, path string) BazelOutPath {
  477. return PathForBazelOutRelative(ctx, "", path)
  478. }
  479. // PathsForBazelOut returns a list of paths representing the paths under an output directory
  480. // dedicated to Bazel-owned outputs.
  481. func PathsForBazelOut(ctx PathContext, paths []string) Paths {
  482. outs := make(Paths, 0, len(paths))
  483. for _, p := range paths {
  484. outs = append(outs, PathForBazelOut(ctx, p))
  485. }
  486. return outs
  487. }