From 61efc7cf4a3a3f0021c077f61f5b93109416fe88 Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Mon, 5 Aug 2024 15:57:25 +0200 Subject: [PATCH] Move LibaryMigration to sbt-plugin and use sbt resolvers --- .scalafix.conf | 2 +- build.sbt | 22 ++-- .../src/main/java/migrate/interfaces/Lib.java | 12 --- .../main/java/migrate/interfaces/Migrate.java | 1 - .../java/migrate/interfaces/MigratedLib.java | 7 -- .../java/migrate/interfaces/MigratedLibs.java | 53 ---------- .../main/scala/migrate/LibraryMigration.scala | 100 ------------------ .../migrate/interfaces/MigrateImpl.scala | 9 -- .../scala/migrate/internal/CrossVersion.scala | 38 ------- plugin/src/main/scala/migrate/LibImpl.scala | 24 ----- .../main/scala/migrate/LibsMigration.scala | 44 +++++--- .../migrate/internal}/CoursierHelper.scala | 28 +++-- .../scala/migrate/internal/InitialLib.scala | 10 +- .../migrate/internal/LibraryMigration.scala | 79 ++++++++++++++ .../scala/migrate/internal/MigratedLib.scala | 26 ++--- .../internal}/LibraryMigrationSuite.scala | 22 ++-- 16 files changed, 170 insertions(+), 307 deletions(-) delete mode 100644 interfaces/migrate/src/main/java/migrate/interfaces/Lib.java delete mode 100644 interfaces/migrate/src/main/java/migrate/interfaces/MigratedLib.java delete mode 100644 interfaces/migrate/src/main/java/migrate/interfaces/MigratedLibs.java delete mode 100644 migrate/src/main/scala/migrate/LibraryMigration.scala delete mode 100644 migrate/src/main/scala/migrate/internal/CrossVersion.scala delete mode 100644 plugin/src/main/scala/migrate/LibImpl.scala rename {migrate/src/main/scala/migrate/utils => plugin/src/main/scala/migrate/internal}/CoursierHelper.scala (63%) rename {migrate => plugin}/src/main/scala/migrate/internal/InitialLib.scala (90%) create mode 100644 plugin/src/main/scala/migrate/internal/LibraryMigration.scala rename {migrate => plugin}/src/main/scala/migrate/internal/MigratedLib.scala (82%) rename {migrate/src/test/scala/migrate => plugin/src/test/scala/migrate/internal}/LibraryMigrationSuite.scala (90%) diff --git a/.scalafix.conf b/.scalafix.conf index bce87caa..6eda849c 100644 --- a/.scalafix.conf +++ b/.scalafix.conf @@ -17,4 +17,4 @@ OrganizeImports { RemoveUnused { imports = false // handled by OrganizeImports -} \ No newline at end of file +} diff --git a/build.sbt b/build.sbt index fba0ac58..3d864dab 100644 --- a/build.sbt +++ b/build.sbt @@ -116,17 +116,19 @@ lazy val `sbt-plugin` = project .settings( scalaVersion := V.scala212, name := "sbt-scala3-migrate", + libraryDependencies ++= Seq( + "io.get-coursier" %% "coursier" % V.coursierApi, + "org.scalatest" %% "scalatest" % V.scalatest % Test + ), scriptedLaunchOpts ++= Seq(s"-Dplugin.version=${version.value}"), - scriptedDependencies := { - scriptedDependencies - .dependsOn( - `migrate-interface` / publishLocal, - `compiler-interface` / publishLocal, - migrate / publishLocal, - `scalafix-rules` / publishLocal - ) - .value - }, + scriptedDependencies := scriptedDependencies + .dependsOn( + `migrate-interface` / publishLocal, + `compiler-interface` / publishLocal, + migrate / publishLocal, + `scalafix-rules` / publishLocal + ) + .value, buildInfoPackage := "migrate", scriptedBufferLog := false, buildInfoKeys := Seq[BuildInfoKey]( diff --git a/interfaces/migrate/src/main/java/migrate/interfaces/Lib.java b/interfaces/migrate/src/main/java/migrate/interfaces/Lib.java deleted file mode 100644 index ee87d309..00000000 --- a/interfaces/migrate/src/main/java/migrate/interfaces/Lib.java +++ /dev/null @@ -1,12 +0,0 @@ -package migrate.interfaces; - -import java.util.Optional; - -public interface Lib { - String getOrganization(); - String getName(); - String getVersion(); - String getCrossVersion(); - String toString(); - Optional getConfigurations(); -} diff --git a/interfaces/migrate/src/main/java/migrate/interfaces/Migrate.java b/interfaces/migrate/src/main/java/migrate/interfaces/Migrate.java index 4baa91fd..2157f978 100644 --- a/interfaces/migrate/src/main/java/migrate/interfaces/Migrate.java +++ b/interfaces/migrate/src/main/java/migrate/interfaces/Migrate.java @@ -28,7 +28,6 @@ void migrate(List unmanagedSources, Path baseDirectory); MigratedScalacOptions migrateScalacOption(List scala3CompilerOptions); - MigratedLibs migrateLibs(List libs, List repositories); void migrateSyntax(List unmanagedSources, Path targetRoot, diff --git a/interfaces/migrate/src/main/java/migrate/interfaces/MigratedLib.java b/interfaces/migrate/src/main/java/migrate/interfaces/MigratedLib.java deleted file mode 100644 index 55a17b5a..00000000 --- a/interfaces/migrate/src/main/java/migrate/interfaces/MigratedLib.java +++ /dev/null @@ -1,7 +0,0 @@ -package migrate.interfaces; - -import java.util.Map; - -public interface MigratedLib { - String formatted(); -} diff --git a/interfaces/migrate/src/main/java/migrate/interfaces/MigratedLibs.java b/interfaces/migrate/src/main/java/migrate/interfaces/MigratedLibs.java deleted file mode 100644 index 9bd2f053..00000000 --- a/interfaces/migrate/src/main/java/migrate/interfaces/MigratedLibs.java +++ /dev/null @@ -1,53 +0,0 @@ -package migrate.interfaces; - -import java.util.List; -import java.util.Map; - -public class MigratedLibs { - private MigratedLib[] validLibraries; - private MigratedLib[] updatedVersions; - private MigratedLib[] crossCompatibleLibraries; - private MigratedLib[] integratedPlugins; - private MigratedLib[] unclassifiedLibraries; - private MigratedLib[] incompatibleLibraries; - - public MigratedLibs( - MigratedLib[] validLibraries, - MigratedLib[] updatedVersions, - MigratedLib[] crossCompatibleLibraries, - MigratedLib[] integratedPlugins, - MigratedLib[] unclassifiedLibraries, - MigratedLib[] incompatibleLibraries - ) { - this.validLibraries = validLibraries; - this.updatedVersions = updatedVersions; - this.crossCompatibleLibraries = crossCompatibleLibraries; - this.integratedPlugins = integratedPlugins; - this.unclassifiedLibraries = unclassifiedLibraries; - this.incompatibleLibraries = incompatibleLibraries; - } - - public MigratedLib[] getValidLibraries() { - return validLibraries; - } - - public MigratedLib[] getUpdatedVersions() { - return updatedVersions; - } - - public MigratedLib[] getCrossCompatibleLibraries() { - return crossCompatibleLibraries; - } - - public MigratedLib[] getIntegratedPlugins() { - return integratedPlugins; - } - - public MigratedLib[] getUnclassifiedLibraries() { - return unclassifiedLibraries; - } - - public MigratedLib[] getIncompatibleLibraries() { - return incompatibleLibraries; - } -} \ No newline at end of file diff --git a/migrate/src/main/scala/migrate/LibraryMigration.scala b/migrate/src/main/scala/migrate/LibraryMigration.scala deleted file mode 100644 index e202d9f8..00000000 --- a/migrate/src/main/scala/migrate/LibraryMigration.scala +++ /dev/null @@ -1,100 +0,0 @@ -package migrate - -import migrate.buildinfo.BuildInfo -import migrate.interfaces.MigratedLib -import migrate.interfaces.MigratedLibs -import migrate.internal.CrossCompatibleLibrary -import migrate.internal.CrossVersion -import migrate.internal.IncompatibleLibrary -import migrate.internal.InitialLib -import migrate.internal.IntegratedPlugin -import migrate.internal.Repository -import migrate.internal.UnclassifiedLibrary -import migrate.internal.UpdatedVersion -import migrate.internal.ValidLibrary -import migrate.utils.CoursierHelper - -object LibraryMigration { - def migrateLibs(libs: Seq[InitialLib], repositories: Seq[Repository]): MigratedLibs = { - val filteredLibs = libs.filterNot(l => InitialLib.migrationFilter.contains((l.organization, l.name))) - val migratedLibs = filteredLibs.map(migrateLib(_, repositories)) - - val validLibs = migratedLibs.collect { case l: ValidLibrary => l }.toArray[MigratedLib] - val updatedVersions = migratedLibs.collect { case l: UpdatedVersion => l }.toArray[MigratedLib] - val crossCompatibleLibraries = migratedLibs.collect { case l: CrossCompatibleLibrary => l }.toArray[MigratedLib] - val integratedPlugins = migratedLibs.collect { case l: IntegratedPlugin => l }.toArray[MigratedLib] - val unclassifiedLibraries = migratedLibs.collect { case l: UnclassifiedLibrary => l }.toArray[MigratedLib] - val incompatibleLibraries = migratedLibs.collect { case l: IncompatibleLibrary => l }.toArray[MigratedLib] - - new MigratedLibs( - validLibs, - updatedVersions, - crossCompatibleLibraries, - integratedPlugins, - unclassifiedLibraries, - incompatibleLibraries) - } - - def migrateLib(lib: InitialLib, repositories: Seq[Repository]): MigratedLib = - if (lib.isCompilerPlugin) migrateCompilerPlugin(lib, repositories) - else migrateRegularLib(lib, repositories) - - def migrateRegularLib(lib: InitialLib, repositories: Seq[Repository]): MigratedLib = - lib.crossVersion match { - case CrossVersion.Disabled => tryParseBinaryVersionAndMigrate(lib, repositories) - case _: CrossVersion.For2_13Use3 => ValidLibrary(lib) - case _: CrossVersion.For3Use2_13 => ValidLibrary(lib) - case _: CrossVersion.Binary => migrateBinaryVersion(lib, repositories) - case _: CrossVersion.Full => migrateFullCrossVersion(lib, repositories) - case CrossVersion.Patch => migrateFullCrossVersion(lib, repositories) - case CrossVersion.Constant("2.13") => - migrateBinaryVersion(lib.copy(crossVersion = CrossVersion.Binary("", "")), repositories) - case CrossVersion.Constant(s"2.13.$_") => - migrateFullCrossVersion(lib.copy(crossVersion = CrossVersion.Binary("", "")), repositories) - case cv: CrossVersion.Constant => UnclassifiedLibrary(lib, s"Unsupported CrossVersion.$cv") - } - - private def tryParseBinaryVersionAndMigrate(lib: InitialLib, repositories: Seq[Repository]): MigratedLib = - lib.name.split("_").toList match { - case name :: suffix :: Nil => - suffix match { - case "2.13" => - val newLib = lib.copy(name = name, crossVersion = CrossVersion.Binary("", "")) - migrateBinaryVersion(newLib, repositories) - case s"2.13.$_" => - val newLib = lib.copy(name = name, crossVersion = CrossVersion.Full("", "")) - migrateFullCrossVersion(newLib, repositories) - case _ => ValidLibrary(lib) - } - case _ => ValidLibrary(lib) - } - - private def migrateBinaryVersion(lib: InitialLib, repositories: Seq[Repository]): MigratedLib = - if (CoursierHelper.isCompatible(lib, "3", repositories)) ValidLibrary(lib) - else { - CoursierHelper.findNewerVersions(lib, "3", repositories).toList match { - case Nil => - if (lib.isCompilerPlugin) IncompatibleLibrary(lib, "Compiler Plugin") - else if (InitialLib.macroLibraries.contains(lib.organization, lib.name)) - IncompatibleLibrary(lib, "Macro Library") - else CrossCompatibleLibrary(lib) - case newerVersions => UpdatedVersion(lib, newerVersions) - } - } - - private def migrateFullCrossVersion(lib: InitialLib, repositories: Seq[Repository]): MigratedLib = - CoursierHelper.findNewerVersions(lib, BuildInfo.scala3Version, repositories) match { - case Nil => - val reason = - if (lib.isCompilerPlugin) "Compiler plugin" - else "Full cross-version" - IncompatibleLibrary(lib, reason) - case newerVersions => UpdatedVersion(lib, newerVersions) - } - - private def migrateCompilerPlugin(lib: InitialLib, repositories: Seq[Repository]): MigratedLib = - (lib.organization, lib.name) match { - case ("org.typelevel", "kind-projector") => IntegratedPlugin(lib, "-Ykind-projector") - case (_, _) => migrateRegularLib(lib, repositories) - } -} diff --git a/migrate/src/main/scala/migrate/interfaces/MigrateImpl.scala b/migrate/src/main/scala/migrate/interfaces/MigrateImpl.scala index a4d96199..81070166 100644 --- a/migrate/src/main/scala/migrate/interfaces/MigrateImpl.scala +++ b/migrate/src/main/scala/migrate/interfaces/MigrateImpl.scala @@ -5,13 +5,10 @@ import java.{util => jutil} import scala.jdk.CollectionConverters._ -import migrate.LibraryMigration import migrate.Scala3Migrate import migrate.ScalacOptionsMigration import migrate.internal.AbsolutePath import migrate.internal.Classpath -import migrate.internal.InitialLib -import migrate.internal.Repository import migrate.utils.ScalaExtensions._ import migrate.utils.ScalafixService @@ -59,12 +56,6 @@ final class MigrateImpl(logger: Logger) extends Migrate { override def migrateScalacOption(scalacOptions: jutil.List[String]): MigratedScalacOptions = ScalacOptionsMigration.migrate(scalacOptions.asScala.toSeq) - override def migrateLibs(libs: jutil.List[Lib], repositories: jutil.List[String]): MigratedLibs = { - val initialLibs = libs.asScala.map(InitialLib.apply).toSeq - val initialRepos = repositories.asScala.map(Repository).toSeq - LibraryMigration.migrateLibs(initialLibs, initialRepos) - } - override def migrateSyntax( unmanagedSources: jutil.List[Path], targetRoot: Path, diff --git a/migrate/src/main/scala/migrate/internal/CrossVersion.scala b/migrate/src/main/scala/migrate/internal/CrossVersion.scala deleted file mode 100644 index 3aaa6b88..00000000 --- a/migrate/src/main/scala/migrate/internal/CrossVersion.scala +++ /dev/null @@ -1,38 +0,0 @@ -package migrate.internal - -sealed trait CrossVersion { - override def toString: String = this match { - case CrossVersion.Binary(prefix: String, suffix: String) => s"Binary($prefix, $suffix)" - case CrossVersion.Disabled => "Disabled()" - case CrossVersion.Constant(value: String) => s"Constant($value)" - case CrossVersion.Patch => "Patch()" - case CrossVersion.Full(prefix: String, suffix: String) => s"Full($prefix, $suffix)" - case CrossVersion.For3Use2_13(prefix: String, suffix: String) => s"For3Use2_13($prefix, $suffix)" - case CrossVersion.For2_13Use3(prefix: String, suffix: String) => s"For3Use2_13($prefix, $suffix)" - } - - def prefix: String = "" - def suffix: String = "" -} - -object CrossVersion { - case class Binary(override val prefix: String, override val suffix: String) extends CrossVersion - case object Disabled extends CrossVersion - case class Constant(value: String) extends CrossVersion - case object Patch extends CrossVersion - case class Full(override val prefix: String, override val suffix: String) extends CrossVersion - case class For3Use2_13(override val prefix: String, override val suffix: String) extends CrossVersion - case class For2_13Use3(override val prefix: String, override val suffix: String) extends CrossVersion - - def apply(value: String): CrossVersion = - value match { - case "Disabled()" => Disabled - case s"Binary($prefix, $suffix)" => Binary(prefix, suffix) - case s"Constant($value)" => Constant(value) - case "Patch()" => Patch - case s"Full($prefix, $suffix)" => Full(prefix, suffix) - case s"For3Use2_13($prefix, $suffix)" => For3Use2_13(prefix, suffix) - case s"For3Use2_13($prefix, $suffix)" => For2_13Use3(prefix, suffix) - case _ => throw new IllegalArgumentException(value) - } -} diff --git a/plugin/src/main/scala/migrate/LibImpl.scala b/plugin/src/main/scala/migrate/LibImpl.scala deleted file mode 100644 index 084fe33d..00000000 --- a/plugin/src/main/scala/migrate/LibImpl.scala +++ /dev/null @@ -1,24 +0,0 @@ -package migrate - -import migrate.interfaces.Lib -import sbt.librarymanagement.ModuleID -import sbt.sbtOptionSyntaxRichOption - -import java.util.Optional - -case class LibImpl(moduleId: ModuleID) extends Lib { - override def getOrganization: String = moduleId.organization - override def getName: String = moduleId.name - override def getVersion: String = moduleId.revision - override def getCrossVersion: String = moduleId.crossVersion.toString() - override def getConfigurations: Optional[String] = moduleId.configurations.asJava - - override def toString: String = - s"${this.getOrganization}:${this.getName}:${this.getVersion}" - - def isCompilerPlugin: Boolean = moduleId.configurations.contains("compile") -} - -object LibImpl { - def apply(lib: ModuleID): Lib = new LibImpl(lib) -} diff --git a/plugin/src/main/scala/migrate/LibsMigration.scala b/plugin/src/main/scala/migrate/LibsMigration.scala index 96ac09b6..9a32226a 100644 --- a/plugin/src/main/scala/migrate/LibsMigration.scala +++ b/plugin/src/main/scala/migrate/LibsMigration.scala @@ -1,12 +1,17 @@ package migrate +import coursier.core.Repository import ScalaMigratePlugin.Keys._ +import lmcoursier.CoursierConfiguration +import lmcoursier.definitions.ToCoursier +import lmcoursier.internal.ResolutionParams +import lmcoursier.internal.Resolvers import Messages._ -import migrate.interfaces.{Lib, MigratedLib, MigratedLibs} +import migrate.internal.* import sbt.Keys import sbt.Def import sbt.MessageOnlyException -import sbt.librarymanagement.MavenRepository +import sbt.util.Logger import scala.io.AnsiColor._ import scala.util.{Failure, Success, Try} @@ -18,43 +23,43 @@ private[migrate] object LibsMigration { val projectId = Keys.thisProject.value.id val scalaVersion = Keys.scalaVersion.value val libraryDependencies = Keys.libraryDependencies.value - val resolvers = Keys.resolvers.value + val csrConfig = (Keys.updateClassifiers / Keys.csrConfiguration).value if (!scalaVersion.startsWith("2.13.") && !scalaVersion.startsWith("3.")) throw new MessageOnlyException(notScala213(scalaVersion, projectId)) log.info(startingMessage(projectId)) - val migrateAPI = ScalaMigratePlugin.getMigrateInstance(log) - val mavenRepositories = resolvers.collect { case mvnRepo: MavenRepository => mvnRepo.root } - val migrated = migrateAPI.migrateLibs(libraryDependencies.map(LibImpl.apply).asJava, mavenRepositories.asJava) + val repositories = getCoursierRepositories(csrConfig, log) + val migrateAPI = ScalaMigratePlugin.getMigrateInstance(log) + val migrated = LibraryMigration.migrateLibs(libraryDependencies.map(InitialLib.apply), repositories) - val validLibs = migrated.getValidLibraries + val validLibs = migrated.collect { case l: ValidLibrary => l } if (validLibs.nonEmpty) { log.info(validMessage(validLibs)) } - val updatedVersions = migrated.getUpdatedVersions + val updatedVersions = migrated.collect { case l: UpdatedVersion => l } if (updatedVersions.nonEmpty) { log.warn(updatedVersionsMessage(updatedVersions)) } - val crossCompatibleLibs = migrated.getCrossCompatibleLibraries + val crossCompatibleLibs = migrated.collect { case l: CrossCompatibleLibrary => l } if (crossCompatibleLibs.nonEmpty) { log.warn(crossCompatibleMessage(crossCompatibleLibs)) } - val integratedPlugins = migrated.getIntegratedPlugins + val integratedPlugins = migrated.collect { case l: IntegratedPlugin => l } if (integratedPlugins.nonEmpty) { log.warn(integratedPluginMessage(integratedPlugins)) } - val unclassifiedLibraries = migrated.getUnclassifiedLibraries + val unclassifiedLibraries = migrated.collect { case l: UnclassifiedLibrary => l } if (unclassifiedLibraries.nonEmpty) { log.warn(unclassifiedMessage(unclassifiedLibraries)) } - val incompatibleLibraries = migrated.getIncompatibleLibraries + val incompatibleLibraries = migrated.collect { case l: IncompatibleLibrary => l } if (incompatibleLibraries.nonEmpty) { log.error(incompatibleMessage(incompatibleLibraries)) } @@ -102,4 +107,19 @@ private[migrate] object LibsMigration { |$RED${BOLD}Incompatible Libraries:$RESET |${incompatibleLibraries.map(_.formatted).mkString("\n")} |""".stripMargin + + // Copied from https://github.com/coursier/sbt-coursier/blob/5610ce56d6fcd9d716d817310be3ef4a2dfc9334/modules/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala#L173-L193 + private def getCoursierRepositories(conf: CoursierConfiguration, log: Logger): Seq[Repository] = { + val ivyProperties = ResolutionParams.defaultIvyProperties(conf.ivyHome) + val authenticationByRepositoryId = conf.authenticationByRepositoryId.toMap + conf.resolvers.flatMap { resolver => + Resolvers.repository( + resolver, + ivyProperties, + log, + authenticationByRepositoryId.get(resolver.name).map(ToCoursier.authentication), + Seq.empty + ) + } + } } diff --git a/migrate/src/main/scala/migrate/utils/CoursierHelper.scala b/plugin/src/main/scala/migrate/internal/CoursierHelper.scala similarity index 63% rename from migrate/src/main/scala/migrate/utils/CoursierHelper.scala rename to plugin/src/main/scala/migrate/internal/CoursierHelper.scala index 13a3d183..dca89e40 100644 --- a/migrate/src/main/scala/migrate/utils/CoursierHelper.scala +++ b/plugin/src/main/scala/migrate/internal/CoursierHelper.scala @@ -1,10 +1,9 @@ -package migrate.utils +package migrate.internal import scala.concurrent.ExecutionContext -import coursier.MavenRepository -import migrate.internal.InitialLib -import migrate.internal.Repository +import coursier.core.Repository +import sbt.librarymanagement._ object CoursierHelper { @@ -14,13 +13,13 @@ object CoursierHelper { } def isCompatible(lib: InitialLib, scalaVersion: String, repositories: Seq[Repository]): Boolean = { - val binaryVersion = lib.crossVersion.prefix + scalaVersion + lib.crossVersion.suffix + val binaryVersion = prefix(lib.crossVersion) + scalaVersion + suffix(lib.crossVersion) val libString = s"${lib.organization}:${lib.name}_$binaryVersion:${lib.version}" coursierComplete(libString, repositories).nonEmpty } private def findAllVersions(lib: InitialLib, scalaVersion: String, repositories: Seq[Repository]): Seq[String] = { - val binaryVersion = lib.crossVersion.prefix + scalaVersion + lib.crossVersion.suffix + val binaryVersion = prefix(lib.crossVersion) + scalaVersion + suffix(lib.crossVersion) val libString = s"${lib.organization}:${lib.name}_${binaryVersion}:" coursierComplete(libString, repositories) } @@ -28,7 +27,7 @@ object CoursierHelper { private def coursierComplete(input: String, repositories: Seq[Repository]): Seq[String] = { val res = coursier.complete .Complete() - .withRepositories(repositories.map(r => MavenRepository(r.url))) + .withRepositories(repositories) .withInput(input) .result() .unsafeRun()(ExecutionContext.global) @@ -48,4 +47,19 @@ object CoursierHelper { } } + private def prefix(crossVersion: CrossVersion): String = crossVersion match { + case v: Binary => v.prefix + case v: Full => v.prefix + case v: For3Use2_13 => v.prefix + case v: For2_13Use3 => v.prefix + case _ => "" + } + + private def suffix(crossVersion: CrossVersion): String = crossVersion match { + case v: Binary => v.suffix + case v: Full => v.suffix + case v: For3Use2_13 => v.suffix + case v: For2_13Use3 => v.suffix + case _ => "" + } } diff --git a/migrate/src/main/scala/migrate/internal/InitialLib.scala b/plugin/src/main/scala/migrate/internal/InitialLib.scala similarity index 90% rename from migrate/src/main/scala/migrate/internal/InitialLib.scala rename to plugin/src/main/scala/migrate/internal/InitialLib.scala index 6637a899..d4a5c69c 100644 --- a/migrate/src/main/scala/migrate/internal/InitialLib.scala +++ b/plugin/src/main/scala/migrate/internal/InitialLib.scala @@ -1,8 +1,8 @@ package migrate.internal import scala.jdk.OptionConverters._ - -import migrate.interfaces.Lib +import sbt.librarymanagement.ModuleID +import sbt.librarymanagement.CrossVersion case class InitialLib( organization: String, @@ -15,10 +15,8 @@ case class InitialLib( } object InitialLib { - def apply(lib: Lib): InitialLib = { - val crossVersion = CrossVersion(lib.getCrossVersion) - InitialLib(lib.getOrganization, lib.getName, lib.getVersion, crossVersion, lib.getConfigurations.toScala) - } + def apply(lib: ModuleID): InitialLib = + InitialLib(lib.organization, lib.name, lib.revision, lib.crossVersion, lib.configurations) def apply(module: String, crossVersion: CrossVersion, configurations: Option[String] = None): InitialLib = { val splited = module.split(":").toList diff --git a/plugin/src/main/scala/migrate/internal/LibraryMigration.scala b/plugin/src/main/scala/migrate/internal/LibraryMigration.scala new file mode 100644 index 00000000..62517a92 --- /dev/null +++ b/plugin/src/main/scala/migrate/internal/LibraryMigration.scala @@ -0,0 +1,79 @@ +package migrate.internal + +import coursier.core.Repository +import sbt.librarymanagement._ +import migrate.BuildInfo + +private[migrate] object LibraryMigration { + def migrateLibs(libs: Seq[InitialLib], repositories: Seq[Repository]): Seq[MigratedLib] = { + val filteredLibs = libs.filterNot(l => InitialLib.migrationFilter.contains((l.organization, l.name))) + filteredLibs.map(migrateLib(_, repositories)) + } + + def migrateLib(lib: InitialLib, repositories: Seq[Repository]): MigratedLib = + if (lib.isCompilerPlugin) migrateCompilerPlugin(lib, repositories) + else migrateRegularLib(lib, repositories) + + def migrateRegularLib(lib: InitialLib, repositories: Seq[Repository]): MigratedLib = { + val FullBinaryVersion = "2\\.13\\..*" + lib.crossVersion match { + case Disabled => tryParseBinaryVersionAndMigrate(lib, repositories) + case _: For2_13Use3 => ValidLibrary(lib) + case _: For3Use2_13 => ValidLibrary(lib) + case _: Binary => migrateBinaryVersion(lib, repositories) + case _: Full => migrateFullCrossVersion(lib, repositories) + case _: Patch => migrateFullCrossVersion(lib, repositories) + case v: Constant if v.value == "2.13" => + migrateBinaryVersion(lib.copy(crossVersion = Binary("", "")), repositories) + case v: Constant if v.value.matches("2\\.13\\..*") => + migrateFullCrossVersion(lib.copy(crossVersion = Binary("", "")), repositories) + case cv: Constant => UnclassifiedLibrary(lib, s"Unsupported CrossVersion.$cv") + } + } + + private def tryParseBinaryVersionAndMigrate(lib: InitialLib, repositories: Seq[Repository]): MigratedLib = { + val FullBinaryVersion = "2\\.13\\..*".r + lib.name.split("_").toList match { + case name :: suffix :: Nil => + suffix match { + case "2.13" => + val newLib = lib.copy(name = name, crossVersion = CrossVersion.Binary("", "")) + migrateBinaryVersion(newLib, repositories) + case FullBinaryVersion(_) => + val newLib = lib.copy(name = name, crossVersion = CrossVersion.Full("", "")) + migrateFullCrossVersion(newLib, repositories) + case _ => ValidLibrary(lib) + } + case _ => ValidLibrary(lib) + } + } + + private def migrateBinaryVersion(lib: InitialLib, repositories: Seq[Repository]): MigratedLib = + if (CoursierHelper.isCompatible(lib, "3", repositories)) ValidLibrary(lib) + else { + CoursierHelper.findNewerVersions(lib, "3", repositories).toList match { + case Nil => + if (lib.isCompilerPlugin) IncompatibleLibrary(lib, "Compiler Plugin") + else if (InitialLib.macroLibraries.contains(lib.organization, lib.name)) + IncompatibleLibrary(lib, "Macro Library") + else CrossCompatibleLibrary(lib) + case newerVersions => UpdatedVersion(lib, newerVersions) + } + } + + private def migrateFullCrossVersion(lib: InitialLib, repositories: Seq[Repository]): MigratedLib = + CoursierHelper.findNewerVersions(lib, BuildInfo.scala3Version, repositories) match { + case Nil => + val reason = + if (lib.isCompilerPlugin) "Compiler plugin" + else "Full cross-version" + IncompatibleLibrary(lib, reason) + case newerVersions => UpdatedVersion(lib, newerVersions) + } + + private def migrateCompilerPlugin(lib: InitialLib, repositories: Seq[Repository]): MigratedLib = + (lib.organization, lib.name) match { + case ("org.typelevel", "kind-projector") => IntegratedPlugin(lib, "-Ykind-projector") + case (_, _) => migrateRegularLib(lib, repositories) + } +} diff --git a/migrate/src/main/scala/migrate/internal/MigratedLib.scala b/plugin/src/main/scala/migrate/internal/MigratedLib.scala similarity index 82% rename from migrate/src/main/scala/migrate/internal/MigratedLib.scala rename to plugin/src/main/scala/migrate/internal/MigratedLib.scala index 74ecaab2..69ea87bc 100644 --- a/migrate/src/main/scala/migrate/internal/MigratedLib.scala +++ b/plugin/src/main/scala/migrate/internal/MigratedLib.scala @@ -1,9 +1,11 @@ package migrate.internal import scala.Console._ +import sbt.librarymanagement._ -import migrate.interfaces.MigratedLib -import migrate.internal.InitialLib +trait MigratedLib { + def formatted: String +} case class ValidLibrary( lib: InitialLib @@ -37,9 +39,9 @@ case class CrossCompatibleLibrary(lib: InitialLib) extends MigratedLib { override def formatted: String = { val formattedConfigs = lib.configurations.map(c => " % " + MigratedLibFormatting.formatConfigs(c)).getOrElse("") lib.crossVersion match { - case CrossVersion.Binary("", "") => + case v: Binary if v.prefix == "" && v.suffix == "" => s"""("${lib.organization}" %% "${lib.name}" % "${lib.version}"$formattedConfigs)$YELLOW.cross(CrossVersion.for3Use2_13)$RESET""" - case CrossVersion.Binary(_, _) => + case _: Binary => s"""("${lib.organization}" %%% "${lib.name}" % "${lib.version}"$formattedConfigs)$YELLOW.cross(CrossVersion.for3Use2_13)$RESET""" case _ => s"""("${lib.organization}" %% "${lib.name}" % "${lib.version}"$formattedConfigs)$YELLOW.cross(CrossVersion.for3Use2_13)$RESET""" @@ -84,15 +86,15 @@ object MigratedLibFormatting { configs: Option[String]): String = { val formattedConfigs = configs.map(c => " % " + formatConfigs(c)).getOrElse("") crossVersion match { - case CrossVersion.Binary("", "") => s""""$org" %% "$name" % "$version"$formattedConfigs""" - case CrossVersion.Binary(_, _) => s""""$org" %%% "$name" % "$version"$formattedConfigs""" - case CrossVersion.Disabled => s""""$org" % "$name" % "$version"$formattedConfigs""" + case v: Binary if v.prefix == "" && v.suffix == "" => s""""$org" %% "$name" % "$version"$formattedConfigs""" + case _: Binary => s""""$org" %%% "$name" % "$version"$formattedConfigs""" + case Disabled => s""""$org" % "$name" % "$version"$formattedConfigs""" case crossVersion => val crossVersionFmt = crossVersion match { - case _: CrossVersion.Full => "full" - case _: CrossVersion.For3Use2_13 => "for3Use2_13" - case _: CrossVersion.For2_13Use3 => "for2_13Use3" - case other => other.toString + case _: Full => "full" + case _: For3Use2_13 => "for3Use2_13" + case _: For2_13Use3 => "for2_13Use3" + case other => other.toString } s"""("$org" %% "$name" % "$version"$formattedConfigs).cross(CrossVersion.$crossVersionFmt)""" } @@ -103,6 +105,6 @@ object MigratedLibFormatting { case "test" => "Test" case "it" => "IntegrationTest" case "compile" => "Compile" - case configs => s"\"$configs\"" + case configs => s""""$configs"""" } } diff --git a/migrate/src/test/scala/migrate/LibraryMigrationSuite.scala b/plugin/src/test/scala/migrate/internal/LibraryMigrationSuite.scala similarity index 90% rename from migrate/src/test/scala/migrate/LibraryMigrationSuite.scala rename to plugin/src/test/scala/migrate/internal/LibraryMigrationSuite.scala index 817d4c20..b7d1a3c7 100644 --- a/migrate/src/test/scala/migrate/LibraryMigrationSuite.scala +++ b/plugin/src/test/scala/migrate/internal/LibraryMigrationSuite.scala @@ -1,16 +1,12 @@ -package migrate +package migrate.internal import scala.Console._ import coursier.Repositories -import migrate.internal.CrossCompatibleLibrary -import migrate.internal.CrossVersion -import migrate.internal.IncompatibleLibrary -import migrate.internal.InitialLib -import migrate.internal.Repository -import migrate.internal.UpdatedVersion -import migrate.internal.ValidLibrary import org.scalatest.funsuite.AnyFunSuiteLike +import sbt.librarymanagement.CrossVersion +import coursier.core.Repository +import coursier.maven.MavenRepository class LibraryMigrationSuite extends AnyFunSuiteLike { val binaryJvm: CrossVersion.Binary = CrossVersion.Binary("", "") @@ -34,7 +30,7 @@ class LibraryMigrationSuite extends AnyFunSuiteLike { val domtypes: InitialLib = InitialLib("com.raquo:domtypes:0.14.3", binaryJs) val domutils: InitialLib = InitialLib("com.raquo:domtestutils:0.14.7", binaryJs) - val defaultRepositories: Seq[Repository] = Seq(Repository(Repositories.central.root)) + val defaultRepositories: Seq[Repository] = Seq(Repositories.central) test("Integrated compiler plugin: kind projector") { val migrated = LibraryMigration.migrateLib(kindProjector, defaultRepositories) @@ -80,7 +76,7 @@ class LibraryMigrationSuite extends AnyFunSuiteLike { } test("available in scala 3 in another repository") { - val repositories = defaultRepositories :+ Repository("https://repo.akka.io/maven") + val repositories = defaultRepositories :+ MavenRepository("https://repo.akka.io/maven") val migrated = LibraryMigration.migrateLib(akka, repositories) assert(migrated.isInstanceOf[ValidLibrary]) } @@ -114,11 +110,7 @@ class LibraryMigrationSuite extends AnyFunSuiteLike { val scalaLib = InitialLib("org.scala-lang:scala-library:2.13.13", CrossVersion.Disabled) val scalajs = InitialLib("org.scala-js:scalajs-compiler:1.5.0", CrossVersion.Disabled) val migratedLibs = LibraryMigration.migrateLibs(Seq(scalaLib, scalajs), defaultRepositories) - assert(migratedLibs.getValidLibraries.isEmpty) - assert(migratedLibs.getUpdatedVersions.isEmpty) - assert(migratedLibs.getCrossCompatibleLibraries.isEmpty) - assert(migratedLibs.getIntegratedPlugins.isEmpty) - assert(migratedLibs.getIncompatibleLibraries.isEmpty) + assert(migratedLibs.isEmpty) } test("Formatting of updated versions") {