diff --git a/mill-scalafix/src/com/goyeau/mill/scalafix/ScalafixCache.scala b/mill-scalafix/src/com/goyeau/mill/scalafix/ScalafixCache.scala new file mode 100644 index 0000000..58aeebc --- /dev/null +++ b/mill-scalafix/src/com/goyeau/mill/scalafix/ScalafixCache.scala @@ -0,0 +1,47 @@ +package com.goyeau.mill.scalafix + +import com.goyeau.mill.scalafix.CoursierUtils +import coursier.core.Repository +import mill.Agg +import mill.scalalib.Dep +import scalafix.interfaces.Scalafix +import scalafix.interfaces.ScalafixArguments + +import java.util.concurrent.ConcurrentHashMap +import scala.jdk.CollectionConverters._ +import scala.ref.SoftReference + +private[scalafix] object ScalafixCache { + + private val scalafixCache = new Cache[(String, java.util.List[coursierapi.Repository]), Scalafix](createFunction = { + case (scalaVersion, repositories) => + Scalafix.fetchAndClassloadInstance(scalaVersion, repositories) + }) + + private val scalafixArgumentsCache = + new Cache[(String, Seq[Repository], Agg[Dep]), ScalafixArguments](createFunction = { + case (scalaVersion, repositories, scalafixIvyDeps) => + val repos = repositories.map(CoursierUtils.toApiRepository).asJava + val deps = scalafixIvyDeps.map(CoursierUtils.toCoordinates).iterator.toSeq.asJava + scalafixCache + .getOrElseCreate((scalaVersion, repos)) + .newArguments() + .withToolClasspath(Seq.empty.asJava, deps, repos) + }) + + def getOrElseCreate(scalaVersion: String, repositories: Seq[Repository], scalafixIvyDeps: Agg[Dep]) = + scalafixArgumentsCache.getOrElseCreate((scalaVersion, repositories, scalafixIvyDeps)) + + private class Cache[A, B <: AnyRef](createFunction: A => B) { + private val cache = new ConcurrentHashMap[A, SoftReference[B]] + + def getOrElseCreate(a: A) = + cache.compute( + a, + { + case (_, v @ SoftReference(_)) => v + case _ => SoftReference(createFunction(a)) + } + )() + } +} diff --git a/mill-scalafix/src/com/goyeau/mill/scalafix/ScalafixModule.scala b/mill-scalafix/src/com/goyeau/mill/scalafix/ScalafixModule.scala index c55eefe..db2a6c2 100644 --- a/mill-scalafix/src/com/goyeau/mill/scalafix/ScalafixModule.scala +++ b/mill-scalafix/src/com/goyeau/mill/scalafix/ScalafixModule.scala @@ -7,7 +7,6 @@ import mill.api.{Logger, PathRef, Result} import mill.scalalib.{Dep, ScalaModule} import mill.define.Command -import scalafix.interfaces.Scalafix import scalafix.interfaces.ScalafixError.* import scala.compat.java8.OptionConverters.* @@ -77,9 +76,8 @@ object ScalafixModule { wd: os.Path ): Result[Unit] = if (sources.nonEmpty) { - val scalafix = Scalafix - .fetchAndClassloadInstance(scalaVersion, repositories.map(CoursierUtils.toApiRepository).asJava) - .newArguments() + val scalafix = ScalafixCache + .getOrElseCreate(scalaVersion, repositories, scalafixIvyDeps) .withParsedArguments(args.asJava) .withWorkingDirectory(wd.toNIO) .withConfig(scalafixConfig.map(_.toNIO).asJava) @@ -87,11 +85,6 @@ object ScalafixModule { .withScalaVersion(scalaVersion) .withScalacOptions(scalacOptions.asJava) .withPaths(sources.map(_.toNIO).asJava) - .withToolClasspath( - Seq.empty.asJava, - scalafixIvyDeps.map(CoursierUtils.toCoordinates).iterator.toSeq.asJava, - repositories.map(CoursierUtils.toApiRepository).asJava - ) log.info(s"Rewriting and linting ${sources.size} Scala sources against ${scalafix.rulesThatWillRun.size} rules") val errors = scalafix.run()