blob: 0d38bdad25319c767240601a73fc6b50cdf38001 [file] [log] [blame]
Liz Kammer620dea62021-04-14 17:36:10 -04001// 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
15package android
16
17import (
Liz Kammer620dea62021-04-14 17:36:10 -040018 "fmt"
19 "path/filepath"
20 "strings"
21
Chris Parsons953b3562021-09-20 15:14:39 -040022 "android/soong/bazel"
23
Liz Kammer620dea62021-04-14 17:36:10 -040024 "github.com/google/blueprint"
25 "github.com/google/blueprint/pathtools"
26)
27
28// bazel_paths contains methods to:
29// * resolve Soong path and module references into bazel.LabelList
30// * resolve Bazel path references into Soong-compatible paths
31//
32// There is often a similar method for Bazel as there is for Soong path handling and should be used
33// in similar circumstances
34//
Usta Shresthadb46a9b2022-07-11 11:29:56 -040035// Bazel Soong
36// ==============================================================
37// BazelLabelForModuleSrc PathForModuleSrc
38// BazelLabelForModuleSrcExcludes PathForModuleSrcExcludes
39// BazelLabelForModuleDeps n/a
40// tbd PathForSource
41// tbd ExistentPathsForSources
42// PathForBazelOut PathForModuleOut
Liz Kammer620dea62021-04-14 17:36:10 -040043//
44// Use cases:
45// * Module contains a property (often tagged `android:"path"`) that expects paths *relative to the
46// module directory*:
47// * BazelLabelForModuleSrcExcludes, if the module also contains an excludes_<propname> property
48// * BazelLabelForModuleSrc, otherwise
49// * Converting references to other modules to Bazel Labels:
50// BazelLabelForModuleDeps
51// * Converting a path obtained from bazel_handler cquery results:
52// PathForBazelOut
53//
54// NOTE: all Soong globs are expanded within Soong rather than being converted to a Bazel glob
55// syntax. This occurs because Soong does not have a concept of crossing package boundaries,
56// so the glob as computed by Soong may contain paths that cross package-boundaries. These
57// would be unknowingly omitted if the glob were handled by Bazel. By expanding globs within
58// Soong, we support identification and detection (within Bazel) use of paths that cross
59// package boundaries.
60//
61// Path resolution:
62// * filepath/globs: resolves as itself or is converted to an absolute Bazel label (e.g.
63// //path/to/dir:<filepath>) if path exists in a separate package or subpackage.
64// * references to other modules (using the ":name{.tag}" syntax). These resolve as a Bazel label
65// for a target. If the Bazel target is in the local module directory, it will be returned
66// relative to the current package (e.g. ":<target>"). Otherwise, it will be returned as an
67// absolute Bazel label (e.g. "//path/to/dir:<target>"). If the reference to another module
68// cannot be resolved,the function will panic. This is often due to the dependency not being added
69// via an AddDependency* method.
70
Usta Shresthadb46a9b2022-07-11 11:29:56 -040071// BazelConversionContext is a minimal context interface to check if a module should be converted by bp2build,
Jingwen Chen55bc8202021-11-02 06:40:51 +000072// with functions containing information to match against allowlists and denylists.
73// If a module is deemed to be convertible by bp2build, then it should rely on a
74// BazelConversionPathContext for more functions for dep/path features.
75type BazelConversionContext interface {
76 Config() Config
Liz Kammer620dea62021-04-14 17:36:10 -040077
Liz Kammer620dea62021-04-14 17:36:10 -040078 Module() Module
Liz Kammer6eff3232021-08-26 08:37:59 -040079 OtherModuleType(m blueprint.Module) string
Liz Kammer620dea62021-04-14 17:36:10 -040080 OtherModuleName(m blueprint.Module) string
81 OtherModuleDir(m blueprint.Module) string
Sam Delmerico24c56032022-03-28 19:53:03 +000082 ModuleErrorf(format string, args ...interface{})
Jingwen Chen55bc8202021-11-02 06:40:51 +000083}
84
85// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in
86// order to form a Bazel-compatible label for conversion.
87type BazelConversionPathContext interface {
88 EarlyModulePathContext
89 BazelConversionContext
90
91 ModuleErrorf(fmt string, args ...interface{})
92 PropertyErrorf(property, fmt string, args ...interface{})
93 GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
94 ModuleFromName(name string) (blueprint.Module, bool)
Liz Kammer6eff3232021-08-26 08:37:59 -040095 AddUnconvertedBp2buildDep(string)
Liz Kammerdaa09ef2021-12-15 15:35:38 -050096 AddMissingBp2buildDep(dep string)
Liz Kammer620dea62021-04-14 17:36:10 -040097}
98
99// BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>"
100// or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
101// module within the given ctx.
Jingwen Chen55bc8202021-11-02 06:40:51 +0000102func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
Chris Parsons953b3562021-09-20 15:14:39 -0400103 return BazelLabelForModuleDepsWithFn(ctx, modules, BazelModuleLabel)
Liz Kammer2d7bbe32021-06-10 18:20:06 -0400104}
105
106// BazelLabelForModuleWholeDepsExcludes expects two lists: modules (containing modules to include in
107// the list), and excludes (modules to exclude from the list). Both of these should contain
108// references to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label
109// list which corresponds to dependencies on the module within the given ctx, and the excluded
110// dependencies. Prebuilt dependencies will be appended with _alwayslink so they can be handled as
111// whole static libraries.
Jingwen Chen55bc8202021-11-02 06:40:51 +0000112func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
Chris Parsons953b3562021-09-20 15:14:39 -0400113 return BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, BazelModuleLabel)
Liz Kammer2d7bbe32021-06-10 18:20:06 -0400114}
115
Chris Parsons953b3562021-09-20 15:14:39 -0400116// BazelLabelForModuleDepsWithFn expects a list of reference to other modules, ("<module>"
117// or ":<module>") and applies moduleToLabelFn to determine and return a Bazel-compatible label
118// which corresponds to dependencies on the module within the given ctx.
Jingwen Chen55bc8202021-11-02 06:40:51 +0000119func BazelLabelForModuleDepsWithFn(ctx BazelConversionPathContext, modules []string,
120 moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList {
Liz Kammer620dea62021-04-14 17:36:10 -0400121 var labels bazel.LabelList
Chris Parsons51f8c392021-08-03 21:01:05 -0400122 // In some cases, a nil string list is different than an explicitly empty list.
123 if len(modules) == 0 && modules != nil {
124 labels.Includes = []bazel.Label{}
125 return labels
126 }
Liz Kammer620dea62021-04-14 17:36:10 -0400127 for _, module := range modules {
128 bpText := module
129 if m := SrcIsModule(module); m == "" {
130 module = ":" + module
131 }
132 if m, t := SrcIsModuleWithTag(module); m != "" {
Chris Parsons953b3562021-09-20 15:14:39 -0400133 l := getOtherModuleLabel(ctx, m, t, moduleToLabelFn)
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500134 if l != nil {
135 l.OriginalModuleName = bpText
136 labels.Includes = append(labels.Includes, *l)
137 }
Liz Kammer620dea62021-04-14 17:36:10 -0400138 } else {
139 ctx.ModuleErrorf("%q, is not a module reference", module)
140 }
141 }
142 return labels
143}
144
Chris Parsons953b3562021-09-20 15:14:39 -0400145// BazelLabelForModuleDepsExcludesWithFn expects two lists: modules (containing modules to include in the
146// list), and excludes (modules to exclude from the list). Both of these should contain references
147// to other modules, ("<module>" or ":<module>"). It applies moduleToLabelFn to determine and return a
148// Bazel-compatible label list which corresponds to dependencies on the module within the given ctx, and
149// the excluded dependencies.
Jingwen Chen55bc8202021-11-02 06:40:51 +0000150func BazelLabelForModuleDepsExcludesWithFn(ctx BazelConversionPathContext, modules, excludes []string,
151 moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList {
Chris Parsons953b3562021-09-20 15:14:39 -0400152 moduleLabels := BazelLabelForModuleDepsWithFn(ctx, RemoveListFromList(modules, excludes), moduleToLabelFn)
Liz Kammer47535c52021-06-02 16:02:22 -0400153 if len(excludes) == 0 {
154 return moduleLabels
155 }
Chris Parsons953b3562021-09-20 15:14:39 -0400156 excludeLabels := BazelLabelForModuleDepsWithFn(ctx, excludes, moduleToLabelFn)
Liz Kammer47535c52021-06-02 16:02:22 -0400157 return bazel.LabelList{
158 Includes: moduleLabels.Includes,
159 Excludes: excludeLabels.Includes,
160 }
161}
162
Jingwen Chen55bc8202021-11-02 06:40:51 +0000163func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label {
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500164 if srcs := BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
165 return srcs[0]
166 }
167 return bazel.Label{}
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200168}
169
Jingwen Chen55bc8202021-11-02 06:40:51 +0000170func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) bazel.Label {
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500171 if srcs := BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
172 return srcs[0]
173 }
174 return bazel.Label{}
Rupert Shuttleworth6e4950a2021-07-27 01:34:59 -0400175}
176
Liz Kammer620dea62021-04-14 17:36:10 -0400177// BazelLabelForModuleSrc expects a list of path (relative to local module directory) and module
178// references (":<module>") and returns a bazel.LabelList{} containing the resolved references in
179// paths, relative to the local module, or Bazel-labels (absolute if in a different package or
180// relative if within the same package).
181// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
182// will have already been handled by the path_deps mutator.
Jingwen Chen55bc8202021-11-02 06:40:51 +0000183func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList {
Liz Kammer620dea62021-04-14 17:36:10 -0400184 return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
185}
186
187// BazelLabelForModuleSrc expects lists of path and excludes (relative to local module directory)
188// and module references (":<module>") and returns a bazel.LabelList{} containing the resolved
189// references in paths, minus those in excludes, relative to the local module, or Bazel-labels
190// (absolute if in a different package or relative if within the same package).
191// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
192// will have already been handled by the path_deps mutator.
Jingwen Chen55bc8202021-11-02 06:40:51 +0000193func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList {
Liz Kammer620dea62021-04-14 17:36:10 -0400194 excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil))
195 excluded := make([]string, 0, len(excludeLabels.Includes))
196 for _, e := range excludeLabels.Includes {
197 excluded = append(excluded, e.Label)
198 }
199 labels := expandSrcsForBazel(ctx, paths, excluded)
200 labels.Excludes = excludeLabels.Includes
201 labels = transformSubpackagePaths(ctx, labels)
202 return labels
203}
204
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000205// Returns true if a prefix + components[:i] is a package boundary.
206//
207// A package boundary is determined by a BUILD file in the directory. This can happen in 2 cases:
208//
209// 1. An Android.bp exists, which bp2build will always convert to a sibling BUILD file.
210// 2. An Android.bp doesn't exist, but a checked-in BUILD/BUILD.bazel file exists, and that file
211// is allowlisted by the bp2build configuration to be merged into the symlink forest workspace.
212func isPackageBoundary(config Config, prefix string, components []string, componentIndex int) bool {
213 prefix = filepath.Join(prefix, filepath.Join(components[:componentIndex+1]...))
214 if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "Android.bp")); exists {
Liz Kammer620dea62021-04-14 17:36:10 -0400215 return true
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000216 } else if config.Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(prefix) {
217 if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD")); exists {
218 return true
219 } else if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD.bazel")); exists {
220 return true
221 }
Liz Kammer620dea62021-04-14 17:36:10 -0400222 }
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000223
224 return false
Liz Kammer620dea62021-04-14 17:36:10 -0400225}
226
227// Transform a path (if necessary) to acknowledge package boundaries
228//
229// e.g. something like
Colin Crossd079e0b2022-08-16 10:27:33 -0700230//
231// async_safe/include/async_safe/CHECK.h
232//
Liz Kammer620dea62021-04-14 17:36:10 -0400233// might become
Colin Crossd079e0b2022-08-16 10:27:33 -0700234//
235// //bionic/libc/async_safe:include/async_safe/CHECK.h
236//
Liz Kammer620dea62021-04-14 17:36:10 -0400237// if the "async_safe" directory is actually a package and not just a directory.
238//
239// In particular, paths that extend into packages are transformed into absolute labels beginning with //.
240func transformSubpackagePath(ctx BazelConversionPathContext, path bazel.Label) bazel.Label {
241 var newPath bazel.Label
242
Jingwen Chen38e62642021-04-19 05:00:15 +0000243 // Don't transform OriginalModuleName
244 newPath.OriginalModuleName = path.OriginalModuleName
Liz Kammer620dea62021-04-14 17:36:10 -0400245
246 if strings.HasPrefix(path.Label, "//") {
247 // Assume absolute labels are already correct (e.g. //path/to/some/package:foo.h)
248 newPath.Label = path.Label
249 return newPath
250 }
251
252 newLabel := ""
253 pathComponents := strings.Split(path.Label, "/")
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000254 foundPackageBoundary := false
Liz Kammer620dea62021-04-14 17:36:10 -0400255 // Check the deepest subdirectory first and work upwards
256 for i := len(pathComponents) - 1; i >= 0; i-- {
257 pathComponent := pathComponents[i]
258 var sep string
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000259 if !foundPackageBoundary && isPackageBoundary(ctx.Config(), ctx.ModuleDir(), pathComponents, i) {
Liz Kammer620dea62021-04-14 17:36:10 -0400260 sep = ":"
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000261 foundPackageBoundary = true
Liz Kammer620dea62021-04-14 17:36:10 -0400262 } else {
263 sep = "/"
264 }
265 if newLabel == "" {
266 newLabel = pathComponent
267 } else {
268 newLabel = pathComponent + sep + newLabel
269 }
270 }
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000271 if foundPackageBoundary {
Liz Kammer620dea62021-04-14 17:36:10 -0400272 // Ensure paths end up looking like //bionic/... instead of //./bionic/...
273 moduleDir := ctx.ModuleDir()
274 if strings.HasPrefix(moduleDir, ".") {
275 moduleDir = moduleDir[1:]
276 }
277 // Make the path into an absolute label (e.g. //bionic/libc/foo:bar.h instead of just foo:bar.h)
278 if moduleDir == "" {
279 newLabel = "//" + newLabel
280 } else {
281 newLabel = "//" + moduleDir + "/" + newLabel
282 }
283 }
284 newPath.Label = newLabel
285
286 return newPath
287}
288
289// Transform paths to acknowledge package boundaries
290// See transformSubpackagePath() for more information
291func transformSubpackagePaths(ctx BazelConversionPathContext, paths bazel.LabelList) bazel.LabelList {
292 var newPaths bazel.LabelList
293 for _, include := range paths.Includes {
294 newPaths.Includes = append(newPaths.Includes, transformSubpackagePath(ctx, include))
295 }
296 for _, exclude := range paths.Excludes {
297 newPaths.Excludes = append(newPaths.Excludes, transformSubpackagePath(ctx, exclude))
298 }
299 return newPaths
300}
301
Romain Jobredeaux1282c422021-10-29 10:52:59 -0400302// Converts root-relative Paths to a list of bazel.Label relative to the module in ctx.
303func RootToModuleRelativePaths(ctx BazelConversionPathContext, paths Paths) []bazel.Label {
304 var newPaths []bazel.Label
305 for _, path := range PathsWithModuleSrcSubDir(ctx, paths, "") {
306 s := path.Rel()
307 newPaths = append(newPaths, bazel.Label{Label: s})
308 }
309 return newPaths
310}
311
Liz Kammer620dea62021-04-14 17:36:10 -0400312// expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local source
313// directory and Bazel target labels, excluding those included in the excludes argument (which
314// should already be expanded to resolve references to Soong-modules). Valid elements of paths
315// include:
Colin Crossd079e0b2022-08-16 10:27:33 -0700316// - filepath, relative to local module directory, resolves as a filepath relative to the local
317// source directory
318// - glob, relative to the local module directory, resolves as filepath(s), relative to the local
319// module directory. Because Soong does not have a concept of crossing package boundaries, the
320// glob as computed by Soong may contain paths that cross package-boundaries that would be
321// unknowingly omitted if the glob were handled by Bazel. To allow identification and detect
322// (within Bazel) use of paths that cross package boundaries, we expand globs within Soong rather
323// than converting Soong glob syntax to Bazel glob syntax. **Invalid for excludes.**
324// - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
325// or OutputFileProducer. These resolve as a Bazel label for a target. If the Bazel target is in
326// the local module directory, it will be returned relative to the current package (e.g.
327// ":<target>"). Otherwise, it will be returned as an absolute Bazel label (e.g.
328// "//path/to/dir:<target>"). If the reference to another module cannot be resolved,the function
329// will panic.
330//
Liz Kammer620dea62021-04-14 17:36:10 -0400331// Properties passed as the paths or excludes argument must have been annotated with struct tag
332// `android:"path"` so that dependencies on other modules will have already been handled by the
333// path_deps mutator.
Jingwen Chen55bc8202021-11-02 06:40:51 +0000334func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList {
Liz Kammer620dea62021-04-14 17:36:10 -0400335 if paths == nil {
336 return bazel.LabelList{}
337 }
338 labels := bazel.LabelList{
339 Includes: []bazel.Label{},
340 }
Jingwen Chen4ecc67d2021-04-27 09:47:02 +0000341
342 // expandedExcludes contain module-dir relative paths, but root-relative paths
343 // are needed for GlobFiles later.
344 var rootRelativeExpandedExcludes []string
345 for _, e := range expandedExcludes {
346 rootRelativeExpandedExcludes = append(rootRelativeExpandedExcludes, filepath.Join(ctx.ModuleDir(), e))
347 }
348
Liz Kammer620dea62021-04-14 17:36:10 -0400349 for _, p := range paths {
350 if m, tag := SrcIsModuleWithTag(p); m != "" {
Chris Parsons953b3562021-09-20 15:14:39 -0400351 l := getOtherModuleLabel(ctx, m, tag, BazelModuleLabel)
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500352 if l != nil && !InList(l.Label, expandedExcludes) {
Jingwen Chen38e62642021-04-19 05:00:15 +0000353 l.OriginalModuleName = fmt.Sprintf(":%s", m)
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500354 labels.Includes = append(labels.Includes, *l)
Liz Kammer620dea62021-04-14 17:36:10 -0400355 }
356 } else {
357 var expandedPaths []bazel.Label
358 if pathtools.IsGlob(p) {
Jingwen Chen4ecc67d2021-04-27 09:47:02 +0000359 // e.g. turn "math/*.c" in
360 // external/arm-optimized-routines to external/arm-optimized-routines/math/*.c
361 rootRelativeGlobPath := pathForModuleSrc(ctx, p).String()
Romain Jobredeaux1282c422021-10-29 10:52:59 -0400362 expandedPaths = RootToModuleRelativePaths(ctx, GlobFiles(ctx, rootRelativeGlobPath, rootRelativeExpandedExcludes))
Liz Kammer620dea62021-04-14 17:36:10 -0400363 } else {
364 if !InList(p, expandedExcludes) {
365 expandedPaths = append(expandedPaths, bazel.Label{Label: p})
366 }
367 }
368 labels.Includes = append(labels.Includes, expandedPaths...)
369 }
370 }
371 return labels
372}
373
374// getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the
375// module. The label will be relative to the current directory if appropriate. The dependency must
376// already be resolved by either deps mutator or path deps mutator.
Jingwen Chen55bc8202021-11-02 06:40:51 +0000377func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string,
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500378 labelFromModule func(BazelConversionPathContext, blueprint.Module) string) *bazel.Label {
Chris Parsons5a34ffb2021-07-21 14:34:58 -0400379 m, _ := ctx.ModuleFromName(dep)
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500380 // The module was not found in an Android.bp file, this is often due to:
381 // * a limited manifest
382 // * a required module not being converted from Android.mk
Liz Kammer620dea62021-04-14 17:36:10 -0400383 if m == nil {
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500384 ctx.AddMissingBp2buildDep(dep)
385 return &bazel.Label{
386 Label: ":" + dep + "__BP2BUILD__MISSING__DEP",
387 }
Liz Kammer620dea62021-04-14 17:36:10 -0400388 }
Liz Kammer6eff3232021-08-26 08:37:59 -0400389 if !convertedToBazel(ctx, m) {
390 ctx.AddUnconvertedBp2buildDep(dep)
391 }
Chris Parsons953b3562021-09-20 15:14:39 -0400392 label := BazelModuleLabel(ctx, ctx.Module())
393 otherLabel := labelFromModule(ctx, m)
394
395 // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
396
Liz Kammer620dea62021-04-14 17:36:10 -0400397 if samePackage(label, otherLabel) {
398 otherLabel = bazelShortLabel(otherLabel)
399 }
400
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500401 return &bazel.Label{
Liz Kammer620dea62021-04-14 17:36:10 -0400402 Label: otherLabel,
403 }
404}
405
Jingwen Chen55bc8202021-11-02 06:40:51 +0000406func BazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
Liz Kammer620dea62021-04-14 17:36:10 -0400407 // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
Liz Kammer6eff3232021-08-26 08:37:59 -0400408 if !convertedToBazel(ctx, module) {
Liz Kammer620dea62021-04-14 17:36:10 -0400409 return bp2buildModuleLabel(ctx, module)
410 }
Liz Kammer6eff3232021-08-26 08:37:59 -0400411 b, _ := module.(Bazelable)
Liz Kammer620dea62021-04-14 17:36:10 -0400412 return b.GetBazelLabel(ctx, module)
413}
414
415func bazelShortLabel(label string) string {
416 i := strings.Index(label, ":")
Jingwen Chen80b6b642021-11-02 06:23:07 +0000417 if i == -1 {
418 panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label))
419 }
Liz Kammer620dea62021-04-14 17:36:10 -0400420 return label[i:]
421}
422
423func bazelPackage(label string) string {
424 i := strings.Index(label, ":")
Jingwen Chen80b6b642021-11-02 06:23:07 +0000425 if i == -1 {
426 panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label))
427 }
Liz Kammer620dea62021-04-14 17:36:10 -0400428 return label[0:i]
429}
430
431func samePackage(label1, label2 string) bool {
432 return bazelPackage(label1) == bazelPackage(label2)
433}
434
Jingwen Chen55bc8202021-11-02 06:40:51 +0000435func bp2buildModuleLabel(ctx BazelConversionContext, module blueprint.Module) string {
Liz Kammer620dea62021-04-14 17:36:10 -0400436 moduleName := ctx.OtherModuleName(module)
437 moduleDir := ctx.OtherModuleDir(module)
438 return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
439}
440
441// BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja.
442type BazelOutPath struct {
443 OutputPath
444}
445
Liz Kammer0f3b7d22021-09-28 13:48:21 -0400446// ensure BazelOutPath implements Path
Liz Kammer620dea62021-04-14 17:36:10 -0400447var _ Path = BazelOutPath{}
Liz Kammer0f3b7d22021-09-28 13:48:21 -0400448
449// ensure BazelOutPath implements genPathProvider
450var _ genPathProvider = BazelOutPath{}
451
452// ensure BazelOutPath implements objPathProvider
Liz Kammer620dea62021-04-14 17:36:10 -0400453var _ objPathProvider = BazelOutPath{}
454
Liz Kammer0f3b7d22021-09-28 13:48:21 -0400455func (p BazelOutPath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath {
456 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
457}
458
Liz Kammer620dea62021-04-14 17:36:10 -0400459func (p BazelOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
460 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
461}
462
Cole Faust9a06d252022-06-03 16:00:11 -0700463// PathForBazelOutRelative returns a BazelOutPath representing the path under an output directory dedicated to
464// bazel-owned outputs. Calling .Rel() on the result will give the input path as relative to the given
465// relativeRoot.
466func PathForBazelOutRelative(ctx PathContext, relativeRoot string, path string) BazelOutPath {
467 validatedPath, err := validatePath(filepath.Join("execroot", "__main__", path))
Liz Kammer620dea62021-04-14 17:36:10 -0400468 if err != nil {
469 reportPathError(ctx, err)
470 }
Cole Faust9a06d252022-06-03 16:00:11 -0700471 relativeRootPath := filepath.Join("execroot", "__main__", relativeRoot)
472 if pathComponents := strings.Split(path, "/"); len(pathComponents) >= 3 &&
473 pathComponents[0] == "bazel-out" && pathComponents[2] == "bin" {
474 // If the path starts with something like: bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/
475 // make it relative to that folder. bazel-out/volatile-status.txt is an example
476 // of something that starts with bazel-out but is not relative to the bin folder
477 relativeRootPath = filepath.Join("execroot", "__main__", pathComponents[0], pathComponents[1], pathComponents[2], relativeRoot)
478 }
Liz Kammer620dea62021-04-14 17:36:10 -0400479
Cole Faust9a06d252022-06-03 16:00:11 -0700480 var relPath string
481 if relPath, err = filepath.Rel(relativeRootPath, validatedPath); err != nil || strings.HasPrefix(relPath, "../") {
482 // We failed to make this path relative to execroot/__main__, fall back to a non-relative path
483 // One case where this happens is when path is ../bazel_tools/something
484 relativeRootPath = ""
485 relPath = validatedPath
486 }
487
488 outputPath := OutputPath{
489 basePath{"", ""},
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200490 ctx.Config().soongOutDir,
Cole Faust9a06d252022-06-03 16:00:11 -0700491 ctx.Config().BazelContext.OutputBase(),
492 }
Liz Kammer620dea62021-04-14 17:36:10 -0400493
494 return BazelOutPath{
Cole Faust9a06d252022-06-03 16:00:11 -0700495 // .withRel() appends its argument onto the current path, and only the most
496 // recently appended part is returned by outputPath.rel().
497 // So outputPath.rel() will return relPath.
498 OutputPath: outputPath.withRel(relativeRootPath).withRel(relPath),
Liz Kammer620dea62021-04-14 17:36:10 -0400499 }
500}
Liz Kammerb6a55bf2021-04-12 15:42:51 -0400501
Cole Faust9a06d252022-06-03 16:00:11 -0700502// PathForBazelOut returns a BazelOutPath representing the path under an output directory dedicated to
503// bazel-owned outputs.
504func PathForBazelOut(ctx PathContext, path string) BazelOutPath {
505 return PathForBazelOutRelative(ctx, "", path)
506}
507
Liz Kammerb6a55bf2021-04-12 15:42:51 -0400508// PathsForBazelOut returns a list of paths representing the paths under an output directory
509// dedicated to Bazel-owned outputs.
510func PathsForBazelOut(ctx PathContext, paths []string) Paths {
511 outs := make(Paths, 0, len(paths))
512 for _, p := range paths {
513 outs = append(outs, PathForBazelOut(ctx, p))
514 }
515 return outs
516}