Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ load("@build_stack_rules_proto//rules:proto_compile_assets.bzl", "proto_compile_
load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup")

# -- Gazelle language "walk" ---
# gazelle:exclude .bcr
# gazelle:exclude .claude
# gazelle:exclude .github
# gazelle:exclude .vscode
# gazelle:exclude .bcr
# gazelle:exclude bazel-bin
# gazelle:exclude bazel-out
# gazelle:exclude bazel-scala-gazelle
# gazelle:exclude bazel-testlogs
# gazelle:exclude bin
# gazelle:exclude vendor
# gazelle:exclude examples
# gazelle:exclude vendor

# -- Gazelle language "resolve" ---
# gazelle:resolve go go github.com/stackb/rules_proto/v4/pkg/protoc @build_stack_rules_proto//pkg/protoc
Expand Down
43 changes: 31 additions & 12 deletions cmd/scalafileextract/scalafileextract.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ func run(args []string) error {
cfg.Parser = parser.NewScalametaParser(
parser.WithHttpClientTimeout(60 * time.Second),
)

if err := cfg.Parser.Start(); err != nil {
return fmt.Errorf("starting parser: %w", err)
}
defer func() {
cfg.Parser.Stop()
}()
Expand All @@ -72,10 +68,16 @@ func run(args []string) error {
}

if cfg.PersistentWorker {
// In persistent worker mode, parser initialization is deferred
// to inside the loop so startup failures are reported per-request
// via WorkResponse rather than killing the worker process.
if err := persistentWork(&cfg); err != nil {
return fmt.Errorf("while performing persistent work: %v", err)
}
} else {
if err := cfg.Parser.Start(); err != nil {
return fmt.Errorf("starting parser: %w", err)
}
if err := batchWork(&cfg); err != nil {
return fmt.Errorf("while performing batch work: %v", err)
}
Expand Down Expand Up @@ -103,16 +105,33 @@ func persistentWork(cfg *Config) error {

batchCfg, err := parseFlags(req.Arguments)
if err != nil {
return fmt.Errorf("parsing work request arguments: %v", err)
}
resp.ExitCode = 1
resp.Output = fmt.Sprintf("parsing work request arguments: %v", err)
} else {
// Ensure parser is running (lazy init + restart if crashed)
if !cfg.Parser.IsRunning() {
cfg.Parser.Stop()
if err := cfg.Parser.Start(); err != nil {
log.Printf("failed to start parser: %v", err)
resp.ExitCode = 1
resp.Output = fmt.Sprintf("starting parser: %v", err)
if err := protobuf.WriteDelimitedTo(&resp, stdout); err != nil {
return fmt.Errorf("writing work response: %v", err)
}
if err := stdout.Flush(); err != nil {
return fmt.Errorf("flushing work response: %v", err)
}
continue
}
}

batchCfg.Parser = cfg.Parser
batchCfg.Cwd = cfg.Cwd
batchCfg.Parser = cfg.Parser
batchCfg.Cwd = cfg.Cwd

if err := batchWork(&batchCfg); err != nil {
// Don't terminate the worker on batch errors; report via WorkResponse
resp.ExitCode = 1
resp.Output = fmt.Sprintf("performing persistent batch: %v", err)
if err := batchWork(&batchCfg); err != nil {
resp.ExitCode = 1
resp.Output = fmt.Sprintf("performing persistent batch: %v", err)
}
}

if err := protobuf.WriteDelimitedTo(&resp, stdout); err != nil {
Expand Down
14 changes: 13 additions & 1 deletion pkg/maven/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
load("@build_stack_scala_gazelle//rules:package_filegroup.bzl", "package_filegroup")
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "maven",
Expand Down Expand Up @@ -27,6 +27,18 @@ package_filegroup(
"coordinate.go",
"multiset.go",
"resolver.go",
"resolver_test.go",
],
visibility = ["//visibility:public"],
)

go_test(
name = "maven_test",
srcs = ["resolver_test.go"],
embed = [":maven"],
deps = [
"//pkg/resolver",
"@bazel_gazelle//label",
"@com_github_google_go_cmp//cmp",
],
)
11 changes: 11 additions & 0 deletions pkg/parser/scalameta_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@ func (s *ScalametaParser) Stop() {
os.RemoveAll(s.processDir)
s.processDir = ""
}
s.httpPort = 0
s.httpUrl = ""
}

// IsRunning reports whether the parser process is alive.
// ProcessState is set by Wait(); if non-nil the process has exited.
func (s *ScalametaParser) IsRunning() bool {
if s.cmd == nil {
return false
}
return s.cmd.ProcessState == nil
}

func (s *ScalametaParser) Start() error {
Expand Down
75 changes: 75 additions & 0 deletions pkg/parser/scalameta_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,81 @@ func mustParseURL(t *testing.T, raw string) *url.URL {
return u
}

func TestIsRunning_NeverStarted(t *testing.T) {
p := NewScalametaParser()
if p.IsRunning() {
t.Error("expected IsRunning() == false for a never-started parser")
}
}

func TestIsRunning_AfterStartAndStop(t *testing.T) {
p := NewScalametaParser()
if err := p.Start(); err != nil {
t.Fatal("Start:", err)
}
if !p.IsRunning() {
t.Error("expected IsRunning() == true after Start()")
}
p.Stop()
if p.IsRunning() {
t.Error("expected IsRunning() == false after Stop()")
}
}

func TestStop_ResetsState(t *testing.T) {
p := NewScalametaParser()
if err := p.Start(); err != nil {
t.Fatal("Start:", err)
}
if p.httpPort == 0 {
t.Error("expected non-zero httpPort after Start()")
}
if p.httpUrl == "" {
t.Error("expected non-empty httpUrl after Start()")
}
p.Stop()
if p.httpPort != 0 {
t.Errorf("expected httpPort == 0 after Stop(), got %d", p.httpPort)
}
if p.httpUrl != "" {
t.Errorf("expected httpUrl == \"\" after Stop(), got %q", p.httpUrl)
}
}

func TestRestart(t *testing.T) {
p := NewScalametaParser()

if err := p.Start(); err != nil {
t.Fatal("first Start:", err)
}
if !p.IsRunning() {
t.Fatal("expected IsRunning() after first Start()")
}

p.Stop()
if p.IsRunning() {
t.Fatal("expected !IsRunning() after Stop()")
}

if err := p.Start(); err != nil {
t.Fatal("second Start:", err)
}
if !p.IsRunning() {
t.Error("expected IsRunning() after restart")
}

// Verify the restarted parser can actually serve requests
response, err := p.Parse(context.Background(), &sppb.ParseRequest{})
if err != nil {
t.Fatal("Parse after restart:", err)
}
if response.Error == "" {
t.Error("expected error response for empty request")
}

p.Stop()
}

func mustWriteTestFiles(t *testing.T, tmpDir string, files []testtools.FileSpec) []string {
var filenames []string
for _, file := range files {
Expand Down
25 changes: 19 additions & 6 deletions rules/java_indexer_aspect.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ COMPILE_TIME = 0

RUNTIME = 1

DEFAULT_REPO_NAME_SEPARATOR = "+"

# Compile-time dependency attributes, grouped by type.
DEPS = [
"_cc_toolchain", # From cc rules
Expand Down Expand Up @@ -246,11 +244,26 @@ def _jarindex_basename(ctx, label):

# see https://bazelbuild.slack.com/archives/C014RARENH0/p1752851984151199?thread_ts=1752594227.746349&cid=C014RARENH0 - we can't get the label apparent name! ("@maven")
def get_apparent_label(label):
parts = label.repo_name.split(DEFAULT_REPO_NAME_SEPARATOR)
apparent_name = parts[len(parts) - 1]
apparent_label = "@%s//%s:%s" % (apparent_name, label.package, label.name)
"""Returns the apparent label string for a bzlmod canonical label.

Splits the canonical repo name on '+' (Bazel 8+) or '~' (Bazel 7) to
extract the apparent repo name (e.g. 'maven' from 'rules_jvm_external+5.3+maven').

Args:
label: A Label object.

return apparent_label
Returns:
A label string using the apparent repo name, e.g. "@maven//pkg:target".
"""
repo_name = label.repo_name
if "+" in repo_name:
parts = repo_name.split("+")
elif "~" in repo_name:
parts = repo_name.split("~")
else:
parts = [repo_name]
apparent_name = parts[len(parts) - 1]
return "@%s//%s:%s" % (apparent_name, label.package, label.name)

def jarindexer_action(ctx, label, kind, executable, jar):
output_file = ctx.actions.declare_file(_jarindex_basename(ctx, label) + ".javaindex.pb")
Expand Down
Loading