Skip to content

Commit d96a7b3

Browse files
committed
refactor: generate package-level rules and aggegate deps at roots
1 parent 171ef6d commit d96a7b3

1 file changed

Lines changed: 89 additions & 29 deletions

File tree

language/starlarklibrary/language.go

Lines changed: 89 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -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"
@@ -63,7 +63,8 @@ var (
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.
8989
func 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.
211209
func (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.
230246
func (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.
317375
func (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

Comments
 (0)