github.com/wolfd/bazel-gazelle@v0.14.0/internal/config/config.go (about) 1 /* Copyright 2017 The Bazel Authors. 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 16 package config 17 18 import ( 19 "flag" 20 "fmt" 21 "go/build" 22 "path/filepath" 23 "strings" 24 25 "github.com/bazelbuild/bazel-gazelle/internal/rule" 26 "github.com/bazelbuild/bazel-gazelle/internal/wspace" 27 ) 28 29 // Config holds information about how Gazelle should run. This is based on 30 // command line arguments, directives, other hints in build files. 31 // 32 // A Config applies to a single directory. A Config is created for the 33 // repository root directory, then copied and modified for each subdirectory. 34 // 35 // Config itself contains only general information. Most configuration 36 // information is language-specific and is stored in Exts. This information 37 // is modified by extensions that implement Configurer. 38 type Config struct { 39 // Dirs is a list of absolute, canonical paths to directories where Gazelle 40 // should run. 41 Dirs []string 42 43 // RepoRoot is the absolute, canonical path to the root directory of the 44 // repository with all symlinks resolved. 45 RepoRoot string 46 47 // RepoName is the name of the repository. 48 RepoName string 49 50 // ReadBuildFilesDir is the absolute path to a directory where 51 // build files should be read from instead of RepoRoot. 52 ReadBuildFilesDir string 53 54 // WriteBuildFilesDir is the absolute path to a directory where 55 // build files should be written to instead of RepoRoot. 56 WriteBuildFilesDir string 57 58 // ValidBuildFileNames is a list of base names that are considered valid 59 // build files. Some repositories may have files named "BUILD" that are not 60 // used by Bazel and should be ignored. Must contain at least one string. 61 ValidBuildFileNames []string 62 63 // ShouldFix determines whether Gazelle attempts to remove and replace 64 // usage of deprecated rules. 65 ShouldFix bool 66 67 // Exts is a set of configurable extensions. Generally, each language 68 // has its own set of extensions, but other modules may provide their own 69 // extensions as well. Values in here may be populated by command line 70 // arguments, directives in build files, or other mechanisms. 71 Exts map[string]interface{} 72 } 73 74 func New() *Config { 75 return &Config{ 76 ValidBuildFileNames: DefaultValidBuildFileNames, 77 Exts: make(map[string]interface{}), 78 } 79 } 80 81 // Clone creates a copy of the configuration for use in a subdirectory. 82 // Note that the Exts map is copied, but its contents are not. 83 // Configurer.Configure should do this, if needed. 84 func (c *Config) Clone() *Config { 85 cc := *c 86 cc.Exts = make(map[string]interface{}) 87 for k, v := range c.Exts { 88 cc.Exts[k] = v 89 } 90 return &cc 91 } 92 93 var DefaultValidBuildFileNames = []string{"BUILD.bazel", "BUILD"} 94 95 func (c *Config) IsValidBuildFileName(name string) bool { 96 for _, n := range c.ValidBuildFileNames { 97 if name == n { 98 return true 99 } 100 } 101 return false 102 } 103 104 func (c *Config) DefaultBuildFileName() string { 105 return c.ValidBuildFileNames[0] 106 } 107 108 // Configurer is the interface for language or library-specific configuration 109 // extensions. Most (ideally all) modifications to Config should happen 110 // via this interface. 111 type Configurer interface { 112 // RegisterFlags registers command-line flags used by the extension. This 113 // method is called once with the root configuration when Gazelle 114 // starts. RegisterFlags may set an initial values in Config.Exts. When flags 115 // are set, they should modify these values. 116 RegisterFlags(fs *flag.FlagSet, cmd string, c *Config) 117 118 // CheckFlags validates the configuration after command line flags are parsed. 119 // This is called once with the root configuration when Gazelle starts. 120 // CheckFlags may set default values in flags or make implied changes. 121 CheckFlags(fs *flag.FlagSet, c *Config) error 122 123 // KnownDirectives returns a list of directive keys that this Configurer can 124 // interpret. Gazelle prints errors for directives that are not recoginized by 125 // any Configurer. 126 KnownDirectives() []string 127 128 // Configure modifies the configuration using directives and other information 129 // extracted from a build file. Configure is called in each directory. 130 // 131 // c is the configuration for the current directory. It starts out as a copy 132 // of the configuration for the parent directory. 133 // 134 // rel is the slash-separated relative path from the repository root to 135 // the current directory. It is "" for the root directory itself. 136 // 137 // f is the build file for the current directory or nil if there is no 138 // existing build file. 139 Configure(c *Config, rel string, f *rule.File) 140 } 141 142 // CommonConfigurer handles language-agnostic command-line flags and directives, 143 // i.e., those that apply to Config itself and not to Config.Exts. 144 type CommonConfigurer struct { 145 repoRoot, buildFileNames, readBuildFilesDir, writeBuildFilesDir string 146 } 147 148 func (cc *CommonConfigurer) RegisterFlags(fs *flag.FlagSet, cmd string, c *Config) { 149 fs.StringVar(&cc.repoRoot, "repo_root", "", "path to a directory which corresponds to go_prefix, otherwise gazelle searches for it.") 150 fs.StringVar(&cc.buildFileNames, "build_file_name", strings.Join(DefaultValidBuildFileNames, ","), "comma-separated list of valid build file names.\nThe first element of the list is the name of output build files to generate.") 151 fs.StringVar(&cc.readBuildFilesDir, "experimental_read_build_files_dir", "", "path to a directory where build files should be read from (instead of -repo_root)") 152 fs.StringVar(&cc.writeBuildFilesDir, "experimental_write_build_files_dir", "", "path to a directory where build files should be written to (instead of -repo_root)") 153 } 154 155 func (cc *CommonConfigurer) CheckFlags(fs *flag.FlagSet, c *Config) error { 156 var err error 157 if cc.repoRoot == "" { 158 cc.repoRoot, err = wspace.Find(".") 159 if err != nil { 160 return fmt.Errorf("-repo_root not specified, and WORKSPACE cannot be found: %v", err) 161 } 162 } 163 c.RepoRoot, err = filepath.Abs(cc.repoRoot) 164 if err != nil { 165 return fmt.Errorf("%s: failed to find absolute path of repo root: %v", cc.repoRoot, err) 166 } 167 c.RepoRoot, err = filepath.EvalSymlinks(c.RepoRoot) 168 if err != nil { 169 return fmt.Errorf("%s: failed to resolve symlinks: %v", cc.repoRoot, err) 170 } 171 c.ValidBuildFileNames = strings.Split(cc.buildFileNames, ",") 172 if cc.readBuildFilesDir != "" { 173 c.ReadBuildFilesDir, err = filepath.Abs(cc.readBuildFilesDir) 174 if err != nil { 175 return fmt.Errorf("%s: failed to find absolute path of -read_build_files_dir: %v", cc.readBuildFilesDir, err) 176 } 177 } 178 if cc.writeBuildFilesDir != "" { 179 c.WriteBuildFilesDir, err = filepath.Abs(cc.writeBuildFilesDir) 180 if err != nil { 181 return fmt.Errorf("%s: failed to find absolute path of -write_build_files_dir: %v", cc.writeBuildFilesDir, err) 182 } 183 } 184 185 return nil 186 } 187 188 func (cc *CommonConfigurer) KnownDirectives() []string { 189 return []string{"build_file_name"} 190 } 191 192 func (cc *CommonConfigurer) Configure(c *Config, rel string, f *rule.File) { 193 if f == nil { 194 return 195 } 196 for _, d := range f.Directives { 197 if d.Key == "build_file_name" { 198 c.ValidBuildFileNames = strings.Split(d.Value, ",") 199 } 200 } 201 } 202 203 // CheckPrefix checks that a string may be used as a prefix. We forbid local 204 // (relative) imports and those beginning with "/". We allow the empty string, 205 // but generated rules must not have an empty importpath. 206 func CheckPrefix(prefix string) error { 207 if strings.HasPrefix(prefix, "/") || build.IsLocalImport(prefix) { 208 return fmt.Errorf("invalid prefix: %q", prefix) 209 } 210 return nil 211 } 212 213 // DependencyMode determines how imports of packages outside of the prefix 214 // are resolved. 215 type DependencyMode int 216 217 const ( 218 // ExternalMode indicates imports should be resolved to external dependencies 219 // (declared in WORKSPACE). 220 ExternalMode DependencyMode = iota 221 222 // VendorMode indicates imports should be resolved to libraries in the 223 // vendor directory. 224 VendorMode 225 ) 226 227 // DependencyModeFromString converts a string from the command line 228 // to a DependencyMode. Valid strings are "external", "vendor". An error will 229 // be returned for an invalid string. 230 func DependencyModeFromString(s string) (DependencyMode, error) { 231 switch s { 232 case "external": 233 return ExternalMode, nil 234 case "vendored": 235 return VendorMode, nil 236 default: 237 return 0, fmt.Errorf("unrecognized dependency mode: %q", s) 238 } 239 } 240 241 // ProtoMode determines how proto rules are generated. 242 type ProtoMode int 243 244 const ( 245 // DefaultProtoMode generates proto_library and new grpc_proto_library rules. 246 // .pb.go files are excluded when there is a .proto file with a similar name. 247 DefaultProtoMode ProtoMode = iota 248 249 // DisableProtoMode ignores .proto files. .pb.go files are treated 250 // as normal sources. 251 DisableProtoMode 252 253 // LegacyProtoMode generates filegroups for .proto files if .pb.go files 254 // are present in the same directory. 255 LegacyProtoMode 256 ) 257 258 func ProtoModeFromString(s string) (ProtoMode, error) { 259 switch s { 260 case "default": 261 return DefaultProtoMode, nil 262 case "disable": 263 return DisableProtoMode, nil 264 case "legacy": 265 return LegacyProtoMode, nil 266 default: 267 return 0, fmt.Errorf("unrecognized proto mode: %q", s) 268 } 269 }