index.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /* Copyright 2018 The Bazel Authors. All rights reserved.
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package resolve
  13. import (
  14. "log"
  15. "github.com/bazelbuild/bazel-gazelle/config"
  16. "github.com/bazelbuild/bazel-gazelle/label"
  17. "github.com/bazelbuild/bazel-gazelle/repo"
  18. "github.com/bazelbuild/bazel-gazelle/rule"
  19. )
  20. // ImportSpec describes a library to be imported. Imp is an import string for
  21. // the library. Lang is the language in which the import string appears (this
  22. // should match Resolver.Name).
  23. type ImportSpec struct {
  24. Lang, Imp string
  25. }
  26. // Resolver is an interface that language extensions can implement to resolve
  27. // dependencies in rules they generate.
  28. type Resolver interface {
  29. // Name returns the name of the language. This should be a prefix of the
  30. // kinds of rules generated by the language, e.g., "go" for the Go extension
  31. // since it generates "go_library" rules.
  32. Name() string
  33. // Imports returns a list of ImportSpecs that can be used to import the rule
  34. // r. This is used to populate RuleIndex.
  35. //
  36. // If nil is returned, the rule will not be indexed. If any non-nil slice is
  37. // returned, including an empty slice, the rule will be indexed.
  38. Imports(c *config.Config, r *rule.Rule, f *rule.File) []ImportSpec
  39. // Embeds returns a list of labels of rules that the given rule embeds. If
  40. // a rule is embedded by another importable rule of the same language, only
  41. // the embedding rule will be indexed. The embedding rule will inherit
  42. // the imports of the embedded rule.
  43. Embeds(r *rule.Rule, from label.Label) []label.Label
  44. // Resolve translates imported libraries for a given rule into Bazel
  45. // dependencies. Information about imported libraries is returned for each
  46. // rule generated by language.GenerateRules in
  47. // language.GenerateResult.Imports. Resolve generates a "deps" attribute (or
  48. // the appropriate language-specific equivalent) for each import according to
  49. // language-specific rules and heuristics.
  50. Resolve(c *config.Config, ix *RuleIndex, rc *repo.RemoteCache, r *rule.Rule, imports interface{}, from label.Label)
  51. }
  52. // RuleIndex is a table of rules in a workspace, indexed by label and by
  53. // import path. Used by Resolver to map import paths to labels.
  54. type RuleIndex struct {
  55. rules []*ruleRecord
  56. labelMap map[label.Label]*ruleRecord
  57. importMap map[ImportSpec][]*ruleRecord
  58. mrslv func(r *rule.Rule, pkgRel string) Resolver
  59. }
  60. // ruleRecord contains information about a rule relevant to import indexing.
  61. type ruleRecord struct {
  62. rule *rule.Rule
  63. label label.Label
  64. file *rule.File
  65. // importedAs is a list of ImportSpecs by which this rule may be imported.
  66. // Used to build a map from ImportSpecs to ruleRecords.
  67. importedAs []ImportSpec
  68. // embeds is the transitive closure of labels for rules that this rule embeds
  69. // (as determined by the Embeds method). This only includes rules in the same
  70. // language (i.e., it includes a go_library embedding a go_proto_library, but
  71. // not a go_proto_library embedding a proto_library).
  72. embeds []label.Label
  73. // embedded indicates whether another rule of the same language embeds this
  74. // rule. Embedded rules should not be indexed.
  75. embedded bool
  76. didCollectEmbeds bool
  77. }
  78. // NewRuleIndex creates a new index.
  79. //
  80. // kindToResolver is a map from rule kinds (for example, "go_library") to
  81. // Resolvers that support those kinds.
  82. func NewRuleIndex(mrslv func(r *rule.Rule, pkgRel string) Resolver) *RuleIndex {
  83. return &RuleIndex{
  84. labelMap: make(map[label.Label]*ruleRecord),
  85. mrslv: mrslv,
  86. }
  87. }
  88. // AddRule adds a rule r to the index. The rule will only be indexed if there
  89. // is a known resolver for the rule's kind and Resolver.Imports returns a
  90. // non-nil slice.
  91. //
  92. // AddRule may only be called before Finish.
  93. func (ix *RuleIndex) AddRule(c *config.Config, r *rule.Rule, f *rule.File) {
  94. var imps []ImportSpec
  95. if rslv := ix.mrslv(r, f.Pkg); rslv != nil {
  96. imps = rslv.Imports(c, r, f)
  97. }
  98. // If imps == nil, the rule is not importable. If imps is the empty slice,
  99. // it may still be importable if it embeds importable libraries.
  100. if imps == nil {
  101. return
  102. }
  103. record := &ruleRecord{
  104. rule: r,
  105. label: label.New(c.RepoName, f.Pkg, r.Name()),
  106. file: f,
  107. importedAs: imps,
  108. }
  109. if _, ok := ix.labelMap[record.label]; ok {
  110. log.Printf("multiple rules found with label %s", record.label)
  111. return
  112. }
  113. ix.rules = append(ix.rules, record)
  114. ix.labelMap[record.label] = record
  115. }
  116. // Finish constructs the import index and performs any other necessary indexing
  117. // actions after all rules have been added. This step is necessary because
  118. // a rule may be indexed differently based on what rules are added later.
  119. //
  120. // Finish must be called after all AddRule calls and before any
  121. // FindRulesByImport calls.
  122. func (ix *RuleIndex) Finish() {
  123. for _, r := range ix.rules {
  124. ix.collectEmbeds(r)
  125. }
  126. ix.buildImportIndex()
  127. }
  128. func (ix *RuleIndex) collectEmbeds(r *ruleRecord) {
  129. if r.didCollectEmbeds {
  130. return
  131. }
  132. resolver := ix.mrslv(r.rule, r.file.Pkg)
  133. r.didCollectEmbeds = true
  134. embedLabels := resolver.Embeds(r.rule, r.label)
  135. r.embeds = embedLabels
  136. for _, e := range embedLabels {
  137. er, ok := ix.findRuleByLabel(e, r.label)
  138. if !ok {
  139. continue
  140. }
  141. ix.collectEmbeds(er)
  142. if resolver == ix.mrslv(er.rule, er.file.Pkg) {
  143. er.embedded = true
  144. r.embeds = append(r.embeds, er.embeds...)
  145. }
  146. r.importedAs = append(r.importedAs, er.importedAs...)
  147. }
  148. }
  149. // buildImportIndex constructs the map used by FindRulesByImport.
  150. func (ix *RuleIndex) buildImportIndex() {
  151. ix.importMap = make(map[ImportSpec][]*ruleRecord)
  152. for _, r := range ix.rules {
  153. if r.embedded {
  154. continue
  155. }
  156. indexed := make(map[ImportSpec]bool)
  157. for _, imp := range r.importedAs {
  158. if indexed[imp] {
  159. continue
  160. }
  161. indexed[imp] = true
  162. ix.importMap[imp] = append(ix.importMap[imp], r)
  163. }
  164. }
  165. }
  166. func (ix *RuleIndex) findRuleByLabel(label label.Label, from label.Label) (*ruleRecord, bool) {
  167. label = label.Abs(from.Repo, from.Pkg)
  168. r, ok := ix.labelMap[label]
  169. return r, ok
  170. }
  171. type FindResult struct {
  172. // Label is the absolute label (including repository and package name) for
  173. // a matched rule.
  174. Label label.Label
  175. // Embeds is the transitive closure of labels for rules that the matched
  176. // rule embeds. It may contains duplicates and does not include the label
  177. // for the rule itself.
  178. Embeds []label.Label
  179. }
  180. // FindRulesByImport attempts to resolve an import string to a rule record.
  181. // imp is the import to resolve (which includes the target language). lang is
  182. // the language of the rule with the dependency (for example, in
  183. // go_proto_library, imp will have ProtoLang and lang will be GoLang).
  184. // from is the rule which is doing the dependency. This is used to check
  185. // vendoring visibility and to check for self-imports.
  186. //
  187. // FindRulesByImport returns a list of rules, since any number of rules may
  188. // provide the same import. Callers may need to resolve ambiguities using
  189. // language-specific heuristics.
  190. func (ix *RuleIndex) FindRulesByImport(imp ImportSpec, lang string) []FindResult {
  191. matches := ix.importMap[imp]
  192. results := make([]FindResult, 0, len(matches))
  193. for _, m := range matches {
  194. if ix.mrslv(m.rule, "").Name() != lang {
  195. continue
  196. }
  197. results = append(results, FindResult{
  198. Label: m.label,
  199. Embeds: m.embeds,
  200. })
  201. }
  202. return results
  203. }
  204. // IsSelfImport returns true if the result's label matches the given label
  205. // or the result's rule transitively embeds the rule with the given label.
  206. // Self imports cause cyclic dependencies, so the caller may want to omit
  207. // the dependency or report an error.
  208. func (r FindResult) IsSelfImport(from label.Label) bool {
  209. if from.Equal(r.Label) {
  210. return true
  211. }
  212. for _, e := range r.Embeds {
  213. if from.Equal(e) {
  214. return true
  215. }
  216. }
  217. return false
  218. }