config.go 6.6 KB

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