Skip to content

Commit d8e9832

Browse files
authored
Allow empty srcs (#138)
* Update java_index args processing * Update test * Allow existing rule srcs to be empty * Allow scala rule to not have srcs * Refactor .CanProvide to include from parameter * Normalize relative labels for source provider canProvide
1 parent c75c261 commit d8e9832

22 files changed

+146
-113
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ func (p *bazelDepsProvider) loadFile(dir string, filename string, scope resolver
472472
}
473473

474474
// CanProvide implements part of the resolver.SymbolProvider interface.
475-
func (p *bazelDepsProvider) CanProvide(dep *resolver.ImportLabel, knownRule func(from label.Label) (*rule.Rule, bool)) bool {
475+
func (p *bazelDepsProvider) CanProvide(dep *resolver.ImportLabel, knownRule func(from label.Label) (*rule.Rule, bool), from label.Label) bool {
476476
if dep.Label.Repo == "bazel_deps" {
477477
return true
478478
}

language/scala/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ go_library(
3838
deps = [
3939
"//build/stack/gazelle/scala/cache",
4040
"//build/stack/gazelle/scala/parse",
41+
"//pkg/bazel",
4142
"//pkg/collections",
4243
"//pkg/glob",
4344
"//pkg/parser",

language/scala/coverage.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package scala
22

33
import (
4+
"fmt"
5+
"sort"
6+
"strings"
7+
48
"github.com/stackb/scala-gazelle/pkg/procutil"
59
)
610

@@ -11,26 +15,40 @@ type packageRuleCoverage struct {
1115
// total represents the total number of rules in a package that we have a
1216
// RuleProvider for.
1317
total int
18+
// kinds represents the kinds of rules covered, and how many each has
19+
kinds map[string]int
1420
}
1521

1622
func (sl *scalaLang) reportCoverage(printf func(format string, v ...any)) {
1723
if !sl.existingScalaRuleCoverageFlagValue {
1824
return
1925
}
2026

27+
kindTotals := make(map[string]int)
28+
2129
var managed int
2230
var total int
2331
for _, pkg := range sl.packages {
2432
managed += pkg.ruleCoverage.managed
2533
total += pkg.ruleCoverage.total
34+
for k, v := range pkg.ruleCoverage.kinds {
35+
kindTotals[k] += v
36+
}
37+
}
38+
39+
kinds := make([]string, 0, len(kindTotals))
40+
for kind := range kindTotals {
41+
kinds = append(kinds, fmt.Sprintf("%s: %d", kind, kindTotals[kind]))
2642
}
43+
sort.Strings(kinds)
44+
totals := strings.Join(kinds, ", ")
2745

2846
var percent float32
2947
if total > 0 {
3048
percent = float32(managed) / float32(total) * 100
3149
}
3250

3351
if procutil.LookupBoolEnv(SCALA_GAZELLE_SHOW_COVERAGE, true) {
34-
printf("scala-gazelle coverage is %0.1f%% (%d/%d)", percent, managed, total)
52+
printf("scala-gazelle coverage is %0.1f%% (%d/%d) %s", percent, managed, total, totals)
3553
}
3654
}

language/scala/existing_scala_rule.go

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,15 @@ func (s *existingScalaRuleProvider) ProvideRule(cfg *scalarule.Config, pkg scala
7474
func (s *existingScalaRuleProvider) ResolveRule(cfg *scalarule.Config, pkg scalarule.Package, r *rule.Rule) scalarule.RuleProvider {
7575
scalaRule, err := pkg.ParseRule(r, "srcs")
7676
if err != nil {
77-
if err == ErrRuleHasNoSrcs {
78-
return nil // no need to print a warning
77+
if err != ErrRuleHasNoSrcs {
78+
log.Printf("skipping %s %s: unable to collect srcs: %v", r.Kind(), r.Name(), err)
79+
return nil
7980
}
80-
log.Printf("skipping %s %s: unable to collect srcs: %v", r.Kind(), r.Name(), err)
81-
return nil
81+
// rule has no srcs. This is OK for binary rules, sometimes they only
82+
// have a main_class.
83+
// if !s.isBinary {
84+
// return nil // no need to print a warning
85+
// }
8286
}
8387
if scalaRule == nil {
8488
log.Panicln("scalaRule should not be nil!")
@@ -133,13 +137,15 @@ func (s *existingScalaRule) Resolve(rctx *scalarule.ResolveContext, importsRaw i
133137
sc.Imports(imports, rctx.Rule, "deps", rctx.From)
134138

135139
commentsSrcs := rctx.Rule.AttrComments("srcs")
136-
commentsSrcs.Before = nil
137-
if sc.ShouldAnnotateImports() {
138-
scalaconfig.AnnotateImports(imports, commentsSrcs, "import: ")
139-
}
140-
if sc.ShouldAnnotateRule() {
141-
ruleComments := makeRuleComments(scalaRule.pb)
142-
commentsSrcs.Before = append(commentsSrcs.Before, ruleComments...)
140+
if commentsSrcs != nil {
141+
commentsSrcs.Before = nil
142+
if sc.ShouldAnnotateImports() {
143+
scalaconfig.AnnotateImports(imports, commentsSrcs, "import: ")
144+
}
145+
if sc.ShouldAnnotateRule() {
146+
ruleComments := makeRuleComments(scalaRule.pb)
147+
commentsSrcs.Before = append(commentsSrcs.Before, ruleComments...)
148+
}
143149
}
144150

145151
if s.isLibrary {

language/scala/scala_package.go

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/bazelbuild/bazel-gazelle/rule"
1515
"github.com/rs/zerolog"
1616

17+
sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse"
1718
"github.com/stackb/scala-gazelle/pkg/glob"
1819
"github.com/stackb/scala-gazelle/pkg/parser"
1920
"github.com/stackb/scala-gazelle/pkg/resolver"
@@ -70,7 +71,9 @@ func newScalaPackage(
7071
cfg: cfg,
7172
rules: make(map[string]*rule.Rule),
7273
resolveWork: make([]func(), 0),
73-
ruleCoverage: &packageRuleCoverage{},
74+
ruleCoverage: &packageRuleCoverage{
75+
kinds: map[string]int{},
76+
},
7477
}
7578

7679
s.gen = s.generateRules(true)
@@ -144,7 +147,7 @@ func (s *scalaPackage) generateRules(enabled bool) []scalarule.RuleProvider {
144147
// TOOD(pcj): consider adding .ContributesToCoverage or some
145148
// other way of tracking which rules contribute to coverage
146149
// calculation.
147-
if provider.Name() != "scala_files" && provider.Name() != "scala_fileset" {
150+
if ruleContributesToCoverage(provider.Name()) {
148151
s.ruleCoverage.total += 1
149152
}
150153
} else {
@@ -158,6 +161,7 @@ func (s *scalaPackage) generateRules(enabled bool) []scalarule.RuleProvider {
158161
for _, rc := range configuredRules {
159162
if !rc.Enabled {
160163
s.logger.Debug().Msgf("%s configuration not enabled, skipping rule generation", rc.Name)
164+
log.Println(s.cfg.Rel(), ": NOT ENABLED RULE:", rc.Name)
161165
continue
162166
}
163167

@@ -188,8 +192,13 @@ func (s *scalaPackage) generateRules(enabled bool) []scalarule.RuleProvider {
188192
s.logger.Debug().Msgf("new resolved rule: %s %s", resolvedRule.Name(), resolvedRule.Kind())
189193
rules = append(rules, resolvedRule)
190194
// TODO: make this an API, not hardcode which rule names contribute to coverage
191-
if resolvedRule.Name() != "scala_files" || resolvedRule.Name() != "scala_fileset" {
195+
if ruleContributesToCoverage(resolvedRule.Name()) {
192196
s.ruleCoverage.managed += 1
197+
s.ruleCoverage.kinds[r.Kind()] += 1
198+
}
199+
} else {
200+
if r.Kind() != "scala_files" {
201+
log.Println(s.cfg.Rel(), ": SKIPPED RULE:", r.Kind(), r.Name())
193202
}
194203
}
195204
}
@@ -223,33 +232,39 @@ func (s *scalaPackage) GeneratedRules() (rules []*rule.Rule) {
223232
}
224233

225234
// ParseRule implements part of the scalarule.Package interface.
226-
func (s *scalaPackage) ParseRule(r *rule.Rule, attrName string) (scalarule.Rule, error) {
235+
func (s *scalaPackage) ParseRule(r *rule.Rule, attrName string) (scalaRule scalarule.Rule, err error) {
227236
dir := filepath.Join(s.repoRootDir(), s.args.Rel)
228237
srcs, err := glob.CollectFilenames(s.args.File, dir, r.Attr(attrName))
229238
if err != nil {
230239
return nil, err
231240
}
241+
232242
scalaSrcs := make([]string, 0, len(srcs))
233243
for _, src := range srcs {
234244
if !strings.HasSuffix(src, ".scala") {
235245
continue
236246
}
237247
scalaSrcs = append(scalaSrcs, src)
238248
}
249+
if len(scalaSrcs) == 0 {
250+
err = ErrRuleHasNoSrcs
251+
}
239252

240253
logger := s.logger.With().Str("kind", r.Kind()).Str("name", r.Name()).Logger()
241254
logger.Debug().Msgf("%d scala files collected from %s", len(scalaSrcs), attrName)
242255

243-
if len(scalaSrcs) == 0 {
244-
return nil, ErrRuleHasNoSrcs
245-
}
246-
247256
from := s.cfg.MaybeRewrite(r.Kind(), label.Label{Pkg: s.args.Rel, Name: r.Name()})
248257

249-
rule, err := s.parser.ParseScalaRule(r.Kind(), from, dir, scalaSrcs...)
250-
if err != nil {
251-
logger.Warn().Err(err).Msg("parse error")
252-
return nil, err
258+
rule := &sppb.Rule{
259+
Label: from.String(),
260+
Kind: r.Kind(),
261+
}
262+
if len(scalaSrcs) > 0 {
263+
rule, err = s.parser.ParseScalaRule(r.Kind(), from, dir, scalaSrcs...)
264+
if err != nil {
265+
logger.Warn().Err(err).Msg("parse error")
266+
return nil, err
267+
}
253268
}
254269

255270
ctx := &scalaRuleContext{
@@ -259,7 +274,9 @@ func (s *scalaPackage) ParseRule(r *rule.Rule, attrName string) (scalarule.Rule,
259274
scope: s.universe,
260275
}
261276

262-
return newScalaRule(logger, ctx, rule), nil
277+
scalaRule = newScalaRule(logger, ctx, rule)
278+
279+
return
263280
}
264281

265282
// repoRootDir return the root directory of the repo.
@@ -307,3 +324,16 @@ func (s *scalaPackage) getProvidedRules(providers []scalarule.RuleProvider, shou
307324
func (p *scalaPackage) infof(format string, args ...any) string {
308325
return fmt.Sprintf("INFO ["+p.args.Rel+"]: "+format, args...)
309326
}
327+
328+
func ruleContributesToCoverage(name string) bool {
329+
switch name {
330+
case "scala_files":
331+
return false
332+
case "scala_fileset":
333+
return false
334+
case "semanticdb_index":
335+
return false
336+
default:
337+
return true
338+
}
339+
}

language/scala/scala_package_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,22 @@ import (
1010
"github.com/rs/zerolog"
1111
"github.com/stretchr/testify/mock"
1212

13+
sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse"
1314
"github.com/stackb/scala-gazelle/pkg/resolver/mocks"
1415
"github.com/stackb/scala-gazelle/pkg/scalaconfig"
15-
"github.com/stackb/scala-gazelle/pkg/scalarule"
1616
)
1717

1818
func TestScalaPackageParseRule(t *testing.T) {
19-
2019
for name, tc := range map[string]struct {
21-
rule *rule.Rule
22-
attrName string
23-
want scalarule.Rule
24-
wantErr string
20+
rule *rule.Rule
21+
attrName string
22+
wantFiles []*sppb.File
23+
wantErr string
2524
}{
2625
"degenerate": {
27-
rule: rule.NewRule("scala_library", "somelib"),
28-
wantErr: "rule has no source files",
26+
rule: rule.NewRule("scala_library", "somelib"),
27+
wantErr: "rule has no source files",
28+
wantFiles: nil,
2929
},
3030
} {
3131
t.Run(name, func(t *testing.T) {
@@ -58,7 +58,7 @@ func TestScalaPackageParseRule(t *testing.T) {
5858
if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
5959
t.Errorf("error (-want +got):\n%s", diff)
6060
}
61-
if diff := cmp.Diff(tc.want, got); diff != "" {
61+
if diff := cmp.Diff(tc.wantFiles, got.Files()); diff != "" {
6262
t.Errorf("(-want +got):\n%s", diff)
6363
}
6464
})

language/scala/scala_rule.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/bazelbuild/bazel-gazelle/rule"
1515
"github.com/rs/zerolog"
1616

17+
"github.com/stackb/scala-gazelle/pkg/bazel"
1718
"github.com/stackb/scala-gazelle/pkg/collections"
1819
"github.com/stackb/scala-gazelle/pkg/procutil"
1920
"github.com/stackb/scala-gazelle/pkg/resolver"
@@ -55,11 +56,11 @@ type scalaRule struct {
5556
exports map[string]resolve.ImportSpec
5657
}
5758

58-
var bazel = "bazel"
59+
var defaultBazelExe = "bazel"
5960

6061
func init() {
6162
if bazelExe, ok := os.LookupEnv("SCALA_GAZELLE_BAZEL_EXECUTABLE"); ok {
62-
bazel = bazelExe
63+
defaultBazelExe = bazelExe
6364
}
6465
}
6566

@@ -430,10 +431,10 @@ func (r *scalaRule) putExport(imp string) {
430431

431432
func (r *scalaRule) fixWildcardImport(rel, filename, wimp string) ([]string, error) {
432433
fixer := wildcardimport.NewFixer(&wildcardimport.FixerOptions{
433-
BazelExecutable: bazel,
434+
BazelExecutable: defaultBazelExe,
434435
})
435436

436-
bwd := wildcardimport.GetBuildWorkspaceDirectory()
437+
bwd := bazel.GetBuildWorkspaceDirectory()
437438

438439
// sometimes the filename already include the relative path.
439440
// FIXME(pcj): ensure pre-parsed files have the same filenames as non-preparsed ones.

pkg/bazel/BUILD.bazel

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ go_library(
66
srcs = ["bazel.go"],
77
importpath = "github.com/stackb/scala-gazelle/pkg/bazel",
88
visibility = ["//visibility:public"],
9-
deps = ["@io_bazel_rules_go//go/tools/bazel:go_default_library"],
9+
deps = [
10+
"//pkg/procutil",
11+
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
12+
],
1013
)
1114

1215
package_filegroup(

pkg/bazel/bazel.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package bazel
22

33
import (
4+
"log"
45
"os"
6+
"os/exec"
57
"path"
68
"regexp"
79

810
"github.com/bazelbuild/rules_go/go/tools/bazel"
11+
"github.com/stackb/scala-gazelle/pkg/procutil"
912
)
1013

1114
// the name of an environment variable at runtime
@@ -14,10 +17,9 @@ const TEST_TMPDIR = "TEST_TMPDIR"
1417
var (
1518
FindBinary = bazel.FindBinary
1619
ListRunfiles = bazel.ListRunfiles
20+
nonWordRe = regexp.MustCompile(`\W+`)
1721
)
1822

19-
var nonWordRe = regexp.MustCompile(`\W+`)
20-
2123
func CleanupLabel(in string) string {
2224
return nonWordRe.ReplaceAllString(in, "_")
2325
}
@@ -30,3 +32,24 @@ func NewTmpDir(prefix string) (string, error) {
3032
}
3133
return os.MkdirTemp("", prefix)
3234
}
35+
36+
func ExecCommand(bazelExe, command, label string) ([]byte, int, error) {
37+
args := []string{command, label}
38+
39+
cmd := exec.Command(bazelExe, args...)
40+
cmd.Dir = GetBuildWorkspaceDirectory()
41+
42+
log.Println("🧱", cmd.String())
43+
output, err := cmd.CombinedOutput()
44+
exitCode := procutil.CmdExitCode(cmd, err)
45+
46+
return output, exitCode, err
47+
}
48+
49+
func GetBuildWorkspaceDirectory() string {
50+
if bwd, ok := os.LookupEnv("BUILD_WORKSPACE_DIRECTORY"); ok {
51+
return bwd
52+
} else {
53+
return "."
54+
}
55+
}

pkg/provider/java_provider.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func (p *JavaProvider) OnEnd() error {
9191
}
9292

9393
// CanProvide implements part of the resolver.SymbolProvider interface.
94-
func (p *JavaProvider) CanProvide(dep *resolver.ImportLabel, expr build.Expr, knownRule func(from label.Label) (*rule.Rule, bool)) bool {
94+
func (p *JavaProvider) CanProvide(dep *resolver.ImportLabel, expr build.Expr, knownRule func(from label.Label) (*rule.Rule, bool), from label.Label) bool {
9595
if _, ok := p.byLabel[dep.Label]; ok {
9696
return true
9797
}

0 commit comments

Comments
 (0)