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 15package android 16 17import ( 18 "fmt" 19 "path/filepath" 20 "strings" 21 22 "android/soong/bazel" 23 24 "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// 35// Bazel Soong 36// ============================================================== 37// BazelLabelForModuleSrc PathForModuleSrc 38// BazelLabelForModuleSrcExcludes PathForModuleSrcExcludes 39// BazelLabelForModuleDeps n/a 40// tbd PathForSource 41// tbd ExistentPathsForSources 42// PathForBazelOut PathForModuleOut 43// 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 71// BazelConversionContext is a minimal context interface to check if a module should be converted by bp2build, 72// 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 77 78 Module() Module 79 OtherModuleType(m blueprint.Module) string 80 OtherModuleName(m blueprint.Module) string 81 OtherModuleDir(m blueprint.Module) string 82 ModuleErrorf(format string, args ...interface{}) 83} 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) 95 AddUnconvertedBp2buildDep(string) 96 AddMissingBp2buildDep(dep string) 97} 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. 102func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList { 103 return BazelLabelForModuleDepsWithFn(ctx, modules, BazelModuleLabel) 104} 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. 112func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList { 113 return BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, BazelModuleLabel) 114} 115 116// 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. 119func BazelLabelForModuleDepsWithFn(ctx BazelConversionPathContext, modules []string, 120 moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList { 121 var labels bazel.LabelList 122 // 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 } 127 for _, module := range modules { 128 bpText := module 129 if m := SrcIsModule(module); m == "" { 130 module = ":" + module 131 } 132 if m, t := SrcIsModuleWithTag(module); m != "" { 133 l := getOtherModuleLabel(ctx, m, t, moduleToLabelFn) 134 if l != nil { 135 l.OriginalModuleName = bpText 136 labels.Includes = append(labels.Includes, *l) 137 } 138 } else { 139 ctx.ModuleErrorf("%q, is not a module reference", module) 140 } 141 } 142 return labels 143} 144 145// 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. 150func BazelLabelForModuleDepsExcludesWithFn(ctx BazelConversionPathContext, modules, excludes []string, 151 moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList { 152 moduleLabels := BazelLabelForModuleDepsWithFn(ctx, RemoveListFromList(modules, excludes), moduleToLabelFn) 153 if len(excludes) == 0 { 154 return moduleLabels 155 } 156 excludeLabels := BazelLabelForModuleDepsWithFn(ctx, excludes, moduleToLabelFn) 157 return bazel.LabelList{ 158 Includes: moduleLabels.Includes, 159 Excludes: excludeLabels.Includes, 160 } 161} 162 163func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label { 164 if srcs := BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 { 165 return srcs[0] 166 } 167 return bazel.Label{} 168} 169 170func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) bazel.Label { 171 if srcs := BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 { 172 return srcs[0] 173 } 174 return bazel.Label{} 175} 176 177// 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. 183func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList { 184 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. 193func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList { 194 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 205// 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 { 215 return true 216 } 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 } 222 } 223 224 return false 225} 226 227// Transform a path (if necessary) to acknowledge package boundaries 228// 229// e.g. something like 230// 231// async_safe/include/async_safe/CHECK.h 232// 233// might become 234// 235// //bionic/libc/async_safe:include/async_safe/CHECK.h 236// 237// 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 243 // Don't transform OriginalModuleName 244 newPath.OriginalModuleName = path.OriginalModuleName 245 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 if strings.HasPrefix(path.Label, "./") { 252 // Drop "./" for consistent handling of paths. 253 // Specifically, to not let "." be considered a package boundary. 254 // Say `inputPath` is `x/Android.bp` and that file has some module 255 // with `srcs=["y/a.c", "z/b.c"]`. 256 // And say the directory tree is: 257 // x 258 // ├── Android.bp 259 // ├── y 260 // │ ├── a.c 261 // │ └── Android.bp 262 // └── z 263 // └── b.c 264 // Then bazel equivalent labels in srcs should be: 265 // //x/y:a.c, x/z/b.c 266 // The above should still be the case if `x/Android.bp` had 267 // srcs=["./y/a.c", "./z/b.c"] 268 // However, if we didn't strip "./", we'd get 269 // //x/./y:a.c, //x/.:z/b.c 270 path.Label = strings.TrimPrefix(path.Label, "./") 271 } 272 pathComponents := strings.Split(path.Label, "/") 273 newLabel := "" 274 foundPackageBoundary := false 275 // Check the deepest subdirectory first and work upwards 276 for i := len(pathComponents) - 1; i >= 0; i-- { 277 pathComponent := pathComponents[i] 278 var sep string 279 if !foundPackageBoundary && isPackageBoundary(ctx.Config(), ctx.ModuleDir(), pathComponents, i) { 280 sep = ":" 281 foundPackageBoundary = true 282 } else { 283 sep = "/" 284 } 285 if newLabel == "" { 286 newLabel = pathComponent 287 } else { 288 newLabel = pathComponent + sep + newLabel 289 } 290 } 291 if foundPackageBoundary { 292 // Ensure paths end up looking like //bionic/... instead of //./bionic/... 293 moduleDir := ctx.ModuleDir() 294 if strings.HasPrefix(moduleDir, ".") { 295 moduleDir = moduleDir[1:] 296 } 297 // Make the path into an absolute label (e.g. //bionic/libc/foo:bar.h instead of just foo:bar.h) 298 if moduleDir == "" { 299 newLabel = "//" + newLabel 300 } else { 301 newLabel = "//" + moduleDir + "/" + newLabel 302 } 303 } 304 newPath.Label = newLabel 305 306 return newPath 307} 308 309// Transform paths to acknowledge package boundaries 310// See transformSubpackagePath() for more information 311func transformSubpackagePaths(ctx BazelConversionPathContext, paths bazel.LabelList) bazel.LabelList { 312 var newPaths bazel.LabelList 313 for _, include := range paths.Includes { 314 newPaths.Includes = append(newPaths.Includes, transformSubpackagePath(ctx, include)) 315 } 316 for _, exclude := range paths.Excludes { 317 newPaths.Excludes = append(newPaths.Excludes, transformSubpackagePath(ctx, exclude)) 318 } 319 return newPaths 320} 321 322// Converts root-relative Paths to a list of bazel.Label relative to the module in ctx. 323func RootToModuleRelativePaths(ctx BazelConversionPathContext, paths Paths) []bazel.Label { 324 var newPaths []bazel.Label 325 for _, path := range PathsWithModuleSrcSubDir(ctx, paths, "") { 326 s := path.Rel() 327 newPaths = append(newPaths, bazel.Label{Label: s}) 328 } 329 return newPaths 330} 331 332// expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local source 333// directory and Bazel target labels, excluding those included in the excludes argument (which 334// should already be expanded to resolve references to Soong-modules). Valid elements of paths 335// include: 336// - filepath, relative to local module directory, resolves as a filepath relative to the local 337// source directory 338// - glob, relative to the local module directory, resolves as filepath(s), relative to the local 339// module directory. Because Soong does not have a concept of crossing package boundaries, the 340// glob as computed by Soong may contain paths that cross package-boundaries that would be 341// unknowingly omitted if the glob were handled by Bazel. To allow identification and detect 342// (within Bazel) use of paths that cross package boundaries, we expand globs within Soong rather 343// than converting Soong glob syntax to Bazel glob syntax. **Invalid for excludes.** 344// - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer 345// or OutputFileProducer. These resolve as a Bazel label for a target. If the Bazel target is in 346// the local module directory, it will be returned relative to the current package (e.g. 347// ":<target>"). Otherwise, it will be returned as an absolute Bazel label (e.g. 348// "//path/to/dir:<target>"). If the reference to another module cannot be resolved,the function 349// will panic. 350// 351// Properties passed as the paths or excludes argument must have been annotated with struct tag 352// `android:"path"` so that dependencies on other modules will have already been handled by the 353// path_deps mutator. 354func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList { 355 if paths == nil { 356 return bazel.LabelList{} 357 } 358 labels := bazel.LabelList{ 359 Includes: []bazel.Label{}, 360 } 361 362 // expandedExcludes contain module-dir relative paths, but root-relative paths 363 // are needed for GlobFiles later. 364 var rootRelativeExpandedExcludes []string 365 for _, e := range expandedExcludes { 366 rootRelativeExpandedExcludes = append(rootRelativeExpandedExcludes, filepath.Join(ctx.ModuleDir(), e)) 367 } 368 369 for _, p := range paths { 370 if m, tag := SrcIsModuleWithTag(p); m != "" { 371 l := getOtherModuleLabel(ctx, m, tag, BazelModuleLabel) 372 if l != nil && !InList(l.Label, expandedExcludes) { 373 l.OriginalModuleName = fmt.Sprintf(":%s", m) 374 labels.Includes = append(labels.Includes, *l) 375 } 376 } else { 377 var expandedPaths []bazel.Label 378 if pathtools.IsGlob(p) { 379 // e.g. turn "math/*.c" in 380 // external/arm-optimized-routines to external/arm-optimized-routines/math/*.c 381 rootRelativeGlobPath := pathForModuleSrc(ctx, p).String() 382 expandedPaths = RootToModuleRelativePaths(ctx, GlobFiles(ctx, rootRelativeGlobPath, rootRelativeExpandedExcludes)) 383 } else { 384 if !InList(p, expandedExcludes) { 385 expandedPaths = append(expandedPaths, bazel.Label{Label: p}) 386 } 387 } 388 labels.Includes = append(labels.Includes, expandedPaths...) 389 } 390 } 391 return labels 392} 393 394// getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the 395// module. The label will be relative to the current directory if appropriate. The dependency must 396// already be resolved by either deps mutator or path deps mutator. 397func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string, 398 labelFromModule func(BazelConversionPathContext, blueprint.Module) string) *bazel.Label { 399 m, _ := ctx.ModuleFromName(dep) 400 // The module was not found in an Android.bp file, this is often due to: 401 // * a limited manifest 402 // * a required module not being converted from Android.mk 403 if m == nil { 404 ctx.AddMissingBp2buildDep(dep) 405 return &bazel.Label{ 406 Label: ":" + dep + "__BP2BUILD__MISSING__DEP", 407 } 408 } 409 if !convertedToBazel(ctx, m) { 410 ctx.AddUnconvertedBp2buildDep(dep) 411 } 412 label := BazelModuleLabel(ctx, ctx.Module()) 413 otherLabel := labelFromModule(ctx, m) 414 415 // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets. 416 417 if samePackage(label, otherLabel) { 418 otherLabel = bazelShortLabel(otherLabel) 419 } 420 421 return &bazel.Label{ 422 Label: otherLabel, 423 } 424} 425 426func BazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string { 427 // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets. 428 if !convertedToBazel(ctx, module) { 429 return bp2buildModuleLabel(ctx, module) 430 } 431 b, _ := module.(Bazelable) 432 return b.GetBazelLabel(ctx, module) 433} 434 435func bazelShortLabel(label string) string { 436 i := strings.Index(label, ":") 437 if i == -1 { 438 panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label)) 439 } 440 return label[i:] 441} 442 443func bazelPackage(label string) string { 444 i := strings.Index(label, ":") 445 if i == -1 { 446 panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label)) 447 } 448 return label[0:i] 449} 450 451func samePackage(label1, label2 string) bool { 452 return bazelPackage(label1) == bazelPackage(label2) 453} 454 455func bp2buildModuleLabel(ctx BazelConversionContext, module blueprint.Module) string { 456 moduleName := ctx.OtherModuleName(module) 457 moduleDir := ctx.OtherModuleDir(module) 458 if moduleDir == Bp2BuildTopLevel { 459 moduleDir = "" 460 } 461 return fmt.Sprintf("//%s:%s", moduleDir, moduleName) 462} 463 464// BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja. 465type BazelOutPath struct { 466 OutputPath 467} 468 469// ensure BazelOutPath implements Path 470var _ Path = BazelOutPath{} 471 472// ensure BazelOutPath implements genPathProvider 473var _ genPathProvider = BazelOutPath{} 474 475// ensure BazelOutPath implements objPathProvider 476var _ objPathProvider = BazelOutPath{} 477 478func (p BazelOutPath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath { 479 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 480} 481 482func (p BazelOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath { 483 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 484} 485 486// PathForBazelOutRelative returns a BazelOutPath representing the path under an output directory dedicated to 487// bazel-owned outputs. Calling .Rel() on the result will give the input path as relative to the given 488// relativeRoot. 489func PathForBazelOutRelative(ctx PathContext, relativeRoot string, path string) BazelOutPath { 490 validatedPath, err := validatePath(filepath.Join("execroot", "__main__", path)) 491 if err != nil { 492 reportPathError(ctx, err) 493 } 494 var relativeRootPath string 495 if pathComponents := strings.SplitN(path, "/", 4); len(pathComponents) >= 3 && 496 pathComponents[0] == "bazel-out" && pathComponents[2] == "bin" { 497 // If the path starts with something like: bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/ 498 // make it relative to that folder. bazel-out/volatile-status.txt is an example 499 // of something that starts with bazel-out but is not relative to the bin folder 500 relativeRootPath = filepath.Join("execroot", "__main__", pathComponents[0], pathComponents[1], pathComponents[2], relativeRoot) 501 } else { 502 relativeRootPath = filepath.Join("execroot", "__main__", relativeRoot) 503 } 504 505 var relPath string 506 if relPath, err = filepath.Rel(relativeRootPath, validatedPath); err != nil || strings.HasPrefix(relPath, "../") { 507 // We failed to make this path relative to execroot/__main__, fall back to a non-relative path 508 // One case where this happens is when path is ../bazel_tools/something 509 relativeRootPath = "" 510 relPath = validatedPath 511 } 512 513 outputPath := OutputPath{ 514 basePath{"", ""}, 515 ctx.Config().soongOutDir, 516 ctx.Config().BazelContext.OutputBase(), 517 } 518 519 return BazelOutPath{ 520 // .withRel() appends its argument onto the current path, and only the most 521 // recently appended part is returned by outputPath.rel(). 522 // So outputPath.rel() will return relPath. 523 OutputPath: outputPath.withRel(relativeRootPath).withRel(relPath), 524 } 525} 526 527// PathForBazelOut returns a BazelOutPath representing the path under an output directory dedicated to 528// bazel-owned outputs. 529func PathForBazelOut(ctx PathContext, path string) BazelOutPath { 530 return PathForBazelOutRelative(ctx, "", path) 531} 532 533// PathsForBazelOut returns a list of paths representing the paths under an output directory 534// dedicated to Bazel-owned outputs. 535func PathsForBazelOut(ctx PathContext, paths []string) Paths { 536 outs := make(Paths, 0, len(paths)) 537 for _, p := range paths { 538 outs = append(outs, PathForBazelOut(ctx, p)) 539 } 540 return outs 541} 542 543// BazelStringOrLabelFromProp splits a Soong module property that can be 544// either a string literal, path (with android:path tag) or a module reference 545// into separate bazel string or label attributes. Bazel treats string and label 546// attributes as distinct types, so this function categorizes a string property 547// into either one of them. 548// 549// e.g. apex.private_key = "foo.pem" can either refer to: 550// 551// 1. "foo.pem" in the current directory -> file target 552// 2. "foo.pem" module -> rule target 553// 3. "foo.pem" file in a different directory, prefixed by a product variable handled 554// in a bazel macro. -> string literal 555// 556// For the first two cases, they are defined using the label attribute. For the third case, 557// it's defined with the string attribute. 558func BazelStringOrLabelFromProp( 559 ctx TopDownMutatorContext, 560 propToDistinguish *string) (bazel.LabelAttribute, bazel.StringAttribute) { 561 562 var labelAttr bazel.LabelAttribute 563 var strAttr bazel.StringAttribute 564 565 if propToDistinguish == nil { 566 // nil pointer 567 return labelAttr, strAttr 568 } 569 570 prop := String(propToDistinguish) 571 if SrcIsModule(prop) != "" { 572 // If it's a module (SrcIsModule will return the module name), set the 573 // resolved label to the label attribute. 574 labelAttr.SetValue(BazelLabelForModuleDepSingle(ctx, prop)) 575 } else { 576 // Not a module name. This could be a string literal or a file target in 577 // the current dir. Check if the path exists: 578 path := ExistentPathForSource(ctx, ctx.ModuleDir(), prop) 579 580 if path.Valid() && parentDir(path.String()) == ctx.ModuleDir() { 581 // If it exists and the path is relative to the current dir, resolve the bazel label 582 // for the _file target_ and set it to the label attribute. 583 // 584 // Resolution is necessary because this could be a file in a subpackage. 585 labelAttr.SetValue(BazelLabelForModuleSrcSingle(ctx, prop)) 586 } else { 587 // Otherwise, treat it as a string literal and assign to the string attribute. 588 strAttr.Value = propToDistinguish 589 } 590 } 591 592 return labelAttr, strAttr 593} 594