1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package main
17
18 import (
19 "bytes"
20 "errors"
21 "flag"
22 "fmt"
23 "io/ioutil"
24 "log"
25 "os"
26 "os/exec"
27 "path/filepath"
28 "runtime"
29 "strings"
30 )
31
32 type genFileInfo struct {
33 base string
34 path string
35 expected bool
36 created bool
37 from *genFileInfo
38 unique bool
39 ambiguious bool
40 }
41
42 func run(args []string) error {
43
44 args, useParamFile, err := expandParamsFiles(args)
45 if err != nil {
46 return err
47 }
48 options := multiFlag{}
49 descriptors := multiFlag{}
50 expected := multiFlag{}
51 imports := multiFlag{}
52 flags := flag.NewFlagSet("protoc", flag.ExitOnError)
53 protoc := flags.String("protoc", "", "The path to the real protoc.")
54 outPath := flags.String("out_path", "", "The base output path to write to.")
55 plugin := flags.String("plugin", "", "The go plugin to use.")
56 importpath := flags.String("importpath", "", "The importpath for the generated sources.")
57 flags.Var(&options, "option", "The plugin options.")
58 flags.Var(&descriptors, "descriptor_set", "The descriptor set to read.")
59 flags.Var(&expected, "expected", "The expected output files.")
60 flags.Var(&imports, "import", "Map a proto file to an import path.")
61 if err := flags.Parse(args); err != nil {
62 return err
63 }
64
65
66
67 tmpDir, err := ioutil.TempDir("", "go_proto")
68 if err != nil {
69 return err
70 }
71 tmpDir = abs(tmpDir)
72 absOutPath := abs(*outPath)
73 defer os.RemoveAll(tmpDir)
74
75 pluginBase := filepath.Base(*plugin)
76 pluginName := strings.TrimSuffix(
77 strings.TrimPrefix(filepath.Base(*plugin), "protoc-gen-"), ".exe")
78 for _, m := range imports {
79 options = append(options, fmt.Sprintf("M%v", m))
80 }
81 if runtime.GOOS == "windows" {
82
83
84 *plugin = "\\\\?\\" + abs(*plugin)
85 }
86 protoc_args := []string{
87 fmt.Sprintf("--%v_out=%v:%v", pluginName, strings.Join(options, ","), tmpDir),
88 "--plugin", fmt.Sprintf("%v=%v", strings.TrimSuffix(pluginBase, ".exe"), *plugin),
89 "--descriptor_set_in", strings.Join(descriptors, string(os.PathListSeparator)),
90 }
91 protoc_args = append(protoc_args, flags.Args()...)
92
93 var cmd *exec.Cmd
94 if useParamFile {
95 paramFile, err := ioutil.TempFile(tmpDir, "protoc-*.params")
96 if err != nil {
97 return fmt.Errorf("error creating param file for protoc: %v", err)
98 }
99 for _, arg := range protoc_args {
100 _, err := fmt.Fprintln(paramFile, arg)
101 if err != nil {
102 return fmt.Errorf("error writing param file for protoc: %v", err)
103 }
104 }
105 cmd = exec.Command(*protoc, "@"+paramFile.Name())
106 } else {
107 cmd = exec.Command(*protoc, protoc_args...)
108 }
109
110 cmd.Stdout = os.Stdout
111 cmd.Stderr = os.Stderr
112 if err := cmd.Run(); err != nil {
113 return fmt.Errorf("error running protoc: %v", err)
114 }
115
116 files := map[string]*genFileInfo{}
117 byBase := map[string]*genFileInfo{}
118 for _, path := range expected {
119 info := &genFileInfo{
120 path: path,
121 base: filepath.Base(path),
122 expected: true,
123 unique: true,
124 }
125 files[info.path] = info
126 if byBase[info.base] != nil {
127 info.unique = false
128 byBase[info.base].unique = false
129 } else {
130 byBase[info.base] = info
131 }
132 }
133
134 filepath.Walk(tmpDir, func(path string, f os.FileInfo, err error) error {
135 relPath, err := filepath.Rel(tmpDir, path)
136 if err != nil {
137 return err
138 }
139 if relPath == "." {
140 return nil
141 }
142
143 if f.IsDir() {
144 if err := os.Mkdir(filepath.Join(absOutPath, relPath), f.Mode()); !os.IsExist(err) {
145 return err
146 }
147 return nil
148 }
149
150 if !strings.HasSuffix(path, ".go") {
151 return nil
152 }
153
154 info := &genFileInfo{
155 path: path,
156 base: filepath.Base(path),
157 created: true,
158 }
159
160 if foundInfo, ok := files[relPath]; ok {
161 foundInfo.created = true
162 foundInfo.from = info
163 return nil
164 }
165 files[relPath] = info
166 copyTo := byBase[info.base]
167 switch {
168 case copyTo == nil:
169
170 case !copyTo.unique:
171
172 case copyTo.from != nil:
173 copyTo.ambiguious = true
174 info.ambiguious = true
175 default:
176 copyTo.from = info
177 copyTo.created = true
178 info.expected = true
179 }
180 return nil
181 })
182 buf := &bytes.Buffer{}
183 for _, f := range files {
184 switch {
185 case f.expected && !f.created:
186
187
188
189 data := []byte("// +build ignore\n\npackage ignore")
190 if err := ioutil.WriteFile(abs(f.path), data, 0644); err != nil {
191 return err
192 }
193 case f.expected && f.ambiguious:
194 fmt.Fprintf(buf, "Ambiguious output %v.\n", f.path)
195 case f.from != nil:
196 data, err := ioutil.ReadFile(f.from.path)
197 if err != nil {
198 return err
199 }
200 if err := ioutil.WriteFile(abs(f.path), data, 0644); err != nil {
201 return err
202 }
203 case !f.expected:
204
205 }
206 if buf.Len() > 0 {
207 fmt.Fprintf(buf, "Check that the go_package option is %q.", *importpath)
208 return errors.New(buf.String())
209 }
210 }
211
212 return nil
213 }
214
215 func main() {
216 if err := run(os.Args[1:]); err != nil {
217 log.Fatal(err)
218 }
219 }
220
View as plain text