Skip to content

Commit bf4a709

Browse files
docs: add cross-compilation guide for Scala 2/3 setups (#1747)
1 parent 0dbebb8 commit bf4a709

File tree

1 file changed

+144
-0
lines changed

1 file changed

+144
-0
lines changed

docs/users/cross-compilation.md

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
id: cross-compilation
3+
title: Cross compilation setups
4+
---
5+
6+
Cross-building the same codebase with Scala 2.13 and Scala 3.3+ is common, but
7+
it collides with how Scalafix resolves rules and compiler options. A rule or
8+
`--scalac-options` flag that only works on Scala 2 can crash Scalafix when the
9+
Scala 3 compilation unit is processed, and vice versa. Until Scalafix grows
10+
conditional configuration, the safest way to avoid those failures is to split
11+
your configuration per Scala version and let the build tool wire the right file.
12+
13+
## Why separate configs are required today
14+
15+
* Scala 2 only flags such as `-Ywarn-unused-import` or SemanticDB options using
16+
`-P:semanticdb` are rejected by Scala 3.
17+
* Some built-in or custom rules depend on compiler symbols that only exist on
18+
one major version.
19+
* The CLI currently ingests a single `.scalafix.conf`; there is no per-target
20+
override like sbt’s `CrossVersion`.
21+
22+
## File layout suggestion
23+
24+
Keep shared defaults in one file and let version-specific files `include` it via
25+
the HOCON `include` syntax. One convenient layout is:
26+
27+
```
28+
.
29+
├── project/
30+
│ └── build config…
31+
└── scalafix/
32+
├── common.conf
33+
├── scala2.conf
34+
└── scala3.conf
35+
```
36+
37+
`common.conf`
38+
```scala
39+
rules = [
40+
DisableSyntax,
41+
OrganizeImports
42+
]
43+
44+
DisableSyntax {
45+
noFinalize = true
46+
}
47+
```
48+
49+
`scala2.conf`
50+
```scala
51+
include "common.conf"
52+
53+
rules += RemoveUnused
54+
55+
// Scala 2 only compilers flags or rule settings
56+
RemoveUnused {
57+
imports = true
58+
}
59+
```
60+
61+
`scala3.conf`
62+
```scala
63+
include "common.conf"
64+
65+
rules += LeakingImplicitClassVal
66+
67+
// Scala 3 specific tweaks go here
68+
OrganizeImports {
69+
groupedImports = Keep
70+
}
71+
```
72+
73+
### Multiple include files
74+
75+
You may split out even more granular snippets (for example `linting.conf`,
76+
`rewrites.conf`) and include them from both `scala2.conf` and `scala3.conf`. The
77+
HOCON syntax supports nested includes, so feel free to create the hierarchy that
78+
matches your team conventions.
79+
80+
## Selecting the right config in sbt
81+
82+
Point `scalafixConfig` at the version-specific file, typically inside a helper
83+
setting applied to all cross-built projects:
84+
85+
```scala
86+
import scalafix.sbt.ScalafixPlugin.autoImport._
87+
88+
lazy val commonSettings = Seq(
89+
scalafixConfig := {
90+
val base = (ThisBuild / baseDirectory).value / "scalafix"
91+
val file =
92+
CrossVersion.partialVersion(scalaVersion.value) match {
93+
case Some((3, _)) => base / "scala3.conf"
94+
case _ => base / "scala2.conf"
95+
}
96+
Some(file)
97+
}
98+
)
99+
100+
lazy val core = project
101+
.settings(commonSettings)
102+
.settings(
103+
scalaVersion := "3.3.3",
104+
crossScalaVersions := Seq("2.13.14", "3.3.3")
105+
)
106+
```
107+
108+
For builds that already differentiate per configuration (`Compile`, `Test`,
109+
`IntegrationTest`), you can set `Compile / scalafixConfig` and
110+
`Test / scalafixConfig` separately if the inputs diverge.
111+
112+
### Command-line usage
113+
114+
When invoking the CLI directly, pass the desired config with `--config`:
115+
116+
```
117+
scalafix --config scalafix/scala2.conf --rules RemoveUnused
118+
scalafix --config scalafix/scala3.conf --rules LeakingImplicitClassVal
119+
```
120+
121+
Your CI job can loop over each target Scala version, selecting the matching
122+
config before running `scalafix --check`.
123+
124+
## Recommendations and current limitations
125+
126+
* Keep rules that truly work on both versions inside `common.conf`.
127+
* Isolate risky scalac options (`-Wunused`, `-Ywarn-unused-import`, `-P:semanticdb`)
128+
inside each version-specific file or sbt setting.
129+
* Document in the repository README which rules run on which Scala version to
130+
reduce confusion for new contributors.
131+
132+
### Looking ahead
133+
134+
Issue [#1747](https://github.com/scalacenter/scalafix/issues/1747) tracks better
135+
ergonomics for cross compilation. Potential improvements include:
136+
137+
* Conditional configuration blocks directly inside `.scalafix.conf` (for example
138+
`if scalaVersion.startsWith("3.")`).
139+
* First-class support for including multiple files via CLI flags.
140+
* Allowing rule selection based on the detected input Scala dialect.
141+
142+
Until those land, the include-based layout above is the recommended, battle-tested
143+
approach.
144+

0 commit comments

Comments
 (0)