...

Source file src/github.com/bazelbuild/bazel-gazelle/language/go/generate.go

Documentation: github.com/bazelbuild/bazel-gazelle/language/go

     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  		// Libraries made for a go_binary should not be exposed to the public.
   482  		visibility = []string{"//visibility:private"}
   483  	} else {
   484  		visibility = g.commonVisibility(pkg.importPath)
   485  	}
   486  	g.setCommonAttrs(goLibrary, pkg.rel, visibility, pkg.library, embed)
   487  	g.setImportAttrs(goLibrary, pkg.importPath)
   488  	return goLibrary
   489  }
   490  
   491  func (g *generator) maybeGenerateAlias(pkg *goPackage, libName string) *rule.Rule {
   492  	if pkg.isCommand() || libName == "" {
   493  		return nil
   494  	}
   495  	gc := getGoConfig(g.c)
   496  	if gc.goNamingConvention == goDefaultLibraryNamingConvention {
   497  		return nil
   498  	}
   499  	alias := rule.NewRule("alias", defaultLibName)
   500  	alias.SetAttr("visibility", g.commonVisibility(pkg.importPath))
   501  	if gc.goNamingConvention == importAliasNamingConvention {
   502  		alias.SetAttr("actual", ":"+libName)
   503  	}
   504  	return alias
   505  }
   506  
   507  func (g *generator) generateBin(pkg *goPackage, library string) *rule.Rule {
   508  	gc := getGoConfig(g.c)
   509  	name := binName(pkg.rel, gc.prefix, g.c.RepoRoot)
   510  	goBinary := rule.NewRule("go_binary", name)
   511  	if !pkg.isCommand() || pkg.binary.sources.isEmpty() && library == "" {
   512  		return goBinary // empty
   513  	}
   514  	visibility := g.commonVisibility(pkg.importPath)
   515  	g.setCommonAttrs(goBinary, pkg.rel, visibility, pkg.binary, library)
   516  	return goBinary
   517  }
   518  
   519  func (g *generator) generateTests(pkg *goPackage, library string) []*rule.Rule {
   520  	gc := getGoConfig(g.c)
   521  	tests := pkg.tests
   522  	if len(tests) == 0 && gc.testMode == defaultTestMode {
   523  		tests = []goTarget{goTarget{}}
   524  	}
   525  	var name func(goTarget) string
   526  	switch gc.testMode {
   527  	case defaultTestMode:
   528  		name = func(goTarget) string {
   529  			return testNameByConvention(gc.goNamingConvention, pkg.importPath)
   530  		}
   531  	case fileTestMode:
   532  		name = func(test goTarget) string {
   533  			if test.sources.hasGo() {
   534  				if srcs := test.sources.buildFlat(); len(srcs) == 1 {
   535  					return testNameFromSingleSource(srcs[0])
   536  				}
   537  			}
   538  			return testNameByConvention(gc.goNamingConvention, pkg.importPath)
   539  		}
   540  	}
   541  	var res []*rule.Rule
   542  	for i, test := range tests {
   543  		goTest := rule.NewRule("go_test", name(test))
   544  		hasGo := test.sources.hasGo()
   545  		if hasGo || i == 0 {
   546  			res = append(res, goTest)
   547  			if !hasGo {
   548  				continue
   549  			}
   550  		}
   551  		var embed string
   552  		if test.hasInternalTest {
   553  			embed = library
   554  		}
   555  		g.setCommonAttrs(goTest, pkg.rel, nil, test, embed)
   556  		if pkg.hasTestdata {
   557  			goTest.SetAttr("data", rule.GlobValue{Patterns: []string{"testdata/**"}})
   558  		}
   559  	}
   560  	return res
   561  }
   562  
   563  // maybePublishToolLib makes the given go_library rule public if needed for nogo.
   564  // Updating it here automatically makes it easier to upgrade org_golang_x_tools.
   565  func (g *generator) maybePublishToolLib(lib *rule.Rule, pkg *goPackage) {
   566  	if pkg.importPath == "golang.org/x/tools/go/analysis/internal/facts" || pkg.importPath == "golang.org/x/tools/internal/facts" {
   567  		// Imported by nogo main. We add a visibility exception.
   568  		lib.SetAttr("visibility", []string{"//visibility:public"})
   569  	}
   570  }
   571  
   572  // maybeGenerateExtraLib generates extra equivalent library targets for
   573  // certain protobuf libraries. These "_gen" targets depend on Well Known Types
   574  // built with go_proto_library and are used together with go_proto_library.
   575  // The original targets are used when proto rule generation is disabled.
   576  func (g *generator) maybeGenerateExtraLib(lib *rule.Rule, pkg *goPackage) *rule.Rule {
   577  	gc := getGoConfig(g.c)
   578  	if gc.prefix != "github.com/golang/protobuf" || gc.prefixRel != "" {
   579  		return nil
   580  	}
   581  
   582  	var r *rule.Rule
   583  	switch pkg.importPath {
   584  	case "github.com/golang/protobuf/descriptor":
   585  		r = rule.NewRule("go_library", "go_default_library_gen")
   586  		r.SetAttr("srcs", pkg.library.sources.buildFlat())
   587  		r.SetAttr("importpath", pkg.importPath)
   588  		r.SetAttr("visibility", []string{"//visibility:public"})
   589  		r.SetAttr("deps", []string{
   590  			"//proto:go_default_library",
   591  			"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
   592  			"@org_golang_google_protobuf//reflect/protodesc:go_default_library",
   593  			"@org_golang_google_protobuf//reflect/protoreflect:go_default_library",
   594  			"@org_golang_google_protobuf//runtime/protoimpl:go_default_library",
   595  		})
   596  
   597  	case "github.com/golang/protobuf/jsonpb":
   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/protoc-gen-go/generator":
   603  		r = rule.NewRule("go_library", "go_default_library_gen")
   604  		r.SetAttr("srcs", pkg.library.sources.buildFlat())
   605  		r.SetAttr("importpath", pkg.importPath)
   606  		r.SetAttr("visibility", []string{"//visibility:public"})
   607  		r.SetAttr("deps", []string{
   608  			"//proto:go_default_library",
   609  			"//protoc-gen-go/generator/internal/remap:go_default_library",
   610  			"@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto",
   611  			"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
   612  		})
   613  
   614  	case "github.com/golang/protobuf/ptypes":
   615  		r = rule.NewRule("go_library", "go_default_library_gen")
   616  		r.SetAttr("srcs", pkg.library.sources.buildFlat())
   617  		r.SetAttr("importpath", pkg.importPath)
   618  		r.SetAttr("visibility", []string{"//visibility:public"})
   619  		r.SetAttr("deps", []string{
   620  			"//proto:go_default_library",
   621  			"@io_bazel_rules_go//proto/wkt:any_go_proto",
   622  			"@io_bazel_rules_go//proto/wkt:duration_go_proto",
   623  			"@io_bazel_rules_go//proto/wkt:timestamp_go_proto",
   624  			"@org_golang_google_protobuf//reflect/protoreflect:go_default_library",
   625  			"@org_golang_google_protobuf//reflect/protoregistry:go_default_library",
   626  		})
   627  	}
   628  
   629  	return r
   630  }
   631  
   632  func (g *generator) setCommonAttrs(r *rule.Rule, pkgRel string, visibility []string, target goTarget, embed string) {
   633  	if !target.sources.isEmpty() {
   634  		r.SetAttr("srcs", target.sources.buildFlat())
   635  	}
   636  	if !target.embedSrcs.isEmpty() {
   637  		r.SetAttr("embedsrcs", target.embedSrcs.build())
   638  	}
   639  	if target.cgo {
   640  		r.SetAttr("cgo", true)
   641  	}
   642  	if !target.clinkopts.isEmpty() {
   643  		r.SetAttr("clinkopts", g.options(target.clinkopts.build(), pkgRel))
   644  	}
   645  	if !target.cppopts.isEmpty() {
   646  		r.SetAttr("cppopts", g.options(target.cppopts.build(), pkgRel))
   647  	}
   648  	if !target.copts.isEmpty() {
   649  		r.SetAttr("copts", g.options(target.copts.build(), pkgRel))
   650  	}
   651  	if !target.cxxopts.isEmpty() {
   652  		r.SetAttr("cxxopts", g.options(target.cxxopts.build(), pkgRel))
   653  	}
   654  	if g.shouldSetVisibility && len(visibility) > 0 {
   655  		r.SetAttr("visibility", visibility)
   656  	}
   657  	if embed != "" {
   658  		r.SetAttr("embed", []string{":" + embed})
   659  	}
   660  	r.SetPrivateAttr(config.GazelleImportsKey, target.imports.build())
   661  }
   662  
   663  func (g *generator) setImportAttrs(r *rule.Rule, importPath string) {
   664  	gc := getGoConfig(g.c)
   665  	r.SetAttr("importpath", importPath)
   666  
   667  	// Set importpath_aliases if we need minimal module compatibility.
   668  	// If a package is part of a module with a v2+ semantic import version
   669  	// suffix, packages that are not part of modules may import it without
   670  	// the suffix.
   671  	if gc.goRepositoryMode && gc.moduleMode && pathtools.HasPrefix(importPath, gc.prefix) && gc.prefixRel == "" {
   672  		if mmcImportPath := pathWithoutSemver(importPath); mmcImportPath != "" {
   673  			r.SetAttr("importpath_aliases", []string{mmcImportPath})
   674  		}
   675  	}
   676  
   677  	if gc.importMapPrefix != "" {
   678  		fromPrefixRel := pathtools.TrimPrefix(g.rel, gc.importMapPrefixRel)
   679  		importMap := path.Join(gc.importMapPrefix, fromPrefixRel)
   680  		if importMap != importPath {
   681  			r.SetAttr("importmap", importMap)
   682  		}
   683  	}
   684  }
   685  
   686  func (g *generator) commonVisibility(importPath string) []string {
   687  	// If the Bazel package name (rel) contains "internal", add visibility for
   688  	// subpackages of the parent.
   689  	// If the import path contains "internal" but rel does not, this is
   690  	// probably an internal submodule. Add visibility for all subpackages.
   691  	relIndex := pathtools.Index(g.rel, "internal")
   692  	importIndex := pathtools.Index(importPath, "internal")
   693  	visibility := getGoConfig(g.c).goVisibility
   694  	if relIndex >= 0 {
   695  		parent := strings.TrimSuffix(g.rel[:relIndex], "/")
   696  		visibility = append(visibility, fmt.Sprintf("//%s:__subpackages__", parent))
   697  	} else if importIndex >= 0 {
   698  		// This entire module is within an internal directory.
   699  		// Identify other repos which should have access too.
   700  		visibility = append(visibility, "//:__subpackages__")
   701  		for _, repo := range g.c.Repos {
   702  			if pathtools.HasPrefix(repo.AttrString("importpath"), importPath[:importIndex]) {
   703  				visibility = append(visibility, "@"+repo.Name()+"//:__subpackages__")
   704  			}
   705  		}
   706  
   707  	} else {
   708  		return []string{"//visibility:public"}
   709  	}
   710  
   711  	// Add visibility for any submodules that have the internal parent as
   712  	// a prefix of their module path.
   713  	if importIndex >= 0 {
   714  		gc := getGoConfig(g.c)
   715  		internalRoot := strings.TrimSuffix(importPath[:importIndex], "/")
   716  		for _, m := range gc.submodules {
   717  			if strings.HasPrefix(m.modulePath, internalRoot) {
   718  				visibility = append(visibility, fmt.Sprintf("@%s//:__subpackages__", m.repoName))
   719  			}
   720  		}
   721  	}
   722  
   723  	return visibility
   724  }
   725  
   726  var (
   727  	// shortOptPrefixes are strings that come at the beginning of an option
   728  	// argument that includes a path, e.g., -Ifoo/bar.
   729  	shortOptPrefixes = []string{"-I", "-L", "-F"}
   730  
   731  	// longOptPrefixes are separate arguments that come before a path argument,
   732  	// e.g., -iquote foo/bar.
   733  	longOptPrefixes = []string{"-I", "-L", "-F", "-iquote", "-isystem"}
   734  )
   735  
   736  // options transforms package-relative paths in cgo options into repository-
   737  // root-relative paths that Bazel can understand. For example, if a cgo file
   738  // in //foo declares an include flag in its copts: "-Ibar", this method
   739  // will transform that flag into "-Ifoo/bar".
   740  func (g *generator) options(opts rule.PlatformStrings, pkgRel string) rule.PlatformStrings {
   741  	fixPath := func(opt string) string {
   742  		if strings.HasPrefix(opt, "/") {
   743  			return opt
   744  		}
   745  		return path.Clean(path.Join(pkgRel, opt))
   746  	}
   747  
   748  	fixGroups := func(groups []string) ([]string, error) {
   749  		fixedGroups := make([]string, len(groups))
   750  		for i, group := range groups {
   751  			opts := strings.Split(group, optSeparator)
   752  			fixedOpts := make([]string, len(opts))
   753  			isPath := false
   754  			for j, opt := range opts {
   755  				if isPath {
   756  					opt = fixPath(opt)
   757  					isPath = false
   758  					goto next
   759  				}
   760  
   761  				for _, short := range shortOptPrefixes {
   762  					if strings.HasPrefix(opt, short) && len(opt) > len(short) {
   763  						opt = short + fixPath(opt[len(short):])
   764  						goto next
   765  					}
   766  				}
   767  
   768  				for _, long := range longOptPrefixes {
   769  					if opt == long {
   770  						isPath = true
   771  						goto next
   772  					}
   773  				}
   774  
   775  			next:
   776  				fixedOpts[j] = escapeOption(opt)
   777  			}
   778  			fixedGroups[i] = strings.Join(fixedOpts, " ")
   779  		}
   780  
   781  		return fixedGroups, nil
   782  	}
   783  
   784  	opts, errs := opts.MapSlice(fixGroups)
   785  	if errs != nil {
   786  		log.Panicf("unexpected error when transforming options with pkg %q: %v", pkgRel, errs)
   787  	}
   788  	return opts
   789  }
   790  
   791  func escapeOption(opt string) string {
   792  	return strings.NewReplacer(
   793  		`\`, `\\`,
   794  		`'`, `\'`,
   795  		`"`, `\"`,
   796  		` `, `\ `,
   797  		"\t", "\\\t",
   798  		"\n", "\\\n",
   799  		"\r", "\\\r",
   800  		"$(", "$(",
   801  		"$", "$$",
   802  	).Replace(opt)
   803  }
   804  
   805  func shouldSetVisibility(args language.GenerateArgs) bool {
   806  	if args.File != nil && args.File.HasDefaultVisibility() {
   807  		return false
   808  	}
   809  
   810  	for _, r := range args.OtherGen {
   811  		// This is kind of the same test as *File.HasDefaultVisibility(),
   812  		// but for previously defined rules.
   813  		if r.Kind() == "package" && r.Attr("default_visibility") != nil {
   814  			return false
   815  		}
   816  	}
   817  	return true
   818  }
   819  

View as plain text