Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 1 | // Mostly copied from Go's src/cmd/gofmt: |
| 2 | // Copyright 2009 The Go Authors. All rights reserved. |
| 3 | // Use of this source code is governed by a BSD-style |
| 4 | // license that can be found in the LICENSE file. |
| 5 | |
| 6 | package main |
| 7 | |
| 8 | import ( |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 9 | "bytes" |
| 10 | "flag" |
| 11 | "fmt" |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 12 | "io" |
| 13 | "io/ioutil" |
| 14 | "os" |
| 15 | "os/exec" |
| 16 | "path/filepath" |
Jeff Gaston | 6cd6ebc | 2017-10-05 19:46:15 -0700 | [diff] [blame] | 17 | |
| 18 | "github.com/google/blueprint/parser" |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 19 | ) |
| 20 | |
| 21 | var ( |
| 22 | // main operation modes |
Jeff Gaston | 2ca9432 | 2017-10-06 16:50:47 -0700 | [diff] [blame] | 23 | list = flag.Bool("l", false, "list files whose formatting differs from bpfmt's") |
| 24 | overwriteSourceFile = flag.Bool("w", false, "write result to (source) file") |
| 25 | writeToStout = flag.Bool("o", false, "write result to stdout") |
| 26 | doDiff = flag.Bool("d", false, "display diffs instead of rewriting files") |
| 27 | sortLists = flag.Bool("s", false, "sort arrays") |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 28 | ) |
| 29 | |
| 30 | var ( |
| 31 | exitCode = 0 |
| 32 | ) |
| 33 | |
| 34 | func report(err error) { |
| 35 | fmt.Fprintln(os.Stderr, err) |
| 36 | exitCode = 2 |
| 37 | } |
| 38 | |
| 39 | func usage() { |
Jeff Gaston | 2ca9432 | 2017-10-06 16:50:47 -0700 | [diff] [blame] | 40 | usageViolation("") |
| 41 | } |
| 42 | |
| 43 | func usageViolation(violation string) { |
| 44 | fmt.Fprintln(os.Stderr, violation) |
Colin Cross | c1d8781 | 2018-02-23 10:55:25 -0800 | [diff] [blame] | 45 | fmt.Fprintln(os.Stderr, "usage: bpfmt [flags] [path ...]") |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 46 | flag.PrintDefaults() |
| 47 | os.Exit(2) |
| 48 | } |
| 49 | |
Jeff Gaston | 6cd6ebc | 2017-10-05 19:46:15 -0700 | [diff] [blame] | 50 | func processFile(filename string, out io.Writer) error { |
| 51 | f, err := os.Open(filename) |
| 52 | if err != nil { |
| 53 | return err |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 54 | } |
Jeff Gaston | 6cd6ebc | 2017-10-05 19:46:15 -0700 | [diff] [blame] | 55 | defer f.Close() |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 56 | |
Jeff Gaston | 6cd6ebc | 2017-10-05 19:46:15 -0700 | [diff] [blame] | 57 | return processReader(filename, f, out) |
| 58 | } |
| 59 | |
| 60 | func processReader(filename string, in io.Reader, out io.Writer) error { |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 61 | src, err := ioutil.ReadAll(in) |
| 62 | if err != nil { |
| 63 | return err |
| 64 | } |
| 65 | |
| 66 | r := bytes.NewBuffer(src) |
| 67 | |
| 68 | file, errs := parser.Parse(filename, r, parser.NewScope(nil)) |
| 69 | if len(errs) > 0 { |
| 70 | for _, err := range errs { |
| 71 | fmt.Fprintln(os.Stderr, err) |
| 72 | } |
| 73 | return fmt.Errorf("%d parsing errors", len(errs)) |
| 74 | } |
| 75 | |
| 76 | if *sortLists { |
| 77 | parser.SortLists(file) |
| 78 | } |
| 79 | |
| 80 | res, err := parser.Print(file) |
| 81 | if err != nil { |
| 82 | return err |
| 83 | } |
| 84 | |
| 85 | if !bytes.Equal(src, res) { |
| 86 | // formatting has changed |
| 87 | if *list { |
| 88 | fmt.Fprintln(out, filename) |
| 89 | } |
Jeff Gaston | 2ca9432 | 2017-10-06 16:50:47 -0700 | [diff] [blame] | 90 | if *overwriteSourceFile { |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 91 | err = ioutil.WriteFile(filename, res, 0644) |
| 92 | if err != nil { |
| 93 | return err |
| 94 | } |
| 95 | } |
| 96 | if *doDiff { |
| 97 | data, err := diff(src, res) |
| 98 | if err != nil { |
| 99 | return fmt.Errorf("computing diff: %s", err) |
| 100 | } |
| 101 | fmt.Printf("diff %s bpfmt/%s\n", filename, filename) |
| 102 | out.Write(data) |
| 103 | } |
| 104 | } |
| 105 | |
Jeff Gaston | 2ca9432 | 2017-10-06 16:50:47 -0700 | [diff] [blame] | 106 | if !*list && !*overwriteSourceFile && !*doDiff { |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 107 | _, err = out.Write(res) |
| 108 | } |
| 109 | |
| 110 | return err |
| 111 | } |
| 112 | |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 113 | func walkDir(path string) { |
Jeff Gaston | 6cd6ebc | 2017-10-05 19:46:15 -0700 | [diff] [blame] | 114 | visitFile := func(path string, f os.FileInfo, err error) error { |
| 115 | if err == nil && f.Name() == "Blueprints" { |
| 116 | err = processFile(path, os.Stdout) |
| 117 | } |
| 118 | if err != nil { |
| 119 | report(err) |
| 120 | } |
| 121 | return nil |
| 122 | } |
| 123 | |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 124 | filepath.Walk(path, visitFile) |
| 125 | } |
| 126 | |
| 127 | func main() { |
Colin Cross | e52c33c | 2019-05-21 10:22:28 -0700 | [diff] [blame] | 128 | flag.Usage = usage |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 129 | flag.Parse() |
| 130 | |
Jeff Gaston | 2ca9432 | 2017-10-06 16:50:47 -0700 | [diff] [blame] | 131 | if !*writeToStout && !*overwriteSourceFile && !*doDiff && !*list { |
| 132 | usageViolation("one of -d, -l, -o, or -w is required") |
| 133 | } |
| 134 | |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 135 | if flag.NArg() == 0 { |
Jeff Gaston | 2ca9432 | 2017-10-06 16:50:47 -0700 | [diff] [blame] | 136 | // file to parse is stdin |
| 137 | if *overwriteSourceFile { |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 138 | fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input") |
Colin Cross | e52c33c | 2019-05-21 10:22:28 -0700 | [diff] [blame] | 139 | os.Exit(2) |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 140 | } |
Jeff Gaston | 6cd6ebc | 2017-10-05 19:46:15 -0700 | [diff] [blame] | 141 | if err := processReader("<standard input>", os.Stdin, os.Stdout); err != nil { |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 142 | report(err) |
| 143 | } |
| 144 | return |
| 145 | } |
| 146 | |
| 147 | for i := 0; i < flag.NArg(); i++ { |
| 148 | path := flag.Arg(i) |
| 149 | switch dir, err := os.Stat(path); { |
| 150 | case err != nil: |
| 151 | report(err) |
| 152 | case dir.IsDir(): |
| 153 | walkDir(path) |
| 154 | default: |
Jeff Gaston | 6cd6ebc | 2017-10-05 19:46:15 -0700 | [diff] [blame] | 155 | if err := processFile(path, os.Stdout); err != nil { |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 156 | report(err) |
| 157 | } |
| 158 | } |
| 159 | } |
Colin Cross | e52c33c | 2019-05-21 10:22:28 -0700 | [diff] [blame] | 160 | |
| 161 | os.Exit(exitCode) |
Colin Cross | 5ad47f4 | 2015-01-08 19:35:10 -0800 | [diff] [blame] | 162 | } |
| 163 | |
| 164 | func diff(b1, b2 []byte) (data []byte, err error) { |
| 165 | f1, err := ioutil.TempFile("", "bpfmt") |
| 166 | if err != nil { |
| 167 | return |
| 168 | } |
| 169 | defer os.Remove(f1.Name()) |
| 170 | defer f1.Close() |
| 171 | |
| 172 | f2, err := ioutil.TempFile("", "bpfmt") |
| 173 | if err != nil { |
| 174 | return |
| 175 | } |
| 176 | defer os.Remove(f2.Name()) |
| 177 | defer f2.Close() |
| 178 | |
| 179 | f1.Write(b1) |
| 180 | f2.Write(b2) |
| 181 | |
| 182 | data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() |
| 183 | if len(data) > 0 { |
| 184 | // diff exits with a non-zero status when the files don't match. |
| 185 | // Ignore that failure as long as we get output. |
| 186 | err = nil |
| 187 | } |
| 188 | return |
| 189 | |
| 190 | } |