common.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // Package common contains common utilities that are shared among other packages.
  2. // See each sub-package for detail.
  3. package common
  4. import (
  5. "fmt"
  6. "go/build"
  7. "io/ioutil"
  8. "os"
  9. "path/filepath"
  10. "strings"
  11. "github.com/xtls/xray-core/common/errors"
  12. )
  13. //go:generate go run github.com/xtls/xray-core/common/errors/errorgen
  14. var (
  15. // ErrNoClue is for the situation that existing information is not enough to make a decision. For example, Router may return this error when there is no suitable route.
  16. ErrNoClue = errors.New("not enough information for making a decision")
  17. )
  18. // Must panics if err is not nil.
  19. func Must(err error) {
  20. if err != nil {
  21. panic(err)
  22. }
  23. }
  24. // Must2 panics if the second parameter is not nil, otherwise returns the first parameter.
  25. func Must2(v interface{}, err error) interface{} {
  26. Must(err)
  27. return v
  28. }
  29. // Error2 returns the err from the 2nd parameter.
  30. func Error2(v interface{}, err error) error {
  31. return err
  32. }
  33. // envFile returns the name of the Go environment configuration file.
  34. // Copy from https://github.com/golang/go/blob/c4f2a9788a7be04daf931ac54382fbe2cb754938/src/cmd/go/internal/cfg/cfg.go#L150-L166
  35. func envFile() (string, error) {
  36. if file := os.Getenv("GOENV"); file != "" {
  37. if file == "off" {
  38. return "", fmt.Errorf("GOENV=off")
  39. }
  40. return file, nil
  41. }
  42. dir, err := os.UserConfigDir()
  43. if err != nil {
  44. return "", err
  45. }
  46. if dir == "" {
  47. return "", fmt.Errorf("missing user-config dir")
  48. }
  49. return filepath.Join(dir, "go", "env"), nil
  50. }
  51. // GetRuntimeEnv returns the value of runtime environment variable,
  52. // that is set by running following command: `go env -w key=value`.
  53. func GetRuntimeEnv(key string) (string, error) {
  54. file, err := envFile()
  55. if err != nil {
  56. return "", err
  57. }
  58. if file == "" {
  59. return "", fmt.Errorf("missing runtime env file")
  60. }
  61. var data []byte
  62. var runtimeEnv string
  63. data, readErr := ioutil.ReadFile(file)
  64. if readErr != nil {
  65. return "", readErr
  66. }
  67. envStrings := strings.Split(string(data), "\n")
  68. for _, envItem := range envStrings {
  69. envItem = strings.TrimSuffix(envItem, "\r")
  70. envKeyValue := strings.Split(envItem, "=")
  71. if strings.EqualFold(strings.TrimSpace(envKeyValue[0]), key) {
  72. runtimeEnv = strings.TrimSpace(envKeyValue[1])
  73. }
  74. }
  75. return runtimeEnv, nil
  76. }
  77. // GetGOBIN returns GOBIN environment variable as a string. It will NOT be empty.
  78. func GetGOBIN() string {
  79. // The one set by user explicitly by `export GOBIN=/path` or `env GOBIN=/path command`
  80. GOBIN := os.Getenv("GOBIN")
  81. if GOBIN == "" {
  82. var err error
  83. // The one set by user by running `go env -w GOBIN=/path`
  84. GOBIN, err = GetRuntimeEnv("GOBIN")
  85. if err != nil {
  86. // The default one that Golang uses
  87. return filepath.Join(build.Default.GOPATH, "bin")
  88. }
  89. if GOBIN == "" {
  90. return filepath.Join(build.Default.GOPATH, "bin")
  91. }
  92. return GOBIN
  93. }
  94. return GOBIN
  95. }
  96. // GetGOPATH returns GOPATH environment variable as a string. It will NOT be empty.
  97. func GetGOPATH() string {
  98. // The one set by user explicitly by `export GOPATH=/path` or `env GOPATH=/path command`
  99. GOPATH := os.Getenv("GOPATH")
  100. if GOPATH == "" {
  101. var err error
  102. // The one set by user by running `go env -w GOPATH=/path`
  103. GOPATH, err = GetRuntimeEnv("GOPATH")
  104. if err != nil {
  105. // The default one that Golang uses
  106. return build.Default.GOPATH
  107. }
  108. if GOPATH == "" {
  109. return build.Default.GOPATH
  110. }
  111. return GOPATH
  112. }
  113. return GOPATH
  114. }
  115. // GetModuleName returns the value of module in `go.mod` file.
  116. func GetModuleName(pathToProjectRoot string) (string, error) {
  117. var moduleName string
  118. loopPath := pathToProjectRoot
  119. for {
  120. if idx := strings.LastIndex(loopPath, string(filepath.Separator)); idx >= 0 {
  121. gomodPath := filepath.Join(loopPath, "go.mod")
  122. gomodBytes, err := ioutil.ReadFile(gomodPath)
  123. if err != nil {
  124. loopPath = loopPath[:idx]
  125. continue
  126. }
  127. gomodContent := string(gomodBytes)
  128. moduleIdx := strings.Index(gomodContent, "module ")
  129. newLineIdx := strings.Index(gomodContent, "\n")
  130. if moduleIdx >= 0 {
  131. if newLineIdx >= 0 {
  132. moduleName = strings.TrimSpace(gomodContent[moduleIdx+6 : newLineIdx])
  133. moduleName = strings.TrimSuffix(moduleName, "\r")
  134. } else {
  135. moduleName = strings.TrimSpace(gomodContent[moduleIdx+6:])
  136. }
  137. return moduleName, nil
  138. }
  139. return "", fmt.Errorf("can not get module path in `%s`", gomodPath)
  140. }
  141. break
  142. }
  143. return moduleName, fmt.Errorf("no `go.mod` file in every parent directory of `%s`", pathToProjectRoot)
  144. }