1
2
3
4
5
6
7 package main
8
9 import (
10 "bytes"
11 "errors"
12 "flag"
13 "fmt"
14 "os"
15 "os/exec"
16 "path/filepath"
17 "simd/archsimd/_gen/gentools"
18 "simd/archsimd/_gen/specdoc"
19 "simd/archsimd/_gen/specgen"
20 "strings"
21 )
22
23 func main() {
24 genOpts := gentools.RegisterFlags(nil)
25 noFillDoc := flag.Bool("no-fill-doc", false, "disable filling doc comments from spec")
26 noFillNames := flag.Bool("no-fill-names", false, "disable filling parameter and result names from spec")
27
28 dirs := []string{
29 filepath.Join(genOpts.GOROOT, "src/simd"),
30 filepath.Join(genOpts.GOROOT, "src/simd/archsimd"),
31 }
32
33 flag.Usage = func() {
34 w := flag.CommandLine.Output()
35 fmt.Fprintf(w, "usage: specfill [flags] [files...]\n\n")
36 fmt.Fprintf(w, "If files are not given, specfill applies to all non-test, non-generated files in:\n")
37 for _, d := range dirs {
38 fmt.Fprintf(w, " %s\n", d)
39 }
40 fmt.Fprintln(w)
41 flag.CommandLine.PrintDefaults()
42 }
43
44 flag.Parse()
45
46 specDir := specgen.MustFindSpecDir(genOpts.GOROOT)
47 specFuncs, err := specgen.Load(specDir, nil)
48 if err != nil {
49 fmt.Fprintf(os.Stderr, "loading spec: %v\n", err)
50 os.Exit(1)
51 }
52 specIdx := specgen.NewIndex(specFuncs)
53
54 var targetFiles []string
55 filesArgs := false
56 if flag.NArg() > 0 {
57 filesArgs = true
58 for _, arg := range flag.Args() {
59 p, err := filepath.Abs(arg)
60 if err != nil {
61 fmt.Fprintf(os.Stderr, "%s: %v\n", arg, err)
62 os.Exit(1)
63 }
64 targetFiles = append(targetFiles, p)
65 }
66 } else {
67 for _, dir := range dirs {
68 entries, err := os.ReadDir(dir)
69 if err != nil {
70 fmt.Fprintf(os.Stderr, "reading %s: %v\n", dir, err)
71 os.Exit(1)
72 }
73 for _, e := range entries {
74 if e.IsDir() || !strings.HasSuffix(e.Name(), ".go") || strings.HasSuffix(e.Name(), "_test.go") {
75 continue
76 }
77 targetFiles = append(targetFiles, filepath.Join(dir, e.Name()))
78 }
79 }
80 }
81
82 var files gentools.Files
83 defer files.FlushOrExit()
84
85 var combinedReport specdoc.Report
86
87 for _, filePath := range targetFiles {
88 relPath, err := filepath.Rel(genOpts.GOROOT, filePath)
89 if err != nil {
90 relPath = filePath
91 }
92 srcRelPath, err := filepath.Rel(filepath.Join(genOpts.GOROOT, "src"), filePath)
93 if err != nil {
94 srcRelPath = strings.TrimPrefix(filepath.ToSlash(relPath), "src/")
95 }
96 src, err := os.ReadFile(filePath)
97 if err != nil {
98 fmt.Fprintf(os.Stderr, "reading %s: %v\n", filePath, err)
99 os.Exit(1)
100 }
101 gen, err := isGenerated(src)
102 if err != nil {
103 fmt.Fprintf(os.Stderr, "%s: %v\n", filePath, err)
104 os.Exit(1)
105 }
106 if gen {
107 if filesArgs {
108 fmt.Fprintf(os.Stderr, "%s: file is generated\n", filePath)
109 os.Exit(1)
110 }
111 continue
112 }
113 rewritten, err := specdoc.Fill(src, specIdx, specdoc.Options{
114 Filename: filepath.ToSlash(relPath),
115 AllowDocRewrite: true,
116 NoFillDoc: *noFillDoc,
117 NoFillNames: *noFillNames,
118 })
119 var rep *specdoc.Report
120 if errors.As(err, &rep) {
121 combinedReport.Merge(rep)
122 } else if err != nil {
123 fmt.Fprintf(os.Stderr, "checking %s: %v\n", filePath, err)
124 os.Exit(1)
125 }
126 if rewritten != nil && !bytes.Equal(src, rewritten) {
127 buf := files.NewGoFile(filepath.ToSlash(srcRelPath))
128 buf.Write(rewritten)
129 }
130 }
131
132 combinedReport.Print(os.Stderr)
133
134 if genOpts.Write {
135 if err := checkCleanWorkingCopy(genOpts.GOROOT); err != nil {
136 fmt.Fprintf(os.Stderr, "refusing to write: %v\n", err)
137 os.Exit(1)
138 }
139 if !combinedReport.Empty() {
140 fmt.Fprintln(os.Stderr, "errors reported; not writing files")
141 os.Exit(1)
142 }
143 }
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163 func checkCleanWorkingCopy(dir string) error {
164 cmd := exec.Command("git", "status", "--porcelain")
165 cmd.Dir = dir
166 out, err := cmd.Output()
167 if err != nil {
168
169 return nil
170 }
171 if len(bytes.TrimSpace(out)) > 0 {
172 return fmt.Errorf("working copy has uncommitted changes:\n%s", out)
173 }
174 return nil
175 }
176
177
178
179
180
181
182
183 func isGenerated(src []byte) (bool, error) {
184 const marker = "// Code generated by"
185 if bytes.HasPrefix(src, []byte(marker)) || bytes.Contains(src, []byte("\n"+marker)) {
186 return true, nil
187 }
188 if bytes.Contains(src, []byte("DO NOT EDIT")) {
189 return false, errors.New("file contains 'DO NOT EDIT' but lacks '// Code generated by' header")
190 }
191 return false, nil
192 }
193
View as plain text