Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

45
app/tool/warden/BUILD Normal file
View File

@@ -0,0 +1,45 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "warden",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "go-common/app/tool/warden",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/tool/warden/generator:go_default_library",
"//app/tool/warden/goparser:go_default_library",
"//app/tool/warden/types:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/tool/warden/generator:all-srcs",
"//app/tool/warden/goparser:all-srcs",
"//app/tool/warden/types:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,9 @@
# v0.3
1. 修正目录结构
# v0.2
1. 使用新目录结构
2. 添加 protoc.sh 脚本
# v0.1
1. 自动生成 proto

View File

@@ -0,0 +1,7 @@
# Owner
weicheng
# Author
weicheng
# Reviewer

11
app/tool/warden/Makefile Normal file
View File

@@ -0,0 +1,11 @@
generator/templates/assets.go: $(shell find generator/templates -name "*.tmpl")
cd generator/templates/; go generate
build: generator/templates/assets.go
go build -o warden
install: generator/templates/assets.go
go install go-common/app/tool/warden
clean:
rm warden

10
app/tool/warden/OWNERS Normal file
View File

@@ -0,0 +1,10 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- weicheng
labels:
- tool
options:
no_parent_owners: true
reviewers:
- weicheng

30
app/tool/warden/README.md Normal file
View File

@@ -0,0 +1,30 @@
# warden proto 自动生成工具
[参考文档](http://info.bilibili.co/display/documentation/gRPC+Golang+Warden+Gen)
# protoc.sh
platform/go-common 仓库 pb 文件生成工具
默认使用 gofast, 可以使用 PROTOC_GEN 环境变量指定.
默认 Proto Import Path 为 go-common 目录, go-common/vendor 目录, /usr/local/include 以及proto文件所在目录, 可以通过 PROTO_PATH 环境变量指定.
### 安装
```bash
# 如果你只有一个 GOPATH 的话
ln -s ${GOPATH}/src/go-common/app/tool/warden/protoc.sh /usr/local/bin/protoc.sh && chmod +x /usr/local/bin/protoc.sh
```
### 使用方法
```
> cd {proto 文件所在目录}
> protoc.sh
```
### TODO
- [ ] 支持更多系统
- [ ] 纠正 pb 文件中错误的 import

View File

@@ -0,0 +1,48 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["genservice_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = [
"gencscode.go",
"genservice.go",
"protoc.go",
],
importpath = "go-common/app/tool/warden/generator",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/tool/warden/generator/templates:go_default_library",
"//app/tool/warden/types:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/tool/warden/generator/templates:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,132 @@
package generator
import (
"fmt"
"os"
"path"
"strings"
"text/template"
assets "go-common/app/tool/warden/generator/templates"
"go-common/app/tool/warden/types"
)
// GenCSCodeOptions options
type GenCSCodeOptions struct {
PbPackage string
RecvPackage string
RecvName string
}
// CSValue ...
type CSValue struct {
options *GenCSCodeOptions
Name string
PbPackage string
RecvName string
RecvPackage string
Imports map[string]struct{}
ClientImports map[string]struct{}
Methods []CSMethod
}
// CSMethod ...
type CSMethod struct {
Name string
Comments []string
ParamBlock string
ReturnBlock string
ParamPbBlock string
}
func (c *CSValue) render(spec *types.ServiceSpec) error {
c.PbPackage = c.options.PbPackage
c.Name = spec.Name
c.RecvName = c.options.RecvName
c.RecvPackage = c.options.RecvPackage
c.Imports = map[string]struct{}{"context": struct{}{}}
c.ClientImports = make(map[string]struct{})
return c.renderMethods(spec.Methods)
}
func (c *CSValue) renderMethods(methods []*types.Method) error {
for _, method := range methods {
csMethod := CSMethod{
Name: method.Name,
Comments: method.Comments,
ParamBlock: c.formatField(method.Parameters),
ReturnBlock: c.formatField(method.Results),
}
c.Methods = append(c.Methods, csMethod)
}
return nil
}
func (c *CSValue) formatField(fields []*types.Field) string {
var ss []string
clientImps := make(map[string]struct{})
for _, field := range fields {
if field.Name == "" {
ss = append(ss, field.Type.String())
} else {
ss = append(ss, fmt.Sprintf("%s %s", field.Name, field.Type))
}
importType(clientImps, field.Type)
}
for k := range clientImps {
if _, ok := c.Imports[k]; !ok {
c.ClientImports[k] = struct{}{}
}
}
return strings.Join(ss, ", ")
}
func importType(m map[string]struct{}, t types.Typer) {
if m == nil {
panic("map is nil")
}
switch v := t.(type) {
case *types.StructType:
m[v.ImportPath] = struct{}{}
for _, f := range v.Fields {
importType(m, f.Type)
}
case *types.ArrayType:
importType(m, v.EltType)
case *types.InterfaceType:
m[v.ImportPath] = struct{}{}
}
}
func renderCSValue(spec *types.ServiceSpec, options *GenCSCodeOptions) (*CSValue, error) {
value := &CSValue{
options: options,
}
return value, value.render(spec)
}
// GenCSCode generator client, server code
func GenCSCode(csdir string, spec *types.ServiceSpec, options *GenCSCodeOptions) error {
value, err := renderCSValue(spec, options)
if err != nil {
return err
}
return genCode(value, "server", csdir)
}
func genCode(value *CSValue, name, dir string) error {
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
fp, err := os.OpenFile(path.Join(dir, fmt.Sprintf("%s.go", name)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer fp.Close()
templateName := fmt.Sprintf("%s.tmpl", name)
t, err := template.New(name).Parse(string(assets.MustAsset(templateName)))
if err != nil {
return err
}
return t.Execute(fp, value)
}

View File

@@ -0,0 +1,275 @@
package generator
import (
"bufio"
"fmt"
"io"
"log"
"os"
"strings"
"text/template"
assets "go-common/app/tool/warden/generator/templates"
"go-common/app/tool/warden/types"
)
const (
protoTemplateName = "service.tmpl"
contextType = "context.Context"
)
// ProtoMessage ProtoMessage
type ProtoMessage struct {
Name string
Fields []ProtoField
}
// ProtoField ProtoField
type ProtoField struct {
FieldID int
FieldType string
FieldName string
}
// ProtoMethod method info
type ProtoMethod struct {
Comments []string
Name string
Req string
Reply string
}
// ProtoValue proto template render value
type ProtoValue struct {
Package string
Name string
GoPackage string
Imports map[string]bool
Messages map[string]ProtoMessage
Methods []ProtoMethod
options *ServiceProtoOptions
}
// ServiceProtoOptions ...
type ServiceProtoOptions struct {
GoPackage string
ProtoPackage string
IgnoreType bool
ImportPaths []string
}
func readProtoPackage(protoFile string) (string, error) {
fp, err := os.Open(protoFile)
if err != nil {
return "", err
}
defer fp.Close()
buf := bufio.NewReader(fp)
for {
line, err := buf.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
return "", err
}
line = strings.TrimSpace(line)
if !strings.HasPrefix(line, "package") {
continue
}
return strings.TrimSpace(strings.TrimRight(line[len("package"):], ";")), nil
}
return "", fmt.Errorf("proto %s miss package define", protoFile)
}
func underscore(s string) string {
cc := []byte(s)
us := make([]byte, 0, len(cc)+3)
pervUp := true
for _, b := range cc {
if 65 <= b && b <= 90 {
if pervUp {
us = append(us, b+32)
} else {
us = append(us, '_', b+32)
}
pervUp = true
} else {
pervUp = false
us = append(us, b)
}
}
return string(us)
}
func (p *ProtoValue) convertType(t types.Typer) (string, error) {
switch v := t.(type) {
case *types.BasicType:
return convertBasicType(v.String())
case *types.ArrayType:
if v.EltType.String() == "byte" {
return "bytes", nil
}
elt, err := p.convertType(v.EltType)
if err != nil {
return "", err
}
return fmt.Sprintf("repeated %s", elt), nil
case *types.MapType:
kt, err := p.convertType(v.KeyType)
if err != nil {
return "", err
}
vt, err := p.convertType(v.ValueType)
if err != nil {
return "", err
}
return fmt.Sprintf("map<%s, %s>", kt, vt), nil
case *types.StructType:
if v.ProtoFile == "" {
messageName := fmt.Sprintf("%s%s", strings.Title(v.Package), v.IdentName)
err := p.renderMessage(messageName, v.Fields)
if err != nil {
return "", err
}
return messageName, nil
}
protoPackage, err := readProtoPackage(v.ProtoFile)
if err != nil {
return "", err
}
p.importPackage(v.ProtoFile)
if p.Package == protoPackage {
return v.IdentName, nil
}
return fmt.Sprintf(".%s.%s", protoPackage, v.IdentName), nil
}
return "", fmt.Errorf("unsupport type %s", t)
}
func convertBasicType(gt string) (string, error) {
switch gt {
case "float64":
return "double", nil
case "float32":
return "float", nil
case "int", "int8", "uint8", "int16", "uint16":
return "int32", nil
case "int64", "int32", "uint32", "uint64", "string", "bool":
return gt, nil
}
return "", fmt.Errorf("unsupport basic type %s", gt)
}
func (p *ProtoValue) render(spec *types.ServiceSpec, options *ServiceProtoOptions) (*ProtoValue, error) {
p.options = options
p.Name = spec.Name
p.GoPackage = options.GoPackage
p.Package = options.ProtoPackage
p.Imports = make(map[string]bool)
p.Messages = make(map[string]ProtoMessage)
return p, p.renderMethods(spec.Methods)
}
func (p *ProtoValue) renderMethods(methods []*types.Method) error {
for _, method := range methods {
protoMethod := ProtoMethod{
Comments: method.Comments,
Name: method.Name,
}
//if len(method.Parameters) == 0 || (len(method.Parameters) == 1 && method.Parameters[0].Type.String() == contextType) {
// p.importPackage(emptyProtoFile)
// protoMethod.Req = emptyProtoMsg
//} else {
// protoMethod.Req = fmt.Sprintf("%sReq", method.Name)
// if err := p.renderMessage(protoMethod.Req, method.Parameters); err != nil {
// return err
// }
//}
//if len(method.Results) == 0 || (len(method.Results) == 1 && method.Results[0].Type.String() == "error") {
// p.importPackage(emptyProtoFile)
// protoMethod.Reply = emptyProtoMsg
//} else {
// protoMethod.Reply = fmt.Sprintf("%sReply", method.Name)
// if err := p.renderMessage(protoMethod.Reply, method.Results); err != nil {
// return err
// }
//}
protoMethod.Req = fmt.Sprintf("%sReq", method.Name)
if err := p.renderMessage(protoMethod.Req, method.Parameters); err != nil {
return err
}
protoMethod.Reply = fmt.Sprintf("%sReply", method.Name)
if err := p.renderMessage(protoMethod.Reply, method.Results); err != nil {
return err
}
p.Methods = append(p.Methods, protoMethod)
}
return nil
}
func (p *ProtoValue) importPackage(imp string) {
for _, importPath := range p.options.ImportPaths {
if strings.HasPrefix(imp, importPath) {
p.Imports[strings.TrimLeft(imp[len(importPath):], "/")] = true
return
}
}
p.Imports[imp] = true
}
func (p *ProtoValue) renderMessage(name string, fields []*types.Field) error {
if _, ok := p.Messages[name]; ok {
return nil
}
message := ProtoMessage{
Name: name,
}
for i, field := range fields {
if field.Type.String() == "error" || field.Type.String() == contextType {
continue
}
fieldName := underscore(field.Name)
if fieldName == "" {
fieldName = fmt.Sprintf("data_%d", i)
}
pField := ProtoField{
FieldID: i + 1,
FieldName: fieldName,
}
ptype, err := p.convertType(field.Type)
if err != nil {
if p.options.IgnoreType {
log.Printf("warning convert type fail %s", err)
ptype = fmt.Sprintf("//FIXME type %s", field.Type)
} else {
return err
}
}
pField.FieldType = ptype
message.Fields = append(message.Fields, pField)
}
p.Messages[name] = message
return nil
}
func renderProtoValue(spec *types.ServiceSpec, options *ServiceProtoOptions) (*ProtoValue, error) {
v := &ProtoValue{}
return v.render(spec, options)
}
// GenServiceProto generator proto service by service spec
func GenServiceProto(out io.Writer, spec *types.ServiceSpec, options *ServiceProtoOptions) error {
value, err := renderProtoValue(spec, options)
if err != nil {
return err
}
assets.MustAsset(protoTemplateName)
t, err := template.New(protoTemplateName).Parse(string(assets.MustAsset(protoTemplateName)))
if err != nil {
return err
}
return t.Execute(out, value)
}

View File

@@ -0,0 +1,27 @@
package generator
import "testing"
func TestUnderscore(t *testing.T) {
type args struct {
s string
}
tests := []struct {
name string
args args
want string
}{
{
name: "remoteIP",
args: args{s: "remoteIP"},
want: "remote_ip",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := underscore(tt.args.s); got != tt.want {
t.Errorf("underscore() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -0,0 +1,46 @@
package generator
import (
"fmt"
"log"
"os"
"os/exec"
"path"
"strings"
)
func findVendorDir() string {
pwd, err := os.Getwd()
if err != nil {
log.Printf("getwd error: %s", err)
}
for dir := pwd; dir != "/" && dir != "."; dir = path.Dir(dir) {
vendorDir := path.Join(dir, "vendor")
if s, err := os.Stat(vendorDir); err == nil && s.IsDir() {
return vendorDir
}
}
return ""
}
// Protoc run protoc generator go source code
func Protoc(protoFile, protocExec, gen string, paths []string) error {
if protocExec == "" {
protocExec = "protoc"
}
if gen == "" {
gen = "gogofast"
}
paths = append(paths, ".", os.Getenv("GOPATH"))
vendorDir := findVendorDir()
if vendorDir != "" {
paths = append(paths, vendorDir)
}
args := []string{"--proto_path", strings.Join(paths, ":"), fmt.Sprintf("--%s_out=plugins=grpc:.", gen), path.Base(protoFile)}
log.Printf("run protoc %s", strings.Join(args, " "))
protoc := exec.Command(protocExec, args...)
protoc.Stdout = os.Stdout
protoc.Stderr = os.Stderr
protoc.Dir = path.Dir(protoFile)
return protoc.Run()
}

View File

@@ -0,0 +1,36 @@
load(
"@io_bazel_rules_go//proto:def.bzl",
"go_proto_library",
)
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"assets.go",
"template.go",
],
importpath = "go-common/app/tool/warden/generator/templates",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,373 @@
// Code generated by go-bindata.
// sources:
// .DS_Store
// BUILD
// assets.go
// client.tmpl
// server.tmpl
// service.tmpl
// template.go
// DO NOT EDIT!
package templates
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
)
func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
clErr := gz.Close()
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
if clErr != nil {
return nil, err
}
return buf.Bytes(), nil
}
type asset struct {
bytes []byte
info os.FileInfo
}
type bindataFileInfo struct {
name string
size int64
mode os.FileMode
modTime time.Time
}
func (fi bindataFileInfo) Name() string {
return fi.name
}
func (fi bindataFileInfo) Size() int64 {
return fi.size
}
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}
func (fi bindataFileInfo) IsDir() bool {
return false
}
func (fi bindataFileInfo) Sys() interface{} {
return nil
}
var _Ds_store = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xd8\x31\x0a\x02\x31\x10\x85\xe1\x37\x31\x45\xc0\x26\xa5\x65\x1a\x0f\xe0\x0d\xc2\xb2\x9e\xc0\x0b\x58\x78\x05\xfb\x1c\x5d\x96\x79\x60\x60\xd5\x4e\x8c\xcb\xfb\x40\xfe\x05\x37\x2a\x16\x31\x23\x00\x9b\xee\xb7\x13\x90\x01\x24\x78\x71\xc4\x4b\x89\x8f\x95\xd0\x5d\x1b\x5f\x43\x44\x44\x44\xc6\x66\x9e\xb4\xff\xf5\x07\x11\x91\xe1\x2c\xfb\x43\x61\x2b\xdb\xbc\xc6\xe7\x03\x1b\xbb\x35\x99\x2d\x6c\x65\x9b\xd7\x78\x5f\x60\x23\x9b\xd8\xcc\x16\xb6\xb2\xcd\xcb\x4d\xcb\x38\x7c\x18\xdf\xd9\x38\xa1\x18\xa7\x10\x2b\x6c\xfd\xce\x77\x23\xf2\xef\x76\x9e\xbc\xfc\xfe\x9f\xdf\xcf\xff\x22\xb2\x61\x16\xe7\xcb\x3c\x3d\x07\x82\xf5\x0d\x00\xae\xdd\xf5\xa7\x43\x40\xf0\x3f\x0b\x0f\xdd\x5a\x1d\x04\x44\x06\xf3\x08\x00\x00\xff\xff\x6a\x00\x88\x6d\x04\x18\x00\x00")
func Ds_storeBytes() ([]byte, error) {
return bindataRead(
_Ds_store,
".DS_Store",
)
}
func Ds_store() (*asset, error) {
bytes, err := Ds_storeBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: ".DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1530586172, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _build = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x91\x41\x6b\xc3\x30\x0c\x85\xef\xf9\x15\xc6\xa7\xb6\xb4\xf5\xbd\x50\xd8\xff\x28\x21\xc8\x89\xea\x99\xc9\x91\xb1\x95\x8e\xf6\xd7\x8f\x38\x29\x21\x50\xb6\xc1\x76\x32\x7a\x3c\x9e\xbe\x27\x13\x43\xb7\xa9\x94\x52\x4a\xbf\x79\x6e\x2c\x3c\x90\x9a\x34\x10\xe6\xc6\xb1\x31\x31\xb1\xf0\xa9\xc3\xeb\xd1\x3e\x48\xef\x27\xa3\xe3\xa6\xe8\x0d\x79\x9b\x20\xdd\xf5\xbe\xda\x56\x55\x84\xf6\x03\x1c\x6e\x3a\xbc\xc2\x40\xd2\xdc\x7c\xf6\xd6\x93\x97\xbb\x3a\xab\x8b\x36\x66\x11\x4e\x71\xb0\xe4\x5b\x5d\x6f\xab\xea\x07\x00\xf7\x6a\xfb\x6a\xef\x32\x4f\x31\x3d\x04\x54\xe7\xe2\x7b\xa2\x2c\xfe\xd1\x90\x53\x9b\x47\xa4\x32\x94\x48\xc8\x19\x25\x1f\x1d\xcf\x8e\x22\x0a\x86\x48\x20\xb8\xc8\xf5\xf4\xf8\x10\x39\x49\x04\x79\x9f\xd6\x1c\x5a\x0e\x81\x7b\x03\x31\x1a\x61\x26\xf3\x09\xa9\xc3\xde\x38\xec\x31\x81\x70\x32\xcf\xa8\x3c\x07\x09\xb8\x42\xa0\x61\x10\x0e\xd0\x83\xc3\x4e\xcf\xe9\xbf\x39\x5b\xe9\x7d\xf5\x84\x2e\xf1\x10\xd7\xb5\xe7\x6f\x38\x8c\x2d\xd7\x85\x1d\xb1\xdd\x5c\xf4\x6e\xa7\xeb\xed\x1f\x39\x92\xbf\x81\xe0\xf7\x20\x40\xf4\x02\xe2\xa2\x4f\x2b\xc2\xfa\xff\x2e\xf2\x15\x00\x00\xff\xff\xe0\x55\x93\x84\xcb\x02\x00\x00")
func buildBytes() ([]byte, error) {
return bindataRead(
_build,
"BUILD",
)
}
func build() (*asset, error) {
bytes, err := buildBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "BUILD", size: 715, mode: os.FileMode(420), modTime: time.Unix(1534390180, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _assetsGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00")
func assetsGoBytes() ([]byte, error) {
return bindataRead(
_assetsGo,
"assets.go",
)
}
func assetsGo() (*asset, error) {
bytes, err := assetsGoBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "assets.go", size: 4096, mode: os.FileMode(420), modTime: time.Unix(1535625546, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _clientTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x52\x4b\x6b\xdc\x30\x10\x3e\xaf\x7e\xc5\xd4\xa4\xc5\x0a\x5b\x09\x92\x5b\x20\x97\x24\x97\x1c\x9a\x84\xb6\xd0\x63\x91\xe5\xb1\x22\x62\x4b\x62\x56\x1b\x77\x11\xfa\xef\x45\x7e\x85\xa6\x94\x5e\x8c\x3c\xaf\xef\x31\x23\x25\x3c\x29\xfd\xa2\x0c\x82\xee\x2d\xba\x08\x06\x1d\x92\x8a\x08\xcd\x09\x46\x45\x2d\xba\x9f\x06\x1d\x0b\x7f\x54\x31\x66\x87\xe0\x29\x42\xcd\x00\x00\xaa\x6e\x88\xd5\xf4\x4a\x89\x94\x33\x08\x67\x2f\x78\xda\xc3\xd9\xab\xea\x8f\x08\x57\xd7\x20\xee\xa7\xfa\x43\xce\x55\x4a\x25\x99\xf3\xda\x80\xae\xcd\xf9\x9f\x7d\xb7\x13\xde\x7f\xba\xa7\x77\x68\xa0\x4a\x49\x3c\x35\x8b\xa0\xb5\xa6\x32\xfe\xb3\xf6\xc3\xe0\x9d\x74\x18\x25\x05\x2d\x67\x5d\x7f\xa5\x55\x08\x4b\x4a\x36\xaa\x57\x4e\x23\xc9\x91\x68\xab\xf3\xa6\x47\x61\x7c\xaf\x9c\x11\x9e\x8c\x34\x14\x74\xc5\x38\x63\xda\xbb\xc3\xea\x45\x24\xc4\xfb\x3b\xb8\x86\x8b\x8b\xcb\xcb\x92\x94\x12\x1e\x70\x84\x94\xc4\x83\x1a\x30\x67\x38\x20\xbd\x5a\xbd\x59\xd9\x1d\x9d\x2e\x15\xb5\xf6\xae\x83\xf3\x99\xc0\xa2\xfb\xd6\xbb\xce\x1a\x0e\xe7\xf3\x2f\x24\xb6\x8b\x8a\x0c\xc6\xe2\x4d\x37\x44\xf1\x2d\x90\x75\xb1\xab\xab\xd6\x1e\xb4\x7f\x45\x3a\x5d\x49\xd9\x62\xa7\x8e\x7d\x94\x1f\xdb\x6a\xbf\xf0\xe1\x6c\x37\xea\xd2\xb4\x8c\x7f\xc0\x71\x1e\x39\xa1\xee\xa1\x48\x11\x3f\x6c\x7c\xbe\x59\x84\x17\xae\xf5\x48\x34\x91\xe6\x9c\xed\xb4\x77\x6e\x0f\x48\x34\x4d\xd1\xe2\xce\xaa\xbe\x34\x47\xfc\x15\xc5\xf7\xc7\xbb\xc7\x9a\xef\x61\x26\xc7\xd9\xce\x76\x53\xe9\x87\x6b\x70\xb6\x2f\xb4\x77\x41\x39\xab\x6b\x24\xe2\x6c\x37\x2f\x8c\x30\x1e\xc9\xc1\xa7\x99\x48\x0a\x4d\x61\xb5\xd9\xf4\x46\xcf\xf1\xcc\x32\x9b\x8c\x5c\x6c\x78\xe7\x60\x3c\x05\xdc\x52\x91\x8e\xba\x18\x55\x10\x8a\xaa\x25\x1e\x1a\xf1\x6e\x74\x99\xb9\x9e\x9d\xf8\x82\xf1\xd9\xb7\x87\x9c\x0b\xca\x16\xbd\xf5\xc3\x80\xae\xdc\x5d\x4a\xa2\x7c\xe6\x6b\x9b\x36\x56\xeb\x75\x2b\xfc\x6d\xb7\x75\x39\x40\x45\x6a\xb8\xe9\xbd\x7e\xc9\x99\x43\x89\x7c\x9d\x84\x6e\xa1\x99\xdb\xec\x47\xe5\x7c\x04\x3b\x84\x1e\x0b\x10\xb6\x15\x67\x99\xad\x38\xbf\x03\x00\x00\xff\xff\x3e\xef\xaf\x37\x9d\x03\x00\x00")
func clientTmplBytes() ([]byte, error) {
return bindataRead(
_clientTmpl,
"client.tmpl",
)
}
func clientTmpl() (*asset, error) {
bytes, err := clientTmplBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "client.tmpl", size: 925, mode: os.FileMode(420), modTime: time.Unix(1535625483, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _serverTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\x92\xcf\x8a\xdb\x30\x10\xc6\xcf\x99\xa7\x18\xcc\x52\xec\x90\xca\xf7\xc2\x9e\x72\xea\xa1\x61\x49\x1f\x60\x91\x95\x59\xd7\x6c\x2c\x69\x47\x8a\xbd\x46\xe8\xdd\x8b\x24\xc7\x34\x0b\xbd\x24\x66\xbe\xf9\xf3\x9b\x6f\xd4\xb6\xf8\x22\xd5\xbb\xec\x09\x1d\xf1\x44\x8c\x3d\x69\x62\xe9\x09\xbb\x05\x67\xc9\x17\xd2\xaf\x3d\x69\xb0\x0f\x59\x00\xc3\x68\x0d\x7b\xac\x01\x11\x31\x04\x96\xba\x27\x7c\x7a\xa7\xe5\x80\x4f\x93\xbc\xde\x08\x7f\x3c\xa3\xf8\x99\xb3\x5c\x8c\x55\x08\x49\xcc\x1f\xa4\x2f\x31\x42\x2e\x4c\xdd\x06\x45\x58\x85\x20\xce\xa4\xa6\x95\x25\xc6\x2a\xcb\xb6\xcb\xca\x4b\xf7\x25\x5e\xf5\xe6\xbb\x32\xe3\x68\x74\x7b\x1d\x3a\x96\xbc\xb4\x9a\x7c\xcb\x56\xb5\x05\xb9\x82\x06\xa0\x6d\xf1\x44\x33\x86\x20\x4e\x72\xa4\x18\xd7\x75\x90\xad\xba\xaf\xf1\x76\xd3\x2a\x25\xd5\x0a\xf7\x45\x15\xbf\xb3\x72\x34\xfa\x6d\xe8\x0f\xe8\x26\xc6\xfd\x4a\x29\x56\xc8\xd2\xad\xc1\xfa\xb1\xa4\xc1\x00\xbb\xd9\xa5\xbd\xd7\xf8\x89\xe6\x22\xd5\xaa\x81\x9d\xed\xc4\x99\xfa\xc1\x79\xe2\x0d\x69\x95\x67\xb7\xf6\xa8\x9b\x03\x7e\x2b\x6c\xc1\x4d\x1c\x1b\xd8\x31\xf9\x1b\x6b\x9c\x1d\x44\x00\xbf\xd8\xed\x50\xce\xf3\x4d\x79\x0c\xc5\xc8\xff\x72\xa6\xb2\x49\x32\xbe\xa2\xed\xc4\x97\xc1\xf8\xbc\x4d\x8b\x00\xf7\x2b\x8a\x5f\xe4\xff\x98\x8b\x8b\x31\x59\xb8\x45\x8f\x66\x1c\x49\xa7\x63\x86\x20\xd2\x4f\xb9\x63\xb2\xb0\x76\x65\x78\x36\xe1\x3e\xa2\x56\xfe\x13\x95\xd1\x9e\x3e\xbd\x38\x96\xff\x03\x32\x7d\xe0\xfe\x5f\x92\x33\x7d\x24\x2b\x1f\x43\xf6\xba\x1c\x90\x98\x4d\x76\x35\x3f\x05\xa9\x07\x55\x57\xda\x78\x1c\x46\x7b\xa5\x84\x42\x97\xaa\x81\x08\xdb\x8b\xfa\x1b\x00\x00\xff\xff\xb2\x36\x1e\x59\xcd\x02\x00\x00")
func serverTmplBytes() ([]byte, error) {
return bindataRead(
_serverTmpl,
"server.tmpl",
)
}
func serverTmpl() (*asset, error) {
bytes, err := serverTmplBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "server.tmpl", size: 717, mode: os.FileMode(420), modTime: time.Unix(1531821454, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _serviceTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\x8f\xcd\x4a\xc4\x40\x10\x84\xef\xf3\x14\xcd\xb0\x07\x17\x24\x8b\x78\x33\xe4\xa4\x08\x7b\x58\x11\xf1\x2e\x43\xb6\x89\x61\x33\x3f\xce\xcc\x06\x43\xd3\xef\x2e\x9d\x5f\x2f\x7b\xab\x29\xbe\xae\xaa\x49\x83\xcb\xe6\x17\x2a\xd0\x21\xfa\xec\x1f\x75\xa9\x54\x30\xf5\xc5\x34\x08\x44\xc5\xfb\x24\x99\x4b\x45\x14\x8d\x6b\x10\x76\x17\x1c\xee\x61\xd7\x9b\xee\x8a\xf0\x54\x41\x71\xb4\xc1\xc7\x9c\x98\x55\x3b\x2a\xd0\x44\x02\x31\xeb\x92\x08\xdd\x99\x59\x29\x1f\x72\xeb\x1d\x34\xfe\x6b\x49\xaf\x40\xf7\x0f\x7a\xcb\x2d\x4e\x98\x92\x69\x50\x82\xec\x24\x65\xc1\x9b\xb1\xc8\x0c\x04\x2b\xf7\xda\x62\x77\x16\x0a\x00\x84\x18\xdf\x9f\x43\x18\xb1\xf9\x39\x5f\x55\xab\x71\x7c\x61\x5e\xd7\xb0\x5a\x54\xc2\xd8\xb7\xf5\x8d\xa2\x13\xe6\x6f\xff\xaf\x69\xb6\x9f\xbd\xb5\xe8\xe4\xc3\x87\x83\x1c\x8e\xb5\x53\x9c\x70\x31\xd4\x5b\xdc\x1d\x51\xf1\x81\x3f\xcc\x7b\x88\x98\xaf\xd1\xa5\xc9\x09\xdd\xc0\xbc\x2f\x61\x5b\xf4\x17\x00\x00\xff\xff\x1d\xec\x9c\x6a\x89\x01\x00\x00")
func serviceTmplBytes() ([]byte, error) {
return bindataRead(
_serviceTmpl,
"service.tmpl",
)
}
func serviceTmpl() (*asset, error) {
bytes, err := serviceTmplBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "service.tmpl", size: 393, mode: os.FileMode(420), modTime: time.Unix(1535599707, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _templateGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2a\x48\x4c\xce\x4e\x4c\x4f\x55\x28\x49\xcd\x2d\xc8\x49\x2c\x49\x2d\xe6\xe2\xd2\xd7\x4f\xcf\xb7\x4a\x4f\xcd\x4b\x2d\x4a\x2c\x49\x55\x48\xcf\xd7\x4d\xca\xcc\x4b\x49\x2c\x49\x54\xd0\x2d\xc8\x4e\x57\x50\xa9\x76\xf7\x0f\x70\x74\xf6\x76\x74\x77\xad\x55\xd0\xcd\x57\x48\x2c\x2e\x4e\x2d\x29\xd6\x4b\xcf\x57\xd0\xe3\x02\x04\x00\x00\xff\xff\x21\xf2\x14\xd7\x4d\x00\x00\x00")
func templateGoBytes() ([]byte, error) {
return bindataRead(
_templateGo,
"template.go",
)
}
func templateGo() (*asset, error) {
bytes, err := templateGoBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "template.go", size: 77, mode: os.FileMode(420), modTime: time.Unix(1531821454, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
}
return a.bytes, nil
}
return nil, fmt.Errorf("Asset %s not found", name)
}
// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
func MustAsset(name string) []byte {
a, err := Asset(name)
if err != nil {
panic("asset: Asset(" + name + "): " + err.Error())
}
return a
}
// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
}
return a.info, nil
}
return nil, fmt.Errorf("AssetInfo %s not found", name)
}
// AssetNames returns the names of the assets.
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
for name := range _bindata {
names = append(names, name)
}
return names
}
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
".DS_Store": Ds_store,
"BUILD": build,
"assets.go": assetsGo,
"client.tmpl": clientTmpl,
"server.tmpl": serverTmpl,
"service.tmpl": serviceTmpl,
"template.go": templateGo,
}
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
// data/
// foo.txt
// img/
// a.png
// b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
cannonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
}
}
if node.Func != nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
rv := make([]string, 0, len(node.Children))
for childName := range node.Children {
rv = append(rv, childName)
}
return rv, nil
}
type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}
var _bintree = &bintree{nil, map[string]*bintree{
".DS_Store": &bintree{Ds_store, map[string]*bintree{}},
"BUILD": &bintree{build, map[string]*bintree{}},
"assets.go": &bintree{assetsGo, map[string]*bintree{}},
"client.tmpl": &bintree{clientTmpl, map[string]*bintree{}},
"server.tmpl": &bintree{serverTmpl, map[string]*bintree{}},
"service.tmpl": &bintree{serviceTmpl, map[string]*bintree{}},
"template.go": &bintree{templateGo, map[string]*bintree{}},
}}
// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
return err
}
info, err := AssetInfo(name)
if err != nil {
return err
}
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
if err != nil {
return err
}
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
if err != nil {
return err
}
return nil
}
// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
if err != nil {
return RestoreAsset(dir, name)
}
// Dir
for _, child := range children {
err = RestoreAssets(dir, filepath.Join(name, child))
if err != nil {
return err
}
}
return nil
}
func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}

View File

@@ -0,0 +1,41 @@
// Package client generate by warden_gen
package client
import (
"fmt"
{{range $key, $value := .Imports}}"{{$key}}"
{{end}}{{range $key, $value := .ClientImports}}"{{$key}}"
{{end}}
pb "{{.PbPackage}}"
"go-common/net/rpc/warden"
"go-common/app/warden/balancer/wrr"
"google.golang.org/grpc"
)
const (
treeID = 2233
)
// New {{.Name}} service client
func New(conf *warden.ClientConfig) *Client {
target := fmt.Sprintf("discovery://default/%d", treeID)
wc := warden.NewClient(conf, grpc.WithBalancerName(wrr.Name))
conn, err := wc.Dial(context.TODO(), target)
if err != nil {
panic(err)
}
return &Client{pb.New{{.Name}}Client(conn)}
}
// Client service client
type Client struct {
grpcClient pb.{{.Name}}Client
}
{{range .Methods}}
// {{range .Comments}}{{.}}{{end}}
func (c *Client) {{.Name}}({{.ParamBlock}}) ({{.ReturnBlock}}) {
panic("not implemented")
}
{{end}}

View File

@@ -0,0 +1,31 @@
// Package server generate by warden_gen
package server
import (
{{range $key, $value := .Imports}}"{{$key}}"{{end}}
service "{{.RecvPackage}}"
pb "{{.PbPackage}}"
"go-common/library/net/rpc/warden"
)
// New {{.Name}} warden rpc server
func New(c *warden.ServerConfig, svr *service.{{.RecvName}}) (*warden.Server) {
ws := warden.NewServer(c)
pb.Register{{.Name}}Server(ws.Server(), &server{svr})
return ws
}
type server struct {
svr *service.{{.RecvName}}
}
var _ pb.{{.Name}}Server = &server{}
{{range .Methods}}
// {{range .Comments}}{{.}}{{end}}
func(s *server) {{.Name}}(ctx context.Context, req *pb.{{.Name}}Req) (*pb.{{.Name}}Reply, error) {
panic("not implemented")
}
{{end}}

View File

@@ -0,0 +1,16 @@
syntax = "proto3";
package {{.Package}};
{{range $key, $value := .Imports}}
import "{{$key}}";{{end}}
option go_package = "v1";
{{range .Messages}}
message {{.Name}} { {{range .Fields}}
{{.FieldType}} {{.FieldName}} = {{.FieldID}};{{end}}
}
{{end}}
service {{.Name}} { {{range .Methods}}
{{range .Comments}}// {{.}} {{end}}
rpc {{.Name}}({{.Req}}) returns({{.Reply}}); {{end}}
}

View File

@@ -0,0 +1,3 @@
package templates
//go:generate go-bindata -pkg ${GOPACKAGE} -o assets.go .

View File

@@ -0,0 +1,38 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["goparser_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["goparser.go"],
importpath = "go-common/app/tool/warden/goparser",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = ["//app/tool/warden/types:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,501 @@
package goparser
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"log"
"os"
"path"
"path/filepath"
"regexp"
"runtime"
"strings"
"go-common/app/tool/warden/types"
)
var protoFileRegexp *regexp.Regexp
const (
optionsPrefix = "+wd:"
)
func init() {
protoFileRegexp = regexp.MustCompile(`//\s+source:\s+(.*\.proto)`)
}
// GoPackage get go package name from file or directory path
func GoPackage(dpath string) (string, error) {
if strings.HasSuffix(dpath, ".go") {
dpath = filepath.Dir(dpath)
}
absDir, err := filepath.Abs(dpath)
if err != nil {
return "", err
}
goPaths := os.Getenv("GOPATH")
if goPaths == "" {
return "", fmt.Errorf("GOPATH not set")
}
for _, goPath := range strings.Split(goPaths, ":") {
srcPath := path.Join(goPath, "src")
if !strings.HasPrefix(absDir, srcPath) {
continue
}
return strings.Trim(absDir[len(srcPath):], "/"), nil
}
return "", fmt.Errorf("give package not under $GOPATH")
}
// Parse service spec with gived path and receiver name
func Parse(name, dpath, recvName, workDir string) (*types.ServiceSpec, error) {
if workDir == "" {
workDir, _ = os.Getwd()
}
ps := &parseState{
name: strings.Title(name),
dpath: dpath,
recvName: recvName,
workDir: workDir,
}
return ps.parse()
}
type parseState struct {
dpath string
recvName string
name string
workDir string
typedb map[string]types.Typer
importPath string
packageName string
methods []*types.Method
}
func (p *parseState) parse() (spec *types.ServiceSpec, err error) {
p.typedb = make(map[string]types.Typer)
if p.importPath, err = GoPackage(p.dpath); err != nil {
return
}
if err := p.searchMethods(); err != nil {
return nil, err
}
return &types.ServiceSpec{
ImportPath: p.importPath,
Name: p.name,
Package: p.packageName,
Receiver: p.recvName,
Methods: p.methods,
}, nil
}
func (p *parseState) searchMethods() error {
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, p.dpath, nil, parser.ParseComments)
if err != nil {
return err
}
if len(pkgs) == 0 {
return fmt.Errorf("no package found on %s", p.dpath)
}
if len(pkgs) > 1 {
return fmt.Errorf("multiple package found on %s", p.dpath)
}
for pkgName, pkg := range pkgs {
//log.Printf("search method in package %s", pkgName)
p.packageName = pkgName
for fn, f := range pkg.Files {
//log.Printf("search method in file %s", fn)
if err = p.searchMethodsInFile(pkg, f); err != nil {
log.Printf("search method in %s err %s", fn, err)
}
}
}
return nil
}
func (p *parseState) searchMethodsInFile(pkg *ast.Package, f *ast.File) error {
for _, decl := range f.Decls {
funcDecl, ok := decl.(*ast.FuncDecl)
if !ok || !funcDecl.Name.IsExported() || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
continue
}
var recvIdent *ast.Ident
recvField := funcDecl.Recv.List[0]
switch rt := recvField.Type.(type) {
case *ast.Ident:
recvIdent = rt
case *ast.StarExpr:
recvIdent = rt.X.(*ast.Ident)
}
if recvIdent == nil {
return fmt.Errorf("unknown recv %v", recvField)
}
if recvIdent.Name != p.recvName {
continue
}
log.Printf("find method %s", funcDecl.Name.Name)
if err := p.parseFuncDecl(pkg, f, funcDecl); err != nil {
return err
}
}
return nil
}
func (p *parseState) parseFuncDecl(pkg *ast.Package, f *ast.File, funcDecl *ast.FuncDecl) error {
//log.Printf("parse method %s", funcDecl.Name.Name)
comments, options := parseComments(funcDecl)
for _, option := range options {
if option == "ignore" {
log.Printf("ignore method %s", funcDecl.Name.Name)
return nil
}
}
ps := typeState{
File: f,
ImportPath: p.importPath,
Pkg: pkg,
WorkDir: p.workDir,
typedb: p.typedb,
PkgDir: p.dpath,
}
parameters, err := ps.parseFieldList(funcDecl.Type.Params, false)
if err != nil {
return err
}
results, err := ps.parseFieldList(funcDecl.Type.Results, false)
if err != nil {
return err
}
method := &types.Method{
Name: funcDecl.Name.Name,
Comments: comments,
Options: options,
Parameters: parameters,
Results: results,
}
p.methods = append(p.methods, method)
return nil
}
type typeState struct {
typedb map[string]types.Typer
ImportPath string
Pkg *ast.Package
File *ast.File
WorkDir string
PkgDir string
}
func (t *typeState) parseType(expr ast.Expr, ident string) (types.Typer, error) {
oldFile := t.File
defer func() {
t.File = oldFile
}()
switch exp := expr.(type) {
case *ast.Ident:
if isBuildIn(exp.Name) {
return &types.BasicType{Name: exp.Name}, nil
}
tid := fmt.Sprintf("%s-%s-%s", t.ImportPath, t.Pkg.Name, exp.Name)
if ty, ok := t.typedb[tid]; ok {
return ty, nil
}
ty, err := t.searchType(exp)
if err != nil {
return nil, err
}
t.typedb[tid] = ty
return ty, nil
case *ast.StarExpr:
t, err := t.parseType(exp.X, ident)
if err != nil {
return nil, err
}
return t.SetReference(), nil
case *ast.SelectorExpr:
return t.parseSel(exp)
case *ast.ArrayType:
et, err := t.parseType(exp.Elt, ident)
if err != nil {
return nil, err
}
return &types.ArrayType{EltType: et}, nil
case *ast.MapType:
kt, err := t.parseType(exp.Key, ident)
if err != nil {
return nil, err
}
vt, err := t.parseType(exp.Value, ident)
if err != nil {
return nil, err
}
return &types.MapType{KeyType: kt, ValueType: vt}, nil
case *ast.InterfaceType:
return &types.InterfaceType{
ImportPath: t.ImportPath,
Package: t.Pkg.Name,
IdentName: ident,
}, nil
case *ast.StructType:
fields, err := t.parseFieldList(exp.Fields, true)
return &types.StructType{
IdentName: ident,
ImportPath: t.ImportPath,
Package: t.Pkg.Name,
Fields: fields,
ProtoFile: findProtoFile(t.PkgDir, t.File),
}, err
}
return nil, fmt.Errorf("unexpect expr %v", expr)
}
func (t *typeState) searchType(ident *ast.Ident) (types.Typer, error) {
//log.Printf("search type %s", ident.Name)
for fn, f := range t.Pkg.Files {
//log.Printf("search in %s", fn)
for _, decl := range f.Decls {
if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.TYPE {
for _, spec := range genDecl.Specs {
typeSpec, ok := spec.(*ast.TypeSpec)
if !ok {
return nil, fmt.Errorf("expect typeSpec get %v in file %s", spec, fn)
}
if typeSpec.Name.Name == ident.Name {
//log.Printf("found in %s", fn)
t.File = f
return t.parseType(typeSpec.Type, ident.Name)
}
}
}
}
}
return nil, fmt.Errorf("type %s not found in package %s", ident.Name, t.Pkg.Name)
}
func lockType(pkg *ast.Package, ident *ast.Ident) (*ast.File, error) {
//log.Printf("lock type %s", ident.Name)
for fn, f := range pkg.Files {
//log.Printf("search in %s", fn)
for _, decl := range f.Decls {
if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.TYPE {
for _, spec := range genDecl.Specs {
typeSpec, ok := spec.(*ast.TypeSpec)
if !ok {
return nil, fmt.Errorf("expect typeSpec get %v in file %s fn", spec, fn)
}
if typeSpec.Name.Name == ident.Name {
return f, nil
}
}
}
}
}
return nil, fmt.Errorf("type %s not found in package %s", ident.Name, pkg.Name)
}
func (t *typeState) parseFieldList(fl *ast.FieldList, filterExported bool) ([]*types.Field, error) {
fields := make([]*types.Field, 0, fl.NumFields())
if fl == nil {
return fields, nil
}
for _, af := range fl.List {
ty, err := t.parseType(af.Type, "")
if err != nil {
return nil, err
}
if af.Names == nil {
fields = append(fields, &types.Field{Type: ty})
} else {
for _, name := range af.Names {
if filterExported && !name.IsExported() {
continue
}
fields = append(fields, &types.Field{Type: ty, Name: name.Name})
}
}
}
return fields, nil
}
func (t *typeState) parseSel(sel *ast.SelectorExpr) (types.Typer, error) {
//log.Printf("parse sel %v.%v", sel.X, sel.Sel)
x, ok := sel.X.(*ast.Ident)
if !ok {
return nil, fmt.Errorf("unsupport sel.X type %v", sel.X)
}
var pkg *ast.Package
var pkgPath string
var err error
var importPath string
var found bool
var pkgs map[string]*ast.Package
for _, spec := range t.File.Imports {
importPath = strings.Trim(spec.Path.Value, "\"")
if spec.Name != nil && spec.Name.Name == x.Name {
pkgPath, err = importPackage(t.WorkDir, importPath)
if err != nil {
return nil, err
}
pkgs, err = parser.ParseDir(token.NewFileSet(), pkgPath, nil, parser.ParseComments)
if err != nil {
return nil, err
}
pkg, err = filterPkgs(pkgs)
if err != nil {
return nil, err
}
found = true
break
}
pkgPath, err = importPackage(t.WorkDir, importPath)
if err != nil {
return nil, err
}
pkgs, err = parser.ParseDir(token.NewFileSet(), pkgPath, nil, parser.ParseComments)
if err != nil {
return nil, err
}
if pkg, ok = pkgs[x.Name]; ok {
found = true
break
}
}
if !found {
return nil, fmt.Errorf("can't found type %s.%s", x.Name, sel.Sel.Name)
}
file, err := lockType(pkg, sel.Sel)
if err != nil {
return nil, err
}
ts := &typeState{
File: file,
Pkg: pkg,
ImportPath: importPath,
WorkDir: t.WorkDir,
typedb: t.typedb,
PkgDir: pkgPath,
}
return ts.searchType(sel.Sel)
}
func filterPkgs(pkgs map[string]*ast.Package) (*ast.Package, error) {
for pname, pkg := range pkgs {
if strings.HasSuffix(pname, "_test") {
continue
}
return pkg, nil
}
return nil, fmt.Errorf("no package found")
}
func importPackage(workDir, importPath string) (string, error) {
//log.Printf("import package %s", importPath)
searchPaths := make([]string, 0, 3)
searchPaths = append(searchPaths, path.Join(runtime.GOROOT(), "src"))
if vendorDir, ok := searchVendor(workDir); ok {
searchPaths = append(searchPaths, vendorDir)
}
for _, goPath := range strings.Split(os.Getenv("GOPATH"), ":") {
searchPaths = append(searchPaths, path.Join(goPath, "src"))
}
var pkgPath string
var found bool
for _, basePath := range searchPaths {
pkgPath = path.Join(basePath, importPath)
if stat, err := os.Stat(pkgPath); err == nil && stat.IsDir() {
found = true
break
}
}
if !found {
return "", fmt.Errorf("can't import package %s", importPath)
}
return pkgPath, nil
}
func searchVendor(workDir string) (vendorDir string, ok bool) {
var err error
if workDir, err = filepath.Abs(workDir); err != nil {
return "", false
}
goPath := os.Getenv("GOPATH")
for {
if !strings.HasPrefix(workDir, goPath) {
break
}
vendorDir := path.Join(workDir, "vendor")
if stat, err := os.Stat(vendorDir); err == nil && stat.IsDir() {
return vendorDir, true
}
workDir = filepath.Dir(workDir)
}
return
}
func parseComments(funcDecl *ast.FuncDecl) (comments []string, options []string) {
if funcDecl.Doc == nil {
return
}
for _, comment := range funcDecl.Doc.List {
text := strings.TrimLeft(comment.Text, "/ ")
if strings.HasPrefix(text, optionsPrefix) {
options = append(options, text[len(optionsPrefix):])
} else {
comments = append(comments, text)
}
}
return
}
func isBuildIn(t string) bool {
switch t {
case "bool", "byte", "complex128", "complex64", "error", "float32",
"float64", "int", "int16", "int32", "int64", "int8",
"rune", "string", "uint", "uint16", "uint32", "uint64", "uint8", "uintptr":
return true
}
return false
}
func findProtoFile(pkgDir string, f *ast.File) string {
if f.Comments == nil {
return ""
}
for _, comment := range f.Comments {
if comment.List == nil {
continue
}
for _, line := range comment.List {
if protoFile := extractProtoFile(line.Text); protoFile != "" {
fixPath := path.Join(pkgDir, protoFile)
if s, err := os.Stat(fixPath); err == nil && !s.IsDir() {
return fixPath
}
return protoFile
}
}
}
return ""
}
func extractProtoFile(line string) string {
matchs := protoFileRegexp.FindStringSubmatch(line)
if len(matchs) > 1 {
return matchs[1]
}
return ""
}

View File

@@ -0,0 +1,75 @@
package goparser
import (
"os"
"testing"
)
var dpath = "/Users/weicheng/Go/src/go-common/app/service/account/service"
//var dpath = "/Users/weicheng/Go/src/playground/testgen/service"
func TestParse(t *testing.T) {
spec, err := Parse("account", dpath, "Service", dpath)
if err != nil {
t.Fatal(err)
}
for _, method := range spec.Methods {
t.Logf("method %s", method.Name)
for _, param := range method.Parameters {
t.Logf(">> param %s", param)
}
for _, result := range method.Results {
t.Logf("<< result %s", result)
}
}
}
func TestExtractProtoFile(t *testing.T) {
comment := "// source: article.proto\n"
protoFile := extractProtoFile(comment)
if protoFile != "article.proto" {
t.Errorf("expect %s get %s", "article.proto", protoFile)
}
}
func TestGoPackage(t *testing.T) {
os.Setenv("GOPATH", "/go:/go1:/go3")
type args struct {
dpath string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "test1",
args: args{"/go/src/hello/hello.go"},
want: "hello",
},
{
name: "test2",
args: args{"/go3/src/hello/foo/hello.go"},
want: "hello/foo",
},
{
name: "test3",
args: args{"/g/src/hello/foo/hello.go"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GoPackage(tt.args.dpath)
if (err != nil) != tt.wantErr {
t.Errorf("GoPackage() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("GoPackage() = %v, want %v", got, tt.want)
}
})
}
}

129
app/tool/warden/main.go Normal file
View File

@@ -0,0 +1,129 @@
package main
import (
"flag"
"fmt"
"log"
"os"
"path"
"strings"
"go-common/app/tool/warden/generator"
"go-common/app/tool/warden/goparser"
"go-common/app/tool/warden/types"
)
const (
// GoCommon .
GoCommon = "go-common"
)
var (
name string
dir string
recvName string
workDir string
protoOut string
csCode string
goPackage string
protoPackage string
ignoreTypeError bool
noprotoc bool
importPaths string
)
func init() {
flag.StringVar(&name, "name", "", "service name")
flag.StringVar(&dir, "dir", "service", "service go code dir")
flag.StringVar(&recvName, "recv", "Service", "receiver name")
flag.StringVar(&workDir, "workdir", ".", "workdir")
flag.StringVar(&csCode, "cs-code", "server/grpc", "server code directory")
flag.StringVar(&protoOut, "proto-out", "api/api.proto", "proto file save path")
flag.StringVar(&goPackage, "go-package", "", "go-package")
flag.StringVar(&protoPackage, "proto-package", "", "proto-package")
flag.BoolVar(&ignoreTypeError, "ignore-type-error", true, "ignore type error")
flag.BoolVar(&noprotoc, "noprotoc", false, "don't run protoc")
flag.StringVar(&importPaths, "proto-path", defaultImportPath(), "specify the directory in which to search for imports.")
}
func defaultImportPath() string {
for _, goPath := range strings.Split(os.Getenv("GOPATH"), ":") {
fixPath := path.Join(goPath, "src", GoCommon)
if _, err := os.Stat(fixPath); err == nil {
return fixPath
}
}
return ""
}
func main() {
var err error
if !flag.Parsed() {
flag.Parse()
}
if name == "" {
log.Fatal("service name required")
}
var servicePackage string
servicePackage, err = goparser.GoPackage(dir)
if err != nil {
log.Fatalf("auto detect gopackage error %s", err)
}
if goPackage == "" {
// auto set go package
goPackage = path.Join(path.Dir(servicePackage), csCode)
}
if protoPackage == "" {
log.Fatal("proto package name required")
}
var spec *types.ServiceSpec
spec, err = goparser.Parse(name, dir, recvName, workDir)
if err != nil {
log.Fatal(err)
}
var paths []string
if importPaths != "" {
paths = strings.Split(importPaths, ",")
}
options := &generator.ServiceProtoOptions{
GoPackage: goPackage,
ProtoPackage: protoPackage,
IgnoreType: ignoreTypeError,
ImportPaths: paths,
}
protoFile := path.Join(workDir, protoOut)
if err = os.MkdirAll(path.Dir(protoFile), 0755); err != nil {
log.Print(err)
}
var protoFp *os.File
protoFp, err = os.OpenFile(protoFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer protoFp.Close()
if err := generator.GenServiceProto(protoFp, spec, options); err != nil {
log.Fatal(err)
}
if !noprotoc {
if err := generator.Protoc(protoFile, "", "", paths); err != nil {
log.Fatal(err)
}
}
csOptions := &generator.GenCSCodeOptions{
PbPackage: path.Join(path.Dir(servicePackage), path.Dir(protoOut)),
RecvName: recvName,
RecvPackage: servicePackage,
}
if err := generator.GenCSCode(csCode, spec, csOptions); err != nil {
log.Fatal(err)
}
fmt.Printf(`
🍺 (゜-゜)つロ 干杯~ !
proto file: %s
server: %s
`, protoFile, path.Join(csCode, "server.go"))
}

135
app/tool/warden/protoc.sh Normal file
View File

@@ -0,0 +1,135 @@
#!/bin/bash
DEFAULT_PROTOC_GEN="gogofast"
DEFAULT_PROTOC="protoc"
GO_COMMON_DIR_NAME="go-common"
USR_INCLUDE_DIR="/usr/local/include"
function _install_protoc() {
osname=$(uname -s)
echo "install protoc ..."
case $osname in
"Darwin" )
brew install protobuf
;;
*)
echo "unknown operating system, need install protobuf manual see: https://developers.google.com/protocol-buffers"
exit 1
;;
esac
}
function _install_protoc_gen() {
local protoc_gen=$1
case $protoc_gen in
"gofast" )
echo "install gofast from github.com/gogo/protobuf/protoc-gen-gofast"
go get github.com/gogo/protobuf/protoc-gen-gofast
;;
"gogofast" )
echo "install gogofast from github.com/gogo/protobuf/protoc-gen-gogofast"
go get github.com/gogo/protobuf/protoc-gen-gogofast
;;
"gogo" )
echo "install gogo from github.com/gogo/protobuf/protoc-gen-gogo"
go get github.com/gogo/protobuf/protoc-gen-gogo
;;
"go" )
echo "install protoc-gen-go from github.com/golang/protobuf"
go get github.com/golang/protobuf/{proto,protoc-gen-go}
;;
*)
echo "can't install protoc-gen-${protoc_gen} automatic !"
exit 1;
;;
esac
}
function _find_go_common_dir() {
local go_common_dir_name=$1
local current_dir=$(pwd)
while [[ "$(basename $current_dir)" != "$go_common_dir_name" ]]; do
current_dir=$(dirname $current_dir)
if [[ "$current_dir" == "/" || "$current_dir" == "." || -z "$current_dir" ]]; then
return 1
fi
done
echo $current_dir
}
function _fix_pb_file() {
local target_dir=$1
echo "fix pb file"
local pb_files=$(find $target_dir -name "*.pb.go" -type f)
local pkg_name_esc=$(echo "$target_dir" | sed 's_/_\\/_g')
for file in $pb_files; do
echo "fix pb file $file"
if [[ $(uname -s) == 'Darwin' ]]; then
sed -i "" -e "s/^import \(.*\) \"app\/\(.*\)\"/import \1 \"go-common\/app\/\2\"/g" $file
else
sed -i"" -E "s/^import\s*(.*)\s*\"app\/(.*)\"/import\1\"go-common\/app\/\2\"/g" $file
fi
done
}
function _esc_string() {
echo $(echo "$1" | sed 's_/_\\/_g')
}
function _run_protoc() {
local proto_dir=$1
local proto_files=$(find $proto_dir -maxdepth 1 -name "*.proto")
if [[ -z $proto_files ]]; then
return
fi
local protoc_cmd="$PROTOC -I$PROTO_PATH --${PROTOC_GEN}_out=plugins=grpc:. ${proto_files}"
echo $protoc_cmd
$protoc_cmd
}
if [[ -z $PROTOC ]]; then
PROTOC=${DEFAULT_PROTOC}
which $PROTOC
if [[ "$?" -ne "0" ]]; then
_install_protoc
fi
fi
if [[ -z $PROTOC_GEN ]]; then
PROTOC_GEN=${DEFAULT_PROTOC_GEN}
which protoc-gen-$PROTOC_GEN
if [[ "$?" -ne "0" ]]; then
_install_protoc_gen $PROTOC_GEN
fi
fi
GO_COMMON_DIR=$(_find_go_common_dir $GO_COMMON_DIR_NAME)
if [[ "$?" != "0" ]]; then
echo "can't find go-common directoy"
exit 1
fi
if [[ -z $PROTO_PATH ]]; then
PROTO_PATH=$GO_COMMON_DIR:$GO_COMMON_DIR/vendor:$USR_INCLUDE_DIR
else
PROTO_PATH=$PROTO_PATH:$GO_COMMON_DIR:$GO_COMMON_DIR/vendor:$USR_INCLUDE_DIR
fi
if [[ ! -z $1 ]]; then
cd $1
fi
TARGET_DIR=$(pwd)
GO_COMMON_DIR_ESC=$(_esc_string "$GO_COMMON_DIR/")
TARGET_DIR=${TARGET_DIR//$GO_COMMON_DIR_ESC/}
# switch to go_common
cd $GO_COMMON_DIR
DIRS=$(find $TARGET_DIR -type d)
for dir in $DIRS; do
echo "run protoc in $dir"
_run_protoc $dir
done
_fix_pb_file $TARGET_DIR

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["types.go"],
importpath = "go-common/app/tool/warden/types",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,153 @@
package types
// ServiceSpec service spec
type ServiceSpec struct {
Name string
// origin service package name
Package string
// service import path
ImportPath string
Receiver string
Methods []*Method
}
// Method service method
type Method struct {
Name string // method name
Comments []string
Options []string
Parameters []*Field
Results []*Field
}
// Field a pair of parameter name and type
type Field struct {
Name string
Type Typer
}
func (f *Field) String() string {
if f.Name == "" {
return f.Type.String()
}
return f.Name + " " + f.Type.String()
}
// Typer type interface
type Typer interface {
String() string
IsReference() bool
SetReference() Typer
}
// BasicType go buildin type
type BasicType struct {
Name string
Reference bool
}
// IsReference return is reference
func (t *BasicType) IsReference() bool {
return t.Reference
}
// SetReference return is reference
func (t *BasicType) SetReference() Typer {
t.Reference = true
return t
}
func (t *BasicType) String() string {
return t.Name
}
// ArrayType array type
type ArrayType struct {
EltType Typer
Reference bool
}
func (t *ArrayType) String() string {
return "[]" + t.EltType.String()
}
// IsReference return is reference
func (t *ArrayType) IsReference() bool {
return t.Reference
}
// SetReference return is reference
func (t *ArrayType) SetReference() Typer {
t.Reference = true
return t
}
// MapType map
type MapType struct {
KeyType Typer
ValueType Typer
Reference bool
}
// IsReference return is reference
func (t *MapType) IsReference() bool {
return t.Reference
}
func (t *MapType) String() string {
return "[" + t.KeyType.String() + "]" + t.ValueType.String()
}
// SetReference return is reference
func (t *MapType) SetReference() Typer {
t.Reference = true
return t
}
// StructType struct type
type StructType struct {
Package string
ImportPath string
IdentName string
Reference bool
Fields []*Field
ProtoFile string
}
func (t *StructType) String() string {
return t.Package + "." + t.IdentName
}
// IsReference return is reference
func (t *StructType) IsReference() bool {
return t.Reference
}
// SetReference return is reference
func (t *StructType) SetReference() Typer {
t.Reference = true
return t
}
// InterfaceType struct type
type InterfaceType struct {
Package string
ImportPath string
IdentName string
Reference bool
}
func (t *InterfaceType) String() string {
return t.Package + "." + t.IdentName
}
// IsReference return is reference
func (t *InterfaceType) IsReference() bool {
return t.Reference
}
// SetReference return is reference
func (t *InterfaceType) SetReference() Typer {
t.Reference = true
return t
}