Commit Graph

18242 Commits

Author SHA1 Message Date
Anatolii Kmetiuk 893e22102b
[2.x] fix: Fixes -V parsing in sbt runners (#9626)
Problem: -V is consumed by the launcher instead of sbt in sbt tasks -V.

Solution: in the parser's -V behavior, lookback to see if there are commands going before it, in which case, forward -V to sbt.
2026-08-19 13:25:00 -04:00
eugene yokota cdf6b25106
[2.x] fix: Avoid rewriting unchanged plugin descriptors (#9612) (#9624) (#9628)
Only rewrite generated plugin descriptors when their normalized lines change, preserving the descriptor timestamp and downstream cache inputs for unchanged plugin discovery.

Add unit coverage plus direct packageBin and assembly Scripted scenarios. The descriptor regressions fail with the former unconditional write and pass with this change; the packageBin fixture also preserves SBT 2’s existing cache-hit behavior.

Co-authored-by: Dmitrii Naumenko <dimanaumenko1994@mail.ru>
Co-authored-by: Codex <noreply@openai.com>
2026-08-19 13:05:17 -04:00
eugene yokota 7115e7c36b
[2.x] Update sbtn to 2.0.0-8753a981 (#9630) 2026-08-19 12:53:20 -04:00
azdrojowa123 3045cd0c46
[2.x] Add a resolvedScalacOptions task that resolves cache placeholders (#9610)
Add a resolvedScalacOptions task that resolves cache placeholders in scalacOptions to absolute machine paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-19 05:36:38 -04:00
eugene yokota 8753a98161
[2.x] fix: sbtn to retry on corrupt active.json (#9617)
**Problem**
sbtn gets stuck when active.json is corrupt.

**Solution**
Delete active.json, and retry.
2026-08-19 02:26:44 -04:00
eugene yokota 6b3d7c6301
[2.x] fix: Fixes cache invalidation on version change (#9471)
**Problem**
packageBin includes version into the file name, which ends up
invalidating the cache.

**Solution**
Define packageInternal, which does not include version in the file name, and used during Compile or Test/compile. Note that publishing and Runtime classpath would continue to use packageBin.
2026-08-19 00:13:59 -04:00
Mai Huy Hoàng a1114188b4
[2.x] perf: Stop re-converting the classpath in compileOptions (#9622)
Both compileOptions sites mapped the whole classpath through
converter.toPath -> converter.toVirtualFile. Going through toPath
defeats FileConverter.toVirtualFile(VirtualFileRef), which already
returns the ref unchanged when it is a VirtualFile, so every entry was
rebuilt from scratch. For a class directory that means walking the
entire output tree, once per dependent.

backendOutput is converted explicitly because it is a settingKey
evaluated once at project load, so reusing it would pin the listing
taken before anything compiled.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 11:40:54 -04:00
Albert Meltzer 8c76375eab
[2.x] fix: Name the platform in CrossVersion(module, scalaModuleInfo) (#9620)
**Problem**
CrossVersion(module, scalaModuleInfo) is handed everything needed to name an
artifact, including ScalaModuleInfo.platform, and the name it returns is what
a caller publishes or resolves under. Nothing covers what it does with the
platform.

**Solution**
Name the artifact in full: platform suffix before cross suffix, matching the
coordinate (sbt/sbt#9117), through addPlatformSuffix, which already knows that
jvm contributes no suffix.

Generated-by: Claude Opus 5
2026-08-18 02:04:57 -04:00
eugene yokota 27c3f035e5
[2.x] feat: Test summary (#9602)
**Problem/Solution**
This extends the idea started with TestRecap, and applies it to
both test success and failures.

1. Existing TestResultLogger trait is extended to handle the summary
   rendering.
2. TestSummary enum is added to control the verbosity via
   commandline option, system property, or a setting.
3. Script test captures the log.
2026-08-17 23:54:32 -04:00
Mai Huy Hoàng f7f337033d
[2.x] perf: Skip re-packaging the class directory when zinc recompiles nothing (#9609)
Reuse the sibling dirzip when zinc reports it wrote nothing. The output
is still declared, so the stored ActionResult stays complete and a later hit
restores the class directory exactly as before -- it is the same
HashedVirtualFileRef the packaging path would have produced, read off disk
rather than rebuilt.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 21:55:57 -04:00
eugene yokota 0ae3c152bd
[2.x] fix: Guard diskcache against path traversal (#9605)
**Problem**
There are several places in diskcache where resolve is called
without guards.

**Solution**
This adds guards to prevent path traversal.
2026-08-17 14:09:03 -04:00
BrianHotopp e8f40d68c3
[2.x] fix: sbtn to log connection errors
onClose now logs sbt server disconnected when the close wasn't initiated by the client.

Generated-by: Oh My Pi (kimi-code/k3)
2026-08-17 14:08:21 -04:00
eugene yokota c0ffc9dc60
[2.x] test: Add a regression test for incremental test (#9608)
**Problem/Solution**
This tests incremental test's stability post-version bump.
2026-08-17 00:07:28 -04:00
kenji yoshida 42f8b949f7
[2.x] ci: Update sbt and plugins (#9603) 2026-08-15 11:20:18 -04:00
kenji yoshida d560776a77
[2.x] refactor: Remove nowarn annotation in CoursierDependencyResolution (#9593) 2026-08-14 08:53:42 -04:00
eugene yokota a94ef44178
[2.x] fix: Forward build.sbt compilation errors to logger (#9599)
**Problem**
1. build.sbt was compiled with console reporter,
   so we couldn't capture the error.
2. Another problem was that reload deleted global log
   so we couldn't get to the previous load failure.

**Solution**
1. This forwards build.sbt compilation errors to the logger.
2. This retains the failed loading log.
3. Using the facility above, this adds negative test for loading.
2026-08-14 08:51:51 -04:00
kenji yoshida 1d6d6ea661
[2.x] refactor: Fix warning in ivyless-publish-http-plugin test (#9597) 2026-08-13 19:35:37 -04:00
kenji yoshida 98d17419d0
[2.x] test: Fix project-matrix test TODO (#9596) 2026-08-13 19:33:28 -04:00
eugene yokota c717609ab2
[2.x] doc: Start sbt 2.1.0 release note (#9595) 2026-08-13 17:00:17 -04:00
kenji yoshida c5eac14c14
[2.x] refactor: Remove unused code (#9589) 2026-08-13 14:50:30 -04:00
Jozef Koval 5da646f977
[2.x] fix: Honor -java-home in the Windows launchers (#9434)
**Problem**

-java-home wasn't fully honored on Windows: sbtw rejected the single-dash form, silently ignored bad paths, and didn't propagate the JDK to the sbt/sbtn child; sbt.bat didn't fix up PATH/JAVACMD, and a project .java-version could override an explicit -java-home. Residual #963 gaps (the original javac-from-PATH bug was fixed long ago).

**Solution**

New SelectedJava seam in sbtw resolves the JDK once (--java-home > JAVACMD > JAVA_HOME > PATH) and applies one env overlay on every launch path; sbt.bat's handler now sets PATH/JAVACMD and a one-hop marker so .java-version defers to an explicit -java-home.

JDK_HOME > JAVA_HOME (the issue's "even more desired" ordering) is intentionally **not** implemented — it only mattered when JAVA_HOME pointed at a JRE without javac, and JDK 17+ (required by sbt 2.x) no longer ships standalone JREs, so JAVA_HOME always resolves to a full JDK.
2026-08-13 13:52:46 -04:00
kenji yoshida a3dad963f0
[2.x] refactor: Remove redundant String.toString (#9592) 2026-08-13 13:49:06 -04:00
kenji yoshida d9f60b6cfd
[2.x] refactor: Add tailrec annotation (#9591) 2026-08-13 13:48:02 -04:00
kenji yoshida 354dbf515b
[2.x] doc: Fix HashAlgo scaladoc (#9590) 2026-08-13 13:47:19 -04:00
kenji yoshida a6ae53c73e
[2.x] Update Eval.compileAndLoad (#9583) 2026-08-12 23:33:08 -04:00
kenji yoshida 784d116bd4
[2.x] Specific error message for defining types (#9579) 2026-08-12 11:51:25 -04:00
Mai Huy Hoàng 0ac15531a8
[2.x] perf: Keep the analysis cached across an action cache output sync (#9550)
The local analysis cache validates an entry against the analysis file's
timestamp and size, so a file re-created with the same content loses its
entry. That is exactly what happens to every analysis sbt writes: the
analysis file is a declared output of compileIncremental, so once the task
body has written it, syncFile deletes it and re-creates it as a symlink into
the CAS. Measured on a two-module build, the file came back four
milliseconds later at the same size under a new timestamp, and the read in
compileIncremental that follows deserialized the analysis it had just
written - one full deserialization per compiled module per compile, which is
the cost the cache exists to avoid. On this repo's own largest analysis
(519K) that read costs 19.5ms, against 0.098ms to hash the file and 0.0025ms
to stat it.

Populating the cache from set() is not enough on its own, and populating it
with the contents handed to set() is wrong: ConsistentAnalysisFormat does not
persist Compilations, so serving the in-memory analysis flips
CompileResult.hasModified from false to true and makes compileTask store the
analysis on every compile, including no-ops. set() now records what a read
of the file returns, and an entry whose size and content hash still match is
served under a new timestamp rather than discarded.

A read whose timestamp and size still match does not hash the file, so a warm
no-op compile costs one stat per read; a read that finds no entry to compare
against hashes the file it is about to deserialize, and set() hashes the file
it wrote, since the file it has to describe is replaced moments later.
Recording an entry cannot fail a compile that has already written its
analysis, so an IO error from stat-ing or hashing that file is dropped.

Across five compiles of two modules with two source edits, the
deserializations go from five to none: previousCompile, compileIncremental,
compileScalaBackend and the dependency analysis read in compileIncSetup are
all served from memory after the first write of each module's analysis.

Nothing a build can observe says whether a compile deserialized an analysis
it already had: an analysis served from the cache is indistinguishable from
one read back, so the unit tests can pin the store's behaviour but not the
compile's. The cache therefore counts what its reads cost - answered from
memory, hashed the file to answer, deserialized it - and a scripted test
reads those counts around a compile. The counts are sbt-private, so the test
reaches them from a helper declared in package sbt under project/.

Two invariants, one per failure mode. A compile with nothing to do must
answer every read from memory without even re-reading the file to hash it,
which pins the timestamp check; dropping it makes 7 of 7 reads hash. A
compile that recompiles must be served the analysis it just wrote, which pins
the fix; keeping develop's cache and adding only the counters deserializes 1
of 7.

Both depend on the compiles being real ones, because an analysis the action
cache served was never written and so there is nothing for the local cache to
have kept. The action cache is global, and its key does not depend on where
the build sits, so a second run of this test would otherwise recompile
nothing: the build points localCacheDirectory inside the sandbox - in Global,
which is the scope cacheStores resolves it in - and the test deletes target
first, so a run cannot inherit what an earlier one compiled.

Generated-by: claude-opus-5 (Claude Code)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 15:57:18 -04:00
eugene yokota b23891a643
Merge pull request #9574 from anatoliykmetyuk/br/fix-9343-export-pipelining
Ensure Java artifacts are exposed when usePipelining is set to true
2026-08-10 15:49:36 -04:00
Anatolii Kmetiuk d4dad933fa Cache pipelined Java compilation 2026-08-10 11:11:47 +09:00
Anatolii Kmetiuk d387b7fe11 Fix Java output when export pipelining is disabled 2026-08-10 11:11:47 +09:00
Mai Huy Hoàng 1c4abbbcbd
[2.x] fix: Don't re-extract a dirzip whose digest already matches (#9555)
syncFile treated an output as up to date only when its digest matched and the
path was already a symlink. packageDirectory installs the archive with
Files.move, so on the write path it is always a regular file and the second
conjunct never held. Every compile therefore fell through to IO.delete +
writeFileAndNotify -> afterFileWrite -> unpackageDirZip, which inflated the
archive it had just written into a temp directory, digest-compared every file in
the output tree, found all of them unchanged, and deleted the temp directory.

The cost is proportional to output size and is paid on every compile of every
module. Measured at ~450 ms for a 2557-file, 33 MB classes directory (11 MB
archive), against a 4.2 s warm single-file edit.

A matching digest already means the local content is the blob's, whether or not
the path is a symlink, so take the up-to-date path in that case. Where the path
is not yet a symlink, still relink it to the CAS to keep deduplication, but
reach afterFileUpToDate rather than afterFileWrite. The symlink-creation
fallback is factored into linkOrCopy and shared with writeFileAndNotify.

For non-dirzip outputs both hooks are no-ops, and that branch already did a
delete-and-relink, so their behaviour is unchanged. The behaviour given up is
that a file inside the extracted directory which diverged from the manifest is
no longer silently restored while the archive digest still matches; on a cache
hit with a differing digest the full unpackage runs as before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 00:39:10 -04:00
eugene yokota 90582a3438
[2.x] fix: Fixes publishing with sbt-pgp (#9533)
**Problem**
Ivyless publishing doesn't work with sbt-pgp.

**Solution**
This refactors the ivyless publishing code and revives publishOrSkip, which is called by the plugin.
Instead of overriding publish task, implement proper PublisherInterface.
2026-08-09 00:37:25 -04:00
eugene yokota b338dfcec2
[2.x] ci: Remove cla-checker v1 (#9570) 2026-08-07 14:48:15 -04:00
eugene yokota 6365a07230
Merge commit from fork
[2.x] fix: Gate LSP calls behind auth
2026-08-06 23:04:54 -04:00
eugene yokota b109068711
[2.x] fix: Preserve file timestamp in local diskcache (#9559)
**Problem**
In theory, we shouldn't have to care about the timestamps,
but for local optimization it's still a useful key for caching.
Currently local Analysis cache gets invalidated after
syncBlob replaces the Analysis file with a symlink.

**Solution**
This preserves the original timestamp when copying files to
the local diskcache.
2026-08-06 20:51:23 -04:00
tanaka takaya 1038618246
[2.x] perf: Memoize the artifact content hash in LibraryManagementCodec (#9563)
**Problem**
Serializing an UpdateReport re-reads every artifact through sjson-new's
File iso to recompute its sha256, so every ActionCache key computation
and Tracked write-back re-reads the whole dependency classpath on every
invocation, including no-ops.

**Solution**
Override the iso with a hash memoized by (path, size, lastModified),
with the same invalidation semantics as the digest cache in sbt/sbt#8363,
and mix it into the generated codec via contrabandCodecParents.

Generated-by: Claude Fable 5
2026-08-06 20:46:13 -04:00
Eugene Yokota fb233e9411 [2.x] Retire textDocument/definition 2026-08-06 14:02:51 -04:00
Eugene Yokota e6ac4ecffc [2.x] fix: Gate LSP calls behind auth
**Problem**
Some custom LSP calls do not check initialize-handshake,
which over TCP includes token-based authentication.

**Solution**
This adds checkAuthenticated check around sbt/exec etc.
2026-08-06 14:02:51 -04:00
BrianHotopp b0c840105b
[2.x] fix: Bound the client boot wait when the forked server never starts (#9549)
The thin client's blockUntilStart loop recursed while the portfile was
missing and the forked process looked valid, with no deadline: a forked
server that died before writing project/target/active.json, or stayed
alive but wedged, hung the client forever with no output. On Windows the
check ignored process death entirely (Properties.isWin short-circuited
the liveness test), making the hang unconditional there.

The wait is now bounded (default 5 minutes, tunable with
-Dsbt.client.boot.timeout.seconds). On expiry the client fails with a
"did not start within N seconds" message and then prints the server's
captured stderr, instead of hanging silently (#9484). The Windows
liveness workaround is preserved; the deadline is what bounds it.

Generated-by: kimi-code/k3 (Oh My Pi)
2026-08-04 15:59:48 -04:00
eugene yokota 380ff6b569
[2.x] test: Add regression test for resource reading (#9552) 2026-08-04 15:03:25 -04:00
BrianHotopp 25445191d7
[2.x] fix: Complete earlyOutputPing on cache-hit and failed compiles (#9542)
With usePipelining enabled, earlyOutputPing was completed only as a side
effect of zinc reporting progress mid-compile (CompileProgress.afterEarlyOutput
via writeEarlyOut / notifyNoEarlyOut in zinc-core). On an action-cache hit zinc
never runs, and on a failed compile it never reports, so the promise stayed
unfulfilled and every task waiting on it - compileEarly and makePickleProducts
across downstream pipelined projects - parked forever. A warm compile;compile
on a multi-module build hung indefinitely (#9486).

The compile task now completes the ping on every resolution path. Zinc's own
completion still wins when zinc ran (tryComplete is atomic, so it can never
pre-empt it); a cache hit completes it from the pickle jar's presence on disk
(absent jar means downstream falls back to a full compile, which is the correct
degradation); a failed compile completes it false so waiters take the full
compile path and propagate the failure instead of hanging.

Generated-by: kimi-code/k3 (Oh My Pi)
2026-08-03 16:28:11 -04:00
eugene yokota 5d7dbdb427
[2.x] sbtn 2.0.0-731e6666 (#9545) 2026-08-03 03:38:20 -04:00
eugene yokota 67faa44a1d
[2.x] test: Add test for Test/baseDirectory (#9544) 2026-08-03 03:32:19 -04:00
eugene yokota 731e666603
[2.x] fix: Fixes AccessDeniedException issue on Windows (#9538)
**Problem**
When test classloader holds on to the JAR file, Windows gets
AccessDefinedException on packageBin.

**Solution**
Flip the default to close the test class loader.
2026-08-03 00:24:41 -04:00
kenji yoshida 040eaaa062
[2.x] refactor: Remove unnecessary match (#9539) 2026-08-02 05:41:01 -04:00
BrianHotopp c407f37739
[2.x] fix: Don't strand the server when a client dies with a terminal query unanswered (#9527)
A client disconnecting with a terminal control query outstanding could park a
server thread forever, wedging prompts and command dispatch for every client
(#6841, #6840):

- VirtualTerminal.cancelRequests drained only 2 of the 8 pending terminal
  maps, so waiters on the set-echo, raw-mode, attributes, and size queues were
  never woken. It now drains all of them, with offer instead of put so the
  shutdown path itself cannot block on a full queue.
- Raw-mode requests were registered in the set-echo map, so their waiters were
  invisible to any raw-mode-specific handling.
- Closing a channel terminal did not wake readers parked on its input stream;
  close now delivers EOF so a prompt blocked on a dead client's input unwinds.
- The failed-load prompt read its answer byte from System.in, which under
  non-virtual IO is the process's own stdin and never carries client input; it
  now reads the active terminal's input stream.
- ServerSessionImpl.close() could not deliver EOF to the peer while its read
  thread was parked in a native read (the native close is never delivered), so
  the server never noticed orderly client disconnects at all. It now shuts
  down socket input first, which wakes the reader and lets the close through.

Regression test: a raw-protocol client that attaches, triggers the failed-load
prompt without answering the raw-mode query, and disconnects; the server must
shut down cleanly (EOF at the prompt maps to 'q') instead of staying parked
forever. Fails on develop with the server still alive and the command loop
parked in setRawMode; passes with this change. VirtualTerminalSpec pins the
drain across all eight maps and that other channels are untouched.

Generated-by: kimi-code/k3 (Oh My Pi)
2026-08-02 02:35:39 -04:00
KilianSwissborg 56c3633874
[2.x] fix: Preserve '=' in remote cache header values (#9534)
**Problem**
`remoteCacheHeaders` entries are parsed in GrpcActionCacheStore.AuthCallCredentials
with h.split("="), which splits on every =. Java's split discards trailing empty
strings, so a Basic auth header such as authorization=Basic dXNlcjpwdw== produces
exactly two elements and matches List(k, v) with the base64 padding silently removed.
The truncated credential is rejected by the cache server with UNAUTHENTICATED.

**Solution**
Split on the first = only, keeping the remainder of the string verbatim as the header
value. The error case narrows to a header containing no = at all.

Generated-by: Claude Opus 5
2026-07-31 14:50:01 -04:00
eugene yokota bb141109e1
Merge pull request #9523 from hoangmaihuy/perf/update-report-interning
[2.x] perf: Intern `UpdateReport` values
2026-07-31 00:58:19 -04:00
eugene yokota 130ee707b9
[2.x] fix: Fixes forked run baseDirectory, take 2 (#9531)
**Problem**
Forked run baseDirectory was changed to current directory in sbt 2.0.4,
which on its own is fine, but it doesn't respect
Compile / run / baseDirectory.

**Solution**
This fixes that.
2026-07-31 00:04:22 -04:00
Mai Huy Hoàng 3a79f269c7
[2.x] perf: Read the resolution's project cache once per report (#9522)
Resolution.projectCache is not a field: it maps projectCache0 into a
version-string-keyed copy on every call. SbtUpdateReport read it once per
dependency, and again per parent POM while assembling inherited licence info, so
for N modules resolved that is N rebuilds of an N-entry immutable map -- turning
a Resolution into an UpdateReport was quadratic in the modules it names.

On a 301-project build this was 61.9% of the CPU update spends, 83% of it
entering through lookupProject.

Read it once per report and reuse it, at every call site including the eviction
loop, which read it three times per conflict. The report produced is unchanged;
only the number of times the same map is built.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 01:17:04 -04:00