pkgpathprefix.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. package testjson
  2. import (
  3. "bytes"
  4. "go/build"
  5. "io/ioutil"
  6. "os"
  7. "path/filepath"
  8. "runtime"
  9. "strconv"
  10. "strings"
  11. )
  12. func relativePackagePath(pkgpath string) string {
  13. if pkgpath == pkgPathPrefix {
  14. return "."
  15. }
  16. return strings.TrimPrefix(pkgpath, pkgPathPrefix+"/")
  17. }
  18. func getPkgPathPrefix() string {
  19. cwd, _ := os.Getwd()
  20. if isGoModuleEnabled() {
  21. prefix := getPkgPathPrefixFromGoModule(cwd)
  22. if prefix != "" {
  23. return prefix
  24. }
  25. }
  26. return getPkgPathPrefixGoPath(cwd)
  27. }
  28. func isGoModuleEnabled() bool {
  29. version := runtime.Version()
  30. if strings.HasPrefix(version, "go1.10") {
  31. return false
  32. }
  33. // Go modules may not be enabled if env var is unset, or set to auto, however
  34. // we can always fall back to using GOPATH as the prefix if a go.mod is not
  35. // found.
  36. return os.Getenv("GO111MODULE") != "off"
  37. }
  38. // TODO: might not work on windows
  39. func getPkgPathPrefixGoPath(cwd string) string {
  40. gopaths := strings.Split(build.Default.GOPATH, string(filepath.ListSeparator))
  41. for _, gopath := range gopaths {
  42. gosrcpath := gopath + "/src/"
  43. if strings.HasPrefix(cwd, gosrcpath) {
  44. return strings.TrimPrefix(cwd, gosrcpath)
  45. }
  46. }
  47. return ""
  48. }
  49. func getPkgPathPrefixFromGoModule(cwd string) string {
  50. filename := goModuleFilePath(cwd)
  51. if filename == "" {
  52. return ""
  53. }
  54. raw, err := ioutil.ReadFile(filename)
  55. if err != nil {
  56. // TODO: log.Warn
  57. return ""
  58. }
  59. return pkgPathFromGoModuleFile(raw)
  60. }
  61. var (
  62. slashSlash = []byte("//")
  63. moduleStr = []byte("module")
  64. )
  65. // Copy of ModulePath from golang.org/src/cmd/go/internal/modfile/read.go
  66. func pkgPathFromGoModuleFile(mod []byte) string {
  67. for len(mod) > 0 {
  68. line := mod
  69. mod = nil
  70. if i := bytes.IndexByte(line, '\n'); i >= 0 {
  71. line, mod = line[:i], line[i+1:]
  72. }
  73. if i := bytes.Index(line, slashSlash); i >= 0 {
  74. line = line[:i]
  75. }
  76. line = bytes.TrimSpace(line)
  77. if !bytes.HasPrefix(line, moduleStr) {
  78. continue
  79. }
  80. line = line[len(moduleStr):]
  81. n := len(line)
  82. line = bytes.TrimSpace(line)
  83. if len(line) == n || len(line) == 0 {
  84. continue
  85. }
  86. if line[0] == '"' || line[0] == '`' {
  87. p, err := strconv.Unquote(string(line))
  88. if err != nil {
  89. return "" // malformed quoted string or multi-line module path
  90. }
  91. return p
  92. }
  93. return string(line)
  94. }
  95. return "" // missing module path
  96. }
  97. // A rough re-implementation of FindModuleRoot from
  98. // golang.org/src/cmd/go/internal/modload/init.go
  99. func goModuleFilePath(cwd string) string {
  100. dir := filepath.Clean(cwd)
  101. for {
  102. path := filepath.Join(dir, "go.mod")
  103. if _, err := os.Stat(path); err == nil {
  104. return path
  105. }
  106. parent := filepath.Dir(dir)
  107. if parent == dir {
  108. return ""
  109. }
  110. dir = parent
  111. }
  112. }
  113. var pkgPathPrefix = getPkgPathPrefix()