project.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. package ctx
  2. import (
  3. "errors"
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. "regexp"
  10. "runtime"
  11. "strings"
  12. "github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
  13. "github.com/tal-tech/go-zero/tools/goctl/util"
  14. "github.com/tal-tech/go-zero/tools/goctl/util/console"
  15. )
  16. var (
  17. errProtobufNotFound = errors.New("github.com/golang/protobuf is not found,please ensure you has already [go get github.com/golang/protobuf]")
  18. )
  19. const (
  20. constGo = "go"
  21. constProtoC = "protoc"
  22. constGoModOn = "go env GO111MODULE"
  23. constGoMod = "go env GOMOD"
  24. constGoModCache = "go env GOMODCACHE"
  25. constGoPath = "go env GOPATH"
  26. constProtoCGenGo = "protoc-gen-go"
  27. )
  28. type (
  29. Project struct {
  30. Path string
  31. Name string
  32. GoPath string
  33. Protobuf Protobuf
  34. GoMod GoMod
  35. }
  36. GoMod struct {
  37. ModOn bool
  38. GoModCache string
  39. GoMod string
  40. Module string
  41. }
  42. Protobuf struct {
  43. Path string
  44. }
  45. )
  46. func prepare(log console.Console) (*Project, error) {
  47. log.Info("check go env ...")
  48. _, err := exec.LookPath(constGo)
  49. if err != nil {
  50. return nil, err
  51. }
  52. _, err = exec.LookPath(constProtoC)
  53. if err != nil {
  54. return nil, err
  55. }
  56. var (
  57. goModOn bool
  58. goMod, goModCache, module string
  59. goPath string
  60. name, path string
  61. protobufModule string
  62. )
  63. ret, err := execx.Run(constGoModOn)
  64. if err != nil {
  65. return nil, err
  66. }
  67. goModOn = strings.TrimSpace(ret) == "on"
  68. ret, err = execx.Run(constGoMod)
  69. if err != nil {
  70. return nil, err
  71. }
  72. goMod = strings.TrimSpace(ret)
  73. ret, err = execx.Run(constGoModCache)
  74. if err != nil {
  75. return nil, err
  76. }
  77. goModCache = strings.TrimSpace(ret)
  78. ret, err = execx.Run(constGoPath)
  79. if err != nil {
  80. return nil, err
  81. }
  82. goPath = strings.TrimSpace(ret)
  83. src := filepath.Join(goPath, "src")
  84. if len(goMod) > 0 {
  85. if goModCache == "" {
  86. goModCache = filepath.Join(goPath, "pkg", "mod")
  87. }
  88. path = filepath.Dir(goMod)
  89. name = filepath.Base(path)
  90. data, err := ioutil.ReadFile(goMod)
  91. if err != nil {
  92. return nil, err
  93. }
  94. module, err = matchModule(data)
  95. if err != nil {
  96. return nil, err
  97. }
  98. protobufModule, err = matchProtoBuf(data)
  99. if err != nil {
  100. return nil, err
  101. }
  102. } else {
  103. if goModCache == "" {
  104. goModCache = src
  105. }
  106. pwd, err := os.Getwd()
  107. if err != nil {
  108. return nil, err
  109. }
  110. if !strings.HasPrefix(pwd, src) {
  111. return nil, fmt.Errorf("%s: project is not in go mod and go path", pwd)
  112. }
  113. r := strings.TrimPrefix(pwd, src+string(filepath.Separator))
  114. name = filepath.Dir(r)
  115. if name == "." {
  116. name = r
  117. }
  118. path = filepath.Join(src, name)
  119. module = name
  120. }
  121. protobuf := filepath.Join(goModCache, protobufModule)
  122. if !util.FileExists(protobuf) {
  123. return nil, fmt.Errorf("expected protobuf module in path: %s,please ensure you has already [go get github.com/golang/protobuf]", protobuf)
  124. }
  125. var protoCGenGoFilename string
  126. os := runtime.GOOS
  127. switch os {
  128. case "darwin":
  129. protoCGenGoFilename = filepath.Join(goPath, "bin", "protoc-gen-go")
  130. case "windows":
  131. protoCGenGoFilename = filepath.Join(goPath, "bin", "protoc-gen-go.exe")
  132. default:
  133. return nil, fmt.Errorf("unexpeted os: %s", os)
  134. }
  135. if !util.FileExists(protoCGenGoFilename) {
  136. sh := "go install " + filepath.Join(protobuf, constProtoCGenGo)
  137. log.Warning(sh)
  138. stdout, err := execx.Run(sh)
  139. if err != nil {
  140. return nil, err
  141. }
  142. log.Info(stdout)
  143. }
  144. if !util.FileExists(protoCGenGoFilename) {
  145. return nil, fmt.Errorf("protoc-gen-go is not found")
  146. }
  147. return &Project{
  148. Name: name,
  149. Path: path,
  150. GoPath: goPath,
  151. Protobuf: Protobuf{
  152. Path: protobuf,
  153. },
  154. GoMod: GoMod{
  155. ModOn: goModOn,
  156. GoModCache: goModCache,
  157. GoMod: goMod,
  158. Module: module,
  159. },
  160. }, nil
  161. }
  162. // github.com/golang/protobuf@{version}
  163. func matchProtoBuf(data []byte) (string, error) {
  164. text := string(data)
  165. re := regexp.MustCompile(`(?m)(github.com/golang/protobuf)\s+(v[0-9.]+)`)
  166. matches := re.FindAllStringSubmatch(text, -1)
  167. if len(matches) == 0 {
  168. return "", errProtobufNotFound
  169. }
  170. groups := matches[0]
  171. if len(groups) < 3 {
  172. return "", errProtobufNotFound
  173. }
  174. return fmt.Sprintf("%s@%s", groups[1], groups[2]), nil
  175. }
  176. func matchModule(data []byte) (string, error) {
  177. text := string(data)
  178. re := regexp.MustCompile(`(?m)^\s*module\s+[a-z0-9/\-.]+$`)
  179. matches := re.FindAllString(text, -1)
  180. if len(matches) == 1 {
  181. target := matches[0]
  182. index := strings.Index(target, "module")
  183. return strings.TrimSpace(target[index+6:]), nil
  184. }
  185. return "", nil
  186. }