@@ -30,9 +30,9 @@ import (
3030 "log"
3131 "maps"
3232 "os"
33- "path"
3433 "path/filepath"
3534 "slices"
35+ "sort"
3636 "strings"
3737
3838 "github.com/bazelbuild/bazel-gazelle/config"
6363 }
6464 starlarkLibraryKindInfo = map [string ]rule.KindInfo {
6565 starlarkLibraryKind : {
66- NonEmptyAttrs : map [string ]bool {"src" : true },
66+ NonEmptyAttrs : map [string ]bool {"srcs" : true },
67+ ResolveAttrs : map [string ]bool {"deps" : true },
6768 },
6869 }
6970 starlarkLibraryLoadInfo = rule.LoadInfo {
@@ -76,7 +77,6 @@ type starlarkLibraryLang struct {
7677 roots []string
7778 repoName string
7879 excludeDirs []string
79- bzlFiles map [string ]bool
8080 bazelVersion string
8181 bazelIgnore []string
8282 logFile string
@@ -87,9 +87,7 @@ type starlarkLibraryLang struct {
8787// NewLanguage is called by Gazelle to install this language extension in a
8888// binary.
8989func NewLanguage () language.Language {
90- return & starlarkLibraryLang {
91- bzlFiles : map [string ]bool {},
92- }
90+ return & starlarkLibraryLang {}
9391}
9492
9593// Name returns the name of the language. This should be a prefix of the kinds
@@ -209,7 +207,25 @@ func (*starlarkLibraryLang) Fix(c *config.Config, f *rule.File) {
209207// If nil is returned, the rule will not be indexed. If any non-nil slice is
210208// returned, including an empty slice, the rule will be indexed.
211209func (ext * starlarkLibraryLang ) Imports (c * config.Config , r * rule.Rule , f * rule.File ) []resolve.ImportSpec {
212- return nil
210+ srcs := r .AttrStrings ("srcs" )
211+ imports := make ([]resolve.ImportSpec , 0 , len (srcs )+ 1 )
212+
213+ for _ , src := range srcs {
214+ spec := resolve.ImportSpec {
215+ Lang : languageName ,
216+ Imp : fmt .Sprintf ("//%s:%s" , f .Pkg , src ),
217+ }
218+
219+ imports = append (imports , spec )
220+ }
221+
222+ // add an import such the one can find *all* the stark_library rules.
223+ imports = append (imports , resolve.ImportSpec {
224+ Lang : languageName ,
225+ Imp : starlarkLibraryKind ,
226+ })
227+
228+ return imports
213229}
214230
215231// Embeds returns a list of labels of rules that the given rule embeds. If a
@@ -228,6 +244,10 @@ func (*starlarkLibraryLang) Embeds(r *rule.Rule, from label.Label) []label.Label
228244// equivalent) for each import according to language-specific rules and
229245// heuristics.
230246func (ext * starlarkLibraryLang ) Resolve (c * config.Config , ix * resolve.RuleIndex , rc * repo.RemoteCache , r * rule.Rule , importsRaw interface {}, from label.Label ) {
247+ switch r .Kind () {
248+ case starlarkLibraryKind :
249+ ext .starlarkLibraryResolve (c , ix , rc , r , importsRaw , from )
250+ }
231251}
232252
233253// GenerateRules extracts build metadata from source files in a directory.
@@ -256,33 +276,37 @@ func (ext *starlarkLibraryLang) GenerateRules(args language.GenerateArgs) (resul
256276
257277 ext .logf ("GenerateRules %v: visiting %s" , ext .roots , args .Rel )
258278
259- // collect all .bzl files
279+ // collect all .bzl srcs
280+ var srcs []string
260281 for _ , f := range append (args .RegularFiles , args .GenFiles ... ) {
261- if ! isBzlSourceFile (f ) {
262- continue
282+ if isBzlSourceFile (f ) {
283+ srcs = append ( srcs , f )
263284 }
264- ext .bzlFiles [path .Join (args .Rel , f )] = true
265285 }
266286
267- if slices .Contains (ext .roots , args .Rel ) {
268- r , imports := ext .starlarkLibraryRule (args )
269- result .Gen = append (result .Gen , r )
270- result .Imports = append (result .Imports , imports )
287+ if len (srcs ) == 0 {
288+ return
271289 }
272290
291+ r , imports := ext .starlarkLibraryRule (args , srcs )
292+ result .Gen = append (result .Gen , r )
293+ result .Imports = append (result .Imports , imports )
294+
295+ // if slices.Contains(ext.roots, args.Rel) {
296+ // r, imports := ext.starlarkLibraryRule(args, srcs)
297+ // result.Gen = append(result.Gen, r)
298+ // result.Imports = append(result.Imports, imports)
299+ // }
300+
273301 return
274302}
275303
276- func (ext * starlarkLibraryLang ) starlarkLibraryRule (args language.GenerateArgs ) (* rule.Rule , []any ) {
277-
304+ func (ext * starlarkLibraryLang ) starlarkLibraryRule (args language.GenerateArgs , srcs []string ) (* rule.Rule , []any ) {
278305 loadsByRelPath := make (map [string ][]string )
279- for workspaceRelPath := range ext .bzlFiles {
280- if ! strings .HasPrefix (workspaceRelPath , args .Rel ) {
281- continue
282- }
283- fullPath := filepath .Join (args .Config .RepoRoot , workspaceRelPath )
284- relPath := strings .TrimPrefix (strings .TrimPrefix (workspaceRelPath , args .Rel ), "/" )
285- _ , stmts , err := getBzlFileLoadsStmts (fullPath )
306+ for _ , src := range srcs {
307+ relPath := filepath .Join (args .Rel , src )
308+ fullPath := filepath .Join (args .Config .RepoRoot , relPath )
309+ _ , stmts , err := getBzlFileLoadsStmts (fullPath , args .Rel , ext .logf )
286310
287311 if err != nil {
288312 ext .logf ("%s: contains syntax errors: %v" , fullPath , err )
@@ -310,16 +334,47 @@ func (ext *starlarkLibraryLang) starlarkLibraryRule(args language.GenerateArgs)
310334 }
311335 r .SetAttr ("visibility" , []string {visibilityPublic })
312336
313- return r , []any {}
337+ return r , []any {loadsByRelPath }
338+ }
339+
340+ func (ext * starlarkLibraryLang ) starlarkLibraryResolve (c * config.Config , ix * resolve.RuleIndex , _ * repo.RemoteCache , r * rule.Rule , _ interface {}, from label.Label ) {
341+ // only perform resolve if this is one of the roots
342+ var root * string
343+ for _ , dir := range ext .roots {
344+ if dir == from .Pkg {
345+ root = & dir
346+ break
347+ }
348+ }
349+ if root == nil {
350+ ext .logf ("skipping deps resolution for %v (not a root: %v)" , from , ext .roots )
351+ return
352+ }
353+
354+ var deps []string
355+
356+ // fetch all rules, then filter by the ones below our root
357+ matches := ix .FindRulesByImportWithConfig (c , resolve.ImportSpec {
358+ Lang : languageName ,
359+ Imp : starlarkLibraryKind ,
360+ }, languageName )
361+ for _ , m := range matches {
362+ depLabel := m .Label .Rel (from .Repo , from .Pkg )
363+ if strings .HasPrefix (depLabel .Pkg , * root ) {
364+ deps = append (deps , depLabel .String ())
365+ }
366+ }
367+
368+ if len (deps ) > 0 {
369+ sort .Strings (deps )
370+ r .SetAttr ("deps" , deps )
371+ }
314372}
315373
316374// Before implements part of the language.LifecycleManager interface.
317375func (ext * starlarkLibraryLang ) Before (context.Context ) {
318376 ext .logf ("Lifecycle: Before() called" )
319377 ext .logf ("roots: %v" , ext .roots )
320- // if len(ext.roots) == 0 {
321- // log.Fatalf("at least one root must be specified")
322- // }
323378}
324379
325380// DoneGeneratingRules implements part of the language.FinishableLanguage interface.
@@ -417,7 +472,7 @@ func isBzlSourceFile(f string) bool {
417472 return strings .HasSuffix (f , fileType ) && ! ignoreSuffix .Matches (f )
418473}
419474
420- func getBzlFileLoadsStmts (path string ) (* build.File , []* build.LoadStmt , error ) {
475+ func getBzlFileLoadsStmts (path , rel string , logf LogFunc ) (* build.File , []* build.LoadStmt , error ) {
421476 f , err := os .ReadFile (path )
422477 if err != nil {
423478 return nil , nil , fmt .Errorf ("os.ReadFile(%q) error: %v" , path , err )
@@ -431,6 +486,11 @@ func getBzlFileLoadsStmts(path string) (*build.File, []*build.LoadStmt, error) {
431486 build .WalkOnce (ast , func (expr * build.Expr ) {
432487 n := * expr
433488 if l , ok := n .(* build.LoadStmt ); ok {
489+ if lbl , err := label .Parse (l .Module .Value ); err == nil {
490+ l .Module .Value = lbl .Abs ("" , rel ).String ()
491+ } else {
492+ logf ("rel=%q: load parse error: %s %v" , rel , l .Module .Value , err )
493+ }
434494 loads = append (loads , l )
435495 }
436496 })
0 commit comments