Skip to content

Support sbt 2.0.0 (drop sbt 1.x cross-build)#620

Merged
xerial merged 2 commits into
mainfrom
resume-sbt2-migration
Jun 16, 2026
Merged

Support sbt 2.0.0 (drop sbt 1.x cross-build)#620
xerial merged 2 commits into
mainfrom
resume-sbt2-migration

Conversation

@xerial

@xerial xerial commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Summary

Resumes the sbt 2 migration now that sbt 2.0.0 is officially released, and completes it by targeting sbt 2.0.0 / Scala 3.8.4 exclusively. Cross-building back to sbt 1.x is dropped — the source-level differences (Scala 3 syntax, sbt 2 cached-task output types, Def.uncached escape hatch, fileConverter plumbing) make a single cross-built source tree impractical.

Plugin changes

  • Bump to sbt 2.0.0 and Scala 3.8.4 (matches sbt 2.0.0's bundled compiler — older Scala 3 versions can't read sbt 2.0.0's TASTy 28.8).
  • Wrap pack tasks in Def.uncached(...) — opts out of sbt 2's typed cache, which doesn't accept File/Path outputs or custom return types without JsonFormat.
  • Drop Def.derive wrappers on file-producing tasks. sbt 2's automatic dynamicFileOutputs broadcasts the derivations into delegated scopes where the underlying task isn't defined, causing build-load failures.
  • Hardcode Runtime config for packLibJars / packModuleEntries (was reading configuration.value, which is now unresolved at project scope after dropping Def.derive).
  • packTargetDir defaults to baseDirectory.value / "target" instead of target.value — sbt 2's target.value resolves to a nested target/out/jvm/u/{project}/ path, not the stable location users expect.
  • Plumb sbt's fileConverter through PluginCompat so HashedVirtualFileRef paths resolve via the build's MappedFileConverter (expands ${OUT} / ${BASE} placeholders) instead of the placeholder-leaving PlainVirtualFileConverter.

Layout & tooling

  • Consolidate scala-2/ and scala-3/ PluginCompat into a single src/main/scala/ tree.
  • Update .scalafmt.conf dialect to scala3.
  • Drop sbt 1.x CI job; simplify command to ./sbt test scripted.

Scripted test fixes

  • Replace force () infix invocation with .force() (Scala 3 rejects the Scala 2 nullary-method infix form).
  • nested-project: distinct project names + crossPaths := false to avoid sbt 2's "Overlapping output directories" check.
  • copy-dependencies: anchor packCopyDependenciesTarget to baseDirectory.value / "target" for the same reason as packTargetDir.
  • Remove sbt 1.x pins on jvm-version-opts and exclude-test-config — they now run under sbt 2.

Test plan

  • ./sbt scalafmtCheckAll — clean
  • ./sbt test — 5 unit tests pass
  • ./sbt scripted — all 17 scripted tests pass under sbt 2.0.0
  • CI green on Java 21 and Java 24

🤖 Generated with Claude Code

This completes the sbt 2 migration. Now that sbt 2.0.0 is officially
released, the plugin targets sbt 2.0.0 / Scala 3.8.4 exclusively.
Cross-building back to sbt 1.x is no longer supported, since the
required source-level changes (Scala 3 syntax, sbt 2 cached-task
output types, Def.uncached escape hatch) would be too invasive to
keep both targets in one source tree.

Plugin changes:
- Bump to sbt 2.0.0 and Scala 3.8.4 (matches sbt 2.0.0's bundled compiler)
- Wrap pack/packArchive*/packCopyDependencies and related tasks in
  Def.uncached(...) — opts out of sbt 2's typed cache, which doesn't
  accept File/Path outputs or custom return types without JsonFormat
- Drop Def.derive wrappers on file-producing tasks: sbt 2's automatic
  dynamicFileOutputs broadcasts those derivations into delegated
  scopes where the underlying task is not defined and the build fails
  to load. The wrapped tasks are project-level, not config-scoped, so
  Def.derive was unnecessary
- Hardcode Runtime config for packLibJars/packModuleEntries instead of
  reading configuration.value at project scope (no longer defined
  without Def.derive)
- Switch packTargetDir default from target.value to
  baseDirectory.value / "target" — sbt 2's target.value resolves to a
  nested target/out/jvm/u/{project}/ path, which is not the stable
  location users expect for pack output
- Plumb sbt's fileConverter through PluginCompat so HashedVirtualFileRef
  paths are resolved with the build's MappedFileConverter (which
  expands ${OUT}/${BASE} placeholders) instead of the placeholder-leaving
  PlainVirtualFileConverter

Source layout & tooling:
- Consolidate scala-2/ and scala-3/ PluginCompat into a single
  src/main/scala/ tree
- Update .scalafmt.conf dialect to scala3
- Drop sbt 1.x CI job; simplify command to `./sbt test scripted`

Scripted test fixes:
- Replace `force ()` infix invocation with `.force()` (Scala 3
  rejects the Scala 2 nullary-method infix form)
- nested-project: set distinct project names + crossPaths := false
  to avoid sbt 2's "Overlapping output directories" check
- copy-dependencies: anchor packCopyDependenciesTarget to
  baseDirectory.value / "target" for the same reason packTargetDir
  was anchored
- Remove sbt 1.x pins on jvm-version-opts and exclude-test-config

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request migrates the sbt-pack plugin to Scala 3 and sbt 2.0.0. Key changes include replacing Def.derive with Def.uncached, integrating sbt 2.x's FileConverter for virtual file compatibility, and updating target directory definitions to avoid nested output directories. Feedback suggests updating packCopyDependenciesTarget to use packTargetDir.value / "lib" instead of target.value / "lib" to maintain consistency with the updated packTargetDir and ensure dependencies are copied to the correct location.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

},
packCopyDependenciesUseSymbolicLinks := true,
packIncludedProjectScopes := Seq("compile->"),
packCopyDependenciesTarget := target.value / "lib",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since packTargetDir was updated to default to baseDirectory.value / "target" to avoid sbt 2.0's nested output directory, packCopyDependenciesTarget should also be updated to use packTargetDir.value / "lib" instead of target.value / "lib". This ensures that copied dependencies are placed in the stable, user-expected target/lib directory by default, maintaining consistency with packTargetDir and sbt 1.x behavior.

    packCopyDependenciesTarget           := packTargetDir.value / "lib",

- .scala-steward.conf: drop the 'scala-library 2.12.*' pin; sbt 2 plugins use Scala 3.
  The pin still lives on the sbt-1 branch where it is still correct.
- .github/dependabot.yml: add a second updates entry targeting the sbt-1 branch so
  GitHub Actions versions stay current on that branch too.

Note: scala-steward only tracks the default branch by default. To get Scala/sbt
update PRs opened against the sbt-1 branch, the repo needs a non-default-branch
entry in the scala-steward fleet config (e.g. scala-steward-org/repos-github-oss).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@xerial xerial merged commit 458fa12 into main Jun 16, 2026
8 checks passed
@xerial xerial deleted the resume-sbt2-migration branch June 16, 2026 07:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant