github.com/bazelbuild/bazel-gazelle@v0.36.1-0.20240520142334-61b277ba6fed/resolve/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 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 }