From 39fa98ef79ae0a926edbe38ed42e9355ed738f05 Mon Sep 17 00:00:00 2001 From: eugene yokota Date: Fri, 13 Mar 2026 22:57:38 -0400 Subject: [PATCH] [2.x] Update to Coursier 2.1.25-M24 (#8381) This forward ports (https://github.com/coursier/sbt-coursier/pull/570), and follows along some of the migrated methods to work through the deprecations. --- build.sbt | 5 +- .../CoursierDependencyResolution.scala | 84 ++++++++---- .../lmcoursier/definitions/ToCoursier.scala | 67 +++++++--- .../lmcoursier/internal/BuildClock.scala | 15 ++- .../internal/InterProjectRepository.scala | 6 +- .../internal/LockedArtifactsRun.scala | 3 +- .../lmcoursier/internal/ResolutionRun.scala | 4 + .../internal/ResolutionSerializer.scala | 122 ++++++++++-------- .../lmcoursier/internal/SbtUpdateReport.scala | 32 ++++- .../TemporaryInMemoryRepository.scala | 3 + .../lmcoursier/internal/UpdateParams.scala | 4 +- .../scala/lmcoursier/internal/UpdateRun.scala | 4 + project/Dependencies.scala | 3 +- .../scala-version-check-exempt/build.sbt | 10 ++ .../update-sbt-classifiers/build.sbt | 1 + 15 files changed, 243 insertions(+), 120 deletions(-) diff --git a/build.sbt b/build.sbt index cad744baa..6eb17df62 100644 --- a/build.sbt +++ b/build.sbt @@ -735,6 +735,7 @@ lazy val mainProj = (project in file("main")) launcherInterface, caffeine, scala3Library, + scalaCollectionCompat, ), libraryDependencies ++= List(scalaPar), contrabandSettings, @@ -1334,6 +1335,7 @@ lazy val lmCoursierShaded = project "coursier", "org.fusesource", "macrocompat", + "io.github.alexarchambault.isterminal", "io.github.alexarchambault.windowsansi", "concurrentrefhashmap", "com.github.ghik", @@ -1343,6 +1345,7 @@ lazy val lmCoursierShaded = project "com.jcraft", "com.lmax", "org.apache.commons", + "org.apache.tika", "org.apache.xbean", "org.codehaus", "org.iq80", @@ -1369,7 +1372,7 @@ lazy val lmCoursierShaded = project case PathList("com", "typesafe") => MergeStrategy.discard case PathList("gigahorse") => MergeStrategy.discard case PathList("jline") => MergeStrategy.discard - case PathList("scala") => MergeStrategy.discard + case PathList("scala", _*) => MergeStrategy.discard case PathList("sjsonnew") => MergeStrategy.discard case PathList("xsbti") => MergeStrategy.discard case PathList("META-INF", "native", _*) => MergeStrategy.first diff --git a/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala b/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala index 8d04cff1f..c0eaee6c3 100644 --- a/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala +++ b/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala @@ -4,7 +4,7 @@ import java.io.File import java.net.{ URI, URLClassLoader } import coursier.{ Organization, Resolution } -import coursier.core.{ Classifier, Configuration } +import coursier.core.{ Classifier, Configuration, Dependency, VariantPublication, Publication } import coursier.cache.CacheDefaults import coursier.util.Artifact import coursier.internal.Typelevel @@ -27,9 +27,9 @@ import lmcoursier.internal.{ import lmcoursier.syntax.* import sbt.librarymanagement.* import sbt.util.Logger -import coursier.core.{ BomDependency, Dependency, Publication } +import coursier.core.BomDependency +import scala.annotation.nowarn import scala.util.control.NonFatal - import scala.util.{ Try, Failure } class CoursierDependencyResolution( @@ -226,7 +226,9 @@ class CoursierDependencyResolution( optionalCrossVer = true, projectPlatform = projectPlatform ) - BomDependency(ToCoursier.module(mod), ver, Configuration.empty) + (BomDependency(ToCoursier.module(mod), ver, Configuration.empty): @nowarn( + "msg=BomDependency is deprecated" + )) } // Coursier fills version from BOM only when versionConstraint is empty (Resolution.processedRootDependencies). // So for deps with "*" or "" and BOMs present, pass empty version so BOM can supply it (sbt#4531). @@ -291,9 +293,15 @@ class CoursierDependencyResolution( .ResolutionParams() .withMaxIterations(conf.maxIterations) .withProfiles(conf.mavenProfiles.toSet) - .withForceVersion(conf.forceVersions.map { (k, v) => (ToCoursier.module(k), v) }.toMap) + .withForceVersion0( + conf.forceVersions + .map: (k, v) => + (ToCoursier.module(k), ToCoursier.versionConstraint(v)) + .toMap + ) .withTypelevel(typelevel) - .withReconciliation(ToCoursier.reconciliation(conf.reconciliation)) + .withReconciliation0(conf.reconciliation.map: (k, v) => + ToCoursier.moduleMatchers(k) -> ToCoursier.constraintReconciliation(v)) .withExclusions(excludeDependencies) .withRules(ToCoursier.sameVersions(conf.sameVersions)), strictOpt = conf.strict.map(ToCoursier.strict), @@ -328,7 +336,9 @@ class CoursierDependencyResolution( def updateParams( resolutions: Map[Configuration, Resolution], - artifacts: Seq[(Dependency, Publication, Artifact, Option[File])] + artifacts: Seq[ + (Dependency, Either[VariantPublication, Publication], Artifact, Option[File]) + ] ) = UpdateParams( thisModule = (ToCoursier.module(mod), ver), @@ -355,7 +365,7 @@ class CoursierDependencyResolution( conf.lockFile, conf.scalaVersion ) - artifactResult <- lockDataOpt match { + artifactResult0 <- lockDataOpt match { case Some(lockData) => LockedArtifactsRun.fetchFromLockFile(lockData, cache0, verbosityLevel, log) match { case Right(arts) => Right(arts) @@ -364,35 +374,43 @@ class CoursierDependencyResolution( log.warn(s"Failed to fetch from lock file: $err, falling back to normal fetch") } ArtifactsRun(artifactsParams(resolutions), verbosityLevel, log) - .map(_.fullDetailedArtifacts) + .map(_.fullDetailedArtifacts0) } case None => ArtifactsRun(artifactsParams(resolutions), verbosityLevel, log) - .map(_.fullDetailedArtifacts) + .map(_.fullDetailedArtifacts0) } } yield { + val artifactResult = artifactResult0.map { + case (d, p: Publication, a, o) => + (d, (Right(p): Either[VariantPublication, Publication]), a, o) + case (d, p: Either[VariantPublication, Publication], a, o) => (d, p, a, o) + } val updateParams0 = updateParams(resolutions, artifactResult) val report = UpdateRun.update(updateParams0, verbosityLevel, log) if (lockDataOpt.isEmpty) { - conf.lockFile.foreach { lockFile => + conf.lockFile.foreach: lockFile => val artifactMap = artifactResult .groupBy(_._1) .view - .mapValues(_.map { case (_, pub, art, _) => - val originalUrl = - lmcoursier.internal.CacheUrlConversion.cacheFileToOriginalUrl(art.url, cache) - (originalUrl, pub.classifier.value, pub.ext.value) + .mapValues(_.map { + case (_, Right(pub), art, _) => + val originalUrl = + lmcoursier.internal.CacheUrlConversion.cacheFileToOriginalUrl(art.url, cache) + (originalUrl, pub.classifier.value, pub.ext.value) + case (_, Left(pub), art, _) => + sys.error("unsupported") }) .toMap - val lockData = ResolutionSerializer.extractLockFileData( + ResolutionSerializer.extractLockFileData( resolutions, resolutionParams, conf.scalaVersion, "2.0.0", artifactMap - ) - LockFile.write(lockFile, lockData) - } + ) match + case Right(lockData) => LockFile.write(lockFile, lockData) + case Left(err) => throw err } report } @@ -406,25 +424,30 @@ class CoursierDependencyResolution( private type DependencyKey = (coursier.core.Module, String) private def dependencyKey(dependency: Dependency): DependencyKey = - dependency.module -> dependency.version + dependency.module -> dependency.versionConstraint.asString private def sortDependencies(dependencies: Seq[Dependency]): Vector[Dependency] = dependencies.toVector.sortBy { dep => - (dep.module.organization.value, dep.module.name.value, dep.version) + (dep.module.organization.value, dep.module.name.value, dep.versionConstraint.asString) } private def safeDependenciesOf( resolution: Resolution, dependency: Dependency ): Vector[Dependency] = - try sortDependencies(resolution.dependenciesOf(dependency, false, false)) + try + resolution.dependenciesOf0(dependency, false, false) match + case Right(deps) => sortDependencies(deps) + case Left(_) => Vector.empty catch { case NonFatal(_) => Vector.empty } private def pathScore(path: Vector[Dependency]): (Int, String) = path.size -> path - .map(dep => s"${dep.module.organization.value}:${dep.module.name.value}:${dep.version}") + .map(dep => + s"${dep.module.organization.value}:${dep.module.name.value}:${dep.versionConstraint.asString}" + ) .mkString("->") private def betterPath( @@ -484,7 +507,9 @@ class CoursierDependencyResolution( } .getOrElse(Vector(failedDependency)) - normalizedRootModule +: resolvedPath.map(dep => toModuleId(dep.module, dep.version)) + normalizedRootModule +: resolvedPath.map(dep => + toModuleId(dep.module, dep.versionConstraint.asString) + ) } private def failedPaths( @@ -493,8 +518,8 @@ class CoursierDependencyResolution( downloadErrors: Seq[coursier.error.ResolutionError.CantDownloadModule] ): Map[ModuleID, Seq[ModuleID]] = downloadErrors.map { err => - val failedDependency = Dependency(err.module, err.version) - val failedModule = toModuleId(err.module, err.version) + val failedDependency = (Dependency(err.module, err.versionConstraint.asString): @nowarn) + val failedModule = toModuleId(err.module, err.versionConstraint.asString) failedModule -> resolvePath(resolution, failedDependency, rootModule) }.toMap @@ -533,7 +558,12 @@ class CoursierDependencyResolution( val r = new ResolveException( downloadErrors.map(_.getMessage), downloadErrors.map { err => - toModuleId(err.module, err.version) + ModuleID( + err.module.organization.value, + err.module.name.value, + err.versionConstraint.asString, + ) + .withExtraAttributes(err.module.attributes) }, resolvedPaths ) diff --git a/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala b/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala index 70b8b8365..92b02eaa6 100644 --- a/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala +++ b/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala @@ -1,7 +1,10 @@ package lmcoursier.definitions +import coursier.core.Overrides +import coursier.version.{ ConstraintReconciliation, VersionConstraint, Version } import lmcoursier.credentials.{ Credentials, DirectCredentials, FileCredentials } import sbt.librarymanagement.InclExclRule +import scala.annotation.nowarn // TODO Make private[lmcoursier] // private[coursier] @@ -10,6 +13,12 @@ object ToCoursier { def configuration(configuration: Configuration): coursier.core.Configuration = coursier.core.Configuration(configuration.value) + def configurationBased(c: Configuration): coursier.core.VariantSelector = + coursier.core.VariantSelector.ConfigurationBased(configuration(c)) + + def variantConfiguration(c: Configuration): coursier.core.Variant = + coursier.core.Variant.Configuration(configuration(c)) + def publication(publication: Publication): coursier.core.Publication = coursier.core.Publication( publication.name, @@ -52,6 +61,7 @@ object ToCoursier { includeByDefault = matcher.includeByDefault ) + @nowarn def reconciliation(r: Reconciliation): coursier.core.Reconciliation = r match { case Reconciliation.Default => coursier.core.Reconciliation.Default @@ -60,11 +70,22 @@ object ToCoursier { case Reconciliation.SemVer => coursier.core.Reconciliation.SemVer } + @nowarn def reconciliation( rs: Vector[(ModuleMatchers, Reconciliation)] ): Vector[(coursier.util.ModuleMatchers, coursier.core.Reconciliation)] = rs map { (m, r) => (moduleMatchers(m), reconciliation(r)) } + def constraintReconciliation(r: Reconciliation): coursier.version.ConstraintReconciliation = + r match + case Reconciliation.Default => ConstraintReconciliation.Default + case Reconciliation.Relaxed => ConstraintReconciliation.Relaxed + case Reconciliation.Strict => ConstraintReconciliation.Strict + case Reconciliation.SemVer => ConstraintReconciliation.SemVer + + def versionConstraint(v: String): VersionConstraint = + VersionConstraint(v) + def sameVersions( sv: Seq[Set[InclExclRule]] ): Seq[(coursier.params.rule.SameVersion, coursier.params.rule.RuleResolution)] = @@ -74,11 +95,14 @@ object ToCoursier { coursier.params.rule.SameVersion(matchers) -> coursier.params.rule.RuleResolution.TryResolve } + def version(v: String): Version = + Version(v) + def dependency(dependency: Dependency): coursier.core.Dependency = coursier.core.Dependency( module(dependency.module), - dependency.version, - configuration(dependency.configuration), + versionConstraint(dependency.version), + configurationBased(dependency.configuration), dependency.exclusions.map { (org, name) => (coursier.core.Organization(org.value), coursier.core.ModuleName(name.value)) }, @@ -89,27 +113,27 @@ object ToCoursier { def project(project: Project): coursier.core.Project = coursier.core.Project( - module(project.module), - project.version, - project.dependencies.map { (conf, dep) => - configuration(conf) -> dependency(dep) + module = module(project.module), + version0 = version(project.version), + dependencies0 = project.dependencies.map { (conf, dep) => + variantConfiguration(conf) -> dependency(dep) }, - project.configurations.map { (k, l) => + configurations = project.configurations.map { (k, l) => configuration(k) -> l.map(configuration) }, - None, - Nil, - project.properties, - Nil, - None, - None, - project.packagingOpt.map(t => coursier.core.Type(t.value)), + parent0 = None, + dependencyManagement0 = Nil, + properties = project.properties, + profiles = Nil, + versions = None, + snapshotVersioning = None, + packagingOpt = project.packagingOpt.map(t => coursier.core.Type(t.value)), relocated = false, - None, - project.publications.map { (conf, pub) => - configuration(conf) -> publication(pub) + actualVersionOpt0 = None, + publications0 = project.publications.map { (conf, pub) => + variantConfiguration(conf) -> publication(pub) }, - coursier.core.Info( + info = coursier.core.Info( project.info.description, project.info.homePage, project.info.licenses, @@ -130,8 +154,11 @@ object ToCoursier { dt.second ) }, - None // TODO Add scm field in lmcoursier.definitions.Info? - ) + None, // TODO Add scm field in lmcoursier.definitions.Info? + ), + overrides = Overrides.empty, + variants = Map.empty, + variantPublications = Map.empty, ) def credentials(credentials: Credentials): coursier.credentials.Credentials = diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/BuildClock.scala b/lm-coursier/src/main/scala/lmcoursier/internal/BuildClock.scala index 5eba110c5..119c7a8ab 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/BuildClock.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/BuildClock.scala @@ -14,14 +14,15 @@ object BuildClock { ): String = { val digest = MessageDigest.getInstance("SHA-1") - dependencies.sortBy(d => (d._1.value, d._2.module.toString, d._2.version)).foreach { - case (config, dep) => + dependencies + .sortBy(d => (d._1.value, d._2.module.toString, d._2.versionConstraint.asString)) + .foreach { case (config, dep) => digest.update(config.value.getBytes("UTF-8")) digest.update(dep.module.organization.value.getBytes("UTF-8")) digest.update(dep.module.name.value.getBytes("UTF-8")) - digest.update(dep.version.getBytes("UTF-8")) - digest.update(dep.configuration.value.getBytes("UTF-8")) - } + digest.update(dep.versionConstraint.asString.getBytes("UTF-8")) + digest.update(dep.variantSelector.repr.getBytes("UTF-8")) + } repositories.foreach { repo => digest.update(repo.toString.getBytes("UTF-8")) @@ -33,9 +34,9 @@ object BuildClock { digest.update(params.params.maxIterations.toString.getBytes("UTF-8")) - params.params.forceVersion.toSeq.sortBy(_._1.toString).foreach { case (mod, ver) => + params.params.forceVersion0.toSeq.sortBy(_._1.toString).foreach { case (mod, ver) => digest.update(mod.toString.getBytes("UTF-8")) - digest.update(ver.getBytes("UTF-8")) + digest.update(ver.asString.getBytes("UTF-8")) } params.params.exclusions.toSeq.sortBy(e => (e._1.value, e._2.value)).foreach { diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/InterProjectRepository.scala b/lm-coursier/src/main/scala/lmcoursier/internal/InterProjectRepository.scala index 73eea7e88..17348540c 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/InterProjectRepository.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/InterProjectRepository.scala @@ -7,7 +7,11 @@ import coursier.util.{ EitherT, Monad } final case class InterProjectRepository(projects: Seq[Project]) extends Repository { private val map = projects - .map(proj => proj.moduleVersion -> proj) + .map(proj => + (proj.moduleVersion0 match + case (m, v) => (m, v.asString) + ) -> proj + ) .toMap def find[F[_]]( diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/LockedArtifactsRun.scala b/lm-coursier/src/main/scala/lmcoursier/internal/LockedArtifactsRun.scala index 81b1dd484..0ee16818a 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/LockedArtifactsRun.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/LockedArtifactsRun.scala @@ -2,6 +2,7 @@ package lmcoursier.internal import coursier.cache.FileCache import coursier.core.{ Classifier, Dependency, Extension, Publication, Type } +import coursier.version.VersionConstraint import coursier.util.Artifact import sbt.util.Logger @@ -36,7 +37,7 @@ object LockedArtifactsRun { val dependency = Dependency( module = module, - version = depLock.version + version = VersionConstraint(depLock.version) ) val classifier = Classifier(artLock.classifier.getOrElse("")) diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionRun.scala b/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionRun.scala index 28506498b..c34aa662e 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionRun.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionRun.scala @@ -12,6 +12,7 @@ import coursier.params.rule.RuleResolution import coursier.util.Task import sbt.util.Logger +import scala.annotation.nowarn import scala.concurrent.duration.FiniteDuration import scala.collection.mutable @@ -44,6 +45,7 @@ object ResolutionRun { val printOptionalMessage = verbosityLevel >= 0 && verbosityLevel <= 1 + @nowarn def depsRepr(deps: Seq[(Configuration, Dependency)]) = deps .map { (config, dep) => @@ -89,6 +91,7 @@ object ResolutionRun { if (verbosityLevel >= 2) log.info(initialMessage) + @nowarn val resolveTask: Resolve[Task] = { Resolve() // re-using various caches from a resolution of a configuration we extend @@ -173,6 +176,7 @@ object ResolutionRun { } } + @nowarn def resolutions( params: ResolutionParams, verbosityLevel: Int, diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionSerializer.scala b/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionSerializer.scala index 60a5d05e9..c48efdc93 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionSerializer.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionSerializer.scala @@ -2,6 +2,9 @@ package lmcoursier.internal import coursier.{ Project, Resolution } import coursier.core.{ ArtifactSource, Configuration, Dependency, Info, Module } +import coursier.error.DependencyError +import coursier.version.VersionConstraint +import scala.annotation.nowarn import scala.collection.immutable.Seq object ResolutionSerializer { @@ -12,7 +15,7 @@ object ResolutionSerializer { scalaVersion: Option[String], sbtVersion: String, artifactMap: Map[Dependency, Seq[(String, String, String)]] - ): LockFileData = { + ): Either[DependencyError, LockFileData] = val buildClock = BuildClock.compute( params.dependencies, params.mainRepositories, @@ -20,70 +23,79 @@ object ResolutionSerializer { params ) - val configurations = resolutions.toSeq + val configurations0 = resolutions.toVector .sortBy(_._1.value) .map { case (config, resolution) => - val dependencies = extractDependencies(resolution, config, artifactMap) - ConfigurationLock(config.value, dependencies.toVector) + extractDependencies(resolution, config, artifactMap) match + case Right(dependencies) => Right(ConfigurationLock(config.value, dependencies.toVector)) + case Left(err) => Left(err) } - .toVector + traverseEither(configurations0).map: configurations => + val metadata = LockFileMetadata( + sbtVersion = sbtVersion, + scalaVersion = scalaVersion + ) - val metadata = LockFileMetadata( - sbtVersion = sbtVersion, - scalaVersion = scalaVersion - ) - - LockFileData( - version = LockFileConstants.currentVersion, - buildClock = buildClock, - configurations = configurations, - metadata = metadata - ) - } + LockFileData( + version = LockFileConstants.currentVersion, + buildClock = buildClock, + configurations = configurations, + metadata = metadata + ) private def extractDependencies( resolution: Resolution, config: Configuration, artifactMap: Map[Dependency, Seq[(String, String, String)]] - ): Seq[DependencyLock] = { + ): Either[DependencyError, Vector[DependencyLock]] = val dependencies = resolution.minDependencies + val xs = dependencies.toVector + .sortBy(d => (d.module.toString, d.versionConstraint.asString)) + .map: dep => + val resolvedVersion: String = resolution.retainedVersions + .get(dep.module) match + case Some(v) => s"$v" + case None => s"${dep.versionConstraint.asString}" - dependencies.toSeq.sortBy(d => (d.module.toString, d.version)).map { dep => - val resolvedVersion: String = resolution.retainedVersions - .get(dep.module) match { - case Some(v) => s"$v" - case None => s"${dep.version}" - } + resolution + .dependenciesOf0(dep, withRetainedVersions = true) + .map: transitives0 => + val transitives = transitives0 + .map: d => + s"${d.module.organization.value}:${d.module.name.value}:${d.versionConstraint.asString}" + .sorted - val transitives = resolution - .dependenciesOf(dep, withRetainedVersions = true) - .map(d => s"${d.module.organization.value}:${d.module.name.value}:${d.version}") - .sorted + val artifacts = + artifactMap.getOrElse(dep, Seq.empty).map { case (url, classifier, ext) => + ArtifactLock( + url = url, + classifier = if (classifier.isEmpty) None else Some(classifier), + extension = ext, + tpe = dep.attributes.`type`.value + ) + } - val artifacts = artifactMap.getOrElse(dep, Seq.empty).map { case (url, classifier, ext) => - ArtifactLock( - url = url, - classifier = if (classifier.isEmpty) None else Some(classifier), - extension = ext, - tpe = dep.attributes.`type`.value - ) - } + DependencyLock( + organization = dep.module.organization.value, + name = dep.module.name.value, + version = resolvedVersion, + configuration = dep.variantSelector.repr, + classifier = dep.attributes.classifier.value match { + case "" => None + case c => Some(c) + }, + tpe = dep.attributes.`type`.value, + transitives = transitives.toVector, + artifacts = artifacts.toVector + ) + traverseEither(xs) - DependencyLock( - organization = dep.module.organization.value, - name = dep.module.name.value, - version = resolvedVersion, - configuration = dep.configuration.value, - classifier = dep.attributes.classifier.value match { - case "" => None - case c => Some(c) - }, - tpe = dep.attributes.`type`.value, - transitives = transitives.toVector, - artifacts = artifacts.toVector - ) + private def traverseEither[A1, A2](xs: Vector[Either[A1, A2]]): Either[A1, Vector[A2]] = + xs.foldLeft(Right(Vector.empty): Either[A1, Vector[A2]]) { + case (Left(acc), x) => Left(acc) + case (Right(acc), Left(x)) => Left(x) + case (Right(acc), Right(x)) => Right(acc ++ Vector(x)) } - } def reconstructResolutions( lockFileData: LockFileData, @@ -120,7 +132,7 @@ object ResolutionSerializer { coursier.ModuleName(depLock.name), Map.empty[String, String] ), - depLock.version + VersionConstraint(depLock.version), ) }.toSet @@ -131,7 +143,7 @@ object ResolutionSerializer { coursier.ModuleName(depLock.name), Map.empty[String, String] ) - val project = Project( + val project = (Project( module = module, version = depLock.version, dependencies = Seq.empty, @@ -147,15 +159,15 @@ object ResolutionSerializer { actualVersionOpt = None, publications = Seq.empty, info = Info.empty - ) + ): @nowarn) (module, depLock.version) -> (EmptyArtifactSource, project) }.toMap - Resolution() + (Resolution() .withRootDependencies(rootDeps) .withDependencies(dependencies) .withForceVersions(forceVersions ++ params.params.forceVersion) - .withProjectCache(projectCache) + .withProjectCache(projectCache): @nowarn) } private object EmptyArtifactSource extends ArtifactSource { diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala b/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala index 678bb1cc7..85663875b 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala @@ -4,14 +4,23 @@ import java.io.File import java.util.{ Collections, GregorianCalendar, WeakHashMap } import coursier.cache.CacheUrl import coursier.{ Attributes, Dependency, Module, Project, Resolution } -import coursier.core.{ Classifier, Configuration, Extension, Info, Publication, Type } +import coursier.core.{ + Classifier, + Configuration, + Extension, + Info, + MinimizedExclusions, + Publication, + Type, + VariantPublication +} import coursier.maven.MavenAttributes import coursier.util.Artifact import sbt.librarymanagement.{ Artifact as _, Configuration as _, * } import sbt.util.Logger +import scala.annotation.nowarn import scala.annotation.tailrec -import coursier.core.MinimizedExclusions private[internal] object SbtUpdateReport { @@ -32,6 +41,7 @@ private[internal] object SbtUpdateReport { private def infoProperties(project: Project): Seq[(String, String)] = project.properties.filter(_._1.startsWith("info.")) + @nowarn private val moduleId = caching[(Dependency, String, Map[String, String]), ModuleID] { (dependency, version, extraProperties) => val mod = sbt.librarymanagement.ModuleID( @@ -75,6 +85,7 @@ private[internal] object SbtUpdateReport { .withExtraAttributes(module.attributes ++ extraProperties) } + @nowarn private val moduleReport = caching[ ( Dependency, @@ -155,13 +166,16 @@ private[internal] object SbtUpdateReport { .withCallers(callers.toVector) } + @nowarn private def moduleReports( thisModule: (Module, String), res: Resolution, interProjectDependencies: Seq[Project], classifiersOpt: Option[Seq[Classifier]], artifactFileOpt: (Module, String, Attributes, Artifact) => Option[File], - fullArtifactsOpt: Option[Map[(Dependency, Publication, Artifact), Option[File]]], + fullArtifactsOpt: Option[ + Map[(Dependency, Either[VariantPublication, Publication], Artifact), Option[File]] + ], log: Logger, includeSignatures: Boolean, classpathOrder: Boolean, @@ -181,7 +195,7 @@ private[internal] object SbtUpdateReport { deps.map { (d, p, a) => val d0 = d.withAttributes(d.attributes.withClassifier(p.classifier)) val a0 = if (missingOk) a.withOptional(true) else a - val f = map.get((d0, p, a0)).flatten + val f = map.get((d0, Right(p), a0)).flatten (d, p, a0, f) // not d0 } case None => @@ -328,6 +342,7 @@ private[internal] object SbtUpdateReport { } } + @nowarn def apply( thisModule: (Module, String), configDependencies: Map[Configuration, Seq[Dependency]], @@ -335,7 +350,9 @@ private[internal] object SbtUpdateReport { interProjectDependencies: Vector[Project], classifiersOpt: Option[Seq[Classifier]], artifactFileOpt: (Module, String, Attributes, Artifact) => Option[File], - fullArtifactsOpt: Option[Map[(Dependency, Publication, Artifact), Option[File]]], + fullArtifactsOpt: Option[ + Map[(Dependency, Either[VariantPublication, Publication], Artifact), Option[File]] + ], log: Logger, includeSignatures: Boolean, classpathOrder: Boolean, @@ -378,8 +395,11 @@ private[internal] object SbtUpdateReport { OrganizationArtifactReport(rep.module.organization, rep.module.name, Vector(rep)) } + def conflicts: Seq[coursier.graph.Conflict] = + try coursier.graph.Conflict(subRes) + catch case e: Throwable if missingOk => Nil val evicted = for { - c <- coursier.graph.Conflict(subRes) + c <- conflicts // ideally, forceVersions should be taken into account by coursier.core.Resolution itself, when // it computes transitive dependencies. It only handles forced versions at a global level for now, // rather than handing them for each dependency (where each dependency could have its own forced diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/TemporaryInMemoryRepository.scala b/lm-coursier/src/main/scala/lmcoursier/internal/TemporaryInMemoryRepository.scala index fb63e0d1f..5d1e71f58 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/TemporaryInMemoryRepository.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/TemporaryInMemoryRepository.scala @@ -7,6 +7,7 @@ import coursier.cache.{ ConnectionBuilder, FileCache } import coursier.core.* import coursier.util.{ Artifact, EitherT, Monad } +import scala.annotation.nowarn import scala.util.Try object TemporaryInMemoryRepository { @@ -134,6 +135,7 @@ final class TemporaryInMemoryRepository private ( val cacheOpt: Option[FileCache[Nothing]] ) extends Repository { + @nowarn def find[F[_]]( module: Module, version: String, @@ -183,6 +185,7 @@ final class TemporaryInMemoryRepository private ( EitherT(F.map(F.point(()))(_ => res)) } + @nowarn def artifacts( dependency: Dependency, project: Project, diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/UpdateParams.scala b/lm-coursier/src/main/scala/lmcoursier/internal/UpdateParams.scala index 6cc3e077c..cb3c1eb84 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/UpdateParams.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/UpdateParams.scala @@ -9,7 +9,9 @@ import coursier.util.Artifact final case class UpdateParams( thisModule: (Module, String), artifacts: Map[Artifact, File], - fullArtifacts: Option[Map[(Dependency, Publication, Artifact), Option[File]]], + fullArtifacts: Option[ + Map[(Dependency, Either[VariantPublication, Publication], Artifact), Option[File]] + ], classifiers: Option[Seq[Classifier]], configs: Map[Configuration, Set[Configuration]], dependencies: Seq[(Configuration, Dependency)], diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/UpdateRun.scala b/lm-coursier/src/main/scala/lmcoursier/internal/UpdateRun.scala index b79f3cec6..643d08c2c 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/UpdateRun.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/UpdateRun.scala @@ -6,11 +6,13 @@ import coursier.core.* import coursier.util.Print import sbt.librarymanagement.UpdateReport import sbt.util.Logger +import scala.annotation.nowarn // private[coursier] object UpdateRun { // Move back to coursier.util (in core module) after 1.0? + @nowarn private def allDependenciesByConfig( res: Map[Configuration, Resolution], depsByConfig: Map[Configuration, Seq[Dependency]], @@ -33,6 +35,7 @@ object UpdateRun { } // Move back to coursier.util (in core module) after 1.0? + @nowarn private def dependenciesWithConfig( res: Map[Configuration, Resolution], depsByConfig: Map[Configuration, Seq[Dependency]], @@ -48,6 +51,7 @@ object UpdateRun { } .toSet + @nowarn def update( params: UpdateParams, verbosityLevel: Int, diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 4e45ea09a..a7ab2326f 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -98,6 +98,7 @@ object Dependencies { val scalaXml = "org.scala-lang.modules" %% "scala-xml" % "2.4.0" val scalaParsers = "org.scala-lang.modules" %% "scala-parser-combinators" % "2.4.0" val scalaPar = "org.scala-lang.modules" %% "scala-parallel-collections" % "1.2.0" + val scalaCollectionCompat = "org.scala-lang.modules" %% "scala-collection-compat" % "2.14.0" val caffeine = "com.github.ben-manes.caffeine" % "caffeine" % "2.8.5" @@ -111,7 +112,7 @@ object Dependencies { // lm-coursier dependencies val dataclassScalafixVersion = "0.3.0" - val coursierVersion = "2.1.23" + val coursierVersion = "2.1.25-M24" val coursier = ("io.get-coursier" %% "coursier" % coursierVersion) .cross(CrossVersion.for3Use2_13) diff --git a/sbt-app/src/sbt-test/dependency-management/scala-version-check-exempt/build.sbt b/sbt-app/src/sbt-test/dependency-management/scala-version-check-exempt/build.sbt index 540318393..26eff543b 100644 --- a/sbt-app/src/sbt-test/dependency-management/scala-version-check-exempt/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/scala-version-check-exempt/build.sbt @@ -4,6 +4,16 @@ ThisBuild / scalaVersion := "2.11.12" libraryDependencies += "org.scala-lang" %% "scala-actors-migration" % "1.1.0" libraryDependencies += "org.scala-lang" %% "scala-pickling" % "0.9.1" +// This is a workaround for https://github.com/coursier/coursier/issues/3525 +// [info] [error] lmcoursier.internal.shaded.coursier.params.rule.SameVersion$CantForceSameVersion: +// Can't force version 2.11.12 for modules org.scala-lang:scala-compiler, org.scala-lang:scala-reflect, org.scala-lang:scala-library +// .... +// [info] [error] Caused by: lmcoursier.internal.shaded.coursier.params.rule.SameVersion$SameVersionConflict: +// Unsatisfied rule SameVersion(HashSet(ModuleMatcher(org.scala-lang:scala-compiler), ModuleMatcher(org.scala-lang:scalap), +// ModuleMatcher(org.scala-lang:scala-actors), ModuleMatcher(org.scala-lang:scala-reflect), ModuleMatcher(org.scala-lang:scala-library))): +// Found versions 2.11.0, 2.11.12 for org.scala-lang:scala-actors, org.scala-lang:scala-compiler, org.scala-lang:scala-library, org.scala-lang:scala-reflect +libraryDependencies += "org.scala-lang" % "scala-actors" % "2.11.12" + lazy val check = taskKey[Unit]("Runs the check") check := { diff --git a/sbt-app/src/sbt-test/dependency-management/update-sbt-classifiers/build.sbt b/sbt-app/src/sbt-test/dependency-management/update-sbt-classifiers/build.sbt index 05d1b4ba3..3f075ed58 100644 --- a/sbt-app/src/sbt-test/dependency-management/update-sbt-classifiers/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/update-sbt-classifiers/build.sbt @@ -53,6 +53,7 @@ lazy val root = (project in file(".")) "org.jline:jline-terminal-jni", "org.reactivestreams:reactive-streams", "org.scala-lang.modules:scala-asm", + "org.scala-lang.modules:scala-collection-compat_3", "org.scala-lang.modules:scala-parallel-collections_3", "org.scala-lang.modules:scala-parser-combinators_3", "org.scala-lang.modules:scala-xml_3",