forked from dsymonds/gotoc
/
main.go
128 lines (112 loc) · 3.02 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path"
"strings"
plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
"goprotobuf.googlecode.com/hg/proto"
"gotoc/parser"
"gotoc/resolver"
)
var (
// Flags
helpShort = flag.Bool("h", false, "Show usage text (same as --help).")
helpLong = flag.Bool("help", false, "Show usage text (same as -h).")
importPath = flag.String("import_path", ".", "Comma-separated list of paths to search for imports.")
pluginBinary = flag.String("plugin", "protoc-gen-go", "The code generator plugin to use.")
)
func fullPath(binary string, paths []string) string {
if strings.Index(binary, "/") >= 0 {
// path with path component
return binary
}
for _, p := range paths {
full := path.Join(p, binary)
fi, err := os.Stat(full)
if err == nil && fi.IsRegular() {
return full
}
}
return ""
}
func main() {
flag.Usage = usage
flag.Parse()
if *helpShort || *helpLong || flag.NArg() == 0 {
flag.Usage()
os.Exit(1)
}
fds, err := parser.ParseFiles(flag.Args(), strings.Split(*importPath, ",", -1))
if err != nil {
log.Exitf("Failed parsing: %v", err)
}
resolver.ResolveSymbols(fds)
fmt.Println("-----")
proto.MarshalText(os.Stdout, fds)
fmt.Println("-----")
// Find plugin.
pluginPath := fullPath(*pluginBinary, strings.Split(os.Getenv("PATH"), ":", -1))
if pluginPath == "" {
log.Exitf("Failed finding plugin binary %q", *pluginBinary)
}
// Start plugin subprocess.
pluginIn, meOut, err := os.Pipe()
if err != nil {
log.Exitf("Failed creating pipe: %v", err)
}
meIn, pluginOut, err := os.Pipe()
if err != nil {
log.Exitf("Failed creating pipe: %v", err)
}
pid, err := os.ForkExec(pluginPath, nil, nil, "/", []*os.File{pluginIn, pluginOut, os.Stderr})
if err != nil {
log.Exitf("Failed forking plugin: %v", err)
}
pluginIn.Close()
pluginOut.Close()
// Send request.
cgRequest := &plugin.CodeGeneratorRequest{
FileToGenerate: flag.Args(),
// TODO: proto_file should be topologically sorted (bottom-up)
ProtoFile: fds.File,
}
buf, err := proto.Marshal(cgRequest)
if err != nil {
log.Exitf("Failed marshaling CG request: %v", err)
}
_, err = meOut.Write(buf)
if err != nil {
log.Exitf("Failed writing CG request: %v", err)
}
meOut.Close()
w, err := os.Wait(pid, 0)
if err != nil {
log.Exitf("Failed waiting for plugin: %v", err)
}
if w.ExitStatus() != 0 {
log.Exitf("Plugin exited with status %d", w.ExitStatus())
}
// Read response.
cgResponse := new(plugin.CodeGeneratorResponse)
if buf, err = ioutil.ReadAll(meIn); err != nil {
log.Exitf("Failed reading CG response: %v", err)
}
if err = proto.Unmarshal(buf, cgResponse); err != nil {
log.Exitf("Failed unmarshaling CG response: %v", err)
}
// TODO: check cgResponse.Error
// TODO: write files
for _, f := range cgResponse.File {
fmt.Printf("--[ %v ]--\n", proto.GetString(f.Name))
fmt.Println(proto.GetString(f.Content))
}
fmt.Println("-----")
}
func usage() {
fmt.Fprintf(os.Stderr, "Usage: %s [options] <foo.proto> ...\n", os.Args[0])
flag.PrintDefaults()
}