main.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "log"
  6. "os"
  7. "os/exec"
  8. "path"
  9. "path/filepath"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/siddontang/go/ioutil2"
  14. "github.com/urfave/cli"
  15. )
  16. func main() {
  17. app := cli.NewApp()
  18. app.Name = "liverpcgen"
  19. app.Usage = "Genproto generate files to call liverpc, include *.pb.go and *.liverpc.go\n" +
  20. "EXAMPLE:\n liverpcgen APP_NAME"
  21. app.Version = "1.0.0"
  22. app.Commands = []cli.Command{
  23. {
  24. Name: "update",
  25. Usage: "update the tool its self",
  26. Action: actionUpdate,
  27. },
  28. }
  29. app.Action = actionGenerate
  30. err := app.Run(os.Args)
  31. if err != nil {
  32. log.Fatal(err)
  33. }
  34. }
  35. func autoUpdate(ctx *cli.Context) (err error) {
  36. tfile, e := ioutil.ReadFile("/tmp/liverpcgentime")
  37. if e != nil {
  38. tfile = []byte{'0'}
  39. }
  40. ts, _ := strconv.ParseInt(string(tfile), 0, 64)
  41. current := time.Now().Unix()
  42. if (current - int64(ts)) > 3600*12 { // 12 hours old
  43. ioutil.WriteFile("/tmp/liverpcgentime", []byte(strconv.FormatInt(current, 10)), 0777)
  44. err = actionUpdate(ctx)
  45. if err != nil {
  46. return
  47. }
  48. }
  49. return
  50. }
  51. // actionGenerate invokes protoc to generate files
  52. func actionGenerate(ctx *cli.Context) (err error) {
  53. if err = autoUpdate(ctx); err != nil {
  54. return
  55. }
  56. appName := ctx.Args().Get(0)
  57. if appName == "" {
  58. cli.ShowAppHelpAndExit(ctx, 1)
  59. }
  60. here := ctx.Args().Get(1)
  61. goPath := initGopath()
  62. goCommonPath := goPath + "/src/go-common"
  63. if !fileExist(goCommonPath) {
  64. return cli.NewExitError("go-common not exist : "+goCommonPath, 1)
  65. }
  66. var rpcPath = goCommonPath + "/app/service/live/" + appName + "/api/liverpc"
  67. if here == "here" {
  68. rpcPath, err = os.Getwd()
  69. if err != nil {
  70. return cli.NewExitError(err, 1)
  71. }
  72. }
  73. if !fileExist(rpcPath) {
  74. os.MkdirAll(rpcPath, 0755)
  75. }
  76. log.Println("Generate for path: " + rpcPath)
  77. // find protos
  78. hasProto := fileExistWithSuffix(rpcPath, ".proto")
  79. if !hasProto {
  80. return cli.NewExitError("No proto files found in path : "+rpcPath, 1)
  81. }
  82. files, err := ioutil.ReadDir(rpcPath)
  83. if err != nil {
  84. return cli.NewExitError("Cannot read dir : "+rpcPath+" error: "+err.Error(), 1)
  85. }
  86. for _, file := range files {
  87. if file.IsDir() {
  88. if strings.HasPrefix(file.Name(), "v") {
  89. //if !file
  90. p := rpcPath + "/" + file.Name()
  91. if fileExistWithSuffix(p, ".proto") {
  92. err = runCmd(fmt.Sprintf("cd %s && protoc -I%s -I%s -I. --gogofaster_out=. --liverpc_out=. %s/*.proto",
  93. rpcPath, goCommonPath+"/vendor", goCommonPath, file.Name()))
  94. if err != nil {
  95. return cli.NewExitError("run protoc err: "+err.Error(), 1)
  96. }
  97. log.Println("generated for path: ", p)
  98. } else {
  99. log.Println("no protofiles found in " + p + " skip")
  100. }
  101. }
  102. }
  103. }
  104. // generate client code
  105. log.Println("generating client.go ...")
  106. resetPrint()
  107. var outputCliDeclares []string
  108. var outputCliAssigns []string
  109. var outputImports []string
  110. split := strings.Split(rpcPath, "/")
  111. clientPkg := split[len(split)-1]
  112. ap("// Code generated by liverpcgen, DO NOT EDIT.")
  113. ap("// source: *.proto files under this directory")
  114. ap("// If you want to change this file, Please see README in go-common/app/tool/liverpc/protoc-gen-liverpc/")
  115. ap("package " + clientPkg)
  116. ap("")
  117. ap("import (")
  118. ap(` "go-common/library/net/rpc/liverpc"`)
  119. pkgPrefix := strings.Replace(rpcPath, goPath+"/src/", "", 1)
  120. files, err = ioutil.ReadDir(rpcPath)
  121. if err != nil {
  122. return cli.NewExitError("Cannot read dir : "+rpcPath+" error: "+err.Error(), 1)
  123. }
  124. for _, file := range files {
  125. if strings.HasPrefix(file.Name(), "client.v") {
  126. pkg := strings.Split(file.Name(), ".")[1]
  127. pkgUpper := strings.ToUpper(pkg)
  128. var b []byte
  129. b, err = ioutil.ReadFile(rpcPath + "/" + file.Name())
  130. fileContent := string(b)
  131. if err != nil {
  132. return cli.NewExitError("fail to read file: "+rpcPath+"/"+file.Name(), 1)
  133. }
  134. fileContent = strings.TrimSuffix(fileContent, "\n")
  135. if fileContent != "" {
  136. names := strings.Split(fileContent, "\n")
  137. for _, name := range names {
  138. apVar(&outputCliDeclares, fmt.Sprintf(" // %s%s presents the controller in liverpc",
  139. pkgUpper, name))
  140. apVar(&outputCliDeclares, fmt.Sprintf(" %s%s %s.%sRPCClient", pkgUpper, name, pkg, name))
  141. apVar(&outputCliAssigns, fmt.Sprintf(" cli.%s%s = %s.New%sRPCClient(realCli)", pkgUpper, name, pkg, name))
  142. }
  143. log.Println("merge " + rpcPath + "/" + file.Name())
  144. apVar(&outputImports, fmt.Sprintf(` "%s/%s"`, pkgPrefix, pkg))
  145. }
  146. os.Remove(rpcPath + "/" + file.Name())
  147. }
  148. }
  149. ap(outputImports...)
  150. ap(")")
  151. ap("")
  152. ap("// Client that represents a liverpc " + appName + " service api")
  153. ap("type Client struct {")
  154. ap(" cli *liverpc.Client")
  155. ap(outputCliDeclares...)
  156. ap("}")
  157. ap("")
  158. discoveryId := strings.Replace(appName, "-", "", -1)
  159. discoveryId = strings.Replace(discoveryId, "_", "", -1)
  160. discoveryId = "live." + discoveryId
  161. ap("// DiscoveryAppId the discovery id is not the tree name")
  162. ap(`var DiscoveryAppId = "` + discoveryId + `"`)
  163. ap("")
  164. ap("// New a Client that represents a liverpc " + discoveryId + " service api")
  165. ap("// conf can be empty, and it will use discovery to find service by default")
  166. ap("// conf.AppID will be overwrite by a fixed value DiscoveryAppId")
  167. ap("// therefore is no need to set")
  168. ap("func New(conf *liverpc.ClientConfig) *Client {")
  169. ap(" if conf == nil {")
  170. ap(" conf = &liverpc.ClientConfig{}")
  171. ap(" }")
  172. ap(" conf.AppID = DiscoveryAppId")
  173. ap(" var realCli = liverpc.NewClient(conf)")
  174. ap(" cli := &Client{cli:realCli}")
  175. ap(" cli.clientInit(realCli)")
  176. ap(" return cli")
  177. ap("}")
  178. ap("")
  179. ap("")
  180. ap("func (cli *Client)GetRawCli() *liverpc.Client {")
  181. ap(" return cli.cli")
  182. ap("}")
  183. ap("")
  184. ap("func (cli *Client)clientInit(realCli *liverpc.Client) {")
  185. ap(outputCliAssigns...)
  186. ap("}")
  187. output := getOutput()
  188. err = ioutil.WriteFile(rpcPath+"/client.go", []byte(output), 0755)
  189. if err != nil {
  190. return cli.NewExitError("write file err : "+err.Error()+"; path: "+rpcPath+"/client.go", 1)
  191. }
  192. runCmd("gofmt -s -w " + rpcPath + "/client.go")
  193. return
  194. }
  195. var buf []string
  196. func resetPrint() {
  197. buf = make([]string, 0)
  198. }
  199. // append to default buf
  200. func ap(args ...string) {
  201. buf = append(buf, args...)
  202. }
  203. // append to var "b"
  204. func apVar(b *[]string, str ...string) {
  205. *b = append(*b, str...)
  206. }
  207. func getOutput() string {
  208. return strings.Join(buf, "\n")
  209. }
  210. // equals to `ls dir/*ext` has result
  211. func fileExistWithSuffix(dir string, ext string) bool {
  212. hasFile := false
  213. filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
  214. if !info.IsDir() {
  215. if strings.HasSuffix(info.Name(), ext) {
  216. hasFile = true
  217. return fmt.Errorf("stop")
  218. }
  219. }
  220. return nil
  221. })
  222. return hasFile
  223. }
  224. // actionUpdate update the tools its self
  225. func actionUpdate(ctx *cli.Context) (err error) {
  226. log.Printf("Updating liverpcgen.....")
  227. goPath := initGopath()
  228. goCommonPath := goPath + "/src/go-common"
  229. if !fileExist(goCommonPath) {
  230. return cli.NewExitError("go-common not exist : "+goCommonPath, 1)
  231. }
  232. cmd1 := fmt.Sprintf("cd %s/app/tool/liverpc/protoc-gen-liverpc && go install", goCommonPath)
  233. cmd2 := fmt.Sprintf("cd %s/app/tool/liverpc/liverpcgen && go install", goCommonPath)
  234. err = runCmd(cmd1)
  235. if err != nil {
  236. return cli.NewExitError("update fail: "+err.Error(), 1)
  237. }
  238. err = runCmd(cmd2)
  239. if err != nil {
  240. return cli.NewExitError("update fail: "+err.Error(), 1)
  241. }
  242. log.Printf("Updated!")
  243. return
  244. }
  245. // runCmd runs the cmd & print output (both stdout & stderr)
  246. func runCmd(cmd string) (err error) {
  247. fmt.Printf("CMD: %s\n", cmd)
  248. out, err := exec.Command("/bin/bash", "-c", cmd).CombinedOutput()
  249. fmt.Print(string(out))
  250. return
  251. }
  252. func fileExist(file string) bool {
  253. return ioutil2.FileExists(file)
  254. }
  255. func initGopath() string {
  256. root, err := goPath()
  257. if err != nil || root == "" {
  258. log.Printf("can not read GOPATH, use ~/go as default GOPATH")
  259. root = path.Join(os.Getenv("HOME"), "go")
  260. }
  261. return root
  262. }
  263. func goPath() (string, error) {
  264. gopaths := strings.Split(os.Getenv("GOPATH"), ":")
  265. if len(gopaths) == 1 {
  266. return gopaths[0], nil
  267. }
  268. for _, gp := range gopaths {
  269. absgp, err := filepath.Abs(gp)
  270. if err != nil {
  271. return "", err
  272. }
  273. if fileExist(absgp + "/src/go-common") {
  274. return absgp, nil
  275. }
  276. }
  277. return "", fmt.Errorf("can't found current gopath")
  278. }