...

Source file src/github.com/bazelbuild/bazel-gazelle/resolve/config.go

Documentation: github.com/bazelbuild/bazel-gazelle/resolve

     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 resolve
    17  
    18  import (
    19  	"flag"
    20  	"log"
    21  	"regexp"
    22  	"strings"
    23  
    24  	"github.com/bazelbuild/bazel-gazelle/config"
    25  	"github.com/bazelbuild/bazel-gazelle/label"
    26  	"github.com/bazelbuild/bazel-gazelle/rule"
    27  )
    28  
    29  // FindRuleWithOverride searches the current configuration for user-specified
    30  // dependency resolution overrides. Overrides specified later (in configuration
    31  // files in deeper directories, or closer to the end of the file) are
    32  // returned first. If no override is found, label.NoLabel is returned.
    33  func FindRuleWithOverride(c *config.Config, imp ImportSpec, lang string) (label.Label, bool) {
    34  	rc := getResolveConfig(c)
    35  	if dep, ok := rc.findOverride(imp, lang); ok {
    36  		return dep, true
    37  	}
    38  	for i := len(rc.regexpOverrides) - 1; i >= 0; i-- {
    39  		o := rc.regexpOverrides[i]
    40  		if o.matches(imp, lang) {
    41  			return o.dep, true
    42  		}
    43  	}
    44  	return label.NoLabel, false
    45  }
    46  
    47  type overrideKey struct {
    48  	imp  ImportSpec
    49  	lang string
    50  }
    51  
    52  type regexpOverrideSpec struct {
    53  	ImpLang  string
    54  	ImpRegex *regexp.Regexp
    55  	lang     string
    56  	dep      label.Label
    57  }
    58  
    59  func (o regexpOverrideSpec) matches(imp ImportSpec, lang string) bool {
    60  	return imp.Lang == o.ImpLang &&
    61  		o.ImpRegex.MatchString(imp.Imp) &&
    62  		(o.lang == "" || o.lang == lang)
    63  }
    64  
    65  type resolveConfig struct {
    66  	overrides       map[overrideKey]label.Label
    67  	regexpOverrides []regexpOverrideSpec
    68  	parent          *resolveConfig
    69  }
    70  
    71  // newResolveConfig creates a new resolveConfig with the given overrides and
    72  // regexpOverrides. If the new overrides are the same as the parent's, the
    73  // parent is returned instead.
    74  func newResolveConfig(parent *resolveConfig, newOverrides map[overrideKey]label.Label, regexpOverrides []regexpOverrideSpec) *resolveConfig {
    75  	if len(newOverrides) == 0 && len(regexpOverrides) == len(parent.regexpOverrides) {
    76  		return parent
    77  	}
    78  	return &resolveConfig{
    79  		overrides:       newOverrides,
    80  		regexpOverrides: regexpOverrides,
    81  		parent:          parent,
    82  	}
    83  }
    84  
    85  // findOverride searches the current configuration for an override matching
    86  // the given import and language. If no override is found, the parent
    87  // configuration is searched recursively.
    88  func (rc *resolveConfig) findOverride(imp ImportSpec, lang string) (label.Label, bool) {
    89  	key := overrideKey{imp: imp, lang: lang}
    90  	if dep, ok := rc.overrides[key]; ok {
    91  		return dep, ok
    92  	}
    93  	if rc.parent != nil {
    94  		return rc.parent.findOverride(imp, lang)
    95  	}
    96  	return label.NoLabel, false
    97  }
    98  
    99  const resolveName = "_resolve"
   100  
   101  func getResolveConfig(c *config.Config) *resolveConfig {
   102  	return c.Exts[resolveName].(*resolveConfig)
   103  }
   104  
   105  type Configurer struct{}
   106  
   107  func (*Configurer) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) {
   108  	c.Exts[resolveName] = &resolveConfig{}
   109  }
   110  
   111  func (*Configurer) CheckFlags(fs *flag.FlagSet, c *config.Config) error { return nil }
   112  
   113  func (*Configurer) KnownDirectives() []string {
   114  	return []string{"resolve", "resolve_regexp"}
   115  }
   116  
   117  func (*Configurer) Configure(c *config.Config, rel string, f *rule.File) {
   118  	if f == nil || len(f.Directives) == 0 {
   119  		return
   120  	}
   121  
   122  	rc := getResolveConfig(c)
   123  	var newOverrides map[overrideKey]label.Label
   124  	regexpOverrides := rc.regexpOverrides[:len(rc.regexpOverrides):len(rc.regexpOverrides)]
   125  
   126  	for _, d := range f.Directives {
   127  		if d.Key == "resolve" {
   128  			parts := strings.Fields(d.Value)
   129  			key := overrideKey{}
   130  			var lbl string
   131  			if len(parts) == 3 {
   132  				key.imp.Lang = parts[0]
   133  				key.lang = parts[0]
   134  				key.imp.Imp = parts[1]
   135  				lbl = parts[2]
   136  			} else if len(parts) == 4 {
   137  				key.imp.Lang = parts[0]
   138  				key.lang = parts[1]
   139  				key.imp.Imp = parts[2]
   140  				lbl = parts[3]
   141  			} else {
   142  				log.Printf("could not parse directive: %s\n\texpected gazelle:resolve source-language [import-language] import-string label", d.Value)
   143  				continue
   144  			}
   145  			dep, err := label.Parse(lbl)
   146  			if err != nil {
   147  				log.Printf("gazelle:resolve %s: %v", d.Value, err)
   148  				continue
   149  			}
   150  			dep = dep.Abs("", rel)
   151  			if newOverrides == nil {
   152  				newOverrides = make(map[overrideKey]label.Label, len(f.Directives))
   153  			}
   154  			newOverrides[key] = dep
   155  		} else if d.Key == "resolve_regexp" {
   156  			parts := strings.Fields(d.Value)
   157  			o := regexpOverrideSpec{}
   158  			var lbl string
   159  			if len(parts) == 3 {
   160  				o.ImpLang = parts[0]
   161  				var err error
   162  				o.ImpRegex, err = regexp.Compile(parts[1])
   163  				if err != nil {
   164  					log.Printf("gazelle:resolve_regexp %s: %v", d.Value, err)
   165  					continue
   166  				}
   167  				lbl = parts[2]
   168  			} else if len(parts) == 4 {
   169  				o.ImpLang = parts[0]
   170  				o.lang = parts[1]
   171  				var err error
   172  				o.ImpRegex, err = regexp.Compile(parts[2])
   173  				if err != nil {
   174  					log.Printf("gazelle:resolve_regexp %s: %v", d.Value, err)
   175  					continue
   176  				}
   177  
   178  				lbl = parts[3]
   179  			} else {
   180  				log.Printf("could not parse directive: %s\n\texpected gazelle:resolve_regexp source-language [import-language] import-string-regex label", d.Value)
   181  				continue
   182  			}
   183  			var err error
   184  			o.dep, err = label.Parse(lbl)
   185  			if err != nil {
   186  				log.Printf("gazelle:resolve_regexp %s: %v", d.Value, err)
   187  				continue
   188  			}
   189  			o.dep = o.dep.Abs("", rel)
   190  			regexpOverrides = append(regexpOverrides, o)
   191  		}
   192  	}
   193  
   194  	c.Exts[resolveName] = newResolveConfig(rc, newOverrides, regexpOverrides)
   195  }
   196  

View as plain text