mod.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. package imports
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "io/ioutil"
  6. "log"
  7. "os"
  8. "path"
  9. "path/filepath"
  10. "regexp"
  11. "sort"
  12. "strconv"
  13. "strings"
  14. "sync"
  15. "time"
  16. "golang.org/x/tools/internal/gopathwalk"
  17. "golang.org/x/tools/internal/module"
  18. )
  19. // moduleResolver implements resolver for modules using the go command as little
  20. // as feasible.
  21. type moduleResolver struct {
  22. env *fixEnv
  23. main *moduleJSON
  24. modsByModPath []*moduleJSON // All modules, ordered by # of path components in module Path...
  25. modsByDir []*moduleJSON // ...or Dir.
  26. }
  27. type moduleJSON struct {
  28. Path string // module path
  29. Version string // module version
  30. Versions []string // available module versions (with -versions)
  31. Replace *moduleJSON // replaced by this module
  32. Time *time.Time // time version was created
  33. Update *moduleJSON // available update, if any (with -u)
  34. Main bool // is this the main module?
  35. Indirect bool // is this module only an indirect dependency of main module?
  36. Dir string // directory holding files for this module, if any
  37. GoMod string // path to go.mod file for this module, if any
  38. Error *moduleErrorJSON // error loading module
  39. }
  40. type moduleErrorJSON struct {
  41. Err string // the error itself
  42. }
  43. func (r *moduleResolver) init() error {
  44. if r.main != nil {
  45. return nil
  46. }
  47. stdout, err := r.env.invokeGo("list", "-m", "-json", "...")
  48. if err != nil {
  49. return err
  50. }
  51. for dec := json.NewDecoder(stdout); dec.More(); {
  52. mod := &moduleJSON{}
  53. if err := dec.Decode(mod); err != nil {
  54. return err
  55. }
  56. if mod.Dir == "" {
  57. if Debug {
  58. log.Printf("module %v has not been downloaded and will be ignored", mod.Path)
  59. }
  60. // Can't do anything with a module that's not downloaded.
  61. continue
  62. }
  63. r.modsByModPath = append(r.modsByModPath, mod)
  64. r.modsByDir = append(r.modsByDir, mod)
  65. if mod.Main {
  66. r.main = mod
  67. }
  68. }
  69. sort.Slice(r.modsByModPath, func(i, j int) bool {
  70. count := func(x int) int {
  71. return strings.Count(r.modsByModPath[x].Path, "/")
  72. }
  73. return count(j) < count(i) // descending order
  74. })
  75. sort.Slice(r.modsByDir, func(i, j int) bool {
  76. count := func(x int) int {
  77. return strings.Count(r.modsByDir[x].Dir, "/")
  78. }
  79. return count(j) < count(i) // descending order
  80. })
  81. return nil
  82. }
  83. // findPackage returns the module and directory that contains the package at
  84. // the given import path, or returns nil, "" if no module is in scope.
  85. func (r *moduleResolver) findPackage(importPath string) (*moduleJSON, string) {
  86. for _, m := range r.modsByModPath {
  87. if !strings.HasPrefix(importPath, m.Path) {
  88. continue
  89. }
  90. pathInModule := importPath[len(m.Path):]
  91. pkgDir := filepath.Join(m.Dir, pathInModule)
  92. if dirIsNestedModule(pkgDir, m) {
  93. continue
  94. }
  95. pkgFiles, err := ioutil.ReadDir(pkgDir)
  96. if err != nil {
  97. continue
  98. }
  99. // A module only contains a package if it has buildable go
  100. // files in that directory. If not, it could be provided by an
  101. // outer module. See #29736.
  102. for _, fi := range pkgFiles {
  103. if ok, _ := r.env.buildContext().MatchFile(pkgDir, fi.Name()); ok {
  104. return m, pkgDir
  105. }
  106. }
  107. }
  108. return nil, ""
  109. }
  110. // findModuleByDir returns the module that contains dir, or nil if no such
  111. // module is in scope.
  112. func (r *moduleResolver) findModuleByDir(dir string) *moduleJSON {
  113. // This is quite tricky and may not be correct. dir could be:
  114. // - a package in the main module.
  115. // - a replace target underneath the main module's directory.
  116. // - a nested module in the above.
  117. // - a replace target somewhere totally random.
  118. // - a nested module in the above.
  119. // - in the mod cache.
  120. // - in /vendor/ in -mod=vendor mode.
  121. // - nested module? Dunno.
  122. // Rumor has it that replace targets cannot contain other replace targets.
  123. for _, m := range r.modsByDir {
  124. if !strings.HasPrefix(dir, m.Dir) {
  125. continue
  126. }
  127. if dirIsNestedModule(dir, m) {
  128. continue
  129. }
  130. return m
  131. }
  132. return nil
  133. }
  134. // dirIsNestedModule reports if dir is contained in a nested module underneath
  135. // mod, not actually in mod.
  136. func dirIsNestedModule(dir string, mod *moduleJSON) bool {
  137. if !strings.HasPrefix(dir, mod.Dir) {
  138. return false
  139. }
  140. mf := findModFile(dir)
  141. if mf == "" {
  142. return false
  143. }
  144. return filepath.Dir(mf) != mod.Dir
  145. }
  146. func findModFile(dir string) string {
  147. for {
  148. f := filepath.Join(dir, "go.mod")
  149. info, err := os.Stat(f)
  150. if err == nil && !info.IsDir() {
  151. return f
  152. }
  153. d := filepath.Dir(dir)
  154. if len(d) >= len(dir) {
  155. return "" // reached top of file system, no go.mod
  156. }
  157. dir = d
  158. }
  159. }
  160. func (r *moduleResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
  161. if err := r.init(); err != nil {
  162. return nil, err
  163. }
  164. names := map[string]string{}
  165. for _, path := range importPaths {
  166. _, packageDir := r.findPackage(path)
  167. if packageDir == "" {
  168. continue
  169. }
  170. name, err := packageDirToName(packageDir)
  171. if err != nil {
  172. continue
  173. }
  174. names[path] = name
  175. }
  176. return names, nil
  177. }
  178. func (r *moduleResolver) scan(_ references) ([]*pkg, error) {
  179. if err := r.init(); err != nil {
  180. return nil, err
  181. }
  182. // Walk GOROOT, GOPATH/pkg/mod, and the main module.
  183. roots := []gopathwalk.Root{
  184. {filepath.Join(r.env.GOROOT, "/src"), gopathwalk.RootGOROOT},
  185. {r.main.Dir, gopathwalk.RootCurrentModule},
  186. }
  187. for _, p := range filepath.SplitList(r.env.GOPATH) {
  188. roots = append(roots, gopathwalk.Root{filepath.Join(p, "/pkg/mod"), gopathwalk.RootModuleCache})
  189. }
  190. // Walk replace targets, just in case they're not in any of the above.
  191. for _, mod := range r.modsByModPath {
  192. if mod.Replace != nil {
  193. roots = append(roots, gopathwalk.Root{mod.Dir, gopathwalk.RootOther})
  194. }
  195. }
  196. var result []*pkg
  197. dupCheck := make(map[string]bool)
  198. var mu sync.Mutex
  199. gopathwalk.Walk(roots, func(root gopathwalk.Root, dir string) {
  200. mu.Lock()
  201. defer mu.Unlock()
  202. if _, dup := dupCheck[dir]; dup {
  203. return
  204. }
  205. dupCheck[dir] = true
  206. subdir := ""
  207. if dir != root.Path {
  208. subdir = dir[len(root.Path)+len("/"):]
  209. }
  210. importPath := filepath.ToSlash(subdir)
  211. if strings.HasPrefix(importPath, "vendor/") {
  212. // Ignore vendor dirs. If -mod=vendor is on, then things
  213. // should mostly just work, but when it's not vendor/
  214. // is a mess. There's no easy way to tell if it's on.
  215. // We can still find things in the mod cache and
  216. // map them into /vendor when -mod=vendor is on.
  217. return
  218. }
  219. switch root.Type {
  220. case gopathwalk.RootCurrentModule:
  221. importPath = path.Join(r.main.Path, filepath.ToSlash(subdir))
  222. case gopathwalk.RootModuleCache:
  223. matches := modCacheRegexp.FindStringSubmatch(subdir)
  224. modPath, err := module.DecodePath(filepath.ToSlash(matches[1]))
  225. if err != nil {
  226. if Debug {
  227. log.Printf("decoding module cache path %q: %v", subdir, err)
  228. }
  229. return
  230. }
  231. importPath = path.Join(modPath, filepath.ToSlash(matches[3]))
  232. case gopathwalk.RootGOROOT:
  233. importPath = subdir
  234. }
  235. // Check if the directory is underneath a module that's in scope.
  236. if mod := r.findModuleByDir(dir); mod != nil {
  237. // It is. If dir is the target of a replace directive,
  238. // our guessed import path is wrong. Use the real one.
  239. if mod.Dir == dir {
  240. importPath = mod.Path
  241. } else {
  242. dirInMod := dir[len(mod.Dir)+len("/"):]
  243. importPath = path.Join(mod.Path, filepath.ToSlash(dirInMod))
  244. }
  245. } else {
  246. // The package is in an unknown module. Check that it's
  247. // not obviously impossible to import.
  248. var modFile string
  249. switch root.Type {
  250. case gopathwalk.RootModuleCache:
  251. matches := modCacheRegexp.FindStringSubmatch(subdir)
  252. modFile = filepath.Join(matches[1], "@", matches[2], "go.mod")
  253. default:
  254. modFile = findModFile(dir)
  255. }
  256. modBytes, err := ioutil.ReadFile(modFile)
  257. if err == nil && !strings.HasPrefix(importPath, modulePath(modBytes)) {
  258. // The module's declared path does not match
  259. // its expected path. It probably needs a
  260. // replace directive we don't have.
  261. return
  262. }
  263. }
  264. // We may have discovered a package that has a different version
  265. // in scope already. Canonicalize to that one if possible.
  266. if _, canonicalDir := r.findPackage(importPath); canonicalDir != "" {
  267. dir = canonicalDir
  268. }
  269. result = append(result, &pkg{
  270. importPathShort: VendorlessPath(importPath),
  271. dir: dir,
  272. })
  273. }, gopathwalk.Options{Debug: Debug, ModulesEnabled: true})
  274. return result, nil
  275. }
  276. // modCacheRegexp splits a path in a module cache into module, module version, and package.
  277. var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`)
  278. var (
  279. slashSlash = []byte("//")
  280. moduleStr = []byte("module")
  281. )
  282. // modulePath returns the module path from the gomod file text.
  283. // If it cannot find a module path, it returns an empty string.
  284. // It is tolerant of unrelated problems in the go.mod file.
  285. //
  286. // Copied from cmd/go/internal/modfile.
  287. func modulePath(mod []byte) string {
  288. for len(mod) > 0 {
  289. line := mod
  290. mod = nil
  291. if i := bytes.IndexByte(line, '\n'); i >= 0 {
  292. line, mod = line[:i], line[i+1:]
  293. }
  294. if i := bytes.Index(line, slashSlash); i >= 0 {
  295. line = line[:i]
  296. }
  297. line = bytes.TrimSpace(line)
  298. if !bytes.HasPrefix(line, moduleStr) {
  299. continue
  300. }
  301. line = line[len(moduleStr):]
  302. n := len(line)
  303. line = bytes.TrimSpace(line)
  304. if len(line) == n || len(line) == 0 {
  305. continue
  306. }
  307. if line[0] == '"' || line[0] == '`' {
  308. p, err := strconv.Unquote(string(line))
  309. if err != nil {
  310. return "" // malformed quoted string or multiline module path
  311. }
  312. return p
  313. }
  314. return string(line)
  315. }
  316. return "" // missing module path
  317. }