Documentation Index Fetch the complete documentation index at: https://mintlify.com/sbt/sbt/llms.txt
Use this file to discover all available pages before exploring further.
This guide explains how to configure cross-building to support multiple Scala versions in a single project.
What is Cross-Building?
Cross-building allows you to compile, test, and publish your project for multiple Scala versions from a single codebase. This is essential for library authors who want to support users on different Scala versions.
Cross-building is primarily used for Scala libraries. Applications typically target a single Scala version.
Configuring Cross-Building
Setting Scala Versions
Define the Scala versions to build against:
// Primary Scala version
scalaVersion := "3.3.1"
// Additional Scala versions for cross-building
crossScalaVersions := Seq ( "2.12.18" , "2.13.12" , "3.3.1" )
The scalaVersion should typically be included in crossScalaVersions.
Cross-Building Commands
Use the + prefix to execute commands for all Scala versions:
Compilation
Testing
Publishing
# Compile for all Scala versions
sbt +compile
# Clean and compile for all versions
sbt +clean +compile
Switching Scala Versions
The ++ command switches the Scala version:
Switch to a specific version
This switches the build to use Scala 2.13.12.
Switch and run a command
Switches to 2.13.12 and compiles.
Force a version not in crossScalaVersions
The ! forces the version even if not in crossScalaVersions.
Use version selectors
Selects the latest 2.13.x version from crossScalaVersions.
Version-Specific Code
Source Directories
sbt automatically includes version-specific source directories:
src/
main/
scala/ # Shared code
scala-2.13/ # Scala 2.13 specific
scala-2/ # All Scala 2.x versions
scala-3/ # Scala 3 specific
Directory matching rules:
scala-2.13/ - Only for Scala 2.13.x
scala-2.12/ - Only for Scala 2.12.x
scala-2/ - All Scala 2.x versions
scala-3/ - All Scala 3.x versions
Conditional Dependencies
Add dependencies for specific Scala versions:
libraryDependencies ++= {
CrossVersion .partialVersion(scalaVersion.value) match {
case Some (( 2 , 13 )) =>
Seq (
"org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.4"
)
case Some (( 3 , _)) =>
Seq (
"org.scala-lang" %% "scala3-library" % scalaVersion.value
)
case _ => Seq .empty
}
}
Conditional Compiler Options
scalacOptions ++= {
CrossVersion .partialVersion(scalaVersion.value) match {
case Some (( 2 , 13 )) =>
Seq ( "-Xlint" , "-Ywarn-unused" , "-deprecation" )
case Some (( 3 , _)) =>
Seq ( "-explain" , "-deprecation" , "-feature" )
case _ => Seq .empty
}
}
Cross-Version Patterns
Using CrossVersion
sbt provides utilities for handling cross-version compatibility:
import sbt . CrossVersion
// Get binary version
val binaryVersion = CrossVersion .binaryScalaVersion(scalaVersion.value)
// "2.13.12" -> "2.13"
// "3.3.1" -> "3"
// Pattern matching on version
CrossVersion .partialVersion(scalaVersion.value) match {
case Some (( 2 , minor)) if minor >= 13 => // Scala 2.13+
case Some (( 3 , _)) => // Scala 3
case _ => // Other versions
}
Cross-Building Libraries
When building libraries, use %% for dependencies:
libraryDependencies ++= Seq (
"org.typelevel" %% "cats-core" % "2.10.0" ,
"co.fs2" %% "fs2-core" % "3.9.3"
)
The %% operator automatically appends the Scala binary version:
Scala 2.13: cats-core_2.13
Scala 3: cats-core_3
Never use %% for Java libraries - they don’t have Scala version suffixes.
For Scala.js or Scala Native:
import sbtcrossproject . CrossPlugin . autoImport .{ crossProject , CrossType }
lazy val myProject = crossProject( JVMPlatform , JSPlatform , NativePlatform )
.crossType( CrossType . Full )
.in(file( "." ))
.settings(
scalaVersion := "3.3.1" ,
crossScalaVersions := Seq ( "2.13.12" , "3.3.1" )
)
.jvmSettings(
// JVM-specific settings
)
.jsSettings(
// Scala.js specific settings
)
.nativeSettings(
// Scala Native specific settings
)
Cross-Building sbt Plugins
For sbt plugins, cross-build against sbt versions:
sbtPlugin := true
// sbt binary version to compile against
pluginCrossBuild / sbtVersion := "2.0.0-M2"
// Support multiple sbt versions
crossSbtVersions := Seq ( "2.0.0-M2" )
# Publish for all sbt versions
sbt +publish
Migration Strategies
Scala 2.13 to Scala 3
Ensure 2.13 compatibility
Make sure your code compiles on Scala 2.13 with: scalacOptions += "-Xsource:3"
Add Scala 3 to crossScalaVersions
crossScalaVersions := Seq ( "2.13.12" , "3.3.1" )
Fix Scala 3 incompatibilities
Use version-specific source directories for code that differs.
Advanced Cross-Version Configuration
Per-Project Cross Versions
lazy val core = project
.settings(
name := "core" ,
crossScalaVersions := Seq ( "2.12.18" , "2.13.12" , "3.3.1" )
)
lazy val extras = project
.dependsOn(core)
.settings(
name := "extras" ,
crossScalaVersions := Seq ( "2.13.12" , "3.3.1" ) // Fewer versions
)
Cross-Version Exclusions
libraryDependencies ++= {
CrossVersion .partialVersion(scalaVersion.value) match {
case Some (( 2 , _)) =>
Seq (
"org.scala-lang.modules" %% "scala-collection-compat" % "2.11.0"
)
case _ => Seq .empty
}
}
Testing Cross-Built Artifacts
Verify all versions build correctly:
# Clean everything
sbt +clean
# Compile all versions
sbt +compile
# Test all versions
sbt +test
# Publish all versions locally
sbt +publishLocal
# Verify artifacts were created
find ~/.ivy2/local -name "*my-library*"
Continuous Integration
Configure CI to test all Scala versions:
name : CI
on : [ push , pull_request ]
jobs :
test :
runs-on : ubuntu-latest
strategy :
matrix :
scala : [ 2.12.18 , 2.13.12 , 3.3.1 ]
steps :
- uses : actions/checkout@v3
- uses : actions/setup-java@v3
with :
java-version : '17'
- name : Test
run : sbt "++${{ matrix.scala }} test"
Common Pitfalls
Common issues when cross-building:
Forgetting to update crossScalaVersions - Always keep this list up to date
Using wrong dependency operators - Use %% for Scala libraries, % for Java libraries
Not testing all versions - Always run +test before publishing
Version-specific code without organization - Use source directories properly
Incompatible compiler flags - Some flags only work on specific Scala versions
Best Practices
Support current versions
At minimum, support the latest patch releases of Scala 2.13 and Scala 3.
Use version-specific directories
Organize version-specific code in scala-2/ and scala-3/ directories.
Test all combinations
Use sbt +test to test all Scala versions before releasing.
Document compatibility
Clearly document which Scala versions your library supports.
Minimize version-specific code
Keep shared code in the common scala/ directory when possible.
Use CrossVersion helpers
Leverage CrossVersion.partialVersion for conditional configuration.
Reference
CrossVersion API
import sbt . CrossVersion
// Get binary version
CrossVersion .binaryScalaVersion( "2.13.12" ) // "2.13"
CrossVersion .binaryScalaVersion( "3.3.1" ) // "3"
// Parse version
CrossVersion .partialVersion( "2.13.12" ) // Some((2, 13))
CrossVersion .partialVersion( "3.3.1" ) // Some((3, 3))