blob: 461f41dddc9424a28c0870008a5c80a772b6c634 [file] [log] [blame]
Jeff Gastonaff66e52017-06-19 15:39:54 -07001// Copyright 2017 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// This file provides a command-line interface to bpfix
16
17// TODO(jeffrygaston) should this file be consolidated with bpfmt.go?
18
19package main
20
21import (
22 "bytes"
23 "flag"
24 "fmt"
25 "io"
26 "io/ioutil"
27 "os"
28 "os/exec"
29 "path/filepath"
30
31 "github.com/google/blueprint/parser"
32
33 "android/soong/bpfix/bpfix"
34)
35
36var (
37 // main operation modes
38 list = flag.Bool("l", false, "list files whose formatting differs from bpfmt's")
39 write = flag.Bool("w", false, "write result to (source) file instead of stdout")
40 doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
41)
42
43var (
44 exitCode = 0
45)
46
47func report(err error) {
48 fmt.Fprintln(os.Stderr, err)
49 exitCode = 2
50}
51
52func openAndProcess(filename string, out io.Writer, fixRequest bpfix.FixRequest) error {
53 f, err := os.Open(filename)
54 if err != nil {
55 return err
56 }
57 defer f.Close()
58 return processFile(filename, f, out, fixRequest)
59}
60
61// If in == nil, the source is the contents of the file with the given filename.
62func processFile(filename string, in io.Reader, out io.Writer, fixRequest bpfix.FixRequest) error {
63 // load the input file
64 src, err := ioutil.ReadAll(in)
65 if err != nil {
66 return err
67 }
68 r := bytes.NewBuffer(src)
69 file, errs := parser.Parse(filename, r, parser.NewScope(nil))
70 if len(errs) > 0 {
71 for _, err := range errs {
72 fmt.Fprintln(os.Stderr, err)
73 }
74 return fmt.Errorf("%d parsing errors", len(errs))
75 }
76
77 // compute and apply any requested fixes
Colin Cross3fad8952018-02-21 16:30:17 -080078 err = bpfix.FixTree(file, fixRequest)
Jeff Gastonaff66e52017-06-19 15:39:54 -070079 if err != nil {
80 return err
81 }
82
83 // output the results
Colin Cross3fad8952018-02-21 16:30:17 -080084 res, err := parser.Print(file)
Jeff Gastonaff66e52017-06-19 15:39:54 -070085 if err != nil {
86 return err
87 }
88 if !bytes.Equal(src, res) {
89 // contents have changed
90 if *list {
91 fmt.Fprintln(out, filename)
92 }
93 if *write {
94 err = ioutil.WriteFile(filename, res, 0644)
95 if err != nil {
96 return err
97 }
98 }
99 if *doDiff {
100 data, err := diff(src, res)
101 if err != nil {
102 return fmt.Errorf("computing diff: %s", err)
103 }
104 fmt.Printf("diff %s bpfix/%s\n", filename, filename)
105 out.Write(data)
106 }
107 }
108 if !*list && !*write && !*doDiff {
109 _, err = out.Write(res)
110 }
111 return err
112}
113
114func makeFileVisitor(fixRequest bpfix.FixRequest) func(string, os.FileInfo, error) error {
115 return func(path string, f os.FileInfo, err error) error {
116 if err == nil && (f.Name() == "Blueprints" || f.Name() == "Android.bp") {
117 err = openAndProcess(path, os.Stdout, fixRequest)
118 }
119 if err != nil {
120 report(err)
121 }
122 return nil
123 }
124}
125
126func walkDir(path string, fixRequest bpfix.FixRequest) {
127 filepath.Walk(path, makeFileVisitor(fixRequest))
128}
129
130func main() {
131 flag.Parse()
132
133 fixRequest := bpfix.NewFixRequest().AddAll()
134
135 if flag.NArg() == 0 {
136 if *write {
137 fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input")
138 exitCode = 2
139 return
140 }
141 if err := processFile("<standard input>", os.Stdin, os.Stdout, fixRequest); err != nil {
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, fixRequest)
154 default:
155 if err := openAndProcess(path, os.Stdout, fixRequest); err != nil {
156 report(err)
157 }
158 }
159 }
160}
161
162func diff(b1, b2 []byte) (data []byte, err error) {
163 f1, err := ioutil.TempFile("", "bpfix")
164 if err != nil {
165 return
166 }
167 defer os.Remove(f1.Name())
168 defer f1.Close()
169
170 f2, err := ioutil.TempFile("", "bpfix")
171 if err != nil {
172 return
173 }
174 defer os.Remove(f2.Name())
175 defer f2.Close()
176
177 f1.Write(b1)
178 f2.Write(b2)
179
180 data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
181 if len(data) > 0 {
182 // diff exits with a non-zero status when the files don't match.
183 // Ignore that failure as long as we get output.
184 err = nil
185 }
186 return
187
188}