github.com/stackb/rules_proto@v0.0.0-20240221195024-5428336c51f1/pkg/rule/rules_closure/closure_js_library.go (about) 1 package rules_closure 2 3 import ( 4 "container/list" 5 "path" 6 7 "github.com/bazelbuild/bazel-gazelle/config" 8 "github.com/bazelbuild/bazel-gazelle/label" 9 "github.com/bazelbuild/bazel-gazelle/resolve" 10 "github.com/bazelbuild/bazel-gazelle/rule" 11 12 "github.com/stackb/rules_proto/pkg/protoc" 13 ) 14 15 const transitiveDepsKey = "_transitive_proto_library_deps" 16 17 var closureJsLibraryKindInfo = rule.KindInfo{ 18 MergeableAttrs: map[string]bool{ 19 "srcs": true, 20 "internal_descriptors": true, 21 "exports": true, 22 "visibility": true, 23 "suppress": true, 24 "lenient": true, 25 }, 26 NonEmptyAttrs: map[string]bool{ 27 "srcs": true, 28 }, 29 ResolveAttrs: map[string]bool{ 30 "deps": true, 31 }, 32 } 33 34 // ClosureJsLibrary implements RuleProvider for 'py_library'-derived rules. 35 type ClosureJsLibrary struct { 36 KindName string 37 RuleNameSuffix string 38 Outputs []string 39 Config *protoc.ProtocConfiguration 40 RuleConfig *protoc.LanguageRuleConfig 41 Resolver protoc.DepsResolver 42 } 43 44 // Kind implements part of the ruleProvider interface. 45 func (s *ClosureJsLibrary) Kind() string { 46 return s.KindName 47 } 48 49 // Name implements part of the ruleProvider interface. 50 func (s *ClosureJsLibrary) Name() string { 51 return s.Config.Library.BaseName() + s.RuleNameSuffix 52 } 53 54 // Srcs computes the srcs list for the rule. 55 func (s *ClosureJsLibrary) Srcs() []string { 56 srcs := make([]string, 0) 57 for _, output := range s.Outputs { 58 srcs = append(srcs, protoc.StripRel(s.Config.Rel, output)) 59 } 60 return srcs 61 } 62 63 // Deps computes the deps list for the rule. 64 func (s *ClosureJsLibrary) Deps() []string { 65 return s.RuleConfig.GetDeps() 66 } 67 68 // Visibility provides visibility labels. 69 func (s *ClosureJsLibrary) Visibility() []string { 70 return s.RuleConfig.GetVisibility() 71 } 72 73 // Rule implements part of the ruleProvider interface. 74 func (s *ClosureJsLibrary) Rule(otherGen ...*rule.Rule) *rule.Rule { 75 newRule := rule.NewRule(s.Kind(), s.Name()) 76 77 newRule.SetAttr("srcs", s.Srcs()) 78 79 visibility := s.Visibility() 80 if len(visibility) > 0 { 81 newRule.SetAttr("visibility", visibility) 82 } 83 84 newRule.SetAttr("suppress", []string{ 85 "JSC_IMPLICITLY_NONNULL_JSDOC", 86 "JSC_UNUSED_LOCAL_ASSIGNMENT", 87 }) 88 89 return newRule 90 } 91 92 // Imports implements part of the RuleProvider interface. 93 func (s *ClosureJsLibrary) Imports(c *config.Config, r *rule.Rule, file *rule.File) []resolve.ImportSpec { 94 if lib, ok := r.PrivateAttr(protoc.ProtoLibraryKey).(protoc.ProtoLibrary); ok { 95 return protoc.ProtoLibraryImportSpecsForKind(r.Kind(), lib) 96 } 97 return nil 98 } 99 100 // Resolve implements part of the RuleProvider interface. 101 func (s *ClosureJsLibrary) Resolve(c *config.Config, ix *resolve.RuleIndex, r *rule.Rule, imports []string, from label.Label) { 102 if s.Resolver != nil { 103 s.Resolver(c, ix, r, imports, from) 104 return 105 } 106 107 protoc.ResolveDepsAttr("deps", false)(c, ix, r, imports, from) 108 r.SetAttr("exports", r.AttrStrings("deps")) 109 110 transitive := ResolveTransitiveProtoLibraryDeps(s.Config.Rel, r) 111 descriptors := make([]string, 0) 112 for _, v := range transitive { 113 descriptors = append(descriptors, v) 114 } 115 116 r.SetAttr("internal_descriptors", protoc.DeduplicateAndSort(descriptors)) 117 } 118 119 func ResolveTransitiveProtoLibraryDeps(rel string, r *rule.Rule) map[string]string { 120 121 lib := r.PrivateAttr(protoc.ProtoLibraryKey) 122 if lib == nil { 123 return nil 124 } 125 library := lib.(protoc.ProtoLibrary) 126 127 libRule := library.Rule() 128 // already created? 129 if transitiveDeps, ok := libRule.PrivateAttr(transitiveDepsKey).(map[string]string); ok { 130 return transitiveDeps 131 } 132 133 // nope. 134 transitiveDeps := make(map[string]string) 135 resolver := protoc.GlobalResolver() 136 137 seen := make(map[string]bool) 138 stack := list.New() 139 for _, src := range library.Srcs() { 140 stack.PushBack(path.Join(rel, src)) 141 } 142 // for every source file in the proto library, gather the list of source 143 // files on which it depends, until there are no more unprocessed sources. 144 // Foreach one check if there is an importmapping for it and record the 145 // association. 146 for { 147 if stack.Len() == 0 { 148 break 149 } 150 current := stack.Front() 151 stack.Remove(current) 152 153 protofile := current.Value.(string) 154 if seen[protofile] { 155 continue 156 } 157 seen[protofile] = true 158 159 depends := resolver.Resolve("proto", "depends", protofile) 160 for _, dep := range depends { 161 stack.PushBack(path.Join(dep.Label.Pkg, dep.Label.Name)) 162 } 163 164 result := resolver.Resolve("proto", "proto", protofile) 165 if len(result) > 0 { 166 first := result[0] 167 transitiveDeps[protofile] = first.Label.String() 168 } 169 } 170 171 libRule.SetPrivateAttr(transitiveDepsKey, transitiveDeps) 172 173 return transitiveDeps 174 }