pkg.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. package depth
  2. import (
  3. "bytes"
  4. "go/build"
  5. "path"
  6. "sort"
  7. "strings"
  8. )
  9. // Pkg represents a Go source package, and its dependencies.
  10. type Pkg struct {
  11. Name string `json:"name"`
  12. SrcDir string `json:"-"`
  13. Internal bool `json:"internal"`
  14. Resolved bool `json:"resolved"`
  15. Test bool `json:"-"`
  16. Tree *Tree `json:"-"`
  17. Parent *Pkg `json:"-"`
  18. Deps []Pkg `json:"deps"`
  19. Raw *build.Package `json:"-"`
  20. }
  21. // Resolve recursively finds all dependencies for the Pkg and the packages it depends on.
  22. func (p *Pkg) Resolve(i Importer) {
  23. // Resolved is always true, regardless of if we skip the import,
  24. // it is only false if there is an error while importing.
  25. p.Resolved = true
  26. name := p.cleanName()
  27. if name == "" {
  28. return
  29. }
  30. // Stop resolving imports if we've reached max depth or found a duplicate.
  31. var importMode build.ImportMode
  32. if p.Tree.hasSeenImport(name) || p.Tree.isAtMaxDepth(p) {
  33. importMode = build.FindOnly
  34. }
  35. pkg, err := i.Import(name, p.SrcDir, importMode)
  36. if err != nil {
  37. // TODO: Check the error type?
  38. p.Resolved = false
  39. return
  40. }
  41. p.Raw = pkg
  42. // Update the name with the fully qualified import path.
  43. p.Name = pkg.ImportPath
  44. // If this is an internal dependency, we may need to skip it.
  45. if pkg.Goroot {
  46. p.Internal = true
  47. if !p.Tree.shouldResolveInternal(p) {
  48. return
  49. }
  50. }
  51. //first we set the regular dependencies, then we add the test dependencies
  52. //sharing the same set. This allows us to mark all test-only deps linearly
  53. unique := make(map[string]struct{})
  54. p.setDeps(i, pkg.Imports, pkg.Dir, unique, false)
  55. if p.Tree.ResolveTest {
  56. p.setDeps(i, append(pkg.TestImports, pkg.XTestImports...), pkg.Dir, unique, true)
  57. }
  58. }
  59. // setDeps takes a slice of import paths and the source directory they are relative to,
  60. // and creates the Deps of the Pkg. Each dependency is also further resolved prior to being added
  61. // to the Pkg.
  62. func (p *Pkg) setDeps(i Importer, imports []string, srcDir string, unique map[string]struct{}, isTest bool) {
  63. for _, imp := range imports {
  64. // Mostly for testing files where cyclic imports are allowed.
  65. if imp == p.Name {
  66. continue
  67. }
  68. // Skip duplicates.
  69. if _, ok := unique[imp]; ok {
  70. continue
  71. }
  72. unique[imp] = struct{}{}
  73. p.addDep(i, imp, srcDir, isTest)
  74. }
  75. sort.Sort(byInternalAndName(p.Deps))
  76. }
  77. // addDep creates a Pkg and it's dependencies from an imported package name.
  78. func (p *Pkg) addDep(i Importer, name string, srcDir string, isTest bool) {
  79. dep := Pkg{
  80. Name: name,
  81. SrcDir: srcDir,
  82. Tree: p.Tree,
  83. Parent: p,
  84. Test: isTest,
  85. }
  86. dep.Resolve(i)
  87. p.Deps = append(p.Deps, dep)
  88. }
  89. // isParent goes recursively up the chain of Pkgs to determine if the name provided is ever a
  90. // parent of the current Pkg.
  91. func (p *Pkg) isParent(name string) bool {
  92. if p.Parent == nil {
  93. return false
  94. }
  95. if p.Parent.Name == name {
  96. return true
  97. }
  98. return p.Parent.isParent(name)
  99. }
  100. // depth returns the depth of the Pkg within the Tree.
  101. func (p *Pkg) depth() int {
  102. if p.Parent == nil {
  103. return 0
  104. }
  105. return p.Parent.depth() + 1
  106. }
  107. // cleanName returns a cleaned version of the Pkg name used for resolving dependencies.
  108. //
  109. // If an empty string is returned, dependencies should not be resolved.
  110. func (p *Pkg) cleanName() string {
  111. name := p.Name
  112. // C 'package' cannot be resolved.
  113. if name == "C" {
  114. return ""
  115. }
  116. // Internal golang_org/* packages must be prefixed with vendor/
  117. //
  118. // Thanks to @davecheney for this:
  119. // https://github.com/davecheney/graphpkg/blob/master/main.go#L46
  120. if strings.HasPrefix(name, "golang_org") {
  121. name = path.Join("vendor", name)
  122. }
  123. return name
  124. }
  125. // String returns a string representation of the Pkg containing the Pkg name and status.
  126. func (p *Pkg) String() string {
  127. b := bytes.NewBufferString(p.Name)
  128. if !p.Resolved {
  129. b.Write([]byte(" (unresolved)"))
  130. }
  131. return b.String()
  132. }
  133. // byInternalAndName ensures a slice of Pkgs are sorted such that the internal stdlib
  134. // packages are always above external packages (ie. github.com/whatever).
  135. type byInternalAndName []Pkg
  136. func (b byInternalAndName) Len() int {
  137. return len(b)
  138. }
  139. func (b byInternalAndName) Swap(i, j int) {
  140. b[i], b[j] = b[j], b[i]
  141. }
  142. func (b byInternalAndName) Less(i, j int) bool {
  143. if b[i].Internal && !b[j].Internal {
  144. return true
  145. } else if !b[i].Internal && b[j].Internal {
  146. return false
  147. }
  148. return b[i].Name < b[j].Name
  149. }