github.com/wolfd/bazel-gazelle@v0.14.0/internal/language/proto/config.go (about) 1 /* Copyright 2018 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 proto 17 18 import ( 19 "flag" 20 "fmt" 21 "log" 22 "path" 23 24 "github.com/bazelbuild/bazel-gazelle/internal/config" 25 "github.com/bazelbuild/bazel-gazelle/internal/rule" 26 ) 27 28 // ProtoConfig contains configuration values related to protos. 29 // 30 // This type is public because other languages need to generate rules based 31 // on protos, so this configuration may be relevant to them. 32 type ProtoConfig struct { 33 // Mode determines how rules are generated for protos. 34 Mode Mode 35 36 // ModeExplicit indicates whether the proto mode was set explicitly. 37 ModeExplicit bool 38 39 // GoPrefix is the current Go prefix (the Go extension may set this in the 40 // root directory only). Used to generate proto rule names in the root 41 // directory when there are no proto files or the proto package name 42 // can't be determined. 43 // TODO(jayconrod): deprecate and remove Go-specific behavior. 44 GoPrefix string 45 46 // groupOption is an option name that Gazelle will use to group .proto 47 // files into proto_library rules. If unset, the proto package name is used. 48 groupOption string 49 } 50 51 func GetProtoConfig(c *config.Config) *ProtoConfig { 52 return c.Exts[protoName].(*ProtoConfig) 53 } 54 55 // Mode determines how proto rules are generated. 56 type Mode int 57 58 const ( 59 // DefaultMode generates proto_library rules. Other languages should generate 60 // library rules based on these (e.g., go_proto_library) and should ignore 61 // checked-in generated files (e.g., .pb.go files) when there is a .proto 62 // file with a similar name. 63 DefaultMode Mode = iota 64 65 // DisableMode ignores .proto files and generates empty proto_library rules. 66 // Checked-in generated files (e.g., .pb.go files) should be treated as 67 // normal sources. 68 DisableMode 69 70 // DisableGlobalMode is similar to DisableMode, but it also prevents 71 // the use of special cases in dependency resolution for well known types 72 // and Google APIs. 73 DisableGlobalMode 74 75 // LegacyMode generates filegroups for .proto files if .pb.go files are 76 // present in the same directory. 77 LegacyMode 78 79 // PackageMode generates a proto_library for each set of .proto files with 80 // the same package name in each directory. 81 PackageMode 82 ) 83 84 func ModeFromString(s string) (Mode, error) { 85 switch s { 86 case "default": 87 return DefaultMode, nil 88 case "disable": 89 return DisableMode, nil 90 case "disable_global": 91 return DisableGlobalMode, nil 92 case "legacy": 93 return LegacyMode, nil 94 case "package": 95 return PackageMode, nil 96 default: 97 return 0, fmt.Errorf("unrecognized proto mode: %q", s) 98 } 99 } 100 101 func (m Mode) String() string { 102 switch m { 103 case DefaultMode: 104 return "default" 105 case DisableMode: 106 return "disable" 107 case DisableGlobalMode: 108 return "disable_global" 109 case LegacyMode: 110 return "legacy" 111 case PackageMode: 112 return "package" 113 default: 114 log.Panicf("unknown mode %d", m) 115 return "" 116 } 117 } 118 119 func (m Mode) ShouldGenerateRules() bool { 120 switch m { 121 case DisableMode, DisableGlobalMode, LegacyMode: 122 return false 123 default: 124 return true 125 } 126 } 127 128 func (m Mode) ShouldIncludePregeneratedFiles() bool { 129 switch m { 130 case DisableMode, DisableGlobalMode, LegacyMode: 131 return true 132 default: 133 return false 134 } 135 } 136 137 func (m Mode) ShouldUseKnownImports() bool { 138 return m != DisableGlobalMode 139 } 140 141 type modeFlag struct { 142 mode *Mode 143 } 144 145 func (f *modeFlag) Set(value string) error { 146 if mode, err := ModeFromString(value); err != nil { 147 return err 148 } else { 149 *f.mode = mode 150 return nil 151 } 152 } 153 154 func (f *modeFlag) String() string { 155 var mode Mode 156 if f != nil && f.mode != nil { 157 mode = *f.mode 158 } 159 return mode.String() 160 } 161 162 func (_ *protoLang) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) { 163 pc := &ProtoConfig{} 164 c.Exts[protoName] = pc 165 166 // Note: the -proto flag does not set the ModeExplicit flag. We want to 167 // be able to switch to DisableMode in vendor directories, even when 168 // this is set for compatibility with older versions. 169 fs.Var(&modeFlag{&pc.Mode}, "proto", "default: generates a proto_library rule for one package\n\tpackage: generates a proto_library rule for for each package\n\tdisable: does not touch proto rules\n\tdisable_global: does not touch proto rules and does not use special cases for protos in dependency resolution") 170 fs.StringVar(&pc.groupOption, "proto_group", "", "option name used to group .proto files into proto_library rules") 171 } 172 173 func (_ *protoLang) CheckFlags(fs *flag.FlagSet, c *config.Config) error { 174 return nil 175 } 176 177 func (_ *protoLang) KnownDirectives() []string { 178 return []string{"proto", "proto_group"} 179 } 180 181 func (_ *protoLang) Configure(c *config.Config, rel string, f *rule.File) { 182 pc := &ProtoConfig{} 183 *pc = *GetProtoConfig(c) 184 c.Exts[protoName] = pc 185 if f != nil { 186 for _, d := range f.Directives { 187 switch d.Key { 188 case "proto": 189 mode, err := ModeFromString(d.Value) 190 if err != nil { 191 log.Print(err) 192 continue 193 } 194 pc.Mode = mode 195 pc.ModeExplicit = true 196 case "proto_group": 197 pc.groupOption = d.Value 198 } 199 } 200 } 201 inferProtoMode(c, rel, f) 202 } 203 204 // inferProtoMode sets ProtoConfig.Mode based on the directory name and the 205 // contents of f. If the proto mode is set explicitly, this function does not 206 // change it. If this is a vendor directory, or go_proto_library is loaded from 207 // another file, proto rule generation is disabled. 208 // 209 // TODO(jayconrod): this logic is archaic, now that rules are generated by 210 // separate language extensions. Proto rule generation should be independent 211 // from Go. 212 func inferProtoMode(c *config.Config, rel string, f *rule.File) { 213 pc := GetProtoConfig(c) 214 if pc.Mode != DefaultMode || pc.ModeExplicit { 215 return 216 } 217 if pc.GoPrefix == wellKnownTypesGoPrefix { 218 pc.Mode = LegacyMode 219 return 220 } 221 if path.Base(rel) == "vendor" { 222 pc.Mode = DisableMode 223 return 224 } 225 if f == nil { 226 return 227 } 228 mode := DefaultMode 229 outer: 230 for _, l := range f.Loads { 231 name := l.Name() 232 if name == "@io_bazel_rules_go//proto:def.bzl" { 233 break 234 } 235 if name == "@io_bazel_rules_go//proto:go_proto_library.bzl" { 236 mode = LegacyMode 237 break 238 } 239 for _, sym := range l.Symbols() { 240 if sym == "go_proto_library" { 241 mode = DisableMode 242 break outer 243 } 244 } 245 } 246 if mode == DefaultMode || pc.Mode == mode || c.ShouldFix && mode == LegacyMode { 247 return 248 } 249 pc.Mode = mode 250 }