resolve.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  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 golang
  13. import (
  14. "errors"
  15. "fmt"
  16. "go/build"
  17. "log"
  18. "path"
  19. "strings"
  20. "github.com/bazelbuild/bazel-gazelle/internal/config"
  21. "github.com/bazelbuild/bazel-gazelle/internal/label"
  22. "github.com/bazelbuild/bazel-gazelle/internal/language/proto"
  23. "github.com/bazelbuild/bazel-gazelle/internal/pathtools"
  24. "github.com/bazelbuild/bazel-gazelle/internal/repos"
  25. "github.com/bazelbuild/bazel-gazelle/internal/resolve"
  26. "github.com/bazelbuild/bazel-gazelle/internal/rule"
  27. )
  28. func (_ *goLang) Imports(_ *config.Config, r *rule.Rule, f *rule.File) []resolve.ImportSpec {
  29. if !isGoLibrary(r.Kind()) {
  30. return nil
  31. }
  32. if importPath := r.AttrString("importpath"); importPath == "" {
  33. return []resolve.ImportSpec{}
  34. } else {
  35. return []resolve.ImportSpec{{goName, importPath}}
  36. }
  37. }
  38. func (_ *goLang) Embeds(r *rule.Rule, from label.Label) []label.Label {
  39. embedStrings := r.AttrStrings("embed")
  40. if isGoProtoLibrary(r.Kind()) {
  41. embedStrings = append(embedStrings, r.AttrString("proto"))
  42. }
  43. embedLabels := make([]label.Label, 0, len(embedStrings))
  44. for _, s := range embedStrings {
  45. l, err := label.Parse(s)
  46. if err != nil {
  47. continue
  48. }
  49. l = l.Abs(from.Repo, from.Pkg)
  50. embedLabels = append(embedLabels, l)
  51. }
  52. return embedLabels
  53. }
  54. func (gl *goLang) Resolve(c *config.Config, ix *resolve.RuleIndex, rc *repos.RemoteCache, r *rule.Rule, from label.Label) {
  55. importsRaw := r.PrivateAttr(config.GazelleImportsKey)
  56. if importsRaw == nil {
  57. // may not be set in tests.
  58. return
  59. }
  60. imports := importsRaw.(rule.PlatformStrings)
  61. r.DelAttr("deps")
  62. resolve := resolveGo
  63. if r.Kind() == "go_proto_library" {
  64. resolve = resolveProto
  65. }
  66. deps, errs := imports.Map(func(imp string) (string, error) {
  67. l, err := resolve(c, ix, rc, r, imp, from)
  68. if err == skipImportError {
  69. return "", nil
  70. } else if err != nil {
  71. return "", err
  72. }
  73. for _, embed := range gl.Embeds(r, from) {
  74. if embed.Equal(l) {
  75. return "", nil
  76. }
  77. }
  78. l = l.Rel(from.Repo, from.Pkg)
  79. return l.String(), nil
  80. })
  81. for _, err := range errs {
  82. log.Print(err)
  83. }
  84. if !deps.IsEmpty() {
  85. if r.Kind() == "go_proto_library" {
  86. // protos may import the same library multiple times by different names,
  87. // so we need to de-duplicate them. Protos are not platform-specific,
  88. // so it's safe to just flatten them.
  89. r.SetAttr("deps", deps.Flat())
  90. } else {
  91. r.SetAttr("deps", deps)
  92. }
  93. }
  94. }
  95. var (
  96. skipImportError = errors.New("std or self import")
  97. notFoundError = errors.New("rule not found")
  98. )
  99. func resolveGo(c *config.Config, ix *resolve.RuleIndex, rc *repos.RemoteCache, r *rule.Rule, imp string, from label.Label) (label.Label, error) {
  100. gc := getGoConfig(c)
  101. pc := proto.GetProtoConfig(c)
  102. if build.IsLocalImport(imp) {
  103. cleanRel := path.Clean(path.Join(from.Pkg, imp))
  104. if build.IsLocalImport(cleanRel) {
  105. return label.NoLabel, fmt.Errorf("relative import path %q from %q points outside of repository", imp, from.Pkg)
  106. }
  107. imp = path.Join(gc.prefix, cleanRel)
  108. }
  109. if isStandard(imp) {
  110. return label.NoLabel, skipImportError
  111. }
  112. if l, ok := resolve.FindRuleWithOverride(c, resolve.ImportSpec{Lang: "go", Imp: imp}, "go"); ok {
  113. return l, nil
  114. }
  115. if pc.Mode.ShouldUseKnownImports() {
  116. // These are commonly used libraries that depend on Well Known Types.
  117. // They depend on the generated versions of these protos to avoid conflicts.
  118. // However, since protoc-gen-go depends on these libraries, we generate
  119. // its rules in disable_global mode (to avoid cyclic dependency), so the
  120. // "go_default_library" versions of these libraries depend on the
  121. // pre-generated versions of the proto libraries.
  122. switch imp {
  123. case "github.com/golang/protobuf/jsonpb":
  124. return label.New("com_github_golang_protobuf", "jsonpb", "go_default_library_gen"), nil
  125. case "github.com/golang/protobuf/descriptor":
  126. return label.New("com_github_golang_protobuf", "descriptor", "go_default_library_gen"), nil
  127. case "github.com/golang/protobuf/ptypes":
  128. return label.New("com_github_golang_protobuf", "ptypes", "go_default_library_gen"), nil
  129. case "google.golang.org/grpc":
  130. return label.New("org_golang_google_grpc", "", "go_default_library"), nil
  131. }
  132. if l, ok := knownGoProtoImports[imp]; ok {
  133. return l, nil
  134. }
  135. }
  136. if l, err := resolveWithIndexGo(ix, imp, from); err == nil || err == skipImportError {
  137. return l, err
  138. } else if err != notFoundError {
  139. return label.NoLabel, err
  140. }
  141. if pathtools.HasPrefix(imp, gc.prefix) {
  142. pkg := path.Join(gc.prefixRel, pathtools.TrimPrefix(imp, gc.prefix))
  143. return label.New("", pkg, config.DefaultLibName), nil
  144. }
  145. if gc.depMode == externalMode {
  146. return resolveExternal(rc, imp)
  147. } else {
  148. return resolveVendored(rc, imp)
  149. }
  150. }
  151. // isStandard returns whether a package is in the standard library.
  152. func isStandard(imp string) bool {
  153. return stdPackages[imp]
  154. }
  155. func resolveWithIndexGo(ix *resolve.RuleIndex, imp string, from label.Label) (label.Label, error) {
  156. matches := ix.FindRulesByImport(resolve.ImportSpec{Lang: "go", Imp: imp}, "go")
  157. var bestMatch resolve.FindResult
  158. var bestMatchIsVendored bool
  159. var bestMatchVendorRoot string
  160. var matchError error
  161. for _, m := range matches {
  162. // Apply vendoring logic for Go libraries. A library in a vendor directory
  163. // is only visible in the parent tree. Vendored libraries supercede
  164. // non-vendored libraries, and libraries closer to from.Pkg supercede
  165. // those further up the tree.
  166. isVendored := false
  167. vendorRoot := ""
  168. parts := strings.Split(m.Label.Pkg, "/")
  169. for i := len(parts) - 1; i >= 0; i-- {
  170. if parts[i] == "vendor" {
  171. isVendored = true
  172. vendorRoot = strings.Join(parts[:i], "/")
  173. break
  174. }
  175. }
  176. if isVendored {
  177. }
  178. if isVendored && !label.New(m.Label.Repo, vendorRoot, "").Contains(from) {
  179. // vendor directory not visible
  180. continue
  181. }
  182. if bestMatch.Label.Equal(label.NoLabel) || isVendored && (!bestMatchIsVendored || len(vendorRoot) > len(bestMatchVendorRoot)) {
  183. // Current match is better
  184. bestMatch = m
  185. bestMatchIsVendored = isVendored
  186. bestMatchVendorRoot = vendorRoot
  187. matchError = nil
  188. } else if (!isVendored && bestMatchIsVendored) || (isVendored && len(vendorRoot) < len(bestMatchVendorRoot)) {
  189. // Current match is worse
  190. } else {
  191. // Match is ambiguous
  192. matchError = fmt.Errorf("multiple rules (%s and %s) may be imported with %q from %s", bestMatch.Label, m.Label, imp, from)
  193. }
  194. }
  195. if matchError != nil {
  196. return label.NoLabel, matchError
  197. }
  198. if bestMatch.Label.Equal(label.NoLabel) {
  199. return label.NoLabel, notFoundError
  200. }
  201. if bestMatch.IsSelfImport(from) {
  202. return label.NoLabel, skipImportError
  203. }
  204. return bestMatch.Label, nil
  205. }
  206. func resolveExternal(rc *repos.RemoteCache, imp string) (label.Label, error) {
  207. prefix, repo, err := rc.Root(imp)
  208. if err != nil {
  209. return label.NoLabel, err
  210. }
  211. var pkg string
  212. if imp != prefix {
  213. pkg = pathtools.TrimPrefix(imp, prefix)
  214. }
  215. return label.New(repo, pkg, config.DefaultLibName), nil
  216. }
  217. func resolveVendored(rc *repos.RemoteCache, imp string) (label.Label, error) {
  218. return label.New("", path.Join("vendor", imp), config.DefaultLibName), nil
  219. }
  220. func resolveProto(c *config.Config, ix *resolve.RuleIndex, rc *repos.RemoteCache, r *rule.Rule, imp string, from label.Label) (label.Label, error) {
  221. pc := proto.GetProtoConfig(c)
  222. if wellKnownProtos[imp] {
  223. return label.NoLabel, skipImportError
  224. }
  225. if l, ok := resolve.FindRuleWithOverride(c, resolve.ImportSpec{Lang: "proto", Imp: imp}, "go"); ok {
  226. return l, nil
  227. }
  228. if l, ok := knownProtoImports[imp]; ok && pc.Mode.ShouldUseKnownImports() {
  229. if l.Equal(from) {
  230. return label.NoLabel, skipImportError
  231. } else {
  232. return l, nil
  233. }
  234. }
  235. if l, err := resolveWithIndexProto(ix, imp, from); err == nil || err == skipImportError {
  236. return l, err
  237. } else if err != notFoundError {
  238. return label.NoLabel, err
  239. }
  240. // As a fallback, guess the label based on the proto file name. We assume
  241. // all proto files in a directory belong to the same package, and the
  242. // package name matches the directory base name. We also assume that protos
  243. // in the vendor directory must refer to something else in vendor.
  244. rel := path.Dir(imp)
  245. if rel == "." {
  246. rel = ""
  247. }
  248. if from.Pkg == "vendor" || strings.HasPrefix(from.Pkg, "vendor/") {
  249. rel = path.Join("vendor", rel)
  250. }
  251. return label.New("", rel, config.DefaultLibName), nil
  252. }
  253. // wellKnownProtos is the set of proto sets for which we don't need to add
  254. // an explicit dependency in go_proto_library.
  255. // TODO(jayconrod): generate from
  256. // @io_bazel_rules_go//proto/wkt:WELL_KNOWN_TYPE_PACKAGES
  257. var wellKnownProtos = map[string]bool{
  258. "google/protobuf/any.proto": true,
  259. "google/protobuf/api.proto": true,
  260. "google/protobuf/compiler_plugin.proto": true,
  261. "google/protobuf/descriptor.proto": true,
  262. "google/protobuf/duration.proto": true,
  263. "google/protobuf/empty.proto": true,
  264. "google/protobuf/field_mask.proto": true,
  265. "google/protobuf/source_context.proto": true,
  266. "google/protobuf/struct.proto": true,
  267. "google/protobuf/timestamp.proto": true,
  268. "google/protobuf/type.proto": true,
  269. "google/protobuf/wrappers.proto": true,
  270. }
  271. func resolveWithIndexProto(ix *resolve.RuleIndex, imp string, from label.Label) (label.Label, error) {
  272. matches := ix.FindRulesByImport(resolve.ImportSpec{Lang: "proto", Imp: imp}, "go")
  273. if len(matches) == 0 {
  274. return label.NoLabel, notFoundError
  275. }
  276. if len(matches) > 1 {
  277. return label.NoLabel, fmt.Errorf("multiple rules (%s and %s) may be imported with %q from %s", matches[0].Label, matches[1].Label, imp, from)
  278. }
  279. if matches[0].IsSelfImport(from) {
  280. return label.NoLabel, skipImportError
  281. }
  282. return matches[0].Label, nil
  283. }
  284. func isGoLibrary(kind string) bool {
  285. return kind == "go_library" || isGoProtoLibrary(kind)
  286. }
  287. func isGoProtoLibrary(kind string) bool {
  288. return kind == "go_proto_library" || kind == "go_grpc_library"
  289. }