config.go 7.9 KB

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