Skip to content

Commit d92cad8

Browse files
committed
add scalafix-loader
1 parent 4769941 commit d92cad8

File tree

19 files changed

+609
-232
lines changed

19 files changed

+609
-232
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ hesitate to ask in the [Discord channel](https://discord.gg/8AHaqGx3Qj).
1919

2020
### For tool integration
2121
- `scalafix-interfaces/` Java facade to run rules within an existing JVM instance.
22+
- `scalafix-loader/` Java implementation to dynamically fetch and load
23+
implementations to run rules.
2224
- `scalafix-versions/` Java implementation to advertize which Scala versions
2325
`scalafix-cli` is published with.
2426

build.sbt

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,25 @@ lazy val versions = project
5151
.disablePlugins(ScalafixPlugin)
5252
.dependsOn(interfaces)
5353

54+
lazy val loader = project
55+
.in(file("scalafix-loader"))
56+
.settings(
57+
moduleName := "scalafix-loader",
58+
javaSettings,
59+
mimaPreviousArtifacts := Set.empty, // TODO: remove after 0.14.4
60+
libraryDependencies ++= Seq(
61+
typesafeConfig,
62+
lombok % Provided
63+
),
64+
javacOptions ++= {
65+
// https://inside.java/2024/06/18/quality-heads-up/
66+
if (jdk > 8) Seq("-proc:full")
67+
else Seq() // only backported to Oracle’s 8u release (8u411)
68+
}
69+
)
70+
.disablePlugins(ScalafixPlugin)
71+
.dependsOn(interfaces, versions)
72+
5473
// Scala 3 macros vendored separately (i.e. without runtime classes), to
5574
// shadow Scala 2.13 macros in the Scala 3 compiler classpath, while producing
5675
// code valid against Scala 2.13 bytecode
@@ -346,18 +365,9 @@ lazy val integration = projectMatrix
346365
"inputSourceroot" ->
347366
resolve(input, Compile / sourceDirectory).value,
348367
"outputSourceroot" ->
349-
resolve(output, Compile / sourceDirectory).value,
350-
"versionsJars" ->
351-
Seq(
352-
(interfaces / Compile / packageBin / artifactPath).value,
353-
(versions / Compile / packageBin / artifactPath).value
354-
)
368+
resolve(output, Compile / sourceDirectory).value
355369
),
356370
Test / test := (Test / test)
357-
.dependsOn(
358-
interfaces / Compile / packageBin,
359-
versions / Compile / packageBin
360-
)
361371
.dependsOn(resolve(cli, publishLocalTransitive))
362372
.dependsOn(
363373
cli.projectRefs
@@ -368,22 +378,18 @@ lazy val integration = projectMatrix
368378
.value,
369379
Test / testWindows := (Test / testWindows)
370380
.dependsOn(
371-
interfaces / Compile / packageBin,
372-
versions / Compile / packageBin
373-
)
374-
.dependsOn(resolve(cli, publishLocalTransitive))
375-
.dependsOn(
376-
cli.projectRefs
381+
(resolve(cli, publishLocalTransitive) +: cli.projectRefs
377382
// always publish Scala 3 artifacts to test Scala 3 minor version fallbacks
378383
.collect { case p @ LocalProject(n) if n.startsWith("cli3") => p }
379-
.map(_ / publishLocalTransitive): _*
384+
.map(_ / publishLocalTransitive)): _*
380385
)
381386
.value
382387
)
383388
.defaultAxes(VirtualAxis.jvm)
384389
.jvmPlatform(CrossVersion.full, cliScalaVersions)
385390
.enablePlugins(BuildInfoPlugin)
386391
.dependsOn(unit % "compile->test")
392+
.dependsOn(loader % "compile->test")
387393

388394
lazy val expect = projectMatrix
389395
.in(file("scalafix-tests/expect"))

project/Dependencies.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ object Dependencies {
2121
val commontTextV = "1.13.1"
2222
val googleDiffV = "1.3.0"
2323
val jgitV = "5.13.3.202401111512-r"
24+
val lombokV = "1.18.38"
2425
val metaconfigV = "0.15.0"
2526
val nailgunV = "0.9.1"
2627
val scalaXmlV = "2.2.0"
2728
val scalametaV = "4.13.5"
2829
val scalatagsV = "0.13.1"
2930
val scalatestV = "3.2.19"
3031
val munitV = "1.1.0"
32+
val typesafeConfigV = "1.4.3"
3133

3234
val bijectionCore = "com.twitter" %% "bijection-core" % bijectionCoreV
3335
val collectionCompat = "org.scala-lang.modules" %% "scala-collection-compat" % collectionCompatV
@@ -36,6 +38,7 @@ object Dependencies {
3638
val coursierInterfaces = "io.get-coursier" % "interface" % coursierInterfaceV
3739
val googleDiff = "com.googlecode.java-diff-utils" % "diffutils" % googleDiffV
3840
val jgit = "org.eclipse.jgit" % "org.eclipse.jgit" % jgitV
41+
val lombok = "org.projectlombok" % "lombok" % lombokV
3942
val metaconfig = "org.scalameta" %% "metaconfig-typesafe-config" % metaconfigV
4043
val metacp = "org.scalameta" %% "metacp" % scalametaV
4144
val nailgunServer = "com.martiansoftware" % "nailgun-server" % nailgunV
@@ -47,6 +50,7 @@ object Dependencies {
4750
val munit = "org.scalameta" %% "munit" % munitV
4851
val semanticdbScalacCore = "org.scalameta" % "semanticdb-scalac-core" % scalametaV cross CrossVersion.full
4952
val semanticdbSharedFor3Use2_13 = "org.scalameta" % "semanticdb-shared" % scalametaV cross CrossVersion.for3Use2_13
53+
val typesafeConfig = "com.typesafe" % "config" % typesafeConfigV
5054

5155
// scala-steward:off
5256

project/Mima.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ object Mima {
77
// See https://github.com/lightbend/mima
88
Seq(
99
ProblemFilters.exclude[Problem]("scalafix.internal.*"),
10-
ProblemFilters.exclude[Problem]("scala.meta.internal.*")
10+
ProblemFilters.exclude[Problem]("scala.meta.internal.*"),
1111
// Exceptions
12+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scalafix.interfaces.ScalafixArguments.withRepositories"),
13+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scalafix.interfaces.ScalafixArguments.withToolDependencyCoordinates"),
14+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scalafix.interfaces.ScalafixArguments.withToolDependencyURLs")
1215
)
1316
}
1417
}

project/ScalafixBuild.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ object ScalafixBuild extends AutoPlugin with GhpagesKeys {
4141
autoScalaLibrary := false
4242
)
4343

44+
lazy val jdk = System.getProperty("java.specification.version").toDouble
45+
4446
// https://github.com/scalameta/scalameta/issues/2485
4547
lazy val coreScalaVersions = Seq(scala212, scala213)
4648
lazy val cliScalaVersions = {
47-
val jdk = System.getProperty("java.specification.version").toDouble
4849
val scala3Versions =
4950
// Scala 3.5 will never support JDK 23
5051
if (jdk >= 23) Seq(scala33, scala36, scala37)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
scalafix.internal.interfaces.ScalafixArgumentsImpl

scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixArgumentsImpl.scala

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,39 +48,44 @@ final case class ScalafixArgumentsImpl(args: Args = Args.default)
4848
}
4949
}
5050

51+
override def withRepositories(
52+
repositories: util.List[Repository]
53+
): ScalafixArguments =
54+
copy(args = args.copy(repositories = repositories.asScala.toList))
55+
5156
override def withRules(rules: util.List[String]): ScalafixArguments =
5257
copy(args = args.copy(rules = rules.asScala.toList))
5358

5459
override def withToolClasspath(
5560
customURLs: util.List[URL]
5661
): ScalafixArguments =
57-
withToolClasspath(
58-
customURLs,
59-
Nil.asJava,
60-
Repository.defaults()
61-
)
62+
withToolDependencyURLs(customURLs)
6263

6364
override def withToolClasspath(
6465
customURLs: util.List[URL],
6566
customDependenciesCoordinates: util.List[String]
6667
): ScalafixArguments =
67-
withToolClasspath(
68-
customURLs,
69-
customDependenciesCoordinates,
70-
Repository.defaults()
71-
)
68+
withToolDependencyCoordinates(customDependenciesCoordinates)
69+
.withToolDependencyURLs(customURLs)
7270

7371
override def withToolClasspath(
7472
customURLs: util.List[URL],
7573
customDependenciesCoordinates: util.List[String],
7674
repositories: util.List[Repository]
7775
): ScalafixArguments = {
76+
withRepositories(repositories)
77+
.withToolDependencyCoordinates(customDependenciesCoordinates)
78+
.withToolDependencyURLs(customURLs)
79+
}
7880

81+
override def withToolDependencyCoordinates(
82+
withToolDependencyCoordinates: java.util.List[String]
83+
): ScalafixArguments = {
7984
val OrganizeImportsCoordinates =
8085
"""com\.github\.liancheng.*:organize-imports:.*""".r
8186

8287
val keptDependencies: Seq[String] =
83-
customDependenciesCoordinates.asScala
88+
withToolDependencyCoordinates.asScala
8489
.collect {
8590
case dep @ OrganizeImportsCoordinates() =>
8691
args.out.println(
@@ -107,7 +112,7 @@ final case class ScalafixArgumentsImpl(args: Args = Args.default)
107112
else Versions.scalaVersion
108113

109114
val customDependenciesJARs = ScalafixCoursier.toolClasspath(
110-
repositories,
115+
args.repositories.asJava,
111116
keptDependencies.asJava,
112117
scalaVersionForDependencies
113118
)
@@ -158,13 +163,23 @@ final case class ScalafixArgumentsImpl(args: Args = Args.default)
158163
}
159164
}
160165

161-
val extraURLs = customURLs.asScala ++ customDependenciesJARs
166+
val extraURLs = customDependenciesJARs
162167
.getFiles()
163168
.asScala
164169
.map(_.toURI().toURL())
165170
val classLoader = new URLClassLoader(
166171
extraURLs.toArray,
167-
getClass.getClassLoader
172+
args.toolClasspath
173+
)
174+
withToolClasspath(classLoader)
175+
}
176+
177+
override def withToolDependencyURLs(
178+
withToolDependencyURLs: java.util.List[java.net.URL]
179+
): ScalafixArguments = {
180+
val classLoader = new URLClassLoader(
181+
withToolDependencyURLs.asScala.toArray,
182+
args.toolClasspath
168183
)
169184
withToolClasspath(classLoader)
170185
}

scalafix-cli/src/main/scala/scalafix/internal/v1/Args.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import java.util.regex.Pattern
1313
import java.util.regex.PatternSyntaxException
1414

1515
import scala.annotation.StaticAnnotation
16+
import scala.jdk.CollectionConverters._
1617
import scala.util.Failure
1718
import scala.util.Success
1819
import scala.util.Try
@@ -22,6 +23,8 @@ import scala.meta.internal.symtab.SymbolTable
2223
import scala.meta.io.AbsolutePath
2324
import scala.meta.io.Classpath
2425

26+
import coursierapi.MavenRepository
27+
import coursierapi.Repository
2528
import metaconfig.Configured._
2629
import metaconfig._
2730
import metaconfig.annotation._
@@ -158,6 +161,8 @@ case class Args(
158161
"The glob syntax is defined by `nio.FileSystem.getPathMatcher`."
159162
)
160163
exclude: List[PathMatcher] = Nil,
164+
@Description("Maven repositories to fetch the artifacts from")
165+
repositories: List[Repository] = Repository.defaults.asScala.toList,
161166
@Description(
162167
"Additional classpath for compiling and classloading custom rules, as a set of filesystem paths, separated by ':' on Unix or ';' on Windows."
163168
)
@@ -483,6 +488,8 @@ object Args extends TPrintImplicits {
483488
ConfDecoder.stringConfDecoder.map(glob =>
484489
FileSystems.getDefault.getPathMatcher("glob:" + glob)
485490
)
491+
implicit val repositoryDecoder: ConfDecoder[Repository] =
492+
ConfDecoder.stringConfDecoder.map(base => MavenRepository.of(base))
486493
implicit val scalaVersionDecoder: ConfDecoder[ScalaVersion] =
487494
ScalafixConfig.scalaVersionDecoder
488495

@@ -505,6 +512,8 @@ object Args extends TPrintImplicits {
505512
ConfEncoder.StringEncoder.contramap(_ => "<stdout>")
506513
implicit val pathMatcherEncoder: ConfEncoder[PathMatcher] =
507514
ConfEncoder.StringEncoder.contramap(_.toString)
515+
implicit val repositoriesEncoder: ConfEncoder[Repository] =
516+
ConfEncoder.StringEncoder.contramap(_.toString)
508517
implicit val callbackEncoder: ConfEncoder[ScalafixMainCallback] =
509518
ConfEncoder.StringEncoder.contramap(_.toString)
510519
implicit val argsEncoder: ConfEncoder[Args] = generic.deriveEncoder

0 commit comments

Comments
 (0)