blob: d272ec2db7b8d5fdb348c38c628f2a167a240f63 [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"
Spandan Dasdc7d7f72023-09-28 21:23:30 +000019 "os"
Liz Kammer620dea62021-04-14 17:36:10 -040020 "path/filepath"
21 "strings"
22
Chris Parsons953b3562021-09-20 15:14:39 -040023 "android/soong/bazel"
24
Liz Kammer620dea62021-04-14 17:36:10 -040025 "github.com/google/blueprint"
26 "github.com/google/blueprint/pathtools"
27)
28
29// bazel_paths contains methods to:
30// * resolve Soong path and module references into bazel.LabelList
31// * resolve Bazel path references into Soong-compatible paths
32//
33// There is often a similar method for Bazel as there is for Soong path handling and should be used
34// in similar circumstances
35//
Usta Shresthadb46a9b2022-07-11 11:29:56 -040036// Bazel Soong
37// ==============================================================
38// BazelLabelForModuleSrc PathForModuleSrc
39// BazelLabelForModuleSrcExcludes PathForModuleSrcExcludes
40// BazelLabelForModuleDeps n/a
41// tbd PathForSource
42// tbd ExistentPathsForSources
43// PathForBazelOut PathForModuleOut
Liz Kammer620dea62021-04-14 17:36:10 -040044//
45// Use cases:
46// * Module contains a property (often tagged `android:"path"`) that expects paths *relative to the
47// module directory*:
48// * BazelLabelForModuleSrcExcludes, if the module also contains an excludes_<propname> property
49// * BazelLabelForModuleSrc, otherwise
50// * Converting references to other modules to Bazel Labels:
51// BazelLabelForModuleDeps
52// * Converting a path obtained from bazel_handler cquery results:
53// PathForBazelOut
54//
55// NOTE: all Soong globs are expanded within Soong rather than being converted to a Bazel glob
56// syntax. This occurs because Soong does not have a concept of crossing package boundaries,
57// so the glob as computed by Soong may contain paths that cross package-boundaries. These
58// would be unknowingly omitted if the glob were handled by Bazel. By expanding globs within
59// Soong, we support identification and detection (within Bazel) use of paths that cross
60// package boundaries.
61//
62// Path resolution:
63// * filepath/globs: resolves as itself or is converted to an absolute Bazel label (e.g.
64// //path/to/dir:<filepath>) if path exists in a separate package or subpackage.
65// * references to other modules (using the ":name{.tag}" syntax). These resolve as a Bazel label
66// for a target. If the Bazel target is in the local module directory, it will be returned
67// relative to the current package (e.g. ":<target>"). Otherwise, it will be returned as an
68// absolute Bazel label (e.g. "//path/to/dir:<target>"). If the reference to another module
69// cannot be resolved,the function will panic. This is often due to the dependency not being added
70// via an AddDependency* method.
71
Usta Shresthadb46a9b2022-07-11 11:29:56 -040072// BazelConversionContext is a minimal context interface to check if a module should be converted by bp2build,
Jingwen Chen55bc8202021-11-02 06:40:51 +000073// with functions containing information to match against allowlists and denylists.
74// If a module is deemed to be convertible by bp2build, then it should rely on a
75// BazelConversionPathContext for more functions for dep/path features.
76type BazelConversionContext interface {
77 Config() Config
Liz Kammer620dea62021-04-14 17:36:10 -040078
Liz Kammer620dea62021-04-14 17:36:10 -040079 Module() Module
Liz Kammer6eff3232021-08-26 08:37:59 -040080 OtherModuleType(m blueprint.Module) string
Liz Kammer620dea62021-04-14 17:36:10 -040081 OtherModuleName(m blueprint.Module) string
82 OtherModuleDir(m blueprint.Module) string
Sam Delmerico24c56032022-03-28 19:53:03 +000083 ModuleErrorf(format string, args ...interface{})
Jingwen Chen55bc8202021-11-02 06:40:51 +000084}
85
86// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in
87// order to form a Bazel-compatible label for conversion.
88type BazelConversionPathContext interface {
89 EarlyModulePathContext
90 BazelConversionContext
91
Liz Kammerc86e0942023-08-11 16:15:12 -040092 ModuleName() string
93 ModuleType() string
Jingwen Chen55bc8202021-11-02 06:40:51 +000094 ModuleErrorf(fmt string, args ...interface{})
95 PropertyErrorf(property, fmt string, args ...interface{})
96 GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
97 ModuleFromName(name string) (blueprint.Module, bool)
Liz Kammer6eff3232021-08-26 08:37:59 -040098 AddUnconvertedBp2buildDep(string)
Liz Kammerdaa09ef2021-12-15 15:35:38 -050099 AddMissingBp2buildDep(dep string)
Liz Kammer620dea62021-04-14 17:36:10 -0400100}
101
102// BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>"
103// or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
104// module within the given ctx.
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000105func BazelLabelForModuleDeps(ctx Bp2buildMutatorContext, modules []string) bazel.LabelList {
Jingwen Chen3e582b82023-10-05 10:03:06 +0000106 return BazelLabelForModuleDepsWithFn(ctx, modules, BazelModuleLabel, /*markAsDeps=*/true)
Liz Kammer2d7bbe32021-06-10 18:20:06 -0400107}
108
109// BazelLabelForModuleWholeDepsExcludes expects two lists: modules (containing modules to include in
110// the list), and excludes (modules to exclude from the list). Both of these should contain
111// references to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label
112// list which corresponds to dependencies on the module within the given ctx, and the excluded
113// dependencies. Prebuilt dependencies will be appended with _alwayslink so they can be handled as
114// whole static libraries.
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000115func BazelLabelForModuleDepsExcludes(ctx Bp2buildMutatorContext, modules, excludes []string) bazel.LabelList {
Chris Parsons953b3562021-09-20 15:14:39 -0400116 return BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, BazelModuleLabel)
Liz Kammer2d7bbe32021-06-10 18:20:06 -0400117}
118
Chris Parsons953b3562021-09-20 15:14:39 -0400119// BazelLabelForModuleDepsWithFn expects a list of reference to other modules, ("<module>"
120// or ":<module>") and applies moduleToLabelFn to determine and return a Bazel-compatible label
121// which corresponds to dependencies on the module within the given ctx.
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000122func BazelLabelForModuleDepsWithFn(ctx Bp2buildMutatorContext, modules []string,
123 moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string,
124 markAsDeps bool) bazel.LabelList {
Liz Kammer620dea62021-04-14 17:36:10 -0400125 var labels bazel.LabelList
Chris Parsons51f8c392021-08-03 21:01:05 -0400126 // In some cases, a nil string list is different than an explicitly empty list.
127 if len(modules) == 0 && modules != nil {
128 labels.Includes = []bazel.Label{}
129 return labels
130 }
Cole Faust5c84c622023-08-01 11:07:02 -0700131 modules = FirstUniqueStrings(modules)
Liz Kammer620dea62021-04-14 17:36:10 -0400132 for _, module := range modules {
133 bpText := module
134 if m := SrcIsModule(module); m == "" {
135 module = ":" + module
136 }
137 if m, t := SrcIsModuleWithTag(module); m != "" {
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000138 l := getOtherModuleLabel(ctx, m, t, moduleToLabelFn, markAsDeps)
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500139 if l != nil {
140 l.OriginalModuleName = bpText
141 labels.Includes = append(labels.Includes, *l)
142 }
Liz Kammer620dea62021-04-14 17:36:10 -0400143 } else {
144 ctx.ModuleErrorf("%q, is not a module reference", module)
145 }
146 }
147 return labels
148}
149
Chris Parsons953b3562021-09-20 15:14:39 -0400150// BazelLabelForModuleDepsExcludesWithFn expects two lists: modules (containing modules to include in the
151// list), and excludes (modules to exclude from the list). Both of these should contain references
152// to other modules, ("<module>" or ":<module>"). It applies moduleToLabelFn to determine and return a
153// Bazel-compatible label list which corresponds to dependencies on the module within the given ctx, and
154// the excluded dependencies.
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000155func BazelLabelForModuleDepsExcludesWithFn(ctx Bp2buildMutatorContext, modules, excludes []string,
Jingwen Chen55bc8202021-11-02 06:40:51 +0000156 moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList {
Jingwen Chen3e582b82023-10-05 10:03:06 +0000157 moduleLabels := BazelLabelForModuleDepsWithFn(ctx, RemoveListFromList(modules, excludes), moduleToLabelFn, /*markAsDeps=*/true)
Liz Kammer47535c52021-06-02 16:02:22 -0400158 if len(excludes) == 0 {
159 return moduleLabels
160 }
Jingwen Chen3e582b82023-10-05 10:03:06 +0000161 excludeLabels := BazelLabelForModuleDepsWithFn(ctx, excludes, moduleToLabelFn, /*markAsDeps=*/false)
Liz Kammer47535c52021-06-02 16:02:22 -0400162 return bazel.LabelList{
163 Includes: moduleLabels.Includes,
164 Excludes: excludeLabels.Includes,
165 }
166}
167
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000168func BazelLabelForModuleSrcSingle(ctx Bp2buildMutatorContext, path string) bazel.Label {
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500169 if srcs := BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
170 return srcs[0]
171 }
172 return bazel.Label{}
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200173}
174
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000175func BazelLabelForModuleDepSingle(ctx Bp2buildMutatorContext, path string) bazel.Label {
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500176 if srcs := BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
177 return srcs[0]
178 }
179 return bazel.Label{}
Rupert Shuttleworth6e4950a2021-07-27 01:34:59 -0400180}
181
Liz Kammer620dea62021-04-14 17:36:10 -0400182// BazelLabelForModuleSrc expects a list of path (relative to local module directory) and module
183// references (":<module>") and returns a bazel.LabelList{} containing the resolved references in
184// paths, relative to the local module, or Bazel-labels (absolute if in a different package or
185// relative if within the same package).
186// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
Spandan Das950091c2023-07-19 22:26:37 +0000187// will have already been handled by the pathdeps mutator.
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000188func BazelLabelForModuleSrc(ctx Bp2buildMutatorContext, paths []string) bazel.LabelList {
Liz Kammer620dea62021-04-14 17:36:10 -0400189 return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
190}
191
192// BazelLabelForModuleSrc expects lists of path and excludes (relative to local module directory)
193// and module references (":<module>") and returns a bazel.LabelList{} containing the resolved
194// references in paths, minus those in excludes, relative to the local module, or Bazel-labels
195// (absolute if in a different package or relative if within the same package).
196// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
Spandan Das950091c2023-07-19 22:26:37 +0000197// will have already been handled by the pathdeps mutator.
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000198func BazelLabelForModuleSrcExcludes(ctx Bp2buildMutatorContext, paths, excludes []string) bazel.LabelList {
199 excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil), false)
Liz Kammer620dea62021-04-14 17:36:10 -0400200 excluded := make([]string, 0, len(excludeLabels.Includes))
201 for _, e := range excludeLabels.Includes {
202 excluded = append(excluded, e.Label)
203 }
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000204 labels := expandSrcsForBazel(ctx, paths, excluded, true)
Liz Kammer620dea62021-04-14 17:36:10 -0400205 labels.Excludes = excludeLabels.Includes
Spandan Das0a8a2752023-06-21 01:50:33 +0000206 labels = TransformSubpackagePaths(ctx.Config(), ctx.ModuleDir(), labels)
Liz Kammer620dea62021-04-14 17:36:10 -0400207 return labels
208}
209
Spandan Das03010662023-08-15 22:06:41 +0000210func BazelLabelForSrcPatternExcludes(ctx BazelConversionPathContext, dir, pattern string, excludes []string) bazel.LabelList {
211 topRelPaths, err := ctx.GlobWithDeps(filepath.Join(dir, pattern), excludes)
212 if err != nil {
213 ctx.ModuleErrorf("Could not search dir: %s for pattern %s due to %v\n", dir, pattern, err)
214 }
215 // An intermediate list of labels relative to `dir` that assumes that there no subpacakges beneath `dir`
216 dirRelLabels := []bazel.Label{}
217 for _, topRelPath := range topRelPaths {
218 dirRelPath := Rel(ctx, dir, topRelPath)
219 dirRelLabels = append(dirRelLabels, bazel.Label{Label: "./" + dirRelPath})
220 }
221 // Return the package boudary resolved labels
222 return TransformSubpackagePaths(ctx.Config(), dir, bazel.MakeLabelList(dirRelLabels))
223}
224
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000225// Returns true if a prefix + components[:i] is a package boundary.
226//
227// A package boundary is determined by a BUILD file in the directory. This can happen in 2 cases:
228//
Usta Shresthad5580312022-09-23 16:46:38 -0400229// 1. An Android.bp exists, which bp2build will always convert to a sibling BUILD file.
230// 2. An Android.bp doesn't exist, but a checked-in BUILD/BUILD.bazel file exists, and that file
231// is allowlisted by the bp2build configuration to be merged into the symlink forest workspace.
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000232func isPackageBoundary(config Config, prefix string, components []string, componentIndex int) bool {
Spandan Dasdc7d7f72023-09-28 21:23:30 +0000233 isSymlink := func(c Config, path string) bool {
234 f, err := c.fs.Lstat(path)
235 if err != nil {
236 // The file does not exist
237 return false
238 }
239 return f.Mode()&os.ModeSymlink == os.ModeSymlink
240 }
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000241 prefix = filepath.Join(prefix, filepath.Join(components[:componentIndex+1]...))
242 if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "Android.bp")); exists {
Liz Kammer620dea62021-04-14 17:36:10 -0400243 return true
Spandan Dasdc7d7f72023-09-28 21:23:30 +0000244 } else if config.Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(prefix) || isSymlink(config, prefix) {
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000245 if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD")); exists {
246 return true
247 } else if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD.bazel")); exists {
248 return true
249 }
Liz Kammer620dea62021-04-14 17:36:10 -0400250 }
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000251
252 return false
Liz Kammer620dea62021-04-14 17:36:10 -0400253}
254
255// Transform a path (if necessary) to acknowledge package boundaries
256//
257// e.g. something like
Colin Crossd079e0b2022-08-16 10:27:33 -0700258//
259// async_safe/include/async_safe/CHECK.h
260//
Liz Kammer620dea62021-04-14 17:36:10 -0400261// might become
Colin Crossd079e0b2022-08-16 10:27:33 -0700262//
263// //bionic/libc/async_safe:include/async_safe/CHECK.h
264//
Liz Kammer620dea62021-04-14 17:36:10 -0400265// if the "async_safe" directory is actually a package and not just a directory.
266//
267// In particular, paths that extend into packages are transformed into absolute labels beginning with //.
Spandan Das0a8a2752023-06-21 01:50:33 +0000268func transformSubpackagePath(cfg Config, dir string, path bazel.Label) bazel.Label {
Liz Kammer620dea62021-04-14 17:36:10 -0400269 var newPath bazel.Label
270
Jingwen Chen38e62642021-04-19 05:00:15 +0000271 // Don't transform OriginalModuleName
272 newPath.OriginalModuleName = path.OriginalModuleName
Liz Kammer1e753242023-06-02 19:03:06 -0400273 // if it wasn't a module, store the original path. We may need the original path to replace
274 // references if it is actually in another package
275 if path.OriginalModuleName == "" {
276 newPath.OriginalModuleName = path.Label
277 }
Liz Kammer620dea62021-04-14 17:36:10 -0400278
279 if strings.HasPrefix(path.Label, "//") {
280 // Assume absolute labels are already correct (e.g. //path/to/some/package:foo.h)
281 newPath.Label = path.Label
282 return newPath
283 }
Usta Shresthad5580312022-09-23 16:46:38 -0400284 if strings.HasPrefix(path.Label, "./") {
285 // Drop "./" for consistent handling of paths.
286 // Specifically, to not let "." be considered a package boundary.
287 // Say `inputPath` is `x/Android.bp` and that file has some module
288 // with `srcs=["y/a.c", "z/b.c"]`.
289 // And say the directory tree is:
290 // x
291 // ├── Android.bp
292 // ├── y
293 // │ ├── a.c
294 // │ └── Android.bp
295 // └── z
296 // └── b.c
297 // Then bazel equivalent labels in srcs should be:
298 // //x/y:a.c, x/z/b.c
299 // The above should still be the case if `x/Android.bp` had
300 // srcs=["./y/a.c", "./z/b.c"]
301 // However, if we didn't strip "./", we'd get
302 // //x/./y:a.c, //x/.:z/b.c
303 path.Label = strings.TrimPrefix(path.Label, "./")
304 }
Liz Kammer620dea62021-04-14 17:36:10 -0400305 pathComponents := strings.Split(path.Label, "/")
Usta Shresthad5580312022-09-23 16:46:38 -0400306 newLabel := ""
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000307 foundPackageBoundary := false
Liz Kammer620dea62021-04-14 17:36:10 -0400308 // Check the deepest subdirectory first and work upwards
309 for i := len(pathComponents) - 1; i >= 0; i-- {
310 pathComponent := pathComponents[i]
311 var sep string
Spandan Das0a8a2752023-06-21 01:50:33 +0000312 if !foundPackageBoundary && isPackageBoundary(cfg, dir, pathComponents, i) {
Liz Kammer620dea62021-04-14 17:36:10 -0400313 sep = ":"
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000314 foundPackageBoundary = true
Liz Kammer620dea62021-04-14 17:36:10 -0400315 } else {
316 sep = "/"
317 }
318 if newLabel == "" {
319 newLabel = pathComponent
320 } else {
321 newLabel = pathComponent + sep + newLabel
322 }
323 }
Jingwen Chen0eeaeb82022-09-21 10:27:42 +0000324 if foundPackageBoundary {
Liz Kammer620dea62021-04-14 17:36:10 -0400325 // Ensure paths end up looking like //bionic/... instead of //./bionic/...
Spandan Das0a8a2752023-06-21 01:50:33 +0000326 moduleDir := dir
Liz Kammer620dea62021-04-14 17:36:10 -0400327 if strings.HasPrefix(moduleDir, ".") {
328 moduleDir = moduleDir[1:]
329 }
330 // Make the path into an absolute label (e.g. //bionic/libc/foo:bar.h instead of just foo:bar.h)
331 if moduleDir == "" {
332 newLabel = "//" + newLabel
333 } else {
334 newLabel = "//" + moduleDir + "/" + newLabel
335 }
336 }
337 newPath.Label = newLabel
338
339 return newPath
340}
341
342// Transform paths to acknowledge package boundaries
343// See transformSubpackagePath() for more information
Spandan Das0a8a2752023-06-21 01:50:33 +0000344func TransformSubpackagePaths(cfg Config, dir string, paths bazel.LabelList) bazel.LabelList {
Liz Kammer620dea62021-04-14 17:36:10 -0400345 var newPaths bazel.LabelList
346 for _, include := range paths.Includes {
Spandan Das0a8a2752023-06-21 01:50:33 +0000347 newPaths.Includes = append(newPaths.Includes, transformSubpackagePath(cfg, dir, include))
Liz Kammer620dea62021-04-14 17:36:10 -0400348 }
349 for _, exclude := range paths.Excludes {
Spandan Das0a8a2752023-06-21 01:50:33 +0000350 newPaths.Excludes = append(newPaths.Excludes, transformSubpackagePath(cfg, dir, exclude))
Liz Kammer620dea62021-04-14 17:36:10 -0400351 }
352 return newPaths
353}
354
Romain Jobredeaux1282c422021-10-29 10:52:59 -0400355// Converts root-relative Paths to a list of bazel.Label relative to the module in ctx.
356func RootToModuleRelativePaths(ctx BazelConversionPathContext, paths Paths) []bazel.Label {
357 var newPaths []bazel.Label
358 for _, path := range PathsWithModuleSrcSubDir(ctx, paths, "") {
359 s := path.Rel()
360 newPaths = append(newPaths, bazel.Label{Label: s})
361 }
362 return newPaths
363}
364
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000365var Bp2buildDepTag bp2buildDepTag
366
367type bp2buildDepTag struct {
368 blueprint.BaseDependencyTag
369}
370
Liz Kammer620dea62021-04-14 17:36:10 -0400371// expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local source
372// directory and Bazel target labels, excluding those included in the excludes argument (which
373// should already be expanded to resolve references to Soong-modules). Valid elements of paths
374// include:
Colin Crossd079e0b2022-08-16 10:27:33 -0700375// - filepath, relative to local module directory, resolves as a filepath relative to the local
376// source directory
377// - glob, relative to the local module directory, resolves as filepath(s), relative to the local
378// module directory. Because Soong does not have a concept of crossing package boundaries, the
379// glob as computed by Soong may contain paths that cross package-boundaries that would be
380// unknowingly omitted if the glob were handled by Bazel. To allow identification and detect
381// (within Bazel) use of paths that cross package boundaries, we expand globs within Soong rather
382// than converting Soong glob syntax to Bazel glob syntax. **Invalid for excludes.**
383// - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
384// or OutputFileProducer. These resolve as a Bazel label for a target. If the Bazel target is in
385// the local module directory, it will be returned relative to the current package (e.g.
386// ":<target>"). Otherwise, it will be returned as an absolute Bazel label (e.g.
387// "//path/to/dir:<target>"). If the reference to another module cannot be resolved,the function
388// will panic.
389//
Liz Kammer620dea62021-04-14 17:36:10 -0400390// Properties passed as the paths or excludes argument must have been annotated with struct tag
391// `android:"path"` so that dependencies on other modules will have already been handled by the
Spandan Das950091c2023-07-19 22:26:37 +0000392// pathdeps mutator.
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000393func expandSrcsForBazel(ctx Bp2buildMutatorContext, paths, expandedExcludes []string, markAsDeps bool) bazel.LabelList {
Liz Kammer620dea62021-04-14 17:36:10 -0400394 if paths == nil {
395 return bazel.LabelList{}
396 }
397 labels := bazel.LabelList{
398 Includes: []bazel.Label{},
399 }
Jingwen Chen4ecc67d2021-04-27 09:47:02 +0000400
401 // expandedExcludes contain module-dir relative paths, but root-relative paths
402 // are needed for GlobFiles later.
403 var rootRelativeExpandedExcludes []string
404 for _, e := range expandedExcludes {
405 rootRelativeExpandedExcludes = append(rootRelativeExpandedExcludes, filepath.Join(ctx.ModuleDir(), e))
406 }
407
Liz Kammer620dea62021-04-14 17:36:10 -0400408 for _, p := range paths {
409 if m, tag := SrcIsModuleWithTag(p); m != "" {
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000410 l := getOtherModuleLabel(ctx, m, tag, BazelModuleLabel, markAsDeps)
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500411 if l != nil && !InList(l.Label, expandedExcludes) {
Spandan Dasf62e80a2023-08-17 22:35:04 +0000412 if strings.HasPrefix(m, "//") {
413 // this is a module in a soong namespace
414 // It appears as //<namespace>:<module_name> in srcs, and not ://<namespace>:<module_name>
415 l.OriginalModuleName = m
416 } else {
417 l.OriginalModuleName = fmt.Sprintf(":%s", m)
418 }
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500419 labels.Includes = append(labels.Includes, *l)
Liz Kammer620dea62021-04-14 17:36:10 -0400420 }
421 } else {
422 var expandedPaths []bazel.Label
423 if pathtools.IsGlob(p) {
Jingwen Chen4ecc67d2021-04-27 09:47:02 +0000424 // e.g. turn "math/*.c" in
425 // external/arm-optimized-routines to external/arm-optimized-routines/math/*.c
426 rootRelativeGlobPath := pathForModuleSrc(ctx, p).String()
Romain Jobredeaux1282c422021-10-29 10:52:59 -0400427 expandedPaths = RootToModuleRelativePaths(ctx, GlobFiles(ctx, rootRelativeGlobPath, rootRelativeExpandedExcludes))
Liz Kammer620dea62021-04-14 17:36:10 -0400428 } else {
429 if !InList(p, expandedExcludes) {
430 expandedPaths = append(expandedPaths, bazel.Label{Label: p})
431 }
432 }
433 labels.Includes = append(labels.Includes, expandedPaths...)
434 }
435 }
436 return labels
437}
438
439// getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the
440// module. The label will be relative to the current directory if appropriate. The dependency must
441// already be resolved by either deps mutator or path deps mutator.
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000442func getOtherModuleLabel(ctx Bp2buildMutatorContext, dep, tag string,
443 labelFromModule func(BazelConversionPathContext, blueprint.Module) string,
444 markAsDep bool) *bazel.Label {
Chris Parsons5a34ffb2021-07-21 14:34:58 -0400445 m, _ := ctx.ModuleFromName(dep)
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500446 // The module was not found in an Android.bp file, this is often due to:
447 // * a limited manifest
448 // * a required module not being converted from Android.mk
Liz Kammer620dea62021-04-14 17:36:10 -0400449 if m == nil {
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500450 ctx.AddMissingBp2buildDep(dep)
451 return &bazel.Label{
452 Label: ":" + dep + "__BP2BUILD__MISSING__DEP",
453 }
Liz Kammer620dea62021-04-14 17:36:10 -0400454 }
Chris Parsons44215bc2023-10-04 21:51:17 +0000455 // Returns true if a dependency from the current module to the target module
456 // should be skipped; doing so is a hack to circumvent certain problematic
457 // scenarios that will be addressed in the future.
458 shouldSkipDep := func(dep string) bool {
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000459 // Don't count dependencies of "libc". This is a hack to circumvent the
460 // fact that, in a variantless build graph, "libc" has a dependency on itself.
Chris Parsons44215bc2023-10-04 21:51:17 +0000461 if ctx.ModuleName() == "libc" {
462 return true
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000463 }
Chris Parsons44215bc2023-10-04 21:51:17 +0000464
465 // TODO: b/303307672: Dependencies on this module happen to "work" because
466 // there is a source file with the same name as this module in the
467 // same directory. We should remove this hack and enforce the underlying
468 // module of this name is the actual one used.
469 if dep == "mke2fs.conf" {
470 return true
471 }
472
473 // TODO: b/303310285: Remove this special-casing once all dependencies of
474 // crtbegin_dynamic are convertible
475 if ctx.ModuleName() == "crtbegin_dynamic" {
476 return true
477 }
478
479 return false
480 }
481 if markAsDep && !shouldSkipDep(dep) {
482 ctx.AddDependency(ctx.Module(), Bp2buildDepTag, dep)
Chris Parsons5f1b3c72023-09-28 20:41:03 +0000483 }
Liz Kammer6eff3232021-08-26 08:37:59 -0400484 if !convertedToBazel(ctx, m) {
485 ctx.AddUnconvertedBp2buildDep(dep)
486 }
Chris Parsons953b3562021-09-20 15:14:39 -0400487 label := BazelModuleLabel(ctx, ctx.Module())
488 otherLabel := labelFromModule(ctx, m)
489
490 // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
Yu Liuf2b94012023-09-19 15:09:10 -0700491 if (tag != "" && m.Name() == "framework-res") ||
492 (tag == ".generated_srcjars" && ctx.OtherModuleType(m) == "java_aconfig_library") {
Romain Jobredeaux9c06ef32023-08-21 18:05:29 -0400493 otherLabel += tag
494 }
Chris Parsons953b3562021-09-20 15:14:39 -0400495
Liz Kammer620dea62021-04-14 17:36:10 -0400496 if samePackage(label, otherLabel) {
497 otherLabel = bazelShortLabel(otherLabel)
498 }
499
Liz Kammerdaa09ef2021-12-15 15:35:38 -0500500 return &bazel.Label{
Liz Kammer620dea62021-04-14 17:36:10 -0400501 Label: otherLabel,
502 }
503}
504
Jingwen Chen55bc8202021-11-02 06:40:51 +0000505func BazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
Liz Kammer620dea62021-04-14 17:36:10 -0400506 // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
Spandan Das64852422023-08-02 21:58:41 +0000507 if !convertedToBazel(ctx, module) || isGoModule(module) {
Liz Kammer620dea62021-04-14 17:36:10 -0400508 return bp2buildModuleLabel(ctx, module)
509 }
Liz Kammer6eff3232021-08-26 08:37:59 -0400510 b, _ := module.(Bazelable)
Liz Kammer620dea62021-04-14 17:36:10 -0400511 return b.GetBazelLabel(ctx, module)
512}
513
514func bazelShortLabel(label string) string {
515 i := strings.Index(label, ":")
Jingwen Chen80b6b642021-11-02 06:23:07 +0000516 if i == -1 {
517 panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label))
518 }
Liz Kammer620dea62021-04-14 17:36:10 -0400519 return label[i:]
520}
521
522func bazelPackage(label string) string {
523 i := strings.Index(label, ":")
Jingwen Chen80b6b642021-11-02 06:23:07 +0000524 if i == -1 {
525 panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label))
526 }
Liz Kammer620dea62021-04-14 17:36:10 -0400527 return label[0:i]
528}
529
530func samePackage(label1, label2 string) bool {
531 return bazelPackage(label1) == bazelPackage(label2)
532}
533
Jingwen Chen55bc8202021-11-02 06:40:51 +0000534func bp2buildModuleLabel(ctx BazelConversionContext, module blueprint.Module) string {
Liz Kammerc86e0942023-08-11 16:15:12 -0400535 moduleName := moduleNameWithPossibleOverride(ctx, module, ctx.OtherModuleName(module))
536 moduleDir := moduleDirWithPossibleOverride(ctx, module, ctx.OtherModuleDir(module))
Jingwen Chen889f2f22022-12-16 08:16:01 +0000537 if moduleDir == Bp2BuildTopLevel {
538 moduleDir = ""
539 }
Liz Kammere1b39a52023-09-18 14:32:18 -0400540 if a, ok := module.(Module); ok && IsModulePrebuilt(a) {
541 moduleName = RemoveOptionalPrebuiltPrefix(moduleName)
542 }
Liz Kammer620dea62021-04-14 17:36:10 -0400543 return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
544}
545
546// BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja.
547type BazelOutPath struct {
548 OutputPath
549}
550
Liz Kammer0f3b7d22021-09-28 13:48:21 -0400551// ensure BazelOutPath implements Path
Liz Kammer620dea62021-04-14 17:36:10 -0400552var _ Path = BazelOutPath{}
Liz Kammer0f3b7d22021-09-28 13:48:21 -0400553
554// ensure BazelOutPath implements genPathProvider
555var _ genPathProvider = BazelOutPath{}
556
557// ensure BazelOutPath implements objPathProvider
Liz Kammer620dea62021-04-14 17:36:10 -0400558var _ objPathProvider = BazelOutPath{}
559
Liz Kammer0f3b7d22021-09-28 13:48:21 -0400560func (p BazelOutPath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath {
561 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
562}
563
Liz Kammer620dea62021-04-14 17:36:10 -0400564func (p BazelOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
565 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
566}
567
Cole Faust9a06d252022-06-03 16:00:11 -0700568// PathForBazelOutRelative returns a BazelOutPath representing the path under an output directory dedicated to
569// bazel-owned outputs. Calling .Rel() on the result will give the input path as relative to the given
570// relativeRoot.
571func PathForBazelOutRelative(ctx PathContext, relativeRoot string, path string) BazelOutPath {
572 validatedPath, err := validatePath(filepath.Join("execroot", "__main__", path))
Liz Kammer620dea62021-04-14 17:36:10 -0400573 if err != nil {
574 reportPathError(ctx, err)
575 }
Cole Faust0abd4b42023-01-10 10:49:18 -0800576 var relativeRootPath string
577 if pathComponents := strings.SplitN(path, "/", 4); len(pathComponents) >= 3 &&
Cole Faust9a06d252022-06-03 16:00:11 -0700578 pathComponents[0] == "bazel-out" && pathComponents[2] == "bin" {
579 // If the path starts with something like: bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/
580 // make it relative to that folder. bazel-out/volatile-status.txt is an example
581 // of something that starts with bazel-out but is not relative to the bin folder
582 relativeRootPath = filepath.Join("execroot", "__main__", pathComponents[0], pathComponents[1], pathComponents[2], relativeRoot)
Cole Faust0abd4b42023-01-10 10:49:18 -0800583 } else {
584 relativeRootPath = filepath.Join("execroot", "__main__", relativeRoot)
Cole Faust9a06d252022-06-03 16:00:11 -0700585 }
Liz Kammer620dea62021-04-14 17:36:10 -0400586
Cole Faust9a06d252022-06-03 16:00:11 -0700587 var relPath string
588 if relPath, err = filepath.Rel(relativeRootPath, validatedPath); err != nil || strings.HasPrefix(relPath, "../") {
589 // We failed to make this path relative to execroot/__main__, fall back to a non-relative path
590 // One case where this happens is when path is ../bazel_tools/something
591 relativeRootPath = ""
592 relPath = validatedPath
593 }
594
595 outputPath := OutputPath{
596 basePath{"", ""},
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200597 ctx.Config().soongOutDir,
Cole Faust9a06d252022-06-03 16:00:11 -0700598 ctx.Config().BazelContext.OutputBase(),
599 }
Liz Kammer620dea62021-04-14 17:36:10 -0400600
601 return BazelOutPath{
Cole Faust9a06d252022-06-03 16:00:11 -0700602 // .withRel() appends its argument onto the current path, and only the most
603 // recently appended part is returned by outputPath.rel().
604 // So outputPath.rel() will return relPath.
605 OutputPath: outputPath.withRel(relativeRootPath).withRel(relPath),
Liz Kammer620dea62021-04-14 17:36:10 -0400606 }
607}
Liz Kammerb6a55bf2021-04-12 15:42:51 -0400608
Cole Faust9a06d252022-06-03 16:00:11 -0700609// PathForBazelOut returns a BazelOutPath representing the path under an output directory dedicated to
610// bazel-owned outputs.
611func PathForBazelOut(ctx PathContext, path string) BazelOutPath {
612 return PathForBazelOutRelative(ctx, "", path)
613}
614
Liz Kammerb6a55bf2021-04-12 15:42:51 -0400615// PathsForBazelOut returns a list of paths representing the paths under an output directory
616// dedicated to Bazel-owned outputs.
617func PathsForBazelOut(ctx PathContext, paths []string) Paths {
618 outs := make(Paths, 0, len(paths))
619 for _, p := range paths {
620 outs = append(outs, PathForBazelOut(ctx, p))
621 }
622 return outs
623}
Jingwen Chen6817bbb2022-10-14 09:56:07 +0000624
625// BazelStringOrLabelFromProp splits a Soong module property that can be
626// either a string literal, path (with android:path tag) or a module reference
627// into separate bazel string or label attributes. Bazel treats string and label
628// attributes as distinct types, so this function categorizes a string property
629// into either one of them.
630//
631// e.g. apex.private_key = "foo.pem" can either refer to:
632//
633// 1. "foo.pem" in the current directory -> file target
634// 2. "foo.pem" module -> rule target
635// 3. "foo.pem" file in a different directory, prefixed by a product variable handled
636// in a bazel macro. -> string literal
637//
638// For the first two cases, they are defined using the label attribute. For the third case,
639// it's defined with the string attribute.
640func BazelStringOrLabelFromProp(
Chris Parsons637458d2023-09-19 20:09:00 +0000641 ctx Bp2buildMutatorContext,
Jingwen Chen6817bbb2022-10-14 09:56:07 +0000642 propToDistinguish *string) (bazel.LabelAttribute, bazel.StringAttribute) {
643
644 var labelAttr bazel.LabelAttribute
645 var strAttr bazel.StringAttribute
646
647 if propToDistinguish == nil {
648 // nil pointer
649 return labelAttr, strAttr
650 }
651
652 prop := String(propToDistinguish)
653 if SrcIsModule(prop) != "" {
654 // If it's a module (SrcIsModule will return the module name), set the
655 // resolved label to the label attribute.
656 labelAttr.SetValue(BazelLabelForModuleDepSingle(ctx, prop))
657 } else {
658 // Not a module name. This could be a string literal or a file target in
659 // the current dir. Check if the path exists:
660 path := ExistentPathForSource(ctx, ctx.ModuleDir(), prop)
661
662 if path.Valid() && parentDir(path.String()) == ctx.ModuleDir() {
663 // If it exists and the path is relative to the current dir, resolve the bazel label
664 // for the _file target_ and set it to the label attribute.
665 //
666 // Resolution is necessary because this could be a file in a subpackage.
667 labelAttr.SetValue(BazelLabelForModuleSrcSingle(ctx, prop))
668 } else {
669 // Otherwise, treat it as a string literal and assign to the string attribute.
670 strAttr.Value = propToDistinguish
671 }
672 }
673
674 return labelAttr, strAttr
675}