You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
8.0 KiB
300 lines
8.0 KiB
package main |
|
|
|
import ( |
|
"fmt" |
|
"io/ioutil" |
|
"log" |
|
"os" |
|
"os/exec" |
|
"path" |
|
"path/filepath" |
|
"strconv" |
|
"strings" |
|
"time" |
|
|
|
"github.com/siddontang/go/ioutil2" |
|
"github.com/urfave/cli" |
|
) |
|
|
|
func main() { |
|
app := cli.NewApp() |
|
app.Name = "liverpcgen" |
|
app.Usage = "Genproto generate files to call liverpc, include *.pb.go and *.liverpc.go\n" + |
|
"EXAMPLE:\n liverpcgen APP_NAME" |
|
app.Version = "1.0.0" |
|
app.Commands = []cli.Command{ |
|
{ |
|
Name: "update", |
|
Usage: "update the tool its self", |
|
Action: actionUpdate, |
|
}, |
|
} |
|
app.Action = actionGenerate |
|
|
|
err := app.Run(os.Args) |
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
} |
|
|
|
func autoUpdate(ctx *cli.Context) (err error) { |
|
tfile, e := ioutil.ReadFile("/tmp/liverpcgentime") |
|
if e != nil { |
|
tfile = []byte{'0'} |
|
} |
|
ts, _ := strconv.ParseInt(string(tfile), 0, 64) |
|
current := time.Now().Unix() |
|
if (current - int64(ts)) > 3600*12 { // 12 hours old |
|
ioutil.WriteFile("/tmp/liverpcgentime", []byte(strconv.FormatInt(current, 10)), 0777) |
|
err = actionUpdate(ctx) |
|
if err != nil { |
|
return |
|
} |
|
} |
|
return |
|
} |
|
|
|
// actionGenerate invokes protoc to generate files |
|
func actionGenerate(ctx *cli.Context) (err error) { |
|
if err = autoUpdate(ctx); err != nil { |
|
return |
|
} |
|
appName := ctx.Args().Get(0) |
|
if appName == "" { |
|
cli.ShowAppHelpAndExit(ctx, 1) |
|
} |
|
here := ctx.Args().Get(1) |
|
goPath := initGopath() |
|
goCommonPath := goPath + "/src/go-common" |
|
if !fileExist(goCommonPath) { |
|
return cli.NewExitError("go-common not exist : "+goCommonPath, 1) |
|
} |
|
var rpcPath = goCommonPath + "/app/service/live/" + appName + "/api/liverpc" |
|
if here == "here" { |
|
rpcPath, err = os.Getwd() |
|
if err != nil { |
|
return cli.NewExitError(err, 1) |
|
} |
|
} |
|
if !fileExist(rpcPath) { |
|
os.MkdirAll(rpcPath, 0755) |
|
} |
|
log.Println("Generate for path: " + rpcPath) |
|
// find protos |
|
hasProto := fileExistWithSuffix(rpcPath, ".proto") |
|
if !hasProto { |
|
return cli.NewExitError("No proto files found in path : "+rpcPath, 1) |
|
} |
|
files, err := ioutil.ReadDir(rpcPath) |
|
if err != nil { |
|
return cli.NewExitError("Cannot read dir : "+rpcPath+" error: "+err.Error(), 1) |
|
} |
|
for _, file := range files { |
|
if file.IsDir() { |
|
if strings.HasPrefix(file.Name(), "v") { |
|
//if !file |
|
p := rpcPath + "/" + file.Name() |
|
if fileExistWithSuffix(p, ".proto") { |
|
err = runCmd(fmt.Sprintf("cd %s && protoc -I%s -I%s -I. --gogofaster_out=. --liverpc_out=. %s/*.proto", |
|
rpcPath, goCommonPath+"/vendor", goCommonPath, file.Name())) |
|
if err != nil { |
|
return cli.NewExitError("run protoc err: "+err.Error(), 1) |
|
} |
|
log.Println("generated for path: ", p) |
|
} else { |
|
log.Println("no protofiles found in " + p + " skip") |
|
} |
|
} |
|
} |
|
} |
|
|
|
// generate client code |
|
log.Println("generating client.go ...") |
|
resetPrint() |
|
var outputCliDeclares []string |
|
var outputCliAssigns []string |
|
var outputImports []string |
|
|
|
split := strings.Split(rpcPath, "/") |
|
clientPkg := split[len(split)-1] |
|
ap("// Code generated by liverpcgen, DO NOT EDIT.") |
|
ap("// source: *.proto files under this directory") |
|
ap("// If you want to change this file, Please see README in go-common/app/tool/liverpc/protoc-gen-liverpc/") |
|
ap("package " + clientPkg) |
|
ap("") |
|
ap("import (") |
|
ap(` "go-common/library/net/rpc/liverpc"`) |
|
pkgPrefix := strings.Replace(rpcPath, goPath+"/src/", "", 1) |
|
|
|
files, err = ioutil.ReadDir(rpcPath) |
|
if err != nil { |
|
return cli.NewExitError("Cannot read dir : "+rpcPath+" error: "+err.Error(), 1) |
|
} |
|
for _, file := range files { |
|
if strings.HasPrefix(file.Name(), "client.v") { |
|
pkg := strings.Split(file.Name(), ".")[1] |
|
pkgUpper := strings.ToUpper(pkg) |
|
var b []byte |
|
b, err = ioutil.ReadFile(rpcPath + "/" + file.Name()) |
|
fileContent := string(b) |
|
if err != nil { |
|
return cli.NewExitError("fail to read file: "+rpcPath+"/"+file.Name(), 1) |
|
} |
|
fileContent = strings.TrimSuffix(fileContent, "\n") |
|
if fileContent != "" { |
|
names := strings.Split(fileContent, "\n") |
|
for _, name := range names { |
|
apVar(&outputCliDeclares, fmt.Sprintf(" // %s%s presents the controller in liverpc", |
|
pkgUpper, name)) |
|
apVar(&outputCliDeclares, fmt.Sprintf(" %s%s %s.%sRPCClient", pkgUpper, name, pkg, name)) |
|
apVar(&outputCliAssigns, fmt.Sprintf(" cli.%s%s = %s.New%sRPCClient(realCli)", pkgUpper, name, pkg, name)) |
|
} |
|
log.Println("merge " + rpcPath + "/" + file.Name()) |
|
apVar(&outputImports, fmt.Sprintf(` "%s/%s"`, pkgPrefix, pkg)) |
|
} |
|
os.Remove(rpcPath + "/" + file.Name()) |
|
} |
|
} |
|
ap(outputImports...) |
|
ap(")") |
|
ap("") |
|
ap("// Client that represents a liverpc " + appName + " service api") |
|
ap("type Client struct {") |
|
ap(" cli *liverpc.Client") |
|
ap(outputCliDeclares...) |
|
ap("}") |
|
ap("") |
|
discoveryId := strings.Replace(appName, "-", "", -1) |
|
discoveryId = strings.Replace(discoveryId, "_", "", -1) |
|
discoveryId = "live." + discoveryId |
|
ap("// DiscoveryAppId the discovery id is not the tree name") |
|
ap(`var DiscoveryAppId = "` + discoveryId + `"`) |
|
ap("") |
|
ap("// New a Client that represents a liverpc " + discoveryId + " service api") |
|
ap("// conf can be empty, and it will use discovery to find service by default") |
|
ap("// conf.AppID will be overwrite by a fixed value DiscoveryAppId") |
|
ap("// therefore is no need to set") |
|
ap("func New(conf *liverpc.ClientConfig) *Client {") |
|
ap(" if conf == nil {") |
|
ap(" conf = &liverpc.ClientConfig{}") |
|
ap(" }") |
|
ap(" conf.AppID = DiscoveryAppId") |
|
ap(" var realCli = liverpc.NewClient(conf)") |
|
ap(" cli := &Client{cli:realCli}") |
|
ap(" cli.clientInit(realCli)") |
|
ap(" return cli") |
|
ap("}") |
|
ap("") |
|
|
|
ap("") |
|
ap("func (cli *Client)GetRawCli() *liverpc.Client {") |
|
ap(" return cli.cli") |
|
ap("}") |
|
ap("") |
|
|
|
ap("func (cli *Client)clientInit(realCli *liverpc.Client) {") |
|
ap(outputCliAssigns...) |
|
ap("}") |
|
output := getOutput() |
|
err = ioutil.WriteFile(rpcPath+"/client.go", []byte(output), 0755) |
|
if err != nil { |
|
return cli.NewExitError("write file err : "+err.Error()+"; path: "+rpcPath+"/client.go", 1) |
|
} |
|
runCmd("gofmt -s -w " + rpcPath + "/client.go") |
|
return |
|
} |
|
|
|
var buf []string |
|
|
|
func resetPrint() { |
|
buf = make([]string, 0) |
|
} |
|
|
|
// append to default buf |
|
func ap(args ...string) { |
|
buf = append(buf, args...) |
|
} |
|
|
|
// append to var "b" |
|
func apVar(b *[]string, str ...string) { |
|
*b = append(*b, str...) |
|
} |
|
|
|
func getOutput() string { |
|
return strings.Join(buf, "\n") |
|
} |
|
|
|
// equals to `ls dir/*ext` has result |
|
func fileExistWithSuffix(dir string, ext string) bool { |
|
hasFile := false |
|
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { |
|
if !info.IsDir() { |
|
if strings.HasSuffix(info.Name(), ext) { |
|
hasFile = true |
|
return fmt.Errorf("stop") |
|
} |
|
} |
|
return nil |
|
}) |
|
return hasFile |
|
} |
|
|
|
// actionUpdate update the tools its self |
|
func actionUpdate(ctx *cli.Context) (err error) { |
|
log.Printf("Updating liverpcgen.....") |
|
goPath := initGopath() |
|
goCommonPath := goPath + "/src/go-common" |
|
if !fileExist(goCommonPath) { |
|
return cli.NewExitError("go-common not exist : "+goCommonPath, 1) |
|
} |
|
cmd1 := fmt.Sprintf("cd %s/app/tool/liverpc/protoc-gen-liverpc && go install", goCommonPath) |
|
cmd2 := fmt.Sprintf("cd %s/app/tool/liverpc/liverpcgen && go install", goCommonPath) |
|
err = runCmd(cmd1) |
|
if err != nil { |
|
return cli.NewExitError("update fail: "+err.Error(), 1) |
|
} |
|
err = runCmd(cmd2) |
|
if err != nil { |
|
return cli.NewExitError("update fail: "+err.Error(), 1) |
|
} |
|
log.Printf("Updated!") |
|
return |
|
} |
|
|
|
// runCmd runs the cmd & print output (both stdout & stderr) |
|
func runCmd(cmd string) (err error) { |
|
fmt.Printf("CMD: %s\n", cmd) |
|
out, err := exec.Command("/bin/bash", "-c", cmd).CombinedOutput() |
|
fmt.Print(string(out)) |
|
return |
|
} |
|
|
|
func fileExist(file string) bool { |
|
return ioutil2.FileExists(file) |
|
} |
|
|
|
func initGopath() string { |
|
root, err := goPath() |
|
if err != nil || root == "" { |
|
log.Printf("can not read GOPATH, use ~/go as default GOPATH") |
|
root = path.Join(os.Getenv("HOME"), "go") |
|
} |
|
return root |
|
} |
|
|
|
func goPath() (string, error) { |
|
gopaths := strings.Split(os.Getenv("GOPATH"), ":") |
|
if len(gopaths) == 1 { |
|
return gopaths[0], nil |
|
} |
|
for _, gp := range gopaths { |
|
absgp, err := filepath.Abs(gp) |
|
if err != nil { |
|
return "", err |
|
} |
|
if fileExist(absgp + "/src/go-common") { |
|
return absgp, nil |
|
} |
|
} |
|
return "", fmt.Errorf("can't found current gopath") |
|
}
|
|
|