github.com/bazelbuild/bazel-gazelle@v0.36.1-0.20240520142334-61b277ba6fed/language/go/generate.go (about) 1 /* Copyright 2018 The Bazel Authors. 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 */ 15 16 package golang 17 18 import ( 19 "fmt" 20 "go/build" 21 "log" 22 "path" 23 "path/filepath" 24 "sort" 25 "strings" 26 "sync" 27 28 "github.com/bazelbuild/bazel-gazelle/config" 29 "github.com/bazelbuild/bazel-gazelle/language" 30 "github.com/bazelbuild/bazel-gazelle/language/proto" 31 "github.com/bazelbuild/bazel-gazelle/pathtools" 32 "github.com/bazelbuild/bazel-gazelle/rule" 33 ) 34 35 func (gl *goLang) GenerateRules(args language.GenerateArgs) language.GenerateResult { 36 // Extract information about proto files. We need this to exclude .pb.go 37 // files and generate go_proto_library rules. 38 c := args.Config 39 pcMode := getProtoMode(c) 40 41 // This is a collection of proto_library rule names that have a corresponding 42 // go_proto_library rule already generated. 43 goProtoRules := make(map[string]struct{}) 44 45 var protoRuleNames []string 46 protoPackages := make(map[string]proto.Package) 47 protoFileInfo := make(map[string]proto.FileInfo) 48 for _, r := range args.OtherGen { 49 if r.Kind() == "go_proto_library" { 50 if proto := r.AttrString("proto"); proto != "" { 51 goProtoRules[proto] = struct{}{} 52 } 53 if protos := r.AttrStrings("protos"); protos != nil { 54 for _, proto := range protos { 55 goProtoRules[proto] = struct{}{} 56 } 57 } 58 59 } 60 if r.Kind() != "proto_library" { 61 continue 62 } 63 pkg := r.PrivateAttr(proto.PackageKey).(proto.Package) 64 protoPackages[r.Name()] = pkg 65 for name, info := range pkg.Files { 66 protoFileInfo[name] = info 67 } 68 protoRuleNames = append(protoRuleNames, r.Name()) 69 } 70 sort.Strings(protoRuleNames) 71 var emptyProtoRuleNames []string 72 for _, r := range args.OtherEmpty { 73 if r.Kind() == "proto_library" { 74 emptyProtoRuleNames = append(emptyProtoRuleNames, r.Name()) 75 } 76 } 77 78 // If proto rule generation is enabled, exclude .pb.go files that correspond 79 // to any .proto files present. 80 regularFiles := append([]string{}, args.RegularFiles...) 81 genFiles := append([]string{}, args.GenFiles...) 82 if !pcMode.ShouldIncludePregeneratedFiles() { 83 keep := func(f string) bool { 84 for _, suffix := range []string{".pb.go", "_grpc.pb.go"} { 85 if strings.HasSuffix(f, suffix) { 86 if _, ok := protoFileInfo[strings.TrimSuffix(f, suffix)+".proto"]; ok { 87 return false 88 } 89 } 90 } 91 return true 92 } 93 filterFiles(®ularFiles, keep) 94 filterFiles(&genFiles, keep) 95 } 96 97 // Split regular files into files which can determine the package name and 98 // import path and other files. 99 var goFiles, otherFiles []string 100 for _, f := range regularFiles { 101 if strings.HasSuffix(f, ".go") { 102 goFiles = append(goFiles, f) 103 } else { 104 otherFiles = append(otherFiles, f) 105 } 106 } 107 108 // Look for a subdirectory named testdata. Only treat it as data if it does 109 // not contain a buildable package. 110 var hasTestdata bool 111 for _, sub := range args.Subdirs { 112 if sub == "testdata" { 113 _, ok := gl.goPkgRels[path.Join(args.Rel, "testdata")] 114 hasTestdata = !ok 115 break 116 } 117 } 118 119 // Build a set of packages from files in this directory. 120 goFileInfos := make([]fileInfo, len(goFiles)) 121 var er *embedResolver 122 for i, name := range goFiles { 123 path := filepath.Join(args.Dir, name) 124 goFileInfos[i] = goFileInfo(path, args.Rel) 125 if len(goFileInfos[i].embeds) > 0 && er == nil { 126 er = newEmbedResolver(args.Dir, args.Rel, c.ValidBuildFileNames, gl.goPkgRels, args.Subdirs, args.RegularFiles, args.GenFiles) 127 } 128 } 129 goPackageMap, goFilesWithUnknownPackage := buildPackages(c, args.Dir, args.Rel, hasTestdata, er, goFileInfos) 130 131 // Select a package to generate rules for. If there is no package, create 132 // an empty package so we can generate empty rules. 133 var protoName string 134 pkg, err := selectPackage(c, args.Dir, goPackageMap) 135 if err != nil { 136 if _, ok := err.(*build.NoGoError); ok { 137 if len(protoPackages) == 1 { 138 for name, ppkg := range protoPackages { 139 if _, ok := goProtoRules[":"+name]; ok { 140 // if a go_proto_library rule already exists for this 141 // proto package, treat it as if the proto package 142 // doesn't exist. 143 pkg = emptyPackage(c, args.Dir, args.Rel, args.File) 144 break 145 } 146 pkg = &goPackage{ 147 name: goProtoPackageName(ppkg), 148 importPath: goProtoImportPath(c, ppkg, args.Rel), 149 proto: protoTargetFromProtoPackage(name, ppkg), 150 } 151 protoName = name 152 break 153 } 154 } else { 155 pkg = emptyPackage(c, args.Dir, args.Rel, args.File) 156 } 157 } else { 158 log.Print(err) 159 } 160 } 161 162 // Try to link the selected package with a proto package. 163 if pkg != nil { 164 if pkg.importPath == "" { 165 if err := pkg.inferImportPath(c); err != nil && pkg.firstGoFile() != "" { 166 inferImportPathErrorOnce.Do(func() { log.Print(err) }) 167 } 168 } 169 for _, name := range protoRuleNames { 170 ppkg := protoPackages[name] 171 if pkg.importPath == goProtoImportPath(c, ppkg, args.Rel) { 172 protoName = name 173 pkg.proto = protoTargetFromProtoPackage(name, ppkg) 174 break 175 } 176 } 177 } 178 179 // Generate rules for proto packages. These should come before the other 180 // Go rules. 181 g := &generator{ 182 c: c, 183 rel: args.Rel, 184 shouldSetVisibility: shouldSetVisibility(args), 185 } 186 var res language.GenerateResult 187 var rules []*rule.Rule 188 var protoEmbed string 189 for _, name := range protoRuleNames { 190 if _, ok := goProtoRules[":"+name]; ok { 191 // if a go_proto_library rule exists for this proto_library rule 192 // already, skip creating another go_proto_library for it, assuming 193 // that a different gazelle extension is responsible for 194 // go_proto_library rule generation. 195 continue 196 } 197 ppkg := protoPackages[name] 198 var rs []*rule.Rule 199 if name == protoName { 200 protoEmbed, rs = g.generateProto(pcMode, pkg.proto, pkg.importPath) 201 } else { 202 target := protoTargetFromProtoPackage(name, ppkg) 203 importPath := goProtoImportPath(c, ppkg, args.Rel) 204 _, rs = g.generateProto(pcMode, target, importPath) 205 } 206 rules = append(rules, rs...) 207 } 208 for _, name := range emptyProtoRuleNames { 209 goProtoName := strings.TrimSuffix(name, "_proto") + goProtoSuffix 210 res.Empty = append(res.Empty, rule.NewRule("go_proto_library", goProtoName)) 211 } 212 if pkg != nil && pcMode == proto.PackageMode && pkg.firstGoFile() == "" { 213 // In proto package mode, don't generate a go_library embedding a 214 // go_proto_library unless there are actually go files. 215 protoEmbed = "" 216 } 217 218 // Complete the Go package and generate rules for that. 219 if pkg != nil { 220 // Add files with unknown packages. This happens when there are parse 221 // or I/O errors. We should keep the file in the srcs list and let the 222 // compiler deal with the error. 223 cgo := pkg.haveCgo() 224 for _, info := range goFilesWithUnknownPackage { 225 if err := pkg.addFile(c, er, info, cgo); err != nil { 226 log.Print(err) 227 } 228 } 229 230 // Process the other static files. 231 for _, file := range otherFiles { 232 info := otherFileInfo(filepath.Join(args.Dir, file)) 233 if err := pkg.addFile(c, er, info, cgo); err != nil { 234 log.Print(err) 235 } 236 } 237 238 // Process generated files. Note that generated files may have the same names 239 // as static files. Bazel will use the generated files, but we will look at 240 // the content of static files, assuming they will be the same. 241 regularFileSet := make(map[string]bool) 242 for _, f := range regularFiles { 243 regularFileSet[f] = true 244 } 245 // Some of the generated files may have been consumed by other rules 246 consumedFileSet := make(map[string]bool) 247 for _, r := range args.OtherGen { 248 for _, f := range r.AttrStrings("srcs") { 249 consumedFileSet[f] = true 250 } 251 if f := r.AttrString("src"); f != "" { 252 consumedFileSet[f] = true 253 } 254 } 255 for _, f := range genFiles { 256 if regularFileSet[f] || consumedFileSet[f] { 257 continue 258 } 259 info := fileNameInfo(filepath.Join(args.Dir, f)) 260 if err := pkg.addFile(c, er, info, cgo); err != nil { 261 log.Print(err) 262 } 263 } 264 265 // Generate Go rules. 266 if protoName == "" { 267 // Empty proto rules for deletion. 268 _, rs := g.generateProto(pcMode, pkg.proto, pkg.importPath) 269 rules = append(rules, rs...) 270 } 271 lib := g.generateLib(pkg, protoEmbed) 272 var libName string 273 if !lib.IsEmpty(goKinds[lib.Kind()]) { 274 libName = lib.Name() 275 } 276 rules = append(rules, lib) 277 g.maybePublishToolLib(lib, pkg) 278 if r := g.maybeGenerateExtraLib(lib, pkg); r != nil { 279 rules = append(rules, r) 280 } 281 if r := g.maybeGenerateAlias(pkg, libName); r != nil { 282 g.maybePublishToolLib(r, pkg) 283 rules = append(rules, r) 284 } 285 rules = append(rules, g.generateBin(pkg, libName)) 286 rules = append(rules, g.generateTests(pkg, libName)...) 287 } 288 289 for _, r := range rules { 290 if r.IsEmpty(goKinds[r.Kind()]) { 291 res.Empty = append(res.Empty, r) 292 } else { 293 res.Gen = append(res.Gen, r) 294 res.Imports = append(res.Imports, r.PrivateAttr(config.GazelleImportsKey)) 295 } 296 } 297 298 if args.File != nil || len(res.Gen) > 0 { 299 gl.goPkgRels[args.Rel] = true 300 } else { 301 for _, sub := range args.Subdirs { 302 if _, ok := gl.goPkgRels[path.Join(args.Rel, sub)]; ok { 303 gl.goPkgRels[args.Rel] = false 304 break 305 } 306 } 307 } 308 309 return res 310 } 311 312 func filterFiles(files *[]string, pred func(string) bool) { 313 w := 0 314 for r := 0; r < len(*files); r++ { 315 f := (*files)[r] 316 if pred(f) { 317 (*files)[w] = f 318 w++ 319 } 320 } 321 *files = (*files)[:w] 322 } 323 324 func buildPackages(c *config.Config, dir, rel string, hasTestdata bool, er *embedResolver, goFiles []fileInfo) (packageMap map[string]*goPackage, goFilesWithUnknownPackage []fileInfo) { 325 // Process .go and .proto files first, since these determine the package name. 326 packageMap = make(map[string]*goPackage) 327 for _, f := range goFiles { 328 if f.packageName == "" { 329 goFilesWithUnknownPackage = append(goFilesWithUnknownPackage, f) 330 continue 331 } 332 if f.packageName == "documentation" { 333 // go/build ignores this package 334 continue 335 } 336 337 if _, ok := packageMap[f.packageName]; !ok { 338 packageMap[f.packageName] = &goPackage{ 339 name: f.packageName, 340 dir: dir, 341 rel: rel, 342 hasTestdata: hasTestdata, 343 } 344 } 345 if err := packageMap[f.packageName].addFile(c, er, f, false); err != nil { 346 log.Print(err) 347 } 348 } 349 return packageMap, goFilesWithUnknownPackage 350 } 351 352 var inferImportPathErrorOnce sync.Once 353 354 // selectPackages selects one Go packages out of the buildable packages found 355 // in a directory. If multiple packages are found, it returns the package 356 // whose name matches the directory if such a package exists. 357 func selectPackage(c *config.Config, dir string, packageMap map[string]*goPackage) (*goPackage, error) { 358 buildablePackages := make(map[string]*goPackage) 359 for name, pkg := range packageMap { 360 if pkg.isBuildable(c) { 361 buildablePackages[name] = pkg 362 } 363 } 364 365 if len(buildablePackages) == 0 { 366 return nil, &build.NoGoError{Dir: dir} 367 } 368 369 if len(buildablePackages) == 1 { 370 for _, pkg := range buildablePackages { 371 return pkg, nil 372 } 373 } 374 375 if pkg, ok := buildablePackages[defaultPackageName(c, dir)]; ok { 376 return pkg, nil 377 } 378 379 err := &build.MultiplePackageError{Dir: dir} 380 for name, pkg := range buildablePackages { 381 // Add the first file for each package for the error message. 382 // Error() method expects these lists to be the same length. File 383 // lists must be non-empty. These lists are only created by 384 // buildPackage for packages with .go files present. 385 err.Packages = append(err.Packages, name) 386 err.Files = append(err.Files, pkg.firstGoFile()) 387 } 388 return nil, err 389 } 390 391 func emptyPackage(c *config.Config, dir, rel string, f *rule.File) *goPackage { 392 var pkgName string 393 if fileContainsGoBinary(c, f) { 394 // If the file contained a go_binary, its library may have a "_lib" suffix. 395 // Set the package name to "main" so that we generate an empty library rule 396 // with that name. 397 pkgName = "main" 398 } else { 399 pkgName = defaultPackageName(c, dir) 400 } 401 pkg := &goPackage{ 402 name: pkgName, 403 dir: dir, 404 rel: rel, 405 } 406 407 return pkg 408 } 409 410 func defaultPackageName(c *config.Config, rel string) string { 411 gc := getGoConfig(c) 412 return pathtools.RelBaseName(rel, gc.prefix, "") 413 } 414 415 type generator struct { 416 c *config.Config 417 rel string 418 shouldSetVisibility bool 419 } 420 421 func (g *generator) generateProto(mode proto.Mode, target protoTarget, importPath string) (string, []*rule.Rule) { 422 if !mode.ShouldGenerateRules() && mode != proto.LegacyMode { 423 // Don't create or delete proto rules in this mode. When proto mode is disabled, 424 // there may be hand-written rules or pre-generated Go files 425 return "", nil 426 } 427 428 gc := getGoConfig(g.c) 429 filegroupName := legacyProtoFilegroupName 430 protoName := target.name 431 if protoName == "" { 432 importPath := InferImportPath(g.c, g.rel) 433 protoName = proto.RuleName(importPath) 434 } 435 goProtoName := strings.TrimSuffix(protoName, "_proto") + goProtoSuffix 436 visibility := g.commonVisibility(importPath) 437 438 if mode == proto.LegacyMode { 439 filegroup := rule.NewRule("filegroup", filegroupName) 440 if target.sources.isEmpty() { 441 return "", []*rule.Rule{filegroup} 442 } 443 filegroup.SetAttr("srcs", target.sources.build()) 444 if g.shouldSetVisibility { 445 filegroup.SetAttr("visibility", visibility) 446 } 447 return "", []*rule.Rule{filegroup} 448 } 449 450 if target.sources.isEmpty() { 451 return "", []*rule.Rule{ 452 rule.NewRule("filegroup", filegroupName), 453 rule.NewRule("go_proto_library", goProtoName), 454 } 455 } 456 457 goProtoLibrary := rule.NewRule("go_proto_library", goProtoName) 458 goProtoLibrary.SetAttr("proto", ":"+protoName) 459 g.setImportAttrs(goProtoLibrary, importPath) 460 if target.hasServices { 461 goProtoLibrary.SetAttr("compilers", gc.goGrpcCompilers) 462 } else if gc.goProtoCompilersSet { 463 goProtoLibrary.SetAttr("compilers", gc.goProtoCompilers) 464 } 465 if g.shouldSetVisibility { 466 goProtoLibrary.SetAttr("visibility", visibility) 467 } 468 goProtoLibrary.SetPrivateAttr(config.GazelleImportsKey, target.imports.build()) 469 return goProtoName, []*rule.Rule{goProtoLibrary} 470 } 471 472 func (g *generator) generateLib(pkg *goPackage, embed string) *rule.Rule { 473 gc := getGoConfig(g.c) 474 name := libNameByConvention(gc.goNamingConvention, pkg.importPath, pkg.name) 475 goLibrary := rule.NewRule("go_library", name) 476 if !pkg.library.sources.hasGo() && embed == "" { 477 return goLibrary // empty 478 } 479 var visibility []string 480 if pkg.isCommand() { 481 // By default, libraries made for a go_binary should not be exposed to the public. 482 visibility = []string{"//visibility:private"} 483 if len(getGoConfig(g.c).goVisibility) > 0 { 484 visibility = getGoConfig(g.c).goVisibility 485 } 486 } else { 487 visibility = g.commonVisibility(pkg.importPath) 488 } 489 g.setCommonAttrs(goLibrary, pkg.rel, visibility, pkg.library, embed) 490 g.setImportAttrs(goLibrary, pkg.importPath) 491 return goLibrary 492 } 493 494 func (g *generator) maybeGenerateAlias(pkg *goPackage, libName string) *rule.Rule { 495 if pkg.isCommand() || libName == "" { 496 return nil 497 } 498 gc := getGoConfig(g.c) 499 if gc.goNamingConvention == goDefaultLibraryNamingConvention { 500 return nil 501 } 502 alias := rule.NewRule("alias", defaultLibName) 503 alias.SetAttr("visibility", g.commonVisibility(pkg.importPath)) 504 if gc.goNamingConvention == importAliasNamingConvention { 505 alias.SetAttr("actual", ":"+libName) 506 } 507 return alias 508 } 509 510 func (g *generator) generateBin(pkg *goPackage, library string) *rule.Rule { 511 gc := getGoConfig(g.c) 512 name := binName(pkg.rel, gc.prefix, g.c.RepoRoot) 513 goBinary := rule.NewRule("go_binary", name) 514 if !pkg.isCommand() || pkg.binary.sources.isEmpty() && library == "" { 515 return goBinary // empty 516 } 517 visibility := g.commonVisibility(pkg.importPath) 518 g.setCommonAttrs(goBinary, pkg.rel, visibility, pkg.binary, library) 519 return goBinary 520 } 521 522 func (g *generator) generateTests(pkg *goPackage, library string) []*rule.Rule { 523 gc := getGoConfig(g.c) 524 tests := pkg.tests 525 if len(tests) == 0 && gc.testMode == defaultTestMode { 526 tests = []goTarget{goTarget{}} 527 } 528 var name func(goTarget) string 529 switch gc.testMode { 530 case defaultTestMode: 531 name = func(goTarget) string { 532 return testNameByConvention(gc.goNamingConvention, pkg.importPath) 533 } 534 case fileTestMode: 535 name = func(test goTarget) string { 536 if test.sources.hasGo() { 537 if srcs := test.sources.buildFlat(); len(srcs) == 1 { 538 return testNameFromSingleSource(srcs[0]) 539 } 540 } 541 return testNameByConvention(gc.goNamingConvention, pkg.importPath) 542 } 543 } 544 var res []*rule.Rule 545 for i, test := range tests { 546 goTest := rule.NewRule("go_test", name(test)) 547 hasGo := test.sources.hasGo() 548 if hasGo || i == 0 { 549 res = append(res, goTest) 550 if !hasGo { 551 continue 552 } 553 } 554 var embed string 555 if test.hasInternalTest { 556 embed = library 557 } 558 g.setCommonAttrs(goTest, pkg.rel, nil, test, embed) 559 if pkg.hasTestdata { 560 goTest.SetAttr("data", rule.GlobValue{Patterns: []string{"testdata/**"}}) 561 } 562 } 563 return res 564 } 565 566 // maybePublishToolLib makes the given go_library rule public if needed for nogo. 567 // Updating it here automatically makes it easier to upgrade org_golang_x_tools. 568 func (g *generator) maybePublishToolLib(lib *rule.Rule, pkg *goPackage) { 569 if pkg.importPath == "golang.org/x/tools/go/analysis/internal/facts" || pkg.importPath == "golang.org/x/tools/internal/facts" { 570 // Imported by nogo main. We add a visibility exception. 571 lib.SetAttr("visibility", []string{"//visibility:public"}) 572 } 573 } 574 575 // maybeGenerateExtraLib generates extra equivalent library targets for 576 // certain protobuf libraries. Historically, these "_gen" targets depend on Well Known Types 577 // built with go_proto_library and are used together with go_proto_library. 578 // However, these are no longer needed and are kept as aliases to be backward-compatible 579 func (g *generator) maybeGenerateExtraLib(lib *rule.Rule, pkg *goPackage) *rule.Rule { 580 gc := getGoConfig(g.c) 581 if gc.prefix != "github.com/golang/protobuf" || gc.prefixRel != "" { 582 return nil 583 } 584 585 var r *rule.Rule 586 switch pkg.importPath { 587 case "github.com/golang/protobuf/descriptor": 588 r = rule.NewRule("alias", "go_default_library_gen") 589 r.SetAttr("actual", ":go_default_library") 590 r.SetAttr("visibility", []string{"//visibility:public"}) 591 592 case "github.com/golang/protobuf/jsonpb": 593 r = rule.NewRule("alias", "go_default_library_gen") 594 r.SetAttr("actual", ":go_default_library") 595 r.SetAttr("visibility", []string{"//visibility:public"}) 596 597 case "github.com/golang/protobuf/protoc-gen-go/generator": 598 r = rule.NewRule("alias", "go_default_library_gen") 599 r.SetAttr("actual", ":go_default_library") 600 r.SetAttr("visibility", []string{"//visibility:public"}) 601 602 case "github.com/golang/protobuf/ptypes": 603 r = rule.NewRule("alias", "go_default_library_gen") 604 r.SetAttr("actual", ":go_default_library") 605 r.SetAttr("visibility", []string{"//visibility:public"}) 606 } 607 608 return r 609 } 610 611 func (g *generator) setCommonAttrs(r *rule.Rule, pkgRel string, visibility []string, target goTarget, embed string) { 612 if !target.sources.isEmpty() { 613 r.SetAttr("srcs", target.sources.buildFlat()) 614 } 615 if !target.embedSrcs.isEmpty() { 616 r.SetAttr("embedsrcs", target.embedSrcs.build()) 617 } 618 if target.cgo { 619 r.SetAttr("cgo", true) 620 } 621 if !target.clinkopts.isEmpty() { 622 r.SetAttr("clinkopts", g.options(target.clinkopts.build(), pkgRel)) 623 } 624 if !target.cppopts.isEmpty() { 625 r.SetAttr("cppopts", g.options(target.cppopts.build(), pkgRel)) 626 } 627 if !target.copts.isEmpty() { 628 r.SetAttr("copts", g.options(target.copts.build(), pkgRel)) 629 } 630 if !target.cxxopts.isEmpty() { 631 r.SetAttr("cxxopts", g.options(target.cxxopts.build(), pkgRel)) 632 } 633 if g.shouldSetVisibility && len(visibility) > 0 { 634 r.SetAttr("visibility", visibility) 635 } 636 if embed != "" { 637 r.SetAttr("embed", []string{":" + embed}) 638 } 639 r.SetPrivateAttr(config.GazelleImportsKey, target.imports.build()) 640 } 641 642 func (g *generator) setImportAttrs(r *rule.Rule, importPath string) { 643 gc := getGoConfig(g.c) 644 r.SetAttr("importpath", importPath) 645 646 // Set importpath_aliases if we need minimal module compatibility. 647 // If a package is part of a module with a v2+ semantic import version 648 // suffix, packages that are not part of modules may import it without 649 // the suffix. 650 if gc.goRepositoryMode && gc.moduleMode && pathtools.HasPrefix(importPath, gc.prefix) && gc.prefixRel == "" { 651 if mmcImportPath := pathWithoutSemver(importPath); mmcImportPath != "" { 652 r.SetAttr("importpath_aliases", []string{mmcImportPath}) 653 } 654 } 655 656 if gc.importMapPrefix != "" { 657 fromPrefixRel := pathtools.TrimPrefix(g.rel, gc.importMapPrefixRel) 658 importMap := path.Join(gc.importMapPrefix, fromPrefixRel) 659 if importMap != importPath { 660 r.SetAttr("importmap", importMap) 661 } 662 } 663 } 664 665 func (g *generator) commonVisibility(importPath string) []string { 666 // If the Bazel package name (rel) contains "internal", add visibility for 667 // subpackages of the parent. 668 // If the import path contains "internal" but rel does not, this is 669 // probably an internal submodule. Add visibility for all subpackages. 670 relIndex := pathtools.Index(g.rel, "internal") 671 importIndex := pathtools.Index(importPath, "internal") 672 visibility := getGoConfig(g.c).goVisibility 673 if relIndex >= 0 { 674 parent := strings.TrimSuffix(g.rel[:relIndex], "/") 675 visibility = append(visibility, fmt.Sprintf("//%s:__subpackages__", parent)) 676 } else if importIndex >= 0 { 677 // This entire module is within an internal directory. 678 // Identify other repos which should have access too. 679 visibility = append(visibility, "//:__subpackages__") 680 for _, repo := range g.c.Repos { 681 if pathtools.HasPrefix(repo.AttrString("importpath"), importPath[:importIndex]) { 682 visibility = append(visibility, "@"+repo.Name()+"//:__subpackages__") 683 } 684 } 685 686 } else { 687 return []string{"//visibility:public"} 688 } 689 690 // Add visibility for any submodules that have the internal parent as 691 // a prefix of their module path. 692 if importIndex >= 0 { 693 gc := getGoConfig(g.c) 694 internalRoot := strings.TrimSuffix(importPath[:importIndex], "/") 695 for _, m := range gc.submodules { 696 if strings.HasPrefix(m.modulePath, internalRoot) { 697 visibility = append(visibility, fmt.Sprintf("@%s//:__subpackages__", m.repoName)) 698 } 699 } 700 } 701 702 return visibility 703 } 704 705 var ( 706 // shortOptPrefixes are strings that come at the beginning of an option 707 // argument that includes a path, e.g., -Ifoo/bar. 708 shortOptPrefixes = []string{"-I", "-L", "-F"} 709 710 // longOptPrefixes are separate arguments that come before a path argument, 711 // e.g., -iquote foo/bar. 712 longOptPrefixes = []string{"-I", "-L", "-F", "-iquote", "-isystem"} 713 ) 714 715 // options transforms package-relative paths in cgo options into repository- 716 // root-relative paths that Bazel can understand. For example, if a cgo file 717 // in //foo declares an include flag in its copts: "-Ibar", this method 718 // will transform that flag into "-Ifoo/bar". 719 func (g *generator) options(opts rule.PlatformStrings, pkgRel string) rule.PlatformStrings { 720 fixPath := func(opt string) string { 721 if strings.HasPrefix(opt, "/") { 722 return opt 723 } 724 return path.Clean(path.Join(pkgRel, opt)) 725 } 726 727 fixGroups := func(groups []string) ([]string, error) { 728 fixedGroups := make([]string, len(groups)) 729 for i, group := range groups { 730 opts := strings.Split(group, optSeparator) 731 fixedOpts := make([]string, len(opts)) 732 isPath := false 733 for j, opt := range opts { 734 if isPath { 735 opt = fixPath(opt) 736 isPath = false 737 goto next 738 } 739 740 for _, short := range shortOptPrefixes { 741 if strings.HasPrefix(opt, short) && len(opt) > len(short) { 742 opt = short + fixPath(opt[len(short):]) 743 goto next 744 } 745 } 746 747 for _, long := range longOptPrefixes { 748 if opt == long { 749 isPath = true 750 goto next 751 } 752 } 753 754 next: 755 fixedOpts[j] = escapeOption(opt) 756 } 757 fixedGroups[i] = strings.Join(fixedOpts, " ") 758 } 759 760 return fixedGroups, nil 761 } 762 763 opts, errs := opts.MapSlice(fixGroups) 764 if errs != nil { 765 log.Panicf("unexpected error when transforming options with pkg %q: %v", pkgRel, errs) 766 } 767 return opts 768 } 769 770 func escapeOption(opt string) string { 771 return strings.NewReplacer( 772 `\`, `\\`, 773 `'`, `\'`, 774 `"`, `\"`, 775 ` `, `\ `, 776 "\t", "\\\t", 777 "\n", "\\\n", 778 "\r", "\\\r", 779 "$(", "$(", 780 "$", "$$", 781 ).Replace(opt) 782 } 783 784 func shouldSetVisibility(args language.GenerateArgs) bool { 785 if args.File != nil && args.File.HasDefaultVisibility() { 786 return false 787 } 788 789 for _, r := range args.OtherGen { 790 // This is kind of the same test as *File.HasDefaultVisibility(), 791 // but for previously defined rules. 792 if r.Kind() == "package" && r.Attr("default_visibility") != nil { 793 return false 794 } 795 } 796 return true 797 }