1
16
17
18 package config
19
20 import (
21 "encoding/json"
22 "flag"
23 "fmt"
24 "io"
25 "io/ioutil"
26 "log"
27 "os"
28 "path/filepath"
29 "strings"
30
31 "github.com/bazelbuild/buildtools/tables"
32 "github.com/bazelbuild/buildtools/warn"
33 "github.com/bazelbuild/buildtools/wspace"
34 )
35
36 const buildifierJSONFilename = ".buildifier.json"
37
38
39 func New() *Config {
40 return &Config{
41 InputType: "auto",
42 }
43 }
44
45
46
47
48
49
50 func FindConfigPath(rootDir string) string {
51 if filename, ok := os.LookupEnv("BUILDIFIER_CONFIG"); ok {
52 return filename
53 }
54 if rootDir == "" {
55 rootDir, _ = os.Getwd()
56 }
57 dirname, err := wspace.Find(
58 rootDir,
59 map[string]func(os.FileInfo) bool{
60 buildifierJSONFilename: func(fi os.FileInfo) bool {
61 return fi.Mode()&os.ModeType == 0
62 },
63 },
64 )
65 if err != nil {
66 return ""
67 }
68 return filepath.Join(dirname, buildifierJSONFilename)
69 }
70
71
72 type Config struct {
73
74
75
76
77 InputType string `json:"type,omitempty"`
78
79 Format string `json:"format,omitempty"`
80
81 Mode string `json:"mode,omitempty"`
82
83 DiffMode bool `json:"diffMode,omitempty"`
84
85 Lint string `json:"lint,omitempty"`
86
87 Warnings string `json:"warnings,omitempty"`
88
89 WarningsList []string `json:"warningsList,omitempty"`
90
91 Recursive bool `json:"recursive,omitempty"`
92
93 Verbose bool `json:"verbose,omitempty"`
94
95
96
97 DiffCommand string `json:"diffCommand,omitempty"`
98
99
100 MultiDiff bool `json:"multiDiff,omitempty"`
101
102
103 TablesPath string `json:"tables,omitempty"`
104
105 AddTablesPath string `json:"addTables,omitempty"`
106
107 WorkspaceRelativePath string `json:"path,omitempty"`
108
109 DisableRewrites ArrayFlags `json:"buildifier_disable,omitempty"`
110
111 AllowSort ArrayFlags `json:"allowsort,omitempty"`
112
113
114 Help bool `json:"-"`
115
116 Version bool `json:"-"`
117
118 ConfigPath string `json:"-"`
119
120 LintWarnings []string `json:"-"`
121 }
122
123
124 func (c *Config) LoadFile() error {
125 file, err := os.Open(c.ConfigPath)
126 if err != nil {
127 return err
128 }
129 defer file.Close()
130 return c.LoadReader(file)
131 }
132
133
134 func (c *Config) LoadReader(in io.Reader) error {
135 data, err := ioutil.ReadAll(in)
136 if err != nil {
137 return fmt.Errorf("reading config: %w", err)
138 }
139 if err := json.Unmarshal(data, c); err != nil {
140 return err
141 }
142 return nil
143 }
144
145
146 func (c *Config) FlagSet(name string, errorHandling flag.ErrorHandling) *flag.FlagSet {
147 flags := flag.NewFlagSet(name, errorHandling)
148
149 flags.BoolVar(&c.Help, "help", false, "print usage information")
150 flags.BoolVar(&c.Version, "version", false, "print the version of buildifier")
151 flags.BoolVar(&c.Verbose, "v", c.Verbose, "print verbose information to standard error")
152 flags.BoolVar(&c.DiffMode, "d", c.DiffMode, "alias for -mode=diff")
153 flags.BoolVar(&c.Recursive, "r", c.Recursive, "find starlark files recursively")
154 flags.BoolVar(&c.MultiDiff, "multi_diff", c.MultiDiff, "the command specified by the -diff_command flag can diff multiple files in the style of tkdiff (default false)")
155 flags.StringVar(&c.Mode, "mode", c.Mode, "formatting mode: check, diff, or fix (default fix)")
156 flags.StringVar(&c.Format, "format", c.Format, "diagnostics format: text or json (default text)")
157 flags.StringVar(&c.DiffCommand, "diff_command", c.DiffCommand, "command to run when the formatting mode is diff (default uses the BUILDIFIER_DIFF, BUILDIFIER_MULTIDIFF, and DISPLAY environment variables to create the diff command)")
158 flags.StringVar(&c.Lint, "lint", c.Lint, "lint mode: off, warn, or fix (default off)")
159 flags.StringVar(&c.Warnings, "warnings", c.Warnings, "comma-separated warnings used in the lint mode or \"all\"")
160 flags.StringVar(&c.WorkspaceRelativePath, "path", c.WorkspaceRelativePath, "assume BUILD file has this path relative to the workspace directory")
161 flags.StringVar(&c.TablesPath, "tables", c.TablesPath, "path to JSON file with custom table definitions which will replace the built-in tables")
162 flags.StringVar(&c.AddTablesPath, "add_tables", c.AddTablesPath, "path to JSON file with custom table definitions which will be merged with the built-in tables")
163 flags.StringVar(&c.InputType, "type", c.InputType, "Input file type: build (for BUILD files), bzl (for .bzl files), workspace (for WORKSPACE files), module (for MODULE.bazel files), default (for generic Starlark files) or auto (default, based on the filename)")
164 flags.StringVar(&c.ConfigPath, "config", "", "path to .buildifier.json config file")
165 flags.Var(&c.AllowSort, "allowsort", "additional sort contexts to treat as safe")
166 flags.Var(&c.DisableRewrites, "buildifier_disable", "list of buildifier rewrites to disable")
167
168 return flags
169 }
170
171
172
173
174 func (c *Config) Validate(args []string) error {
175 if err := ValidateInputType(&c.InputType); err != nil {
176 return err
177 }
178
179 if err := ValidateFormat(&c.Format, &c.Mode); err != nil {
180 return err
181 }
182
183 if err := ValidateModes(&c.Mode, &c.Lint, &c.DiffMode); err != nil {
184 return err
185 }
186
187
188
189 if (c.WorkspaceRelativePath != "" || c.Mode == "print_if_changed") && len(args) > 1 {
190 return fmt.Errorf("can only format one file when using -path flag or -mode=print_if_changed")
191 }
192
193 if c.TablesPath != "" {
194 if err := tables.ParseAndUpdateJSONDefinitions(c.TablesPath, false); err != nil {
195 return fmt.Errorf("failed to parse %s for -tables: %w", c.TablesPath, err)
196 }
197 }
198
199 if c.AddTablesPath != "" {
200 if err := tables.ParseAndUpdateJSONDefinitions(c.AddTablesPath, true); err != nil {
201 return fmt.Errorf("failed to parse %s for -add_tables: %w", c.AddTablesPath, err)
202 }
203 }
204
205 warningsList := c.WarningsList
206 if c.Warnings != "" {
207 warningsList = append(warningsList, c.Warnings)
208 }
209 warnings := strings.Join(warningsList, ",")
210 lintWarnings, err := ValidateWarnings(&warnings, &warn.AllWarnings, &warn.DefaultWarnings)
211 if err != nil {
212 return err
213 }
214 c.LintWarnings = lintWarnings
215
216 return nil
217 }
218
219
220
221 func (c *Config) String() string {
222 data, err := json.MarshalIndent(c, "", " ")
223 if err != nil {
224 log.Panicf("config marshal json: %v", err)
225 }
226 return string(data)
227 }
228
229
230 type ArrayFlags []string
231
232
233 func (i *ArrayFlags) String() string {
234 return strings.Join(*i, ",")
235 }
236
237
238 func (i *ArrayFlags) Set(value string) error {
239 *i = append(*i, value)
240 return nil
241 }
242
243
244 func Example() *Config {
245 c := New()
246 c.InputType = "auto"
247 c.Mode = "fix"
248 c.Lint = "fix"
249 c.WarningsList = warn.AllWarnings
250 return c
251 }
252
View as plain text