in language/go/generate.go [35:310]
func (gl *goLang) GenerateRules(args language.GenerateArgs) language.GenerateResult {
// Extract information about proto files. We need this to exclude .pb.go
// files and generate go_proto_library rules.
c := args.Config
pcMode := getProtoMode(c)
// This is a collection of proto_library rule names that have a corresponding
// go_proto_library rule already generated.
goProtoRules := make(map[string]struct{})
var protoRuleNames []string
protoPackages := make(map[string]proto.Package)
protoFileInfo := make(map[string]proto.FileInfo)
for _, r := range args.OtherGen {
if r.Kind() == "go_proto_library" {
if proto := r.AttrString("proto"); proto != "" {
goProtoRules[proto] = struct{}{}
}
if protos := r.AttrStrings("protos"); protos != nil {
for _, proto := range protos {
goProtoRules[proto] = struct{}{}
}
}
}
if r.Kind() != "proto_library" {
continue
}
pkg := r.PrivateAttr(proto.PackageKey).(proto.Package)
protoPackages[r.Name()] = pkg
for name, info := range pkg.Files {
protoFileInfo[name] = info
}
protoRuleNames = append(protoRuleNames, r.Name())
}
sort.Strings(protoRuleNames)
var emptyProtoRuleNames []string
for _, r := range args.OtherEmpty {
if r.Kind() == "proto_library" {
emptyProtoRuleNames = append(emptyProtoRuleNames, r.Name())
}
}
// If proto rule generation is enabled, exclude .pb.go files that correspond
// to any .proto files present.
regularFiles := append([]string{}, args.RegularFiles...)
genFiles := append([]string{}, args.GenFiles...)
if !pcMode.ShouldIncludePregeneratedFiles() {
keep := func(f string) bool {
for _, suffix := range []string{".pb.go", "_grpc.pb.go"} {
if strings.HasSuffix(f, suffix) {
if _, ok := protoFileInfo[strings.TrimSuffix(f, suffix)+".proto"]; ok {
return false
}
}
}
return true
}
filterFiles(®ularFiles, keep)
filterFiles(&genFiles, keep)
}
// Split regular files into files which can determine the package name and
// import path and other files.
var goFiles, otherFiles []string
for _, f := range regularFiles {
if strings.HasSuffix(f, ".go") {
goFiles = append(goFiles, f)
} else {
otherFiles = append(otherFiles, f)
}
}
// Look for a subdirectory named testdata. Only treat it as data if it does
// not contain a buildable package.
var hasTestdata bool
for _, sub := range args.Subdirs {
if sub == "testdata" {
hasTestdata = !gl.goPkgRels[path.Join(args.Rel, "testdata")]
break
}
}
// Build a set of packages from files in this directory.
goFileInfos := make([]fileInfo, len(goFiles))
var er *embedResolver
for i, name := range goFiles {
path := filepath.Join(args.Dir, name)
goFileInfos[i] = goFileInfo(path, args.Rel)
if len(goFileInfos[i].embeds) > 0 && er == nil {
er = newEmbedResolver(args.Dir, args.Rel, c.ValidBuildFileNames, gl.goPkgRels, args.Subdirs, args.RegularFiles, args.GenFiles)
}
}
goPackageMap, goFilesWithUnknownPackage := buildPackages(c, args.Dir, args.Rel, hasTestdata, er, goFileInfos)
// Select a package to generate rules for. If there is no package, create
// an empty package so we can generate empty rules.
var protoName string
pkg, err := selectPackage(c, args.Dir, goPackageMap)
if err != nil {
if _, ok := err.(*build.NoGoError); ok {
if len(protoPackages) == 1 {
for name, ppkg := range protoPackages {
if _, ok := goProtoRules[":"+name]; ok {
// if a go_proto_library rule already exists for this
// proto package, treat it as if the proto package
// doesn't exist.
pkg = emptyPackage(c, args.Dir, args.Rel, args.File)
break
}
pkg = &goPackage{
name: goProtoPackageName(ppkg),
importPath: goProtoImportPath(c, ppkg, args.Rel),
proto: protoTargetFromProtoPackage(name, ppkg),
}
protoName = name
break
}
} else {
pkg = emptyPackage(c, args.Dir, args.Rel, args.File)
}
} else {
log.Print(err)
}
}
// Try to link the selected package with a proto package.
if pkg != nil {
if pkg.importPath == "" {
if err := pkg.inferImportPath(c); err != nil && pkg.firstGoFile() != "" {
inferImportPathErrorOnce.Do(func() { log.Print(err) })
}
}
for _, name := range protoRuleNames {
ppkg := protoPackages[name]
if pkg.importPath == goProtoImportPath(c, ppkg, args.Rel) {
protoName = name
pkg.proto = protoTargetFromProtoPackage(name, ppkg)
break
}
}
}
// Generate rules for proto packages. These should come before the other
// Go rules.
g := &generator{
c: c,
rel: args.Rel,
shouldSetVisibility: args.File == nil || !args.File.HasDefaultVisibility(),
}
var res language.GenerateResult
var rules []*rule.Rule
var protoEmbed string
for _, name := range protoRuleNames {
if _, ok := goProtoRules[":"+name]; ok {
// if a go_proto_library rule exists for this proto_library rule
// already, skip creating another go_proto_library for it, assuming
// that a different gazelle extension is responsible for
// go_proto_library rule generation.
continue
}
ppkg := protoPackages[name]
var rs []*rule.Rule
if name == protoName {
protoEmbed, rs = g.generateProto(pcMode, pkg.proto, pkg.importPath)
} else {
target := protoTargetFromProtoPackage(name, ppkg)
importPath := goProtoImportPath(c, ppkg, args.Rel)
_, rs = g.generateProto(pcMode, target, importPath)
}
rules = append(rules, rs...)
}
for _, name := range emptyProtoRuleNames {
goProtoName := strings.TrimSuffix(name, "_proto") + "_go_proto"
res.Empty = append(res.Empty, rule.NewRule("go_proto_library", goProtoName))
}
if pkg != nil && pcMode == proto.PackageMode && pkg.firstGoFile() == "" {
// In proto package mode, don't generate a go_library embedding a
// go_proto_library unless there are actually go files.
protoEmbed = ""
}
// Complete the Go package and generate rules for that.
if pkg != nil {
// Add files with unknown packages. This happens when there are parse
// or I/O errors. We should keep the file in the srcs list and let the
// compiler deal with the error.
cgo := pkg.haveCgo()
for _, info := range goFilesWithUnknownPackage {
if err := pkg.addFile(c, er, info, cgo); err != nil {
log.Print(err)
}
}
// Process the other static files.
for _, file := range otherFiles {
info := otherFileInfo(filepath.Join(args.Dir, file))
if err := pkg.addFile(c, er, info, cgo); err != nil {
log.Print(err)
}
}
// Process generated files. Note that generated files may have the same names
// as static files. Bazel will use the generated files, but we will look at
// the content of static files, assuming they will be the same.
regularFileSet := make(map[string]bool)
for _, f := range regularFiles {
regularFileSet[f] = true
}
// Some of the generated files may have been consumed by other rules
consumedFileSet := make(map[string]bool)
for _, r := range args.OtherGen {
for _, f := range r.AttrStrings("srcs") {
consumedFileSet[f] = true
}
if f := r.AttrString("src"); f != "" {
consumedFileSet[f] = true
}
}
for _, f := range genFiles {
if regularFileSet[f] || consumedFileSet[f] {
continue
}
info := fileNameInfo(filepath.Join(args.Dir, f))
if err := pkg.addFile(c, er, info, cgo); err != nil {
log.Print(err)
}
}
// Generate Go rules.
if protoName == "" {
// Empty proto rules for deletion.
_, rs := g.generateProto(pcMode, pkg.proto, pkg.importPath)
rules = append(rules, rs...)
}
lib := g.generateLib(pkg, protoEmbed)
var libName string
if !lib.IsEmpty(goKinds[lib.Kind()]) {
libName = lib.Name()
}
rules = append(rules, lib)
g.maybePublishToolLib(lib, pkg)
if r := g.maybeGenerateExtraLib(lib, pkg); r != nil {
rules = append(rules, r)
}
if r := g.maybeGenerateAlias(pkg, libName); r != nil {
g.maybePublishToolLib(r, pkg)
rules = append(rules, r)
}
rules = append(rules,
g.generateBin(pkg, libName),
g.generateTest(pkg, libName))
}
for _, r := range rules {
if r.IsEmpty(goKinds[r.Kind()]) {
res.Empty = append(res.Empty, r)
} else {
res.Gen = append(res.Gen, r)
res.Imports = append(res.Imports, r.PrivateAttr(config.GazelleImportsKey))
}
}
if args.File != nil || len(res.Gen) > 0 {
gl.goPkgRels[args.Rel] = true
} else {
for _, sub := range args.Subdirs {
if gl.goPkgRels[path.Join(args.Rel, sub)] {
gl.goPkgRels[args.Rel] = true
break
}
}
}
return res
}