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(&regularFiles, 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  }