123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- /* Copyright 2018 The Bazel Authors. All rights reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package proto
- import (
- "flag"
- "fmt"
- "log"
- "path"
- "github.com/bazelbuild/bazel-gazelle/internal/config"
- "github.com/bazelbuild/bazel-gazelle/internal/rule"
- )
- // ProtoConfig contains configuration values related to protos.
- //
- // This type is public because other languages need to generate rules based
- // on protos, so this configuration may be relevant to them.
- type ProtoConfig struct {
- // Mode determines how rules are generated for protos.
- Mode Mode
- // ModeExplicit indicates whether the proto mode was set explicitly.
- ModeExplicit bool
- // GoPrefix is the current Go prefix (the Go extension may set this in the
- // root directory only). Used to generate proto rule names in the root
- // directory when there are no proto files or the proto package name
- // can't be determined.
- // TODO(jayconrod): deprecate and remove Go-specific behavior.
- GoPrefix string
- // groupOption is an option name that Gazelle will use to group .proto
- // files into proto_library rules. If unset, the proto package name is used.
- groupOption string
- }
- func GetProtoConfig(c *config.Config) *ProtoConfig {
- return c.Exts[protoName].(*ProtoConfig)
- }
- // Mode determines how proto rules are generated.
- type Mode int
- const (
- // DefaultMode generates proto_library rules. Other languages should generate
- // library rules based on these (e.g., go_proto_library) and should ignore
- // checked-in generated files (e.g., .pb.go files) when there is a .proto
- // file with a similar name.
- DefaultMode Mode = iota
- // DisableMode ignores .proto files and generates empty proto_library rules.
- // Checked-in generated files (e.g., .pb.go files) should be treated as
- // normal sources.
- DisableMode
- // DisableGlobalMode is similar to DisableMode, but it also prevents
- // the use of special cases in dependency resolution for well known types
- // and Google APIs.
- DisableGlobalMode
- // LegacyMode generates filegroups for .proto files if .pb.go files are
- // present in the same directory.
- LegacyMode
- // PackageMode generates a proto_library for each set of .proto files with
- // the same package name in each directory.
- PackageMode
- )
- func ModeFromString(s string) (Mode, error) {
- switch s {
- case "default":
- return DefaultMode, nil
- case "disable":
- return DisableMode, nil
- case "disable_global":
- return DisableGlobalMode, nil
- case "legacy":
- return LegacyMode, nil
- case "package":
- return PackageMode, nil
- default:
- return 0, fmt.Errorf("unrecognized proto mode: %q", s)
- }
- }
- func (m Mode) String() string {
- switch m {
- case DefaultMode:
- return "default"
- case DisableMode:
- return "disable"
- case DisableGlobalMode:
- return "disable_global"
- case LegacyMode:
- return "legacy"
- case PackageMode:
- return "package"
- default:
- log.Panicf("unknown mode %d", m)
- return ""
- }
- }
- func (m Mode) ShouldGenerateRules() bool {
- switch m {
- case DisableMode, DisableGlobalMode, LegacyMode:
- return false
- default:
- return true
- }
- }
- func (m Mode) ShouldIncludePregeneratedFiles() bool {
- switch m {
- case DisableMode, DisableGlobalMode, LegacyMode:
- return true
- default:
- return false
- }
- }
- func (m Mode) ShouldUseKnownImports() bool {
- return m != DisableGlobalMode
- }
- type modeFlag struct {
- mode *Mode
- }
- func (f *modeFlag) Set(value string) error {
- if mode, err := ModeFromString(value); err != nil {
- return err
- } else {
- *f.mode = mode
- return nil
- }
- }
- func (f *modeFlag) String() string {
- var mode Mode
- if f != nil && f.mode != nil {
- mode = *f.mode
- }
- return mode.String()
- }
- func (_ *protoLang) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) {
- pc := &ProtoConfig{}
- c.Exts[protoName] = pc
- // Note: the -proto flag does not set the ModeExplicit flag. We want to
- // be able to switch to DisableMode in vendor directories, even when
- // this is set for compatibility with older versions.
- 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")
- fs.StringVar(&pc.groupOption, "proto_group", "", "option name used to group .proto files into proto_library rules")
- }
- func (_ *protoLang) CheckFlags(fs *flag.FlagSet, c *config.Config) error {
- return nil
- }
- func (_ *protoLang) KnownDirectives() []string {
- return []string{"proto", "proto_group"}
- }
- func (_ *protoLang) Configure(c *config.Config, rel string, f *rule.File) {
- pc := &ProtoConfig{}
- *pc = *GetProtoConfig(c)
- c.Exts[protoName] = pc
- if f != nil {
- for _, d := range f.Directives {
- switch d.Key {
- case "proto":
- mode, err := ModeFromString(d.Value)
- if err != nil {
- log.Print(err)
- continue
- }
- pc.Mode = mode
- pc.ModeExplicit = true
- case "proto_group":
- pc.groupOption = d.Value
- }
- }
- }
- inferProtoMode(c, rel, f)
- }
- // inferProtoMode sets ProtoConfig.Mode based on the directory name and the
- // contents of f. If the proto mode is set explicitly, this function does not
- // change it. If this is a vendor directory, or go_proto_library is loaded from
- // another file, proto rule generation is disabled.
- //
- // TODO(jayconrod): this logic is archaic, now that rules are generated by
- // separate language extensions. Proto rule generation should be independent
- // from Go.
- func inferProtoMode(c *config.Config, rel string, f *rule.File) {
- pc := GetProtoConfig(c)
- if pc.Mode != DefaultMode || pc.ModeExplicit {
- return
- }
- if pc.GoPrefix == wellKnownTypesGoPrefix {
- pc.Mode = LegacyMode
- return
- }
- if path.Base(rel) == "vendor" {
- pc.Mode = DisableMode
- return
- }
- if f == nil {
- return
- }
- mode := DefaultMode
- outer:
- for _, l := range f.Loads {
- name := l.Name()
- if name == "@io_bazel_rules_go//proto:def.bzl" {
- break
- }
- if name == "@io_bazel_rules_go//proto:go_proto_library.bzl" {
- mode = LegacyMode
- break
- }
- for _, sym := range l.Symbols() {
- if sym == "go_proto_library" {
- mode = DisableMode
- break outer
- }
- }
- }
- if mode == DefaultMode || pc.Mode == mode || c.ShouldFix && mode == LegacyMode {
- return
- }
- pc.Mode = mode
- }
|