github.com/stackb/rules_proto@v0.0.0-20240221195024-5428336c51f1/pkg/language/protobuf/generate.go (about) 1 package protobuf 2 3 import ( 4 "log" 5 "path" 6 7 "github.com/bazelbuild/bazel-gazelle/config" 8 "github.com/bazelbuild/bazel-gazelle/label" 9 "github.com/bazelbuild/bazel-gazelle/language" 10 11 "github.com/stackb/rules_proto/pkg/protoc" 12 ) 13 14 // GenerateRules extracts build metadata from source files in a directory. 15 // GenerateRules is called in each directory where an update is requested in 16 // depth-first post-order. 17 // 18 // args contains the arguments for GenerateRules. This is passed as a struct to 19 // avoid breaking implementations in the future when new fields are added. 20 // 21 // A GenerateResult struct is returned. Optional fields may be added to this 22 // type in the future. 23 // 24 // Any non-fatal errors this function encounters should be logged using 25 // log.Print. 26 func (pl *protobufLang) GenerateRules(args language.GenerateArgs) language.GenerateResult { 27 cfg := pl.getOrCreatePackageConfig(args.Config) 28 29 files := make(map[string]*protoc.File) 30 for _, f := range args.RegularFiles { 31 if !protoc.IsProtoFile(f) { 32 continue 33 } 34 file := protoc.NewFile(args.Rel, f) 35 if err := file.Parse(); err != nil { 36 log.Printf("warning: unparseable proto file dir=%s, file=%s: %v", args.Dir, file.Basename, err) 37 continue 38 } 39 files[f] = file 40 41 // Record the list of dependencies for this proto file. Dependents are 42 // encoded as labels as a matter of practicality given the API of the 43 // resolver. 44 for _, imp := range file.Imports() { 45 dir := path.Dir(imp.Filename) 46 if dir == "." { 47 dir = "" 48 } 49 pl.resolver.Provide( 50 "proto", 51 "depends", 52 path.Join(file.Dir, file.Basename), 53 label.New("", dir, path.Base(imp.Filename)), 54 ) 55 } 56 } 57 58 protoLibraries := make([]protoc.ProtoLibrary, 0) 59 for _, r := range args.OtherGen { 60 internalLabel := label.New("", args.Rel, r.Name()) 61 protoc.GlobalRuleIndex().Put(internalLabel, r) 62 63 if r.Kind() != "proto_library" { 64 continue 65 } 66 67 srcs := r.AttrStrings("srcs") 68 srcLabels := make([]label.Label, len(srcs)) 69 for i, src := range srcs { 70 srcLabel, err := label.Parse(src) 71 if err != nil { 72 log.Fatalf("%s %q: unparseable source label %q: %v", r.Kind(), r.Name(), src, err) 73 } 74 srcLabels[i] = srcLabel 75 76 // record the label that "provides" each proto file. 77 pl.resolver.Provide( 78 "proto", 79 "proto", 80 path.Join(args.Rel, src), 81 internalLabel, 82 ) 83 } 84 85 lib := protoc.NewOtherProtoLibrary(args.File, r, matchingFiles(files, srcLabels)...) 86 protoLibraries = append(protoLibraries, lib) 87 } 88 89 pkg := protoc.NewPackage(args.Rel, cfg, protoLibraries...) 90 pl.packages[args.Rel] = pkg 91 92 rules := pkg.Rules() 93 94 // special case if we want to override com_google_protobuf or go_googleapis 95 // deps. 96 if pl.reresolveKnownProtoImports && len(protoLibraries) > 0 { 97 rules = append(rules, makeProtoOverrideRule(protoLibraries)) 98 } 99 100 imports := make([]interface{}, len(rules)) 101 for i, r := range rules { 102 imports[i] = r.PrivateAttr(config.GazelleImportsKey) 103 internalLabel := label.New("", args.Rel, r.Name()) 104 protoc.GlobalRuleIndex().Put(internalLabel, r) 105 } 106 107 // special case if this is the root BUILD file and the user requested to 108 // write the imports file. 109 if args.Rel == "" && pl.importsOutFile != "" { 110 if err := protoc.GlobalResolver().SaveFile(pl.importsOutFile, pl.repoName); err != nil { 111 log.Printf("error saving import file: %s: %v", pl.importsOutFile, err) 112 } 113 } 114 115 return language.GenerateResult{ 116 Gen: rules, 117 Imports: imports, 118 Empty: pkg.Empty(), 119 } 120 } 121 122 func matchingFiles(files map[string]*protoc.File, srcs []label.Label) []*protoc.File { 123 matching := make([]*protoc.File, 0) 124 for _, src := range srcs { 125 if file, ok := files[src.Name]; ok { 126 matching = append(matching, file) 127 } 128 } 129 return matching 130 }