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  }