diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b926e0f97..e70192e33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -152,7 +152,6 @@ jobs: ./sbt -v --client "scripted dependency-management/* project-load/* project-matrix/* java/* run/*" ./sbt -v --client "scripted plugins/*" ./sbt -v --client "scripted nio/*" - ./sbt -v --client "scripted ivy/*" - name: Build and test (4) if: ${{ matrix.jobtype == 4 }} shell: bash diff --git a/build.sbt b/build.sbt index 889221cbf..a688fc3a2 100644 --- a/build.sbt +++ b/build.sbt @@ -747,7 +747,6 @@ lazy val mainProj = (project in file("main")) runProj, commandProj, collectionProj, - lmIvy, zincLmIntegrationProj, utilLogging, ) @@ -818,21 +817,6 @@ lazy val mainProj = (project in file("main")) .dependsOn(lmCore, lmCoursierShadedPublishing) .configure(addSbtIO, addSbtCompilerInterface, addSbtZincCompileCore) -lazy val sbtIvyProj = (project in file("sbt-ivy")) - .dependsOn(sbtProj, lmIvy) - .settings( - testedBaseSettings, - name := "sbt-ivy", - sbtPlugin := true, - pluginCrossBuild / sbtVersion := version.value, - libraryDependencies += { - // https://github.com/scala/scala3/issues/18487 - "net.hamnaberg" %% "dataclass-annotation" % dataclassScalafixVersion % Provided - }, - mimaPreviousArtifacts := Set.empty, // new module, no previous artifacts - ) - .configure(addSbtIO) - // Strictly for bringing implicits and aliases from subsystems into the top-level sbt namespace through a single package object // technically, we need a dependency on all of mainProj's dependencies, but we don't do that since this is strictly an integration project // with the sole purpose of providing certain identifiers without qualification (with a package object) @@ -847,6 +831,12 @@ lazy val sbtProj = (project in file("sbt-app")) javaOptions ++= Seq("-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"), mimaSettings, mimaBinaryIssueFilters ++= sbtIgnoredProblems, + mimaBinaryIssueFilters ++= Vector( + // Dropped the top-level Ivy-specific UpdateOptions alias; use + // sbt.internal.librarymanagement.ivy.UpdateOptions directly if needed. + exclude[DirectMissingMethodProblem]("sbt.Import.UpdateOptions"), + exclude[DirectMissingMethodProblem]("sbt.package.UpdateOptions"), + ), ) .settings( Test / run / connectInput := true, @@ -1096,7 +1086,6 @@ def allProjects = mainSettingsProj, zincLmIntegrationProj, mainProj, - sbtIvyProj, sbtProj, bundledLauncherProj, sbtClientProj, @@ -1108,7 +1097,6 @@ def allProjects = coreMacrosProj, remoteCacheProj, lmCore, - lmIvy, lmCoursierDefinitions, lmCoursier, lmCoursierShaded, @@ -1271,29 +1259,6 @@ lazy val lmCore = (project in file("lm-core")) .dependsOn(utilLogging, utilPosition, utilCache) .configure(addSbtIO, addSbtCompilerInterface) -lazy val lmIvy = (project in file("lm-ivy")) - .enablePlugins(ContrabandPlugin, JsonCodecPlugin) - .dependsOn(lmCore) - .settings( - exportJars := false, - commonSettings, - lmTestSettings, - name := "librarymanagement-ivy", - contrabandSjsonNewVersion := sjsonNewVersion, - libraryDependencies ++= Seq( - ivy, - sjsonNewScalaJson.value, - sjsonNewCore.value, - scalacheck % Test, - scalaVerify % Test, - hedgehog % Test, - ), - libraryDependencies ++= scalatest, - contrabandSettings, - Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat, - mimaSettings, - ) - lazy val lmCoursierSettings: Seq[Setting[?]] = Def.settings( baseSettings, headerLicense := Some( diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/ivy/IvyCredentials.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/ivy/IvyCredentials.scala new file mode 100644 index 000000000..03ef19e0f --- /dev/null +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/ivy/IvyCredentials.scala @@ -0,0 +1,17 @@ +package sbt.internal.librarymanagement +package ivy + +import java.io.File +import sbt.librarymanagement.{ Credentials, CredentialUtils } + +// Kept for backward compatibility with sbt2-compat +private[sbt] object IvyCredentials: + def forHost(sc: Seq[Credentials], host: String) = + CredentialUtils.forHost(sc, host) + def allDirect(sc: Seq[Credentials]): Seq[Credentials.DirectCredentials] = + CredentialUtils.allDirect(sc) + def toDirect(c: Credentials): Credentials.DirectCredentials = + CredentialUtils.toDirect(c) + def loadCredentials(path: File): Either[String, Credentials.DirectCredentials] = + CredentialUtils.loadCredentials(path) +end IvyCredentials diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/mavenint/PomExtraAttributeKeys.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/mavenint/PomExtraAttributeKeys.scala new file mode 100644 index 000000000..067f1650e --- /dev/null +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/mavenint/PomExtraAttributeKeys.scala @@ -0,0 +1,14 @@ +package sbt.internal.librarymanagement +package mavenint + +/** + * Keys for the extra pom properties used to propagate the sbtVersion/scalaVersion + * attributes for sbt plugin dependencies, plus the Maven packagings that sbt treats as jars. + */ +object PomExtraAttributeKeys: + val SbtVersionKey = "sbtVersion" + val ScalaVersionKey = "scalaVersion" + + // packagings that should be jars, but that Ivy doesn't handle as jars + val JarPackagings = Set("eclipse-plugin", "hk2-jar", "orbit", "scala-jar") +end PomExtraAttributeKeys diff --git a/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/ExternalIvyConfiguration.scala b/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/ExternalIvyConfiguration.scala deleted file mode 100644 index cc03ba888..000000000 --- a/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/ExternalIvyConfiguration.scala +++ /dev/null @@ -1,66 +0,0 @@ -/** - * This code is generated using [[https://www.scala-sbt.org/contraband]]. - */ - -// DO NOT EDIT MANUALLY -package sbt.internal.librarymanagement.ivy -final class ExternalIvyConfiguration private ( - lock: Option[xsbti.GlobalLock], - log: Option[xsbti.Logger], - updateOptions: sbt.internal.librarymanagement.ivy.UpdateOptions, - val baseDirectory: Option[java.io.File], - val uri: Option[java.net.URI], - val extraResolvers: Vector[sbt.librarymanagement.Resolver]) extends sbt.internal.librarymanagement.ivy.IvyConfiguration(lock, log, updateOptions) with Serializable { - - private def this() = this(None, None, sbt.internal.librarymanagement.ivy.UpdateOptions(), None, None, Vector()) - - override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { - case x: ExternalIvyConfiguration => (this.lock == x.lock) && (this.log == x.log) && (this.updateOptions == x.updateOptions) && (this.baseDirectory == x.baseDirectory) && (this.uri == x.uri) && (this.extraResolvers == x.extraResolvers) - case _ => false - }) - override def hashCode: Int = { - 37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.librarymanagement.ivy.ExternalIvyConfiguration".##) + lock.##) + log.##) + updateOptions.##) + baseDirectory.##) + uri.##) + extraResolvers.##) - } - override def toString: String = { - "ExternalIvyConfiguration(" + lock + ", " + log + ", " + updateOptions + ", " + baseDirectory + ", " + uri + ", " + extraResolvers + ")" - } - private def copy(lock: Option[xsbti.GlobalLock] = lock, log: Option[xsbti.Logger] = log, updateOptions: sbt.internal.librarymanagement.ivy.UpdateOptions = updateOptions, baseDirectory: Option[java.io.File] = baseDirectory, uri: Option[java.net.URI] = uri, extraResolvers: Vector[sbt.librarymanagement.Resolver] = extraResolvers): ExternalIvyConfiguration = { - new ExternalIvyConfiguration(lock, log, updateOptions, baseDirectory, uri, extraResolvers) - } - def withLock(lock: Option[xsbti.GlobalLock]): ExternalIvyConfiguration = { - copy(lock = lock) - } - def withLock(lock: xsbti.GlobalLock): ExternalIvyConfiguration = { - copy(lock = Option(lock)) - } - def withLog(log: Option[xsbti.Logger]): ExternalIvyConfiguration = { - copy(log = log) - } - def withLog(log: xsbti.Logger): ExternalIvyConfiguration = { - copy(log = Option(log)) - } - def withUpdateOptions(updateOptions: sbt.internal.librarymanagement.ivy.UpdateOptions): ExternalIvyConfiguration = { - copy(updateOptions = updateOptions) - } - def withBaseDirectory(baseDirectory: Option[java.io.File]): ExternalIvyConfiguration = { - copy(baseDirectory = baseDirectory) - } - def withBaseDirectory(baseDirectory: java.io.File): ExternalIvyConfiguration = { - copy(baseDirectory = Option(baseDirectory)) - } - def withUri(uri: Option[java.net.URI]): ExternalIvyConfiguration = { - copy(uri = uri) - } - def withUri(uri: java.net.URI): ExternalIvyConfiguration = { - copy(uri = Option(uri)) - } - def withExtraResolvers(extraResolvers: Vector[sbt.librarymanagement.Resolver]): ExternalIvyConfiguration = { - copy(extraResolvers = extraResolvers) - } -} -object ExternalIvyConfiguration { - - def apply(): ExternalIvyConfiguration = new ExternalIvyConfiguration() - def apply(lock: Option[xsbti.GlobalLock], log: Option[xsbti.Logger], updateOptions: sbt.internal.librarymanagement.ivy.UpdateOptions, baseDirectory: Option[java.io.File], uri: Option[java.net.URI], extraResolvers: Vector[sbt.librarymanagement.Resolver]): ExternalIvyConfiguration = new ExternalIvyConfiguration(lock, log, updateOptions, baseDirectory, uri, extraResolvers) - def apply(lock: xsbti.GlobalLock, log: xsbti.Logger, updateOptions: sbt.internal.librarymanagement.ivy.UpdateOptions, baseDirectory: java.io.File, uri: java.net.URI, extraResolvers: Vector[sbt.librarymanagement.Resolver]): ExternalIvyConfiguration = new ExternalIvyConfiguration(Option(lock), Option(log), updateOptions, Option(baseDirectory), Option(uri), extraResolvers) -} diff --git a/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/ExternalIvyConfigurationFormats.scala b/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/ExternalIvyConfigurationFormats.scala deleted file mode 100644 index 79e21f998..000000000 --- a/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/ExternalIvyConfigurationFormats.scala +++ /dev/null @@ -1,37 +0,0 @@ -/** - * This code is generated using [[https://www.scala-sbt.org/contraband]]. - */ - -// DO NOT EDIT MANUALLY -package sbt.internal.librarymanagement.ivy -import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ExternalIvyConfigurationFormats { self: sbt.internal.librarymanagement.formats.GlobalLockFormat & sbt.internal.librarymanagement.formats.LoggerFormat & sbt.internal.librarymanagement.ivy.formats.UpdateOptionsFormat & sbt.librarymanagement.ResolverFormats & sjsonnew.BasicJsonProtocol => -given ExternalIvyConfigurationFormat: JsonFormat[sbt.internal.librarymanagement.ivy.ExternalIvyConfiguration] = new JsonFormat[sbt.internal.librarymanagement.ivy.ExternalIvyConfiguration] { - override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.librarymanagement.ivy.ExternalIvyConfiguration = { - __jsOpt match { - case Some(__js) => - unbuilder.beginObject(__js) - val lock = unbuilder.readField[Option[xsbti.GlobalLock]]("lock") - val log = unbuilder.readField[Option[xsbti.Logger]]("log") - val updateOptions = unbuilder.readField[sbt.internal.librarymanagement.ivy.UpdateOptions]("updateOptions") - val baseDirectory = unbuilder.readField[Option[java.io.File]]("baseDirectory") - val uri = unbuilder.readField[Option[java.net.URI]]("uri") - val extraResolvers = unbuilder.readField[Vector[sbt.librarymanagement.Resolver]]("extraResolvers") - unbuilder.endObject() - sbt.internal.librarymanagement.ivy.ExternalIvyConfiguration(lock, log, updateOptions, baseDirectory, uri, extraResolvers) - case None => - deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.internal.librarymanagement.ivy.ExternalIvyConfiguration, builder: Builder[J]): Unit = { - builder.beginObject() - builder.addField("lock", obj.lock) - builder.addField("log", obj.log) - builder.addField("updateOptions", obj.updateOptions) - builder.addField("baseDirectory", obj.baseDirectory) - builder.addField("uri", obj.uri) - builder.addField("extraResolvers", obj.extraResolvers) - builder.endObject() - } -} -} diff --git a/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/InlineIvyConfiguration.scala b/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/InlineIvyConfiguration.scala deleted file mode 100644 index 90148ca16..000000000 --- a/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/InlineIvyConfiguration.scala +++ /dev/null @@ -1,82 +0,0 @@ -/** - * This code is generated using [[https://www.scala-sbt.org/contraband]]. - */ - -// DO NOT EDIT MANUALLY -package sbt.internal.librarymanagement.ivy -final class InlineIvyConfiguration private ( - lock: Option[xsbti.GlobalLock], - log: Option[xsbti.Logger], - updateOptions: sbt.internal.librarymanagement.ivy.UpdateOptions, - val paths: Option[sbt.librarymanagement.IvyPaths], - val resolvers: Vector[sbt.librarymanagement.Resolver], - val otherResolvers: Vector[sbt.librarymanagement.Resolver], - val moduleConfigurations: Vector[sbt.librarymanagement.ModuleConfiguration], - val checksums: Vector[String], - val managedChecksums: Boolean, - val resolutionCacheDir: Option[java.io.File]) extends sbt.internal.librarymanagement.ivy.IvyConfiguration(lock, log, updateOptions) with Serializable { - - private def this() = this(None, None, sbt.internal.librarymanagement.ivy.UpdateOptions(), None, sbt.librarymanagement.Resolver.defaults, Vector.empty, Vector.empty, sbt.internal.librarymanagement.ivy.IvyDefaults.defaultChecksums, false, None) - - override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { - case x: InlineIvyConfiguration => (this.lock == x.lock) && (this.log == x.log) && (this.updateOptions == x.updateOptions) && (this.paths == x.paths) && (this.resolvers == x.resolvers) && (this.otherResolvers == x.otherResolvers) && (this.moduleConfigurations == x.moduleConfigurations) && (this.checksums == x.checksums) && (this.managedChecksums == x.managedChecksums) && (this.resolutionCacheDir == x.resolutionCacheDir) - case _ => false - }) - override def hashCode: Int = { - 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.librarymanagement.ivy.InlineIvyConfiguration".##) + lock.##) + log.##) + updateOptions.##) + paths.##) + resolvers.##) + otherResolvers.##) + moduleConfigurations.##) + checksums.##) + managedChecksums.##) + resolutionCacheDir.##) - } - override def toString: String = { - "InlineIvyConfiguration(" + lock + ", " + log + ", " + updateOptions + ", " + paths + ", " + resolvers + ", " + otherResolvers + ", " + moduleConfigurations + ", " + checksums + ", " + managedChecksums + ", " + resolutionCacheDir + ")" - } - private def copy(lock: Option[xsbti.GlobalLock] = lock, log: Option[xsbti.Logger] = log, updateOptions: sbt.internal.librarymanagement.ivy.UpdateOptions = updateOptions, paths: Option[sbt.librarymanagement.IvyPaths] = paths, resolvers: Vector[sbt.librarymanagement.Resolver] = resolvers, otherResolvers: Vector[sbt.librarymanagement.Resolver] = otherResolvers, moduleConfigurations: Vector[sbt.librarymanagement.ModuleConfiguration] = moduleConfigurations, checksums: Vector[String] = checksums, managedChecksums: Boolean = managedChecksums, resolutionCacheDir: Option[java.io.File] = resolutionCacheDir): InlineIvyConfiguration = { - new InlineIvyConfiguration(lock, log, updateOptions, paths, resolvers, otherResolvers, moduleConfigurations, checksums, managedChecksums, resolutionCacheDir) - } - def withLock(lock: Option[xsbti.GlobalLock]): InlineIvyConfiguration = { - copy(lock = lock) - } - def withLock(lock: xsbti.GlobalLock): InlineIvyConfiguration = { - copy(lock = Option(lock)) - } - def withLog(log: Option[xsbti.Logger]): InlineIvyConfiguration = { - copy(log = log) - } - def withLog(log: xsbti.Logger): InlineIvyConfiguration = { - copy(log = Option(log)) - } - def withUpdateOptions(updateOptions: sbt.internal.librarymanagement.ivy.UpdateOptions): InlineIvyConfiguration = { - copy(updateOptions = updateOptions) - } - def withPaths(paths: Option[sbt.librarymanagement.IvyPaths]): InlineIvyConfiguration = { - copy(paths = paths) - } - def withPaths(paths: sbt.librarymanagement.IvyPaths): InlineIvyConfiguration = { - copy(paths = Option(paths)) - } - def withResolvers(resolvers: Vector[sbt.librarymanagement.Resolver]): InlineIvyConfiguration = { - copy(resolvers = resolvers) - } - def withOtherResolvers(otherResolvers: Vector[sbt.librarymanagement.Resolver]): InlineIvyConfiguration = { - copy(otherResolvers = otherResolvers) - } - def withModuleConfigurations(moduleConfigurations: Vector[sbt.librarymanagement.ModuleConfiguration]): InlineIvyConfiguration = { - copy(moduleConfigurations = moduleConfigurations) - } - def withChecksums(checksums: Vector[String]): InlineIvyConfiguration = { - copy(checksums = checksums) - } - def withManagedChecksums(managedChecksums: Boolean): InlineIvyConfiguration = { - copy(managedChecksums = managedChecksums) - } - def withResolutionCacheDir(resolutionCacheDir: Option[java.io.File]): InlineIvyConfiguration = { - copy(resolutionCacheDir = resolutionCacheDir) - } - def withResolutionCacheDir(resolutionCacheDir: java.io.File): InlineIvyConfiguration = { - copy(resolutionCacheDir = Option(resolutionCacheDir)) - } -} -object InlineIvyConfiguration { - - def apply(): InlineIvyConfiguration = new InlineIvyConfiguration() - def apply(lock: Option[xsbti.GlobalLock], log: Option[xsbti.Logger], updateOptions: sbt.internal.librarymanagement.ivy.UpdateOptions, paths: Option[sbt.librarymanagement.IvyPaths], resolvers: Vector[sbt.librarymanagement.Resolver], otherResolvers: Vector[sbt.librarymanagement.Resolver], moduleConfigurations: Vector[sbt.librarymanagement.ModuleConfiguration], checksums: Vector[String], managedChecksums: Boolean, resolutionCacheDir: Option[java.io.File]): InlineIvyConfiguration = new InlineIvyConfiguration(lock, log, updateOptions, paths, resolvers, otherResolvers, moduleConfigurations, checksums, managedChecksums, resolutionCacheDir) - def apply(lock: xsbti.GlobalLock, log: xsbti.Logger, updateOptions: sbt.internal.librarymanagement.ivy.UpdateOptions, paths: sbt.librarymanagement.IvyPaths, resolvers: Vector[sbt.librarymanagement.Resolver], otherResolvers: Vector[sbt.librarymanagement.Resolver], moduleConfigurations: Vector[sbt.librarymanagement.ModuleConfiguration], checksums: Vector[String], managedChecksums: Boolean, resolutionCacheDir: java.io.File): InlineIvyConfiguration = new InlineIvyConfiguration(Option(lock), Option(log), updateOptions, Option(paths), resolvers, otherResolvers, moduleConfigurations, checksums, managedChecksums, Option(resolutionCacheDir)) -} diff --git a/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/InlineIvyConfigurationFormats.scala b/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/InlineIvyConfigurationFormats.scala deleted file mode 100644 index 07c8346f8..000000000 --- a/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/InlineIvyConfigurationFormats.scala +++ /dev/null @@ -1,45 +0,0 @@ -/** - * This code is generated using [[https://www.scala-sbt.org/contraband]]. - */ - -// DO NOT EDIT MANUALLY -package sbt.internal.librarymanagement.ivy -import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait InlineIvyConfigurationFormats { self: sbt.internal.librarymanagement.formats.GlobalLockFormat & sbt.internal.librarymanagement.formats.LoggerFormat & sbt.internal.librarymanagement.ivy.formats.UpdateOptionsFormat & sbt.librarymanagement.IvyPathsFormats & sbt.librarymanagement.ResolverFormats & sbt.librarymanagement.ModuleConfigurationFormats & sjsonnew.BasicJsonProtocol => -given InlineIvyConfigurationFormat: JsonFormat[sbt.internal.librarymanagement.ivy.InlineIvyConfiguration] = new JsonFormat[sbt.internal.librarymanagement.ivy.InlineIvyConfiguration] { - override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.librarymanagement.ivy.InlineIvyConfiguration = { - __jsOpt match { - case Some(__js) => - unbuilder.beginObject(__js) - val lock = unbuilder.readField[Option[xsbti.GlobalLock]]("lock") - val log = unbuilder.readField[Option[xsbti.Logger]]("log") - val updateOptions = unbuilder.readField[sbt.internal.librarymanagement.ivy.UpdateOptions]("updateOptions") - val paths = unbuilder.readField[Option[sbt.librarymanagement.IvyPaths]]("paths") - val resolvers = unbuilder.readField[Vector[sbt.librarymanagement.Resolver]]("resolvers") - val otherResolvers = unbuilder.readField[Vector[sbt.librarymanagement.Resolver]]("otherResolvers") - val moduleConfigurations = unbuilder.readField[Vector[sbt.librarymanagement.ModuleConfiguration]]("moduleConfigurations") - val checksums = unbuilder.readField[Vector[String]]("checksums") - val managedChecksums = unbuilder.readField[Boolean]("managedChecksums") - val resolutionCacheDir = unbuilder.readField[Option[java.io.File]]("resolutionCacheDir") - unbuilder.endObject() - sbt.internal.librarymanagement.ivy.InlineIvyConfiguration(lock, log, updateOptions, paths, resolvers, otherResolvers, moduleConfigurations, checksums, managedChecksums, resolutionCacheDir) - case None => - deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.internal.librarymanagement.ivy.InlineIvyConfiguration, builder: Builder[J]): Unit = { - builder.beginObject() - builder.addField("lock", obj.lock) - builder.addField("log", obj.log) - builder.addField("updateOptions", obj.updateOptions) - builder.addField("paths", obj.paths) - builder.addField("resolvers", obj.resolvers) - builder.addField("otherResolvers", obj.otherResolvers) - builder.addField("moduleConfigurations", obj.moduleConfigurations) - builder.addField("checksums", obj.checksums) - builder.addField("managedChecksums", obj.managedChecksums) - builder.addField("resolutionCacheDir", obj.resolutionCacheDir) - builder.endObject() - } -} -} diff --git a/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/IvyConfiguration.scala b/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/IvyConfiguration.scala deleted file mode 100644 index fbc13fc00..000000000 --- a/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/IvyConfiguration.scala +++ /dev/null @@ -1,21 +0,0 @@ -/** - * This code is generated using [[https://www.scala-sbt.org/contraband]]. - */ - -// DO NOT EDIT MANUALLY -package sbt.internal.librarymanagement.ivy -abstract class IvyConfiguration( - val lock: Option[xsbti.GlobalLock], - val log: Option[xsbti.Logger], - val updateOptions: sbt.internal.librarymanagement.ivy.UpdateOptions) extends Serializable { - - def this() = this(None, None, sbt.internal.librarymanagement.ivy.UpdateOptions()) - - - override def toString: String = { - "IvyConfiguration(" + lock + ", " + log + ", " + updateOptions + ")" - } -} -object IvyConfiguration { - -} diff --git a/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/IvyConfigurationFormats.scala b/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/IvyConfigurationFormats.scala deleted file mode 100644 index c00441af4..000000000 --- a/lm-ivy/src/main/contraband-scala/sbt/internal/librarymanagement/ivy/IvyConfigurationFormats.scala +++ /dev/null @@ -1,11 +0,0 @@ -/** - * This code is generated using [[https://www.scala-sbt.org/contraband]]. - */ - -// DO NOT EDIT MANUALLY -package sbt.internal.librarymanagement.ivy - -import _root_.sjsonnew.JsonFormat -trait IvyConfigurationFormats { self: sbt.internal.librarymanagement.formats.GlobalLockFormat & sbt.internal.librarymanagement.formats.LoggerFormat & sbt.internal.librarymanagement.ivy.formats.UpdateOptionsFormat & sbt.librarymanagement.IvyPathsFormats & sbt.librarymanagement.ResolverFormats & sbt.librarymanagement.ModuleConfigurationFormats & sjsonnew.BasicJsonProtocol & sbt.internal.librarymanagement.ivy.InlineIvyConfigurationFormats & sbt.internal.librarymanagement.ivy.ExternalIvyConfigurationFormats => -given IvyConfigurationFormat: JsonFormat[sbt.internal.librarymanagement.ivy.IvyConfiguration] = flatUnionFormat2[sbt.internal.librarymanagement.ivy.IvyConfiguration, sbt.internal.librarymanagement.ivy.InlineIvyConfiguration, sbt.internal.librarymanagement.ivy.ExternalIvyConfiguration]("type") -} diff --git a/lm-ivy/src/main/contraband/lm-ivy.json b/lm-ivy/src/main/contraband/lm-ivy.json deleted file mode 100644 index d67af5a68..000000000 --- a/lm-ivy/src/main/contraband/lm-ivy.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "codecNamespace": "sbt.internal.librarymanagement.ivy", - "types": [ - { - "name": "IvyConfiguration", - "namespace": "sbt.internal.librarymanagement.ivy", - "target": "Scala", - "type": "interface", - "fields": [ - { - "name": "lock", - "type": "xsbti.GlobalLock?", - "default": "None", - "since": "0.0.1" - }, - { - "name": "log", - "type": "xsbti.Logger?", - "default": "None", - "since": "0.0.1" - }, - { - "name": "updateOptions", - "type": "sbt.internal.librarymanagement.ivy.UpdateOptions", - "default": "sbt.internal.librarymanagement.ivy.UpdateOptions()", - "since": "0.0.1" - } - ], - "types": [ - { - "name": "InlineIvyConfiguration", - "namespace": "sbt.internal.librarymanagement.ivy", - "target": "Scala", - "type": "record", - "fields": [ - { - "name": "paths", - "type": "sbt.librarymanagement.IvyPaths?", - "default": "None", - "since": "0.0.1" - }, - { - "name": "resolvers", - "type": "sbt.librarymanagement.Resolver*", - "default": "sbt.librarymanagement.Resolver.defaults", - "since": "0.0.1" - }, - { - "name": "otherResolvers", - "type": "sbt.librarymanagement.Resolver*", - "default": "Vector.empty", - "since": "0.0.1" - }, - { - "name": "moduleConfigurations", - "type": "sbt.librarymanagement.ModuleConfiguration*", - "default": "Vector.empty", - "since": "0.0.1" - }, - { - "name": "checksums", - "type": "String*", - "default": "sbt.internal.librarymanagement.ivy.IvyDefaults.defaultChecksums", - "since": "0.0.1" - }, - { - "name": "managedChecksums", - "type": "Boolean", - "default": "false", - "since": "0.0.1" - }, - { - "name": "resolutionCacheDir", - "type": "java.io.File?", - "default": "None", - "since": "0.0.1" - } - ] - }, - { - "name": "ExternalIvyConfiguration", - "namespace": "sbt.internal.librarymanagement.ivy", - "target": "Scala", - "type": "record", - "fields": [ - { - "name": "baseDirectory", - "type": "java.io.File?", - "default": "None", - "since": "0.0.1" - }, - { - "name": "uri", - "type": "java.net.URI?", - "default": "None", - "since": "0.0.1" - }, - { - "name": "extraResolvers", - "type": "sbt.librarymanagement.Resolver*", - "default": "Vector()", - "since": "0.0.1" - } - ] - } - ] - } - ] -} diff --git a/lm-ivy/src/main/java/internal/librarymanagement/ResolverAdapter.java b/lm-ivy/src/main/java/internal/librarymanagement/ResolverAdapter.java deleted file mode 100644 index d983a6149..000000000 --- a/lm-ivy/src/main/java/internal/librarymanagement/ResolverAdapter.java +++ /dev/null @@ -1,16 +0,0 @@ -package sbt.internal.librarymanagement; - -import java.util.Map; -import org.apache.ivy.plugins.resolver.DependencyResolver; - -// implements the methods with raw types -@SuppressWarnings("rawtypes") -public abstract class ResolverAdapter implements DependencyResolver { - public String[] listTokenValues(String token, Map otherTokenValues) { - return new String[0]; - } - - public Map[] listTokenValues(String[] tokens, Map criteria) { - return new Map[0]; - } -} diff --git a/lm-ivy/src/main/scala/org/apache/ivy/plugins/parser/m2/ReplaceMavenConfigurationMappings.scala b/lm-ivy/src/main/scala/org/apache/ivy/plugins/parser/m2/ReplaceMavenConfigurationMappings.scala deleted file mode 100644 index f0ed69715..000000000 --- a/lm-ivy/src/main/scala/org/apache/ivy/plugins/parser/m2/ReplaceMavenConfigurationMappings.scala +++ /dev/null @@ -1,135 +0,0 @@ -package org.apache.ivy.plugins.parser.m2 - -import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor; - -/** - * It turns out there was a very subtle, and evil, issue sitting the Ivy/maven configuration, and it - * related to dependency mapping. A mapping of `foo->bar(*)` means that the local configuration - * `foo` depends on the remote configuration `bar`, if it exists, or *ALL CONFIGURATIONS* if `bar` - * does not exist. Since the default Ivy configuration mapping was using the random `master` - * configuration, which AFAICT is NEVER specified, just an assumed default, this would cause leaks - * between maven + ivy projects. - * - * i.e. if a maven POM depends on a module denoted by an ivy.xml file, then you'd wind up accidentally - * bleeding ALL the ivy module's configurations into the maven module's configurations. - * - * This fix works around the issue, by assuming that if there is no `master` configuration, than the - * maven default of `compile` is intended. As sbt forces generated `ivy.xml` files to abide by - * maven conventions, this works in all of our test cases. The only scenario where it wouldn't work - * is those who have custom ivy.xml files *and* have pom.xml files which rely on those custom ivy.xml files, - * a very unlikely situation where the workaround is: "define a master configuration". - * - * Also see: http://ant.apache.org/ivy/history/2.3.0/ivyfile/dependency.html - * and: http://svn.apache.org/repos/asf/ant/ivy/core/tags/2.3.0/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorBuilder.java - */ -object ReplaceMavenConfigurationMappings { - - def addMappings(dd: DefaultDependencyDescriptor, scope: String, isOptional: Boolean) = { - val mapping = ReplaceMavenConfigurationMappings.REPLACEMENT_MAVEN_MAPPINGS.get(scope) - mapping.addMappingConfs(dd, isOptional) - } - - val REPLACEMENT_MAVEN_MAPPINGS = { - // Here we copy paste from Ivy - val REPLACEMENT_MAPPINGS = new java.util.HashMap[String, PomModuleDescriptorBuilder.ConfMapper] - - // NOTE - This code is copied from org.apache.ivy.plugins.parser.m2.PomModuleDescriptorBuilder - // except with altered default configurations... - REPLACEMENT_MAPPINGS.put( - "compile", - new PomModuleDescriptorBuilder.ConfMapper { - def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean): Unit = { - if (isOptional) { - dd.addDependencyConfiguration("optional", "compile(*)") - // FIX - Here we take a more conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("optional", "master(compile)") - } else { - dd.addDependencyConfiguration("compile", "compile(*)") - // FIX - Here we take a more conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("compile", "master(compile)") - dd.addDependencyConfiguration("runtime", "runtime(*)") - } - } - } - ) - REPLACEMENT_MAPPINGS.put( - "provided", - new PomModuleDescriptorBuilder.ConfMapper { - def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean): Unit = { - if (isOptional) { - dd.addDependencyConfiguration("optional", "compile(*)") - dd.addDependencyConfiguration("optional", "provided(*)") - dd.addDependencyConfiguration("optional", "runtime(*)") - // FIX - Here we take a more conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("optional", "master(compile)") - } else { - dd.addDependencyConfiguration("provided", "compile(*)") - dd.addDependencyConfiguration("provided", "provided(*)") - dd.addDependencyConfiguration("provided", "runtime(*)") - // FIX - Here we take a more conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("provided", "master(compile)") - } - } - } - ) - - REPLACEMENT_MAPPINGS.put( - "runtime", - new PomModuleDescriptorBuilder.ConfMapper { - def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean): Unit = { - if (isOptional) { - dd.addDependencyConfiguration("optional", "compile(*)") - dd.addDependencyConfiguration("optional", "provided(*)") - // FIX - Here we take a more conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("optional", "master(compile)") - } else { - dd.addDependencyConfiguration("runtime", "compile(*)") - dd.addDependencyConfiguration("runtime", "runtime(*)") - // FIX - Here we take a more conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("runtime", "master(compile)") - } - } - } - ) - - REPLACEMENT_MAPPINGS.put( - "test", - new PomModuleDescriptorBuilder.ConfMapper { - def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean): Unit = { - dd.addDependencyConfiguration("test", "runtime(*)") - // FIX - Here we take a more conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("test", "master(compile)") - } - } - ) - - REPLACEMENT_MAPPINGS.put( - "system", - new PomModuleDescriptorBuilder.ConfMapper { - def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean): Unit = { - // FIX - Here we take a more conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("system", "master(compile)") - } - } - ) - - REPLACEMENT_MAPPINGS - } - - def init(): Unit = { - // Here we mutate a static final field, because we have to AND because it's evil. - try { - val map = PomModuleDescriptorBuilder.MAVEN2_CONF_MAPPING - .asInstanceOf[java.util.Map[String, PomModuleDescriptorBuilder.ConfMapper]] - map.clear() - map.putAll(REPLACEMENT_MAVEN_MAPPINGS) - } catch { - case e: Exception => - // TODO - Log that Ivy may not be configured correctly and you could have maven/ivy issues. - throw new RuntimeException( - "FAILURE to install Ivy maven hooks. Your ivy-maven interaction may suffer resolution errors", - e - ) - } - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ComponentManager.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ComponentManager.scala deleted file mode 100644 index 052a2ec74..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ComponentManager.scala +++ /dev/null @@ -1,111 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt.internal.librarymanagement - -import java.io.File -import java.util.concurrent.Callable -import sbt.util.Logger -import sbt.librarymanagement.* -import scala.util.Using - -/** - * A component manager provides access to the pieces of xsbt that are distributed as components. - * There are two types of components. The first type is compiled subproject jars with their dependencies. - * The second type is a subproject distributed as a source jar so that it can be compiled against a specific - * version of Scala. - * - * The component manager provides services to install and retrieve components to the local repository. - * This is used for compiled source jars so that the compilation need not be repeated for other projects on the same - * machine. - */ -class ComponentManager( - globalLock: xsbti.GlobalLock, - provider: xsbti.ComponentProvider, - ivyHome: Option[File], - val log: Logger -) { - private val ivyCache = new IvyCache(ivyHome) - - /** Get all of the files for component 'id', throwing an exception if no files exist for the component. */ - def files(id: String)(ifMissing: IfMissing): Iterable[File] = { - def fromGlobal = - lockGlobalCache { - try { - update(id); getOrElse(createAndCache) - } catch { - case _: NotInCache => createAndCache - } - } - def getOrElse(orElse: => Iterable[File]): Iterable[File] = { - val existing = provider.component(id) - if (existing.isEmpty) orElse else existing - } - def notFound = invalid("Could not find required component '" + id + "'") - def createAndCache = - ifMissing match { - case IfMissing.Fail => notFound - case d: IfMissing.Define => - d() - if (d.cache) cache(id) - getOrElse(notFound) - } - - lockLocalCache { getOrElse(fromGlobal) } - } - - /** This is used to lock the local cache in project/boot/. By checking the local cache first, we can avoid grabbing a global lock. */ - private def lockLocalCache[T](action: => T): T = lock(provider.lockFile)(action) - - /** This is used to ensure atomic access to components in the global Ivy cache. */ - private def lockGlobalCache[T](action: => T): T = lock(ivyCache.lockFile)(action) - private def lock[T](file: File)(action: => T): T = - globalLock(file, new Callable[T] { def call = action }) - - /** Get the file for component 'id', throwing an exception if no files or multiple files exist for the component. */ - def file(id: String)(ifMissing: IfMissing): File = - files(id)(ifMissing).toList match { - case x :: Nil => x - case xs => - invalid("Expected single file for component '" + id + "', found: " + xs.mkString(", ")) - } - private def invalid(msg: String) = throw new InvalidComponent(msg) - - def define(id: String, files: Iterable[File]) = lockLocalCache { - provider.defineComponent(id, files.toSeq.toArray) - } - - /** Retrieve the file for component 'id' from the local repository. */ - private def update(id: String): Unit = - ivyCache.withCachedJar(sbtModuleID(id), Some(globalLock), log)(jar => define(id, Seq(jar))) - - private def sbtModuleID(id: String) = - ModuleID(SbtArtifacts.Organization, id, ComponentManager.stampedVersion) - - /** Install the files for component 'id' to the local repository. This is usually used after writing files to the directory returned by 'location'. */ - def cache(id: String): Unit = - ivyCache.cacheJar(sbtModuleID(id), file(id)(IfMissing.Fail), Some(globalLock), log) - def clearCache(id: String): Unit = lockGlobalCache { - ivyCache.clearCachedJar(sbtModuleID(id), Some(globalLock), log) - } -} -class InvalidComponent(msg: String, cause: Throwable) extends RuntimeException(msg, cause) { - def this(msg: String) = this(msg, null) -} -sealed trait IfMissing -object IfMissing { - object Fail extends IfMissing - final class Define(val cache: Boolean, define: => Unit) extends IfMissing { - def apply() = define - } -} -object ComponentManager { - lazy val (version, timestamp) = { - val properties = new java.util.Properties - Using.resource(getClass.getResourceAsStream("/xsbt.version.properties")) { propertiesStream => - properties.load(propertiesStream) - } - (properties.getProperty("version"), properties.getProperty("timestamp")) - } - lazy val stampedVersion = version + "_" + timestamp -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ConvertResolver.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ConvertResolver.scala deleted file mode 100644 index 5bb43b487..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ConvertResolver.scala +++ /dev/null @@ -1,467 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt.internal.librarymanagement - -import java.net.URI -import java.util.Collections - -import org.apache.ivy.core.module.descriptor.DependencyDescriptor -import org.apache.ivy.core.resolve.{ DownloadOptions, ResolveData } -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.plugins.repository.{ RepositoryCopyProgressListener, Resource, TransferEvent } -import org.apache.ivy.plugins.resolver.{ - BasicResolver, - DependencyResolver, - IBiblioResolver, - RepositoryResolver -} -import org.apache.ivy.plugins.resolver.{ - AbstractPatternsBasedResolver, - AbstractSshBasedResolver, - FileSystemResolver, - SFTPResolver, - SshResolver, - URLResolver -} -import org.apache.ivy.plugins.repository.url.URLRepository as URLRepo -import org.apache.ivy.plugins.repository.file.{ FileResource, FileRepository as FileRepo } -import java.io.{ File, IOException } -import java.util.Date - -import org.apache.ivy.core.module.descriptor.Artifact as IArtifact -import org.apache.ivy.core.module.id.ModuleRevisionId -import org.apache.ivy.core.module.descriptor.DefaultArtifact -import org.apache.ivy.core.report.DownloadReport -import org.apache.ivy.plugins.resolver.util.{ ResolvedResource, ResourceMDParser } -import org.apache.ivy.util.{ ChecksumHelper, FileUtil, Message } -import scala.jdk.CollectionConverters.* -import sbt.internal.librarymanagement.ivy.UpdateOptions -import sbt.internal.librarymanagement.mavenint.PomExtraDependencyAttributes -import sbt.io.IO -import sbt.util.Logger -import sbt.librarymanagement.* - -private[sbt] object ConvertResolver { - import UpdateOptions.ResolverConverter - - /** - * This class contains all the reflective lookups used in the - * checksum-friendly URL publishing shim. - */ - private object ChecksumFriendlyURLResolver { - import java.lang.reflect.AccessibleObject - private def reflectiveLookup[A <: AccessibleObject](f: Class[?] => A): Option[A] = - try { - val cls = classOf[RepositoryResolver] - val thing = f(cls) - thing.setAccessible(true) - Some(thing) - } catch { - case (_: java.lang.NoSuchFieldException) | (_: java.lang.SecurityException) | - (_: java.lang.NoSuchMethodException) => - None - } - private val signerNameField: Option[java.lang.reflect.Field] = - reflectiveLookup(_.getDeclaredField("signerName")) - private val putChecksumMethod: Option[java.lang.reflect.Method] = - reflectiveLookup( - _.getDeclaredMethod( - "putChecksum", - classOf[IArtifact], - classOf[File], - classOf[String], - classOf[Boolean], - classOf[String] - ) - ) - private val putSignatureMethod: Option[java.lang.reflect.Method] = - reflectiveLookup( - _.getDeclaredMethod( - "putSignature", - classOf[IArtifact], - classOf[File], - classOf[String], - classOf[Boolean] - ) - ) - } - - /** - * The default behavior of ivy's overwrite flags ignores the fact that a lot of repositories - * will autogenerate checksums *for* an artifact if it doesn't already exist. Therefore - * if we succeed in publishing an artifact, we need to just blast the checksums in place. - * This acts as a "shim" on RepositoryResolvers so that we can hook our methods into - * both the IBiblioResolver + URLResolver without having to duplicate the code in two - * places. However, this does mean our use of reflection is awesome. - * - * TODO - See about contributing back to ivy. - */ - private trait ChecksumFriendlyURLResolver extends RepositoryResolver { - import ChecksumFriendlyURLResolver.* - private def signerName: String = signerNameField match { - case Some(field) => field.get(this).asInstanceOf[String] - case None => null - } - override protected def put( - artifact: IArtifact, - src: File, - dest: String, - overwrite: Boolean - ): Unit = { - // verify the checksum algorithms before uploading artifacts! - val checksums = getChecksumAlgorithms() - val repository = getRepository() - for { - checksum <- checksums - if !ChecksumHelper.isKnownAlgorithm(checksum) - } throw new IllegalArgumentException("Unknown checksum algorithm: " + checksum) - repository.put(artifact, src, dest, overwrite) - if !dest.endsWith(".asc") then - for checksum <- checksums do - putChecksumMethod match - case Some(method) => - method.invoke(this, artifact, src, dest, true: java.lang.Boolean, checksum) - case None => // TODO - issue warning? - if (signerName != null) { - putSignatureMethod match { - case None => () - case Some(method) => method.invoke(artifact, src, dest, true: java.lang.Boolean); () - } - } - } - } - - private[librarymanagement] val ManagedChecksums = "sbt.managedChecksums" - - /** Converts the given sbt resolver into an Ivy resolver. */ - def apply( - r: Resolver, - settings: IvySettings, - updateOptions: UpdateOptions, - log: Logger - ): DependencyResolver = - (updateOptions.resolverConverter orElse defaultConvert)((r, settings, log)) - - /** The default implementation of converter. */ - lazy val defaultConvert: ResolverConverter = { (r, settings, log) => - val managedChecksums = Option(settings.getVariable(ManagedChecksums)) match { - case Some(x) => x.toBoolean - case _ => false - } - r match { - case repo: MavenRepository => { - val pattern = Collections.singletonList( - Resolver.resolvePattern(repo.root, Resolver.mavenStyleBasePattern) - ) - final class PluginCapableResolver - extends IBiblioResolver - with ChecksumFriendlyURLResolver - with DescriptorRequired { - override val managedChecksumsEnabled: Boolean = managedChecksums - override def getResource(resource: Resource, dest: File): Long = get(resource, dest) - def setPatterns(): Unit = { - // done this way for access to protected methods. - setArtifactPatterns(pattern) - setIvyPatterns(pattern) - } - override protected def findResourceUsingPattern( - mrid: ModuleRevisionId, - pattern: String, - artifact: IArtifact, - rmdparser: ResourceMDParser, - date: Date - ): ResolvedResource = { - val extraAttributes = - mrid.getExtraAttributes.asScala.toMap.asInstanceOf[Map[String, String]] - getSbtPluginCrossVersion(extraAttributes) match { - case Some(sbtCrossVersion) => - // if the module is an sbt plugin - // we first try to resolve the artifact with the sbt cross version suffix - // and we fallback to the one without the suffix - val newArtifact = DefaultArtifact.cloneWithAnotherName( - artifact, - artifact.getName + sbtCrossVersion - ) - val resolved = - super.findResourceUsingPattern(mrid, pattern, newArtifact, rmdparser, date) - if (resolved != null) resolved - else super.findResourceUsingPattern(mrid, pattern, artifact, rmdparser, date) - case None => - super.findResourceUsingPattern(mrid, pattern, artifact, rmdparser, date) - } - } - } - val resolver = new PluginCapableResolver - if (repo.localIfFile) resolver.setRepository(new LocalIfFileRepo) - initializeMavenStyle(resolver, repo.name, repo.root) - resolver - .setPatterns() // has to be done after initializeMavenStyle, which calls methods that overwrite the patterns - resolver - } - case repo: SshRepository => { - val resolver = new SshResolver with DescriptorRequired with ThreadSafeSshBasedResolver { - override val managedChecksumsEnabled: Boolean = managedChecksums - override def getResource(resource: Resource, dest: File): Long = get(resource, dest) - } - initializeSSHResolver(resolver, repo, settings) - repo.publishPermissions.foreach(perm => resolver.setPublishPermissions(perm)) - resolver - } - case repo: SftpRepository => { - val resolver = new SFTPResolver with ThreadSafeSshBasedResolver - initializeSSHResolver(resolver, repo, settings) - resolver - } - case repo: FileRepository => { - val resolver = - new FileSystemResolver with ChecksumFriendlyURLResolver with DescriptorRequired { - // Workaround for #1156 - // Temporarily in sbt 0.13.x we deprecate overwriting - // in local files for non-changing revisions. - // This will be fully enforced in sbt 1.0. - setRepository(new WarnOnOverwriteFileRepo()) - override val managedChecksumsEnabled: Boolean = managedChecksums - override def getResource(resource: Resource, dest: File): Long = get(resource, dest) - } - resolver.setName(repo.name) - initializePatterns(resolver, repo.patterns, settings) - import repo.configuration.{ isLocal, isTransactional } - resolver.setLocal(isLocal) - isTransactional.foreach(value => resolver.setTransactional(value.toString)) - resolver - } - case repo: URLRepository => { - val resolver = new URLResolver with ChecksumFriendlyURLResolver with DescriptorRequired { - override val managedChecksumsEnabled: Boolean = managedChecksums - override def getResource(resource: Resource, dest: File): Long = get(resource, dest) - } - resolver.setName(repo.name) - initializePatterns(resolver, repo.patterns, settings) - resolver - } - case repo: ChainedResolver => - IvySbt.resolverChain(repo.name, repo.resolvers, settings, log) - case repo: RawRepository => - repo.resolver match { - case r: DependencyResolver => r - } - } - } - - private def getSbtPluginCrossVersion(extraAttributes: Map[String, String]): Option[String] = { - for { - sbtVersion <- extraAttributes.get(PomExtraDependencyAttributes.SbtVersionKey) - scalaVersion <- extraAttributes.get(PomExtraDependencyAttributes.ScalaVersionKey) - } yield s"_${scalaVersion}_$sbtVersion" - } - - private sealed trait DescriptorRequired extends BasicResolver { - // Works around implementation restriction to access protected method `get` - def getResource(resource: Resource, dest: File): Long - - /** - * Defines an option to tell ivy to disable checksums when downloading and - * let the user handle verifying these checksums. - * - * This means that the checksums are stored in the ivy cache directory. This - * is good for reproducibility from outside ivy. Sbt can check that jars are - * not corrupted, ever, independently of trusting whatever it's there in the - * local directory. - */ - def managedChecksumsEnabled: Boolean - - private def downloadChecksum( - resource: Resource, - targetChecksumFile: File, - algorithm: String - ): Boolean = { - if (!ChecksumHelper.isKnownAlgorithm(algorithm)) - throw new IllegalArgumentException(s"Unknown checksum algorithm: $algorithm") - - val checksumResource = resource.clone(s"${resource.getName}.$algorithm") - if (!checksumResource.exists) false - else { - Message.debug(s"$algorithm file found for $resource: downloading...") - // Resource must be cleaned up outside of this function if it's invalid - getResource(checksumResource, targetChecksumFile) - true - } - } - - private final val PartEnd = ".part" - private final val JarEnd = ".jar" - private final val TemporaryJar = JarEnd + PartEnd - - override def getAndCheck(resource: Resource, target: File): Long = { - val targetPath = target.getAbsolutePath - if (!managedChecksumsEnabled || !targetPath.endsWith(TemporaryJar)) { - super.getAndCheck(resource, target) - } else { - // +ivy deviation - val size = getResource(resource, target) - val checksumAlgorithms = getChecksumAlgorithms - checksumAlgorithms.foldLeft(false) { (checked, algorithm) => - // Continue checking until we hit a failure - val checksumFile = new File(targetPath.stripSuffix(PartEnd) + s".$algorithm") - if (checked) checked - else downloadChecksum(resource, checksumFile, algorithm) - } - // -ivy deviation - size - } - } - - override def getDependency(dd: DependencyDescriptor, data: ResolveData) = { - val prev = descriptorString(isAllownomd) - setDescriptor(descriptorString(hasExplicitURL(dd))) - val t = - try super.getDependency(dd, data) - finally setDescriptor(prev) - t - } - def descriptorString(optional: Boolean) = - if (optional) BasicResolver.DESCRIPTOR_OPTIONAL else BasicResolver.DESCRIPTOR_REQUIRED - def hasExplicitURL(dd: DependencyDescriptor): Boolean = - dd.getAllDependencyArtifacts.exists(_.getUrl != null) - } - private def initializeMavenStyle(resolver: IBiblioResolver, name: String, root: String): Unit = { - resolver.setName(name) - resolver.setM2compatible(true) - resolver.setRoot(root) - } - private def initializeSSHResolver( - resolver: AbstractSshBasedResolver, - repo: SshBasedRepository, - settings: IvySettings - ): Unit = { - resolver.setName(repo.name) - resolver.setPassfile(null) - initializePatterns(resolver, repo.patterns, settings) - initializeConnection(resolver, repo.connection) - } - private def initializeConnection( - resolver: AbstractSshBasedResolver, - connection: SshConnection - ): Unit = { - import resolver.* - import connection.* - hostname.foreach(setHost) - port.foreach(setPort) - authentication foreach { - case pa: PasswordAuthentication => - setUser(pa.user) - pa.password.foreach(setUserPassword) - case kfa: KeyFileAuthentication => - setKeyFile(kfa.keyfile) - kfa.password.foreach(setKeyFilePassword) - setUser(kfa.user) - } - } - private def initializePatterns( - resolver: AbstractPatternsBasedResolver, - patterns: Patterns, - settings: IvySettings - ): Unit = { - resolver.setM2compatible(patterns.isMavenCompatible) - resolver.setDescriptor( - if (patterns.descriptorOptional) BasicResolver.DESCRIPTOR_OPTIONAL - else BasicResolver.DESCRIPTOR_REQUIRED - ) - resolver.setCheckconsistency(!patterns.skipConsistencyCheck) - patterns.ivyPatterns.foreach(p => resolver.addIvyPattern(settings.substitute(p))) - patterns.artifactPatterns.foreach(p => resolver.addArtifactPattern(settings.substitute(p))) - } - - /** - * A custom Ivy URLRepository that returns FileResources for file URLs. - * This allows using the artifacts from the Maven local repository instead of copying them to the Ivy cache. - */ - private final class LocalIfFileRepo extends URLRepo { - private val repo = new WarnOnOverwriteFileRepo() - private val progress = new RepositoryCopyProgressListener(this); - override def getResource(source: String) = { - val uri = new URI(source) - if (uri.getScheme == IO.FileScheme) - new FileResource(repo, IO.toFile(uri)) - else - super.getResource(source) - } - - override def put(source: File, destination: String, overwrite: Boolean): Unit = { - val uri = new URI(destination) - try { - if (uri.getScheme != IO.FileScheme) super.put(source, destination, overwrite) - else { - // Here we duplicate the put method for files so we don't just bail on trying ot use Http handler - val resource = getResource(destination) - if (!overwrite && resource.exists()) { - throw new IOException(s"destination file exists and overwrite == false"); - } - fireTransferInitiated(resource, TransferEvent.REQUEST_PUT); - try { - val totalLength = source.length - if (totalLength > 0) { - progress.setTotalLength(totalLength); - } - FileUtil.copy(source, new java.io.File(uri), progress, overwrite) - () - } catch { - case ex: IOException => - fireTransferError(ex) - throw ex - case ex: RuntimeException => - fireTransferError(ex) - throw ex - } finally { - progress.setTotalLength(null); - } - } - } catch { - // This error could be thrown either by super.put or the above - case ex: IOException if ex.getMessage.contains("destination file exists") => - throw new IOException( - s"""PUT operation failed because the destination file exists and overwriting is disabled: - | source : $source - | destination: $destination - |If you have a staging repository that has failed, drop it and start over. - |Otherwise fix the double publishing, or relax the setting as follows: - | publishConfiguration := publishConfiguration.value.withOverwrite(true) - | publishLocalConfiguration := publishLocalConfiguration.value.withOverwrite(true) - | - |If you have a remote cache repository, you can enable overwriting as follows: - | pushRemoteCacheConfiguration := pushRemoteCacheConfiguration.value.withOverwrite(true) - |""".stripMargin, - ex - ) - } - } - } - - private final class WarnOnOverwriteFileRepo extends FileRepo() { - override def put(source: java.io.File, destination: String, overwrite: Boolean): Unit = { - try super.put(source, destination, overwrite) - catch { - case e: java.io.IOException if e.getMessage.contains("destination already exists") => - val overwriteWarning = - if destination.contains("-SNAPSHOT") then s"Attempting to overwrite $destination" - else - s"Attempting to overwrite $destination (non-SNAPSHOT)\n\tYou need to remove it from the cache manually to take effect." - import org.apache.ivy.util.Message - Message.warn(overwriteWarning) - super.put(source, destination, true) - } - } - } - - private sealed trait ThreadSafeSshBasedResolver - extends org.apache.ivy.plugins.resolver.AbstractSshBasedResolver { -//uncomment to test non-threadsafe behavior -// private def lock = new Object - private val lock = org.apache.ivy.plugins.repository.ssh.SshCache.getInstance - override def download(artifacts: Array[IArtifact], options: DownloadOptions): DownloadReport = - lock.synchronized { - super.download(artifacts, options) - } - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/CustomPomParser.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/CustomPomParser.scala deleted file mode 100644 index 2d1e6cd08..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/CustomPomParser.scala +++ /dev/null @@ -1,344 +0,0 @@ -package sbt.internal.librarymanagement - -import org.apache.ivy.core.module.id.ModuleRevisionId -import org.apache.ivy.core.module.descriptor.{ - DefaultArtifact, - DefaultExtendsDescriptor, - DefaultModuleDescriptor, - ModuleDescriptor -} -import org.apache.ivy.core.module.descriptor.{ DefaultDependencyDescriptor, DependencyDescriptor } -import org.apache.ivy.plugins.parser.{ - ModuleDescriptorParser, - ModuleDescriptorParserRegistry, - ParserSettings -} -import org.apache.ivy.plugins.parser.m2.{ - ReplaceMavenConfigurationMappings, - PomModuleDescriptorBuilder, - PomModuleDescriptorParser -} -import org.apache.ivy.plugins.repository.Resource -import org.apache.ivy.plugins.namespace.NamespaceTransformer -import org.apache.ivy.util.extendable.ExtendableItem - -import java.io.{ File, InputStream } -import java.net.URL -import sbt.internal.librarymanagement.mavenint.{ - PomExtraDependencyAttributes, - SbtPomExtraProperties -} -import sbt.io.Hash -import scala.collection.immutable.ArraySeq - -// @deprecated("We now use an Aether-based pom parser.", "0.13.8") -final class CustomPomParser( - delegate: ModuleDescriptorParser, - transform: (ModuleDescriptorParser, ModuleDescriptor) => ModuleDescriptor -) extends ModuleDescriptorParser { - override def parseDescriptor( - ivySettings: ParserSettings, - descriptorURL: URL, - validate: Boolean - ) = - transform(this, delegate.parseDescriptor(ivySettings, descriptorURL, validate)) - - override def parseDescriptor( - ivySettings: ParserSettings, - descriptorURL: URL, - res: Resource, - validate: Boolean - ) = - transform(this, delegate.parseDescriptor(ivySettings, descriptorURL, res, validate)) - - override def toIvyFile(is: InputStream, res: Resource, destFile: File, md: ModuleDescriptor) = - delegate.toIvyFile(is, res, destFile, md) - - override def accept(res: Resource) = delegate.accept(res) - override def getType() = delegate.getType() - override def getMetadataArtifact(mrid: ModuleRevisionId, res: Resource) = - delegate.getMetadataArtifact(mrid, res) -} -// @deprecated("We now use an Aether-based pom parser.", "0.13.8") -object CustomPomParser { - - // Evil hackery to override the default maven pom mappings. - ReplaceMavenConfigurationMappings.init() - - /** The key prefix that indicates that this is used only to store extra information and is not intended for dependency resolution. */ - val InfoKeyPrefix = SbtPomExtraProperties.POM_INFO_KEY_PREFIX - val ApiURLKey = SbtPomExtraProperties.POM_API_KEY - val VersionSchemeKey = SbtPomExtraProperties.VERSION_SCHEME_KEY - - val SbtVersionKey = PomExtraDependencyAttributes.SbtVersionKey - val ScalaVersionKey = PomExtraDependencyAttributes.ScalaVersionKey - val ExtraAttributesKey = PomExtraDependencyAttributes.ExtraAttributesKey - private val unqualifiedKeys = - Set(SbtVersionKey, ScalaVersionKey, ExtraAttributesKey, ApiURLKey, VersionSchemeKey) - - /** - * In the new POM format of sbt plugins, the dependency to an sbt plugin - * contains the sbt cross-version _2.12_1.0. The reason is we want Maven to be able - * to resolve the dependency using the pattern: - * /_2.12_1.0//_2.12_1.0-.pom - * In sbt 1.x we use extra-attributes to resolve sbt plugins, so here we must remove - * the sbt cross-version and keep the extra-attributes. - * Parsing a dependency found in the new POM format produces the same module as - * if it is found in the old POM format. It used not to contain the sbt cross-version - * suffix, but that was invalid. - * Hence we can resolve conflicts between new and old POM formats. - * - * To compare the two formats you can look at the POMs in: - * https://repo1.maven.org/maven2/ch/epfl/scala/sbt-plugin-example-diamond_2.12_1.0/0.5.0/ - */ - private def removeSbtCrossVersion( - properties: Map[String, String], - moduleName: String - ): String = { - val sbtCrossVersion = for { - sbtVersion <- properties.get(s"e:$SbtVersionKey") - scalaVersion <- properties.get(s"e:$ScalaVersionKey") - } yield s"_${scalaVersion}_$sbtVersion" - sbtCrossVersion.map(moduleName.stripSuffix).getOrElse(moduleName) - } - - // packagings that should be jars, but that Ivy doesn't handle as jars - // TODO - move this elsewhere. - val JarPackagings = Set("eclipse-plugin", "hk2-jar", "orbit", "scala-jar") - val default = new CustomPomParser(PomModuleDescriptorParser.getInstance, defaultTransform) - - private val TransformedHashKey = "e:sbtTransformHash" - // A hash of the parameters transformation is based on. - // If a descriptor has a different hash, we need to retransform it. - private def makeCoords(mrid: ModuleRevisionId): String = - s"${mrid.getOrganisation}:${mrid.getName}:${mrid.getRevision}" - - // We now include the ModuleID in a hash, to ensure that parent-pom transformations don't corrupt child poms. - private def MakeTransformHash(md: ModuleDescriptor): String = { - val coords: String = makeCoords(md.getModuleRevisionId) - - hash((unqualifiedKeys ++ JarPackagings ++ Set(coords)).toSeq.sorted) - } - - private def hash(ss: Seq[String]): String = - Hash.toHex(Hash(ss.flatMap(_.getBytes("UTF-8")).toArray)) - - // Unfortunately, ModuleDescriptorParserRegistry is add-only and is a singleton instance. - lazy val registerDefault: Unit = ModuleDescriptorParserRegistry.getInstance.addParser(default) - - def defaultTransform(parser: ModuleDescriptorParser, md: ModuleDescriptor): ModuleDescriptor = - if (transformedByThisVersion(md)) md - else defaultTransformImpl(parser, md) - - private def transformedByThisVersion(md: ModuleDescriptor): Boolean = { - val oldTransformedHashKey = "sbtTransformHash" - val extraInfo = md.getExtraInfo - val MyHash = MakeTransformHash(md) - // sbt 0.13.1 used "sbtTransformHash" instead of "e:sbtTransformHash" until #1192 so read both - Option(extraInfo).isDefined && - (Option(extraInfo.get(TransformedHashKey)) - .orElse(Option(extraInfo.get(oldTransformedHashKey))) match - case Some(MyHash) => true - case _ => false) - } - - private def defaultTransformImpl( - parser: ModuleDescriptorParser, - md: ModuleDescriptor - ): ModuleDescriptor = { - val properties = getPomProperties(md) - - // Extracts extra attributes (currently, sbt and Scala versions) stored in the element of the pom. - // These are attached to the module itself. - val filtered = shouldBeUnqualified(properties) - - // Extracts extra attributes for the dependencies. - // Because the tag in pom.xml cannot include additional metadata, - // sbt includes extra attributes in a 'extraDependencyAttributes' property. - // This is read/written from/to a pure string (no element structure) because Ivy only - // parses the immediate text nodes of the property. - val extraDepAttributes = getDependencyExtra(filtered) - - val unqualify = toUnqualify(filtered) - - // Here we always add extra attributes. There's a scenario where parent-pom information corrupts child-poms with "e:" namespaced xml elements - // and we have to force the every generated xml file to have the appropriate xml namespace - addExtra(unqualify, extraDepAttributes, parser, md) - } - // The element of the pom is used to store additional metadata, such as for sbt plugins or for the base URL for API docs. - // This is done because the pom XSD does not appear to allow extra metadata anywhere else. - // The extra sbt plugin metadata in pom.xml does not need to be readable by maven, but the other information may be. - // However, the pom.xml needs to be valid in all cases because other tools like repository managers may read the pom.xml. - private[sbt] def getPomProperties(md: ModuleDescriptor): Map[String, String] = { - import scala.jdk.CollectionConverters.* - PomModuleDescriptorBuilder - .extractPomProperties(md.getExtraInfo) - .asInstanceOf[java.util.Map[String, String]] - .asScala - .toMap - } - private[sbt] def toUnqualify(propertyAttributes: Map[String, String]): Map[String, String] = - (propertyAttributes - ExtraAttributesKey) map { (k, v) => ("e:" + k, v) } - - private def shouldBeUnqualified(m: Map[String, String]): Map[String, String] = - m.view.filterKeys(unqualifiedKeys).toMap - - private def addExtra( - properties: Map[String, String], - id: ModuleRevisionId - ): ModuleRevisionId = { - import scala.jdk.CollectionConverters.* - val oldExtra = qualifiedExtra(id) - val newExtra = (oldExtra ++ properties).asJava - // remove the sbt plugin cross version from the resolved ModuleRevisionId - // sbt-plugin-example_2.12_1.0 => sbt-plugin-example - val nameWithoutCrossVersion = removeSbtCrossVersion(properties, id.getName) - ModuleRevisionId.newInstance( - id.getOrganisation, - nameWithoutCrossVersion, - id.getBranch, - id.getRevision, - newExtra - ) - } - - private def getDependencyExtra( - m: Map[String, String] - ): Map[ModuleRevisionId, Map[String, String]] = - PomExtraDependencyAttributes.getDependencyExtra(m) - - def qualifiedExtra(item: ExtendableItem): Map[String, String] = - PomExtraDependencyAttributes.qualifiedExtra(item) - def filterCustomExtra(item: ExtendableItem, include: Boolean): Map[String, String] = - qualifiedExtra(item).view.filterKeys { k => - qualifiedIsExtra(k) == include - }.toMap - - def writeDependencyExtra(s: Seq[DependencyDescriptor]): Seq[String] = - PomExtraDependencyAttributes.writeDependencyExtra(s) - - // parses the sequence of dependencies with extra attribute information, with one dependency per line - def readDependencyExtra(s: String): Seq[ModuleRevisionId] = - PomExtraDependencyAttributes.readDependencyExtra(s) - - def qualifiedIsExtra(k: String): Boolean = PomExtraDependencyAttributes.qualifiedIsExtra(k) - - // Reduces the id to exclude custom extra attributes - // This makes the id suitable as a key to associate a dependency parsed from a element - // with the extra attributes from the section - def simplify(id: ModuleRevisionId): ModuleRevisionId = PomExtraDependencyAttributes.simplify(id) - - private def addExtra( - dep: DependencyDescriptor, - extra: Map[ModuleRevisionId, Map[String, String]] - ): DependencyDescriptor = { - val extras = if (extra.isEmpty) None else extra get simplify(dep.getDependencyRevisionId) - extras match { - case None => dep - case Some(extraAttrs) => transform(dep, revId => addExtra(extraAttrs, revId)) - } - } - private def transform( - dep: DependencyDescriptor, - f: ModuleRevisionId => ModuleRevisionId - ): DependencyDescriptor = - DefaultDependencyDescriptor.transformInstance( - dep, - namespaceTransformer(dep.getDependencyRevisionId, f), - false - ) - - private def namespaceTransformer( - txId: ModuleRevisionId, - f: ModuleRevisionId => ModuleRevisionId - ): NamespaceTransformer = - new NamespaceTransformer { - def transform(revId: ModuleRevisionId): ModuleRevisionId = - if (revId == txId) f(revId) else revId - def isIdentity = false - } - - // TODO: It would be better if we can make dd.isForce to `false` when VersionRange.isVersionRange is `true`. - private def stripVersionRange(dd: DependencyDescriptor): DependencyDescriptor = - VersionRange.stripMavenVersionRange(dd.getDependencyRevisionId.getRevision) match { - case Some(newVersion) => - val id = dd.getDependencyRevisionId - val newId = ModuleRevisionId.newInstance( - id.getOrganisation, - id.getName, - id.getBranch, - newVersion, - id.getExtraAttributes - ) - transform(dd, _ => newId) - case None => dd - } - - import scala.jdk.CollectionConverters.* - def addExtra( - properties: Map[String, String], - dependencyExtra: Map[ModuleRevisionId, Map[String, String]], - parser: ModuleDescriptorParser, - md: ModuleDescriptor - ): ModuleDescriptor = { - val dmd = new DefaultModuleDescriptor(parser, md.getResource) - - val mrid = addExtra(properties, md.getModuleRevisionId) - val resolvedMrid = addExtra(properties, md.getResolvedModuleRevisionId) - dmd.setModuleRevisionId(mrid) - dmd.setResolvedModuleRevisionId(resolvedMrid) - - dmd.setDefault(md.isDefault) - dmd.setHomePage(md.getHomePage) - dmd.setDescription(md.getDescription) - dmd.setLastModified(md.getLastModified) - dmd.setStatus(md.getStatus()) - dmd.setPublicationDate(md.getPublicationDate()) - dmd.setResolvedPublicationDate(md.getResolvedPublicationDate()) - - for (l <- md.getLicenses) dmd.addLicense(l) - for ((key, value) <- md.getExtraInfo.asInstanceOf[java.util.Map[String, String]].asScala) - dmd.addExtraInfo(key, value) - dmd.addExtraInfo( - TransformedHashKey, - MakeTransformHash(md) - ) // mark as transformed by this version, so we don't need to do it again - for ( - (key, value) <- md.getExtraAttributesNamespaces - .asInstanceOf[java.util.Map[String, String]] - .asScala - ) dmd.addExtraAttributeNamespace(key, value) - IvySbt.addExtraNamespace(dmd) - - val withExtra = ArraySeq.unsafeWrapArray(md.getDependencies) map { dd => - addExtra(dd, dependencyExtra) - } - val withVersionRangeMod: Seq[DependencyDescriptor] = - if (LMSysProp.modifyVersionRange) withExtra map { stripVersionRange } else withExtra - val unique = IvySbt.mergeDuplicateDefinitions(withVersionRangeMod) - unique foreach dmd.addDependency - - for (ed <- md.getInheritedDescriptors) - dmd.addInheritedDescriptor( - new DefaultExtendsDescriptor(md, ed.getLocation, ed.getExtendsTypes) - ) - for (conf <- md.getConfigurations) { - dmd.addConfiguration(conf) - for (art <- md.getArtifacts(conf.getName)) { - val ext = art.getExt - val newExt = if (JarPackagings(ext)) "jar" else ext - val nart = new DefaultArtifact( - mrid, - art.getPublicationDate, - art.getName, - art.getType, - newExt, - art.getUrl, - art.getQualifiedExtraAttributes - ) - dmd.addArtifact(conf.getName, nart) - } - } - dmd - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/CustomXmlParser.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/CustomXmlParser.scala deleted file mode 100644 index a72a65339..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/CustomXmlParser.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt.internal.librarymanagement - -import java.io.ByteArrayInputStream -import java.net.URL - -import org.apache.ivy.core.module.descriptor.{ - DefaultDependencyDescriptor, - DefaultModuleDescriptor -} -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser -import org.apache.ivy.plugins.repository.Resource -import org.apache.ivy.plugins.repository.url.URLResource - -/** Subclasses the default Ivy file parser in order to provide access to protected methods. */ -private[sbt] object CustomXmlParser extends XmlModuleDescriptorParser { - import XmlModuleDescriptorParser.Parser - class CustomParser(settings: IvySettings, defaultConfig: Option[String]) - extends Parser(CustomXmlParser, settings) { - def setSource(url: URL) = { - super.setResource(new URLResource(url)) - super.setInput(url) - } - def setInput(bytes: Array[Byte]): Unit = setInput(new ByteArrayInputStream(bytes)) - - /** Overridden because the super implementation overwrites the module descriptor. */ - override def setResource(res: Resource): Unit = () - override def setMd(md: DefaultModuleDescriptor) = { - super.setMd(md) - if (defaultConfig.isDefined) setDefaultConfMapping("*->default(compile)") - } - override def parseDepsConfs(confs: String, dd: DefaultDependencyDescriptor) = - super.parseDepsConfs(confs, dd) - override def getDefaultConf = defaultConfig.getOrElse(super.getDefaultConf) - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ErrorLoggingURLHandler.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ErrorLoggingURLHandler.scala deleted file mode 100644 index 8da55968a..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ErrorLoggingURLHandler.scala +++ /dev/null @@ -1,75 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt.internal.librarymanagement - -import java.io.{ File, FileInputStream, IOException } -import java.net.{ HttpURLConnection, URL } -import org.apache.ivy.util.url.{ BasicURLHandler, IvyAuthenticator } -import org.apache.ivy.util.{ CopyProgressListener, FileUtil, Message } -import org.apache.ivy.Ivy -import scala.io.Source -import scala.util.Using - -private[librarymanagement] class ErrorLoggingURLHandler extends BasicURLHandler { - private val ErrorBodyTruncateLen = 1024 - - override def upload( - source: File, - dest: URL, - l: CopyProgressListener - ): Unit = { - if (dest.getProtocol != "http" && dest.getProtocol != "https") { - throw new UnsupportedOperationException( - "URL repository only support HTTP PUT at the moment" - ) - } - - IvyAuthenticator.install() - - var conn: HttpURLConnection = null - try { - val normalizedDest = normalizeToURL(dest) - conn = normalizedDest.openConnection().asInstanceOf[HttpURLConnection] - conn.setDoOutput(true) - conn.setRequestMethod("PUT") - conn.setRequestProperty("User-Agent", "Apache Ivy/" + Ivy.getIvyVersion) - conn.setRequestProperty( - "Accept", - "application/octet-stream, application/json, application/xml, */*" - ) - conn.setRequestProperty("Content-type", "application/octet-stream") - conn.setRequestProperty("Content-length", source.length().toString) - conn.setInstanceFollowRedirects(true) - - val in = new FileInputStream(source) - try { - val os = conn.getOutputStream - FileUtil.copy(in, os, l) - } finally { - try in.close() - catch { case _: IOException => } - } - - val responseCode = conn.getResponseCode - val responseMessage = conn.getResponseMessage - - val errorBody = Option(conn.getErrorStream).map { stream => - Using.resource(stream) { s => - val body = Source.fromInputStream(s, "UTF-8").mkString - if (body.length > ErrorBodyTruncateLen) - body.take(ErrorBodyTruncateLen) + "..." - else body - } - } - - errorBody.filter(_.nonEmpty).foreach { body => - Message.error(s"Server response body: $body") - } - - validatePutStatusCode(dest, responseCode, responseMessage) - } finally { - if (conn != null) conn.disconnect() - } - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/FakeResolver.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/FakeResolver.scala deleted file mode 100644 index 68e3fc159..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/FakeResolver.scala +++ /dev/null @@ -1,217 +0,0 @@ -package sbt - -import java.io.File -import java.net.URI - -import org.apache.ivy.core.cache.ArtifactOrigin -import org.apache.ivy.core.cache.{ DefaultRepositoryCacheManager, RepositoryCacheManager } -import org.apache.ivy.core.module.descriptor.{ - Artifact as IvyArtifact, - DefaultArtifact, - DefaultDependencyArtifactDescriptor, - DefaultModuleDescriptor, - DependencyArtifactDescriptor, - DependencyDescriptor -} -import org.apache.ivy.core.module.id.ModuleRevisionId -import org.apache.ivy.core.report.ArtifactDownloadReport -import org.apache.ivy.core.report.{ DownloadReport, DownloadStatus } -import org.apache.ivy.core.report.MetadataArtifactDownloadReport -import org.apache.ivy.core.resolve.{ DownloadOptions, ResolveData, ResolvedModuleRevision } -import org.apache.ivy.core.search.{ ModuleEntry, OrganisationEntry, RevisionEntry } -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.plugins.namespace.Namespace -import org.apache.ivy.plugins.resolver.{ DependencyResolver, ResolverSettings } -import org.apache.ivy.plugins.resolver.util.ResolvedResource - -import FakeResolver.* - -/** - * A fake `DependencyResolver` that statically serves predefined artifacts. - */ -private[sbt] class FakeResolver(private var name: String, cacheDir: File, modules: ModulesMap) - extends DependencyResolver { - - private object Artifact { - def unapply(art: IvyArtifact): Some[(String, String, String)] = { - val revisionID = art.getModuleRevisionId() - val organisation = revisionID.getOrganisation - val name = revisionID.getName - val revision = revisionID.getRevision - Some((organisation, name, revision)) - } - - def unapply(dd: DependencyDescriptor): Some[(String, String, String)] = { - val module = dd.getDependencyId() - val organisation = module.getOrganisation - val name = module.getName - val mrid = dd.getDependencyRevisionId() - val revision = mrid.getRevision() - Some((organisation, name, revision)) - } - } - - override def publish(artifact: IvyArtifact, src: File, overwrite: Boolean): Unit = - throw new UnsupportedOperationException("This resolver doesn't support publishing.") - - override def abortPublishTransaction(): Unit = - throw new UnsupportedOperationException("This resolver doesn't support publishing.") - - override def beginPublishTransaction(module: ModuleRevisionId, overwrite: Boolean): Unit = - throw new UnsupportedOperationException("This resolver doesn't support publishing.") - - override def commitPublishTransaction(): Unit = - throw new UnsupportedOperationException("This resolver doesn't support publishing.") - - override def download( - artifact: ArtifactOrigin, - options: DownloadOptions - ): ArtifactDownloadReport = { - - val report = new ArtifactDownloadReport(artifact.getArtifact) - val path = new URI(artifact.getLocation).getPath - val localFile = new File(path) - - if (path.nonEmpty && localFile.exists) { - report.setLocalFile(localFile) - report.setDownloadStatus(DownloadStatus.SUCCESSFUL) - report.setSize(localFile.length) - } else { - report.setDownloadStatus(DownloadStatus.FAILED) - } - - report - } - - override def download(artifacts: Array[IvyArtifact], options: DownloadOptions): DownloadReport = { - val report = new DownloadReport - - artifacts foreach { art => - Option(locate(art)) foreach (o => report.addArtifactReport(download(o, options))) - } - - report - } - - override def dumpSettings(): Unit = () - - override def exists(artifact: IvyArtifact): Boolean = { - val Artifact(organisation, name, revision) = artifact - modules.get((organisation, name, revision)).isDefined - } - - // This is a fake resolver and we don't have Ivy files. Ivy's spec says we can return `null` if - // we can't find the module descriptor. - override def findIvyFileRef(dd: DependencyDescriptor, data: ResolveData): ResolvedResource = null - - override def getDependency( - dd: DependencyDescriptor, - data: ResolveData - ): ResolvedModuleRevision = { - - val Artifact(organisation, name, revision) = dd - val mrid = dd.getDependencyRevisionId() - - val artifact = modules get ((organisation, name, revision)) map { arts => - val artifacts: Array[DependencyArtifactDescriptor] = arts.toArray.map(_.artifactOf(dd)) - val moduleDescriptor = DefaultModuleDescriptor.newDefaultInstance(mrid, artifacts) - val defaultArtifact = arts.headOption match { - case Some(FakeArtifact(name, tpe, ext, _)) => - new DefaultArtifact(mrid, new java.util.Date, name, tpe, ext) - case None => null - } - val metadataReport = new MetadataArtifactDownloadReport(defaultArtifact) - metadataReport.setDownloadStatus(DownloadStatus.SUCCESSFUL) - - new ResolvedModuleRevision(this, this, moduleDescriptor, metadataReport) - } - - artifact.orNull - - } - - override def getName(): String = name - - override val getNamespace: Namespace = { - val ns = new Namespace() - ns.setName(name) - ns - } - - override val getRepositoryCacheManager: RepositoryCacheManager = { - val cacheName = name + "-cache" - val ivySettings = new IvySettings() - val baseDir = cacheDir - new DefaultRepositoryCacheManager(cacheName, ivySettings, baseDir) - } - - override def listModules(organisation: OrganisationEntry): Array[ModuleEntry] = - modules.keys.collect { - case (o, m, _) if o == organisation.getOrganisation => - val organisationEntry = new OrganisationEntry(this, o) - new ModuleEntry(organisationEntry, m) - }.toArray - - override def listOrganisations(): Array[OrganisationEntry] = - modules.keys.map { case (o, _, _) => new OrganisationEntry(this, o) }.toArray - - override def listRevisions(module: ModuleEntry): Array[RevisionEntry] = - modules.keys.collect { - case (o, m, v) if o == module.getOrganisation && m == module.getModule => - new RevisionEntry(module, v) - }.toArray - - override def listTokenValues( - tokens: Array[String], - criteria: java.util.Map[?, ?] - ): Array[java.util.Map[?, ?]] = - Array.empty - - override def listTokenValues( - token: String, - otherTokenValues: java.util.Map[?, ?] - ): Array[String] = - Array.empty - - override def locate(art: IvyArtifact): ArtifactOrigin = { - val Artifact(moduleOrganisation, moduleName, moduleRevision) = art - val artifact = - for { - artifacts <- modules get ((moduleOrganisation, moduleName, moduleRevision)) - artifact <- artifacts find (a => - a.name == art.getName && a.tpe == art.getType && a.ext == art.getExt - ) - } yield new ArtifactOrigin(art, /* isLocal = */ true, artifact.file.toURI.toURL.toString) - - artifact.orNull - - } - - override def reportFailure(art: IvyArtifact): Unit = () - override def reportFailure(): Unit = () - - override def setName(name: String): Unit = { - this.name = name - getNamespace.setName(name) - } - - override def setSettings(settings: ResolverSettings): Unit = () - -} - -private[sbt] object FakeResolver { - - type ModulesMap = Map[(String, String, String), Seq[FakeArtifact]] - - final case class FakeArtifact(name: String, tpe: String, ext: String, file: File) { - def artifactOf(dd: DependencyDescriptor): DependencyArtifactDescriptor = - new DefaultDependencyArtifactDescriptor( - dd, - name, - tpe, - ext, - file.toURI.toURL, - new java.util.HashMap - ) - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/Ivy.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/Ivy.scala deleted file mode 100644 index cb7e2557a..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/Ivy.scala +++ /dev/null @@ -1,1136 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt.internal.librarymanagement - -import java.io.File -import java.net.URI -import java.util.concurrent.Callable - -import org.apache.ivy.Ivy -import org.apache.ivy.core.IvyPatternHelper -import org.apache.ivy.core.cache.{ CacheMetadataOptions, DefaultRepositoryCacheManager } -import org.apache.ivy.core.event.EventManager -import org.apache.ivy.core.module.descriptor.{ - DefaultArtifact, - DefaultDependencyArtifactDescriptor, - MDArtifact, - Artifact as IArtifact -} -import org.apache.ivy.core.module.descriptor.{ - DefaultDependencyDescriptor, - DefaultModuleDescriptor, - DependencyDescriptor, - License, - ModuleDescriptor -} -import org.apache.ivy.core.module.descriptor.OverrideDependencyDescriptorMediator -import org.apache.ivy.core.module.id.{ ModuleId, ModuleRevisionId } -import org.apache.ivy.core.resolve.* -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.core.sort.SortEngine -import org.apache.ivy.plugins.matcher.PatternMatcher -import org.apache.ivy.plugins.resolver.DependencyResolver -import org.apache.ivy.util.{ Message, MessageLogger } -import org.apache.ivy.util.extendable.ExtendableItem -import org.apache.ivy.util.url.* -import scala.xml.NodeSeq -import scala.collection.mutable -import scala.collection.immutable.ArraySeq -import scala.util.{ Success, Failure } -import sbt.util.* -import sbt.internal.librarymanagement.ivy.* -import sbt.librarymanagement.{ ModuleDescriptorConfiguration as InlineConfiguration, * } -import sbt.librarymanagement.syntax.* - -import IvyInternalDefaults.* -import Resolver.PluginPattern -import ivyint.{ - CachedResolutionResolveCache, - CachedResolutionResolveEngine, - ParallelResolveEngine, - SbtDefaultDependencyDescriptor, -} -import sjsonnew.JsonFormat -import sjsonnew.support.murmurhash.Hasher - -final class IvySbt( - val configuration: IvyConfiguration, -) { self => - /* - * ========== Configuration/Setup ============ - * This part configures the Ivy instance by first creating the logger interface to ivy, then IvySettings, and then the Ivy instance. - * These are lazy so that they are loaded within the right context. This is important so that no Ivy XML configuration needs to be loaded, - * saving some time. This is necessary because Ivy has global state (IvyContext, Message, DocumentBuilder, ...). - */ - - private def withDefaultLogger[T](logger: MessageLogger)(f: => T): T = { - def action() = - IvySbt.synchronized { - val originalLogger = Message.getDefaultLogger - Message.setDefaultLogger(logger) - try { - f - } finally { - Message.setDefaultLogger(originalLogger) - } - } - // Ivy is neither thread-safe nor can the cache be used concurrently. - // If provided a GlobalLock, we can use that to ensure safe access to the cache. - // Otherwise, we can at least synchronize within the JVM. - // For thread-safety in particular, Ivy uses a static DocumentBuilder, which is not thread-safe. - configuration.lock match { - case Some(lock) => lock(ivyLockFile, new Callable[T] { def call = action() }) - case None => action() - } - } - - private lazy val basicUrlHandler: URLHandler = new ErrorLoggingURLHandler - - private lazy val settings: IvySettings = { - val dispatcher: URLHandlerDispatcher = URLHandlerRegistry.getDefault match { - // If the default is already a URLHandlerDispatcher then just use that - case disp: URLHandlerDispatcher => disp - - // Otherwise wrap the existing URLHandler in a URLHandlerDispatcher - // while retaining the existing URLHandler as the default. - case default => - val disp: URLHandlerDispatcher = new URLHandlerDispatcher() - disp.setDefault(default) - URLHandlerRegistry.setDefault(disp) - disp - } - - // Ignore configuration.updateOptions.gigahorse due to sbt/sbt#6912 - val urlHandler: URLHandler = basicUrlHandler - - // Only set the urlHandler for the http/https protocols so we do not conflict with any other plugins - // that might register other protocol handlers. - // For example https://github.com/frugalmechanic/fm-sbt-s3-resolver registers "s3" - dispatcher.setDownloader("http", urlHandler) - dispatcher.setDownloader("https", urlHandler) - - val is = new IvySettings - is.setCircularDependencyStrategy( - configuration.updateOptions.circularDependencyLevel.ivyStrategy - ) - CustomPomParser.registerDefault - val log = getLog(configuration.log) - - configuration match { - case e: ExternalIvyConfiguration => - val baseDirectory = getBaseDirectory(e.baseDirectory) - is.setBaseDir(baseDirectory) - IvySbt.addResolvers(e.extraResolvers, is, log) - IvySbt.loadURI(is, e.uri.getOrElse(sys.error("uri must be specified!"))) - case i: InlineIvyConfiguration => - val paths = getIvyPaths(i.paths) - is.setBaseDir(new File(paths.baseDirectory)) - is.setVariable("ivy.checksums", i.checksums mkString ",") - is.setVariable(ConvertResolver.ManagedChecksums, i.managedChecksums.toString) - paths.ivyHome.foreach { (h) => is.setDefaultIvyUserDir(new File(h)) } - IvySbt.configureCache(is, i.resolutionCacheDir) - IvySbt.setResolvers(is, i.resolvers, i.otherResolvers, configuration.updateOptions, log) - IvySbt.setModuleConfigurations(is, i.moduleConfigurations, log) - } - is - } - - /** - * Defines a parallel [[CachedResolutionResolveEngine]]. - * - * This is defined here because it needs access to [[mkIvy]]. - */ - private class ParallelCachedResolutionResolveEngine( - settings: IvySettings, - eventManager: EventManager, - sortEngine: SortEngine - ) extends ParallelResolveEngine(settings, eventManager, sortEngine) - with CachedResolutionResolveEngine { - def makeInstance: Ivy = mkIvy - val cachedResolutionResolveCache: CachedResolutionResolveCache = - IvySbt.cachedResolutionResolveCache - val projectResolver: Option[ProjectResolver] = { - val res = settings.getResolver(ProjectResolver.InterProject) - Option(res.asInstanceOf[ProjectResolver]) - } - } - - /** - * Provides a default ivy implementation that decides which resolution - * engine to use depending on the passed ivy configuration options. - */ - private class IvyImplementation extends Ivy { - private val loggerEngine = new SbtMessageLoggerEngine - override def getLoggerEngine: SbtMessageLoggerEngine = loggerEngine - override def bind(): Unit = { - val settings = getSettings - val eventManager = new EventManager() - val sortEngine = new SortEngine(settings) - - // We inject the deps we need before we can hook our resolve engine. - setSortEngine(sortEngine) - setEventManager(eventManager) - - val resolveEngine = { - // Decide to use cached resolution if user enabled it - if (configuration.updateOptions.cachedResolution) - new ParallelCachedResolutionResolveEngine(settings, eventManager, sortEngine) - else new ParallelResolveEngine(settings, eventManager, sortEngine) - } - - setResolveEngine(resolveEngine) - super.bind() - } - } - - private[sbt] def mkIvy: Ivy = { - val ivy = new IvyImplementation() - ivy.setSettings(settings) - ivy.bind() - val logger = new IvyLoggerInterface(getLog(configuration.log)) - ivy.getLoggerEngine.pushLogger(logger) - ivy - } - - private lazy val ivy: Ivy = mkIvy - // Must be the same file as is used in Update in the launcher - private lazy val ivyLockFile = new File(settings.getDefaultIvyUserDir, ".sbt.ivy.lock") - - // ========== End Configuration/Setup ============ - - /** Uses the configured Ivy instance within a safe context. */ - def withIvy[T](log: Logger)(f: Ivy => T): T = - withIvy(new IvyLoggerInterface(log))(f) - - def withIvy[T](log: MessageLogger)(f: Ivy => T): T = - withDefaultLogger(log) { - // See #429 - We always insert a helper authenticator here which lets us get more useful authentication errors. - ivyint.ErrorMessageAuthenticator.install() - ivy.pushContext() - ivy.getLoggerEngine.pushLogger(log) - try { - f(ivy) - } finally { - ivy.getLoggerEngine.popLogger() - ivy.popContext() - } - } - - /** Cleans cached resolution cache. */ - private[sbt] def cleanCachedResolutionCache(): Unit = { - if (!configuration.updateOptions.cachedResolution) () - else IvySbt.cachedResolutionResolveCache.clean() - } - - /** - * In the new POM format of sbt plugins, we append the sbt-cross version _2.12_1.0 to - * the module artifactId, and the artifactIds of its dependencies that are sbt plugins. - * - * The goal is to produce a valid Maven POM, a POM that Maven can resolve: - * Maven will try and succeed to resolve the POM of pattern: - * /_2.12_1.0//_2.12_1.0-.pom - */ - final class Module(rawModuleSettings: ModuleSettings, appendSbtCrossVersion: Boolean) - extends sbt.librarymanagement.ModuleDescriptor { self => - - def this(rawModuleSettings: ModuleSettings) = - this(rawModuleSettings, appendSbtCrossVersion = false) - - val moduleSettings: ModuleSettings = - rawModuleSettings match { - case ic: InlineConfiguration => - val icWithCross: ModuleSettings = IvySbt.substituteCross(ic) - if appendSbtCrossVersion then IvySbt.appendSbtCrossVersion(icWithCross) - else icWithCross - case m => m - } - - def directDependencies: Vector[ModuleID] = - moduleSettings match { - case x: InlineConfiguration => x.dependencies - case _ => Vector() - } - - def configurations = - moduleSettings match { - case ic: InlineConfiguration => ic.configurations - case _: PomConfiguration => Configurations.default ++ Configurations.defaultInternal - case _: IvyFileConfiguration => - Configurations.default ++ Configurations.defaultInternal - } - - def scalaModuleInfo: Option[ScalaModuleInfo] = moduleSettings.scalaModuleInfo - - def owner = IvySbt.this - def withModule[T](log: Logger)(f: (Ivy, DefaultModuleDescriptor, String) => T): T = - withIvy[T](log) { ivy => - f(ivy, moduleDescriptor0, defaultConfig0) - } - - def moduleDescriptor(log: Logger): DefaultModuleDescriptor = withModule(log)((_, md, _) => md) - def dependencyMapping(log: Logger): (ModuleRevisionId, ModuleDescriptor) = { - val md = moduleDescriptor(log) - (md.getModuleRevisionId, md) - } - def defaultConfig(log: Logger): String = withModule(log)((_, _, dc) => dc) - // these should only be referenced by withModule because lazy vals synchronize on this object - // withIvy explicitly locks the IvySbt object, so they have to be done in the right order to avoid deadlock - private lazy val (moduleDescriptor0: DefaultModuleDescriptor, defaultConfig0: String) = { - val (baseModule, baseConfiguration) = - moduleSettings match { - case ic: InlineConfiguration => configureInline(ic, getLog(configuration.log)) - case pc: PomConfiguration => configurePom(pc) - case ifc: IvyFileConfiguration => configureIvyFile(ifc) - } - - val configs = configurations - moduleSettings.scalaModuleInfo foreach { is => - val svc = configs filter Configurations.underScalaVersion map { _.name } - IvyScalaUtil.checkModule(baseModule, svc, getLog(configuration.log))(is) - } - IvySbt.addExtraNamespace(baseModule) - (baseModule, baseConfiguration) - } - private def configureInline(ic: InlineConfiguration, log: Logger) = { - import ic.* - val moduleID = newConfiguredModuleID(module, moduleInfo, ic.configurations) - IvySbt.setConflictManager(moduleID, conflictManager, ivy.getSettings) - val defaultConf = defaultConfiguration getOrElse Configuration.of( - "Default", - ModuleDescriptor.DEFAULT_CONFIGURATION - ) - log.debug( - s"Using inline dependencies specified in Scala${(if (ivyXML.isEmpty) "" else " and XML")}." - ) - - val parser = IvySbt.parseIvyXML( - ivy.getSettings, - IvySbt.wrapped(module, ivyXML), - moduleID, - defaultConf.name, - ic.validate - ) - IvySbt.addMainArtifact(moduleID) - IvySbt.addOverrides(moduleID, overrides, ivy.getSettings.getMatcher(PatternMatcher.EXACT)) - IvySbt.addExcludes(moduleID, excludes, ic.scalaModuleInfo) - val transformedDeps = IvySbt.overrideDirect(dependencies, overrides) - IvySbt.addDependencies(moduleID, transformedDeps, parser) - (moduleID, parser.getDefaultConf) - } - private def newConfiguredModuleID( - module: ModuleID, - moduleInfo: ModuleInfo, - configurations: Iterable[Configuration] - ) = { - val mod = new DefaultModuleDescriptor(IvySbt.toID(module), "release", null, false) - mod.setLastModified(System.currentTimeMillis) - mod.setDescription(moduleInfo.description) - moduleInfo.homepage foreach { h => - mod.setHomePage(h.toString) - } - moduleInfo.licenses foreach { l => - mod.addLicense(new License(l.spdxId, l.uri.toString)) - } - IvySbt.addConfigurations(mod, configurations) - IvySbt.addArtifacts(mod, module.explicitArtifacts) - mod - } - - /** Parses the Maven pom 'pomFile' from the given `PomConfiguration`. */ - private def configurePom(pc: PomConfiguration) = { - val md = CustomPomParser.default.parseDescriptor(settings, toURL(pc.file), pc.validate) - val dmd = IvySbt.toDefaultModuleDescriptor(md) - IvySbt.addConfigurations(dmd, Configurations.defaultInternal) - val defaultConf = Configurations.DefaultMavenConfiguration.name - for (is <- pc.scalaModuleInfo) if (pc.autoScalaTools) { - val confParser = new CustomXmlParser.CustomParser(settings, Some(defaultConf)) - confParser.setMd(dmd) - addScalaToolDependencies(dmd, confParser, is) - } - (dmd, defaultConf) - } - - /** Parses the Ivy file 'ivyFile' from the given `IvyFileConfiguration`. */ - private def configureIvyFile(ifc: IvyFileConfiguration) = { - val parser = new CustomXmlParser.CustomParser(settings, None) - parser.setValidate(ifc.validate) - parser.setSource(toURL(ifc.file)) - parser.parse() - val dmd = IvySbt.toDefaultModuleDescriptor(parser.getModuleDescriptor()) - for (is <- ifc.scalaModuleInfo) - if (ifc.autoScalaTools) - addScalaToolDependencies(dmd, parser, is) - (dmd, parser.getDefaultConf) - } - private def addScalaToolDependencies( - dmd: DefaultModuleDescriptor, - parser: CustomXmlParser.CustomParser, - is: ScalaModuleInfo - ): Unit = { - IvySbt.addConfigurations(dmd, Configurations.ScalaTool :: Nil) - IvySbt.addDependencies( - dmd, - ScalaArtifacts.toolDependencies(is.scalaOrganization, is.scalaFullVersion), - parser - ) - } - private def toURL(file: File) = file.toURI.toURL - - // Todo: We just need writing side of this codec. We can clean up the reads. - private[sbt] object AltLibraryManagementCodec - extends sjsonnew.BasicJsonProtocol - with sbt.librarymanagement.LibraryManagementCodec - with sbt.internal.librarymanagement.formats.GlobalLockFormat - with sbt.internal.librarymanagement.formats.LoggerFormat - with sbt.internal.librarymanagement.ivy.formats.UpdateOptionsFormat - with sbt.librarymanagement.IvyPathsFormats - with sbt.librarymanagement.ResolverFormats - with sbt.librarymanagement.ModuleConfigurationFormats: - import sbt.io.Hash - type InlineIvyHL = ( - Option[IvyPaths], - Vector[Resolver], - Vector[Resolver], - Vector[ModuleConfiguration], - Vector[String], - Boolean - ) - def inlineIvyToHL(i: InlineIvyConfiguration): InlineIvyHL = - ( - i.paths, - i.resolvers, - i.otherResolvers, - i.moduleConfigurations, - i.checksums, - i.managedChecksums - ) - - type ExternalIvyHL = (Option[PlainFileInfo], Array[Byte]) - def externalIvyToHL(e: ExternalIvyConfiguration): ExternalIvyHL = - ( - e.baseDirectory.map(FileInfo.exists.apply), - e.uri.map(Hash.contentsIfLocal).getOrElse(Array.empty) - ) - - // Redefine to use a subset of properties, that are serializable - given InlineIvyConfigurationFormat: JsonFormat[InlineIvyConfiguration] = { - def hlToInlineIvy(i: InlineIvyHL): InlineIvyConfiguration = { - val ( - paths, - resolvers, - otherResolvers, - moduleConfigurations, - checksums, - managedChecksums - ) = i - InlineIvyConfiguration() - .withPaths(paths) - .withResolvers(resolvers) - .withOtherResolvers(otherResolvers) - .withModuleConfigurations(moduleConfigurations) - .withManagedChecksums(managedChecksums) - .withChecksums(checksums) - } - projectFormat[InlineIvyConfiguration, InlineIvyHL](inlineIvyToHL, hlToInlineIvy) - } - - // Redefine to use a subset of properties, that are serializable - given ExternalIvyConfigurationFormat: JsonFormat[ExternalIvyConfiguration] = { - def hlToExternalIvy(e: ExternalIvyHL): ExternalIvyConfiguration = { - val (baseDirectory, _) = e - ExternalIvyConfiguration( - None, - Some(NullLogger), - UpdateOptions(), - baseDirectory.map(_.file), - None /* the original uri is destroyed.. */, - Vector.empty - ) - } - projectFormat[ExternalIvyConfiguration, ExternalIvyHL](externalIvyToHL, hlToExternalIvy) - } - - // Redefine to switch to unionFormat - given IvyConfigurationFormat: JsonFormat[IvyConfiguration] = - unionFormat2[IvyConfiguration, InlineIvyConfiguration, ExternalIvyConfiguration] - - object NullLogger extends sbt.internal.util.BasicLogger { - override def control(event: sbt.util.ControlEvent.Value, message: => String): Unit = () - override def log(level: Level.Value, message: => String): Unit = () - override def logAll(events: Seq[sbt.util.LogEvent]): Unit = () - override def success(message: => String): Unit = () - override def trace(t: => Throwable): Unit = () - } - end AltLibraryManagementCodec - - def extraInputHash: Long = - import AltLibraryManagementCodec.given - Hasher.hash(owner.configuration) match - case Success(keyHash) => keyHash.toLong - case Failure(_) => 0L - } -} - -private[sbt] object IvySbt { - val DefaultIvyConfigFilename = "ivysettings.xml" - val DefaultIvyFilename = "ivy.xml" - val DefaultMavenFilename = "pom.xml" - val DefaultChecksums = IvyDefaults.defaultChecksums - private[sbt] def cachedResolutionResolveCache: CachedResolutionResolveCache = - new CachedResolutionResolveCache - - def defaultIvyFile(project: File) = new File(project, DefaultIvyFilename) - def defaultIvyConfiguration(project: File) = new File(project, DefaultIvyConfigFilename) - def defaultPOM(project: File) = new File(project, DefaultMavenFilename) - - def loadURI(is: IvySettings, uri: URI): Unit = { - if (uri.getScheme == "file") - is.load(new File(uri)) // IVY-1114 - else - is.load(uri.toURL) - } - - /** - * Sets the resolvers for 'settings' to 'resolvers'. This is done by creating a new chain and making it the default. - * 'other' is for resolvers that should be in a different chain. These are typically used for publishing or other actions. - */ - private def setResolvers( - settings: IvySettings, - resolvers: Seq[Resolver], - other: Seq[Resolver], - updateOptions: UpdateOptions, - log: Logger - ): Unit = { - def makeChain(label: String, name: String, rs: Seq[Resolver]) = { - log.debug(label + " repositories:") - val chain = resolverChain(name, rs, settings, updateOptions, log) - settings.addResolver(chain) - chain - } - makeChain("Other", "sbt-other", other) - val mainChain = makeChain("Default", "sbt-chain", resolvers) - settings.setDefaultResolver(mainChain.getName) - } - - // TODO: Expose the changing semantics to the caller so that users can specify a regex - private[sbt] def isChanging(dd: DependencyDescriptor): Boolean = - dd.isChanging || isChanging(dd.getDependencyRevisionId) - private[sbt] def isChanging(module: ModuleID): Boolean = - module.revision.endsWith("-SNAPSHOT") - private[sbt] def isChanging(mrid: ModuleRevisionId): Boolean = - mrid.getRevision.endsWith("-SNAPSHOT") - - def resolverChain( - name: String, - resolvers: Seq[Resolver], - settings: IvySettings, - log: Logger - ): DependencyResolver = resolverChain(name, resolvers, settings, UpdateOptions(), log) - - def resolverChain( - name: String, - resolvers: Seq[Resolver], - settings: IvySettings, - updateOptions: UpdateOptions, - log: Logger - ): DependencyResolver = { - val ivyResolvers = resolvers.map(r => ConvertResolver(r, settings, updateOptions, log)) - val (projectResolvers, rest) = - ivyResolvers.partition(_.getName == ProjectResolver.InterProject) - if (projectResolvers.isEmpty) ivyint.SbtChainResolver(name, rest, settings, updateOptions, log) - else { - // Force that we always look at the project resolver first by wrapping the chain resolver - val delegatedName = s"$name-delegate" - val delegate = ivyint.SbtChainResolver(delegatedName, rest, settings, updateOptions, log) - val initialResolvers = projectResolvers :+ delegate - val freshOptions = UpdateOptions() - .withLatestSnapshots(false) - .withModuleResolvers(updateOptions.moduleResolvers) - ivyint.SbtChainResolver(name, initialResolvers, settings, freshOptions, log) - } - } - - def addResolvers(resolvers: Seq[Resolver], settings: IvySettings, log: Logger): Unit = { - for (r <- resolvers) { - log.debug("\t" + r) - settings.addResolver(ConvertResolver(r, settings, UpdateOptions(), log)) - } - } - - /** - * A hack to detect if the given artifact is an automatically generated request for a classifier, - * as opposed to a user-initiated declaration. It relies on Ivy prefixing classifier with m:, while sbt uses e:. - * Clearly, it would be better to have an explicit option in Ivy to control this. - */ - def hasImplicitClassifier(artifact: IArtifact): Boolean = { - import scala.jdk.CollectionConverters.* - artifact.getQualifiedExtraAttributes.asScala.keys - .exists(_.asInstanceOf[String].startsWith("m:")) - } - private def setModuleConfigurations( - settings: IvySettings, - moduleConfigurations: Seq[ModuleConfiguration], - log: Logger - ): Unit = { - val existing = settings.getResolverNames - for (moduleConf <- moduleConfigurations) { - import moduleConf.* - import IvyPatternHelper.* - import PatternMatcher.* - if (!existing.contains(resolver.name)) - settings.addResolver(ConvertResolver(resolver, settings, UpdateOptions(), log)) - val attributes = javaMap( - Map(MODULE_KEY -> name, ORGANISATION_KEY -> organization, REVISION_KEY -> revision) - ) - settings.addModuleConfiguration( - attributes, - settings.getMatcher(EXACT_OR_REGEXP), - resolver.name, - null, - null, - null - ) - } - } - - private def configureCache(settings: IvySettings, resCacheDir: Option[File]): Unit = { - configureResolutionCache(settings, resCacheDir) - configureRepositoryCache(settings) - } - private def configureResolutionCache(settings: IvySettings, resCacheDir: Option[File]) = { - val base = resCacheDir getOrElse settings.getDefaultResolutionCacheBasedir - settings.setResolutionCacheManager(new ResolutionCache(base, settings)) - } - // set the artifact resolver to be the main resolver. - // this is because sometimes the artifact resolver saved in the cache is not correct - // the common case is for resolved.getArtifactResolver to be inter-project from a different project's publish-local - // if there are problems with this, a less aggressive fix might be to only reset the artifact resolver when it is a ProjectResolver - // a possible problem is that fetching artifacts is slower, due to the full chain being the artifact resolver instead of the specific resolver - // This also fixes #760, which occurs when metadata exists in a repository, but the artifact doesn't. - private[sbt] def resetArtifactResolver( - resolved: ResolvedModuleRevision - ): ResolvedModuleRevision = - if (resolved eq null) null - else { - val desc = resolved.getDescriptor - val updatedDescriptor = CustomPomParser.defaultTransform(desc.getParser, desc) - new ResolvedModuleRevision( - resolved.getResolver, - resolved.getResolver, - updatedDescriptor, - resolved.getReport, - resolved.isForce - ) - } - - private def configureRepositoryCache(settings: IvySettings): Unit = { - val cacheDir = settings.getDefaultRepositoryCacheBasedir() - val manager = new DefaultRepositoryCacheManager("default-cache", settings, cacheDir) { - override def findModuleInCache( - dd: DependencyDescriptor, - revId: ModuleRevisionId, - options: CacheMetadataOptions, - r: String - ) = { - // ignore and reset the resolver- not ideal, but avoids thrashing. - val resolved = resetArtifactResolver(super.findModuleInCache(dd, revId, options, null)) - // invalidate the cache if the artifact was removed from the local repository - if (resolved == null) null - else if (isProjectResolver(resolved.getResolver)) { - resolved.getReport.getLocalFile.delete() - null - } else { - val origin = resolved.getReport.getArtifactOrigin - if (!origin.isLocal) resolved - else { - val file = new File(origin.getLocation) - if (file == null || file.exists) resolved - else { - resolved.getReport.getLocalFile.delete() - null - } - } - } - } - private def isProjectResolver(r: DependencyResolver): Boolean = r match { - case _: ProjectResolver => true - case _ => false - } - // ignore the original resolver wherever possible to avoid issues like #704 - override def saveResolvers( - descriptor: ModuleDescriptor, - metadataResolverName: String, - artifactResolverName: String - ): Unit = () - } - manager.setArtifactPattern(PluginPattern + manager.getArtifactPattern) - manager.setDataFilePattern(PluginPattern + manager.getDataFilePattern) - manager.setIvyPattern(PluginPattern + manager.getIvyPattern) - manager.setUseOrigin(true) - manager.setChangingMatcher(PatternMatcher.REGEXP) - manager.setChangingPattern(".*-SNAPSHOT") - settings.addRepositoryCacheManager(manager) - settings.setDefaultRepositoryCacheManager(manager) - } - def toIvyConfiguration(configuration: Configuration) = { - import org.apache.ivy.core.module.descriptor.Configuration as IvyConfig - import IvyConfig.Visibility.* - import configuration.* - new IvyConfig( - name, - if (isPublic) PUBLIC else PRIVATE, - description, - extendsConfigs.map(_.name).toArray, - transitive, - null - ) - } - def addExtraNamespace(dmd: DefaultModuleDescriptor): Unit = - dmd.addExtraAttributeNamespace("e", "http://ant.apache.org/ivy/extra") - - /** Adds the ivy.xml main artifact. */ - private def addMainArtifact(moduleID: DefaultModuleDescriptor): Unit = { - val artifact = DefaultArtifact.newIvyArtifact( - moduleID.getResolvedModuleRevisionId, - moduleID.getPublicationDate - ) - moduleID.setModuleArtifact(artifact) - moduleID.check() - } - private def setConflictManager( - moduleID: DefaultModuleDescriptor, - conflict: ConflictManager, - is: IvySettings - ): Unit = { - val mid = ModuleId.newInstance(conflict.organization, conflict.module) - val matcher = is.getMatcher(PatternMatcher.EXACT_OR_REGEXP) - val manager = is.getConflictManager(conflict.name) - moduleID.addConflictManager(mid, matcher, manager) - } - - /** Converts the given sbt module id into an Ivy ModuleRevisionId. */ - def toID(m: ModuleID) = { - import m.* - ModuleRevisionId.newInstance( - organization, - name, - branchName.orNull, - revision, - javaMap(extraAttributes) - ) - } - - private def substituteCross(m: ModuleSettings): ModuleSettings = - m.scalaModuleInfo match { - case None => m - case Some(is) => substituteCross(m, is.scalaFullVersion, is.scalaBinaryVersion, is.platform) - } - - private def substituteCross( - m: ModuleSettings, - scalaFullVersion: String, - scalaBinaryVersion: String, - platform: Option[String] - ): ModuleSettings = { - m match - case ic: InlineConfiguration => - val applyPlatform: ModuleID => ModuleID = substitutePlatform(platform) - val applyCross = CrossVersion(scalaFullVersion, scalaBinaryVersion) - val transform: ModuleID => ModuleID = (m: ModuleID) => applyCross(applyPlatform(m)) - def propagateCrossVersion(moduleID: ModuleID): ModuleID = { - val crossExclusions: Vector[ExclusionRule] = - moduleID.exclusions.map(CrossVersion.substituteCross(_, ic.scalaModuleInfo)) - transform(moduleID) - .withExclusions(crossExclusions) - } - ic.withModule(transform(ic.module)) - .withDependencies(ic.dependencies.map(propagateCrossVersion)) - .withOverrides(ic.overrides map transform) - case m => m - } - - private def substitutePlatform(platform: Option[String]): ModuleID => ModuleID = { - def addSuffix(m: ModuleID, platformName: String): ModuleID = - platformName match - case "" | Platform.jvm => m - case _ => m.withName(s"${m.name}_$platformName") - (m: ModuleID) => - m.crossVersion match - case _: Disabled => m - case _ => - (platform, m.platformOpt) match - case (Some(p), None) => addSuffix(m, p) - case (_, Some(p)) => addSuffix(m, p) - case _ => m - } - - private def appendSbtCrossVersion(m: ModuleSettings): ModuleSettings = - m match - case ic: InlineConfiguration => - ic.withModule(appendSbtCrossVersion(ic.module)) - .withDependencies(ic.dependencies.map(appendSbtCrossVersion)) - .withOverrides(ic.overrides.map(appendSbtCrossVersion)) - case m => m - - private def appendSbtCrossVersion(mid: ModuleID): ModuleID = { - val crossVersion = for { - scalaVersion <- mid.extraAttributes.get("e:scalaVersion") - sbtVersion <- mid.extraAttributes.get("e:sbtVersion") - } yield s"_${scalaVersion}_$sbtVersion" - crossVersion - .filter(!mid.name.endsWith(_)) - .map(cv => mid.withName(mid.name + cv)) - .getOrElse(mid) - } - - private def toIvyArtifact( - moduleID: ModuleDescriptor, - a: Artifact, - allConfigurations: Vector[ConfigRef] - ): MDArtifact = { - val artifact = new MDArtifact(moduleID, a.name, a.`type`, a.extension, null, extra(a, false)) - copyConfigurations( - a, - (ref: ConfigRef) => { artifact.addConfiguration(ref.name) }, - allConfigurations - ) - artifact - } - def getExtraAttributes(revID: ExtendableItem): Map[String, String] = { - import scala.jdk.CollectionConverters.* - revID.getExtraAttributes.asInstanceOf[java.util.Map[String, String]].asScala.toMap - } - private[sbt] def extra( - artifact: Artifact, - unqualify: Boolean = false - ): java.util.Map[String, String] = { - val ea = artifact.classifier match { - case Some(c) => artifact.extra("e:classifier" -> c); case None => artifact - } - javaMap(ea.extraAttributes, unqualify) - } - private[sbt] def javaMap(m: Map[String, String], unqualify: Boolean = false) = { - import scala.jdk.CollectionConverters.* - val map = if (unqualify) m map { (k, v) => (k.stripPrefix("e:"), v) } else m - if (map.isEmpty) null else map.asJava - } - - /** Creates a full ivy file for 'module' using the 'dependencies' XML as the part after the <info>...</info> section. */ - private def wrapped(module: ModuleID, dependencies: NodeSeq) = { - - { - if (hasInfo(module, dependencies)) - NodeSeq.Empty - else - addExtraAttributes(defaultInfo(module), module.extraAttributes) - } - {dependencies} - { - // this is because Ivy adds a default artifact if none are specified. - if ((dependencies \\ "publications").isEmpty) else NodeSeq.Empty - } - - } - private def defaultInfo(module: ModuleID): scala.xml.Elem = { - import module.* - val base = - branchName.fold(base) { br => - base % new scala.xml.UnprefixedAttribute("branch", br, scala.xml.Null) - } - } - private def addExtraAttributes( - elem: scala.xml.Elem, - extra: Map[String, String] - ): scala.xml.Elem = - extra.foldLeft(elem) { case (e, (key, value)) => - e % new scala.xml.UnprefixedAttribute(key, value, scala.xml.Null) - } - private def hasInfo(module: ModuleID, x: scala.xml.NodeSeq) = { - val info = {x} \ "info" - if (info.nonEmpty) { - def check(found: NodeSeq, expected: String, label: String) = - if (found.isEmpty) sys.error("Missing " + label + " in inline Ivy XML.") - else { - val str = found.text - if (str != expected) - sys.error( - "Inconsistent " + label + " in inline Ivy XML. Expected '" + expected + "', got '" + str + "'" - ) - } - check(info \ "@organisation", module.organization, "organisation") - check(info \ "@module", module.name, "name") - check(info \ "@revision", module.revision, "version") - } - info.nonEmpty - } - - /** Parses the given in-memory Ivy file 'xml', using the existing 'moduleID' and specifying the given 'defaultConfiguration'. */ - private def parseIvyXML( - settings: IvySettings, - xml: scala.xml.NodeSeq, - moduleID: DefaultModuleDescriptor, - defaultConfiguration: String, - validate: Boolean - ): CustomXmlParser.CustomParser = - parseIvyXML(settings, xml.toString, moduleID, defaultConfiguration, validate) - - /** Parses the given in-memory Ivy file 'xml', using the existing 'moduleID' and specifying the given 'defaultConfiguration'. */ - private def parseIvyXML( - settings: IvySettings, - xml: String, - moduleID: DefaultModuleDescriptor, - defaultConfiguration: String, - validate: Boolean - ): CustomXmlParser.CustomParser = { - val parser = new CustomXmlParser.CustomParser(settings, Some(defaultConfiguration)) - parser.setMd(moduleID) - parser.setValidate(validate) - parser.setInput(xml.getBytes) - parser.parse() - parser - } - - def inconsistentDuplicateWarning(moduleID: DefaultModuleDescriptor): List[String] = { - import IvyRetrieve.toModuleID - val dds = ArraySeq.unsafeWrapArray(moduleID.getDependencies) - val deps = dds flatMap { dd => - val module = toModuleID(dd.getDependencyRevisionId) - dd.getModuleConfigurations map (c => module.withConfigurations(Some(c))) - } - inconsistentDuplicateWarning(deps) - } - - def inconsistentDuplicateWarning(dependencies: Seq[ModuleID]): List[String] = { - val warningHeader = - "Multiple dependencies with the same organization/name but different versions. To avoid conflict, pick one version:" - val out: mutable.ListBuffer[String] = mutable.ListBuffer() - (dependencies groupBy { dep => - (dep.organization, dep.name, dep.configurations) - }) foreach { - case (_, vs) if vs.size > 1 => - val v0 = vs.head - (vs find { _.revision != v0.revision }) foreach { _ => - out += s" * ${v0.organization}:${v0.name}:(" + (vs map { _.revision }) - .mkString(", ") + ")" - } - case _ => () - } - if (out.isEmpty) Nil - else warningHeader :: out.toList - } - - /** This method is used to add inline dependencies to the provided module. */ - def addDependencies( - moduleID: DefaultModuleDescriptor, - dependencies: Seq[ModuleID], - parser: CustomXmlParser.CustomParser - ): Unit = { - val converted = dependencies map { dependency => - convertDependency(moduleID, dependency, parser) - } - val unique = - if (hasDuplicateDependencies(converted)) mergeDuplicateDefinitions(converted) else converted - unique foreach moduleID.addDependency - } - - /** Determines if there are multiple dependency definitions for the same dependency ID. */ - def hasDuplicateDependencies(dependencies: Seq[DependencyDescriptor]): Boolean = { - val ids = dependencies.map(_.getDependencyRevisionId) - ids.toSet.size != ids.size - } - - /** - * Combines the artifacts, includes, and excludes of duplicate dependency definitions. - * This is somewhat fragile and is only intended to workaround Ivy (or sbt's use of Ivy) not handling this case properly. - * In particular, Ivy will create multiple dependency entries when converting a pom with a dependency on a classified artifact and a non-classified artifact: - * https://github.com/sbt/sbt/issues/468 - * It will also allow users to declare dependencies on classified modules in different configurations: - * https://groups.google.com/d/topic/simple-build-tool/H2MdAARz6e0/discussion - * as well as basic multi-classifier handling: #285, #419, #480. - * Multiple dependency definitions should otherwise be avoided as much as possible. - */ - def mergeDuplicateDefinitions( - dependencies: Seq[DependencyDescriptor] - ): Seq[DependencyDescriptor] = { - // need to preserve basic order of dependencies: can't use dependencies.groupBy - val deps = new java.util.LinkedHashMap[ModuleRevisionId, List[DependencyDescriptor]] - for (dd <- dependencies) { - val id = dd.getDependencyRevisionId - val updated = deps.get(id) match - case null => dd :: Nil - case v => dd :: v - deps.put(id, updated) - } - - import scala.jdk.CollectionConverters.* - deps.values.asScala.toSeq.flatMap { dds => - val mergeable = dds.lazyZip(dds.tail).forall(ivyint.MergeDescriptors.mergeable) - if (mergeable) dds.reverse.reduceLeft(ivyint.MergeDescriptors.apply) :: Nil else dds - } - } - - /** Transforms an sbt ModuleID into an Ivy DefaultDependencyDescriptor. */ - def convertDependency( - moduleID: DefaultModuleDescriptor, - dependency: ModuleID, - parser: CustomXmlParser.CustomParser - ): DefaultDependencyDescriptor = { - val dependencyDescriptor = new DefaultDependencyDescriptor( - moduleID, - toID(dependency), - dependency.isForce, - dependency.isChanging, - dependency.isTransitive - ) with SbtDefaultDependencyDescriptor { - def dependencyModuleId = dependency - } - dependency.configurations match { - case None => // The configuration for this dependency was not explicitly specified, so use the default - parser.parseDepsConfs(parser.getDefaultConf, dependencyDescriptor) - case Some( - confs - ) => // The configuration mapping (looks like: test->default) was specified for this dependency - parser.parseDepsConfs(confs, dependencyDescriptor) - } - for (artifact <- dependency.explicitArtifacts) { - import artifact.{ name, `type`, extension, url } - val extraMap = extra(artifact) - val ivyArtifact = new DefaultDependencyArtifactDescriptor( - dependencyDescriptor, - name, - `type`, - extension, - url.map(_.toURL).orNull, - extraMap - ) - copyConfigurations(artifact, (ref: ConfigRef) => { ivyArtifact.addConfiguration(ref.name) }) - for (conf <- dependencyDescriptor.getModuleConfigurations) - dependencyDescriptor.addDependencyArtifact(conf, ivyArtifact) - } - for (excls <- dependency.exclusions) { - for (conf <- dependencyDescriptor.getModuleConfigurations) { - dependencyDescriptor.addExcludeRule( - conf, - IvyScalaUtil.excludeRule( - excls.organization, - excls.name, - excls.configurations map { _.name }, - excls.artifact - ) - ) - } - } - for (incls <- dependency.inclusions) { - for (conf <- dependencyDescriptor.getModuleConfigurations) { - dependencyDescriptor.addIncludeRule( - conf, - IvyScalaUtil.includeRule( - incls.organization, - incls.name, - incls.configurations map { _.name }, - incls.artifact - ) - ) - } - } - - dependencyDescriptor - } - def copyConfigurations(artifact: Artifact, addConfiguration: ConfigRef => Unit): Unit = - copyConfigurations(artifact, addConfiguration, Vector(ConfigRef("*"))) - - private def copyConfigurations( - artifact: Artifact, - addConfiguration: ConfigRef => Unit, - allConfigurations: Vector[ConfigRef] - ): Unit = { - val confs = - if (artifact.configurations.isEmpty) allConfigurations - else artifact.configurations - confs foreach addConfiguration - } - - def addExcludes( - moduleID: DefaultModuleDescriptor, - excludes: Seq[ExclusionRule], - scalaModuleInfo: Option[ScalaModuleInfo] - ): Unit = excludes.foreach(exclude => addExclude(moduleID, scalaModuleInfo)(exclude)) - - def addExclude(moduleID: DefaultModuleDescriptor, scalaModuleInfo: Option[ScalaModuleInfo])( - exclude0: ExclusionRule - ): Unit = { - // this adds _2.11 postfix - val exclude = CrossVersion.substituteCross(exclude0, scalaModuleInfo) - val confs = - if (exclude.configurations.isEmpty) moduleID.getConfigurationsNames.toList - else exclude.configurations map { _.name } - val excludeRule = - IvyScalaUtil.excludeRule(exclude.organization, exclude.name, confs, exclude.artifact) - moduleID.addExcludeRule(excludeRule) - } - - def addOverrides( - moduleID: DefaultModuleDescriptor, - overrides: Vector[ModuleID], - matcher: PatternMatcher - ): Unit = - overrides foreach addOverride(moduleID, matcher) - def addOverride(moduleID: DefaultModuleDescriptor, matcher: PatternMatcher)( - overrideDef: ModuleID - ): Unit = { - val overrideID = new ModuleId(overrideDef.organization, overrideDef.name) - val overrideWith = new OverrideDependencyDescriptorMediator(null, overrideDef.revision) - moduleID.addDependencyDescriptorMediator(overrideID, matcher, overrideWith) - } - - /** - * It is necessary to explicitly modify direct dependencies because Ivy gives - * "IllegalStateException: impossible to get artifacts when data has not been loaded." - * when a direct dependency is overridden with a newer version." - */ - def overrideDirect(dependencies: Seq[ModuleID], overrides: Vector[ModuleID]): Seq[ModuleID] = { - def key(id: ModuleID) = (id.organization, id.name) - val overridden = overrides.map(id => (key(id), id.revision)).toMap - dependencies map { dep => - overridden get key(dep) match { - case Some(rev) => dep.withRevision(rev) - case None => dep - } - } - } - - /** This method is used to add inline artifacts to the provided module. */ - def addArtifacts(moduleID: DefaultModuleDescriptor, artifacts: Iterable[Artifact]): Unit = - for (art <- mapArtifacts(moduleID, artifacts.toSeq); c <- art.getConfigurations) - moduleID.addArtifact(c, art) - - def addConfigurations( - mod: DefaultModuleDescriptor, - configurations: Iterable[Configuration] - ): Unit = - configurations.foreach(config => mod.addConfiguration(toIvyConfiguration(config))) - - def mapArtifacts(moduleID: ModuleDescriptor, artifacts: Seq[Artifact]): Seq[IArtifact] = { - lazy val allConfigurations = moduleID.getPublicConfigurationsNames.toVector map ConfigRef.apply - for (artifact <- artifacts) yield toIvyArtifact(moduleID, artifact, allConfigurations) - } - - /** - * This code converts the given ModuleDescriptor to a DefaultModuleDescriptor by casting or generating an error. - * Ivy 2.0.0 always produces a DefaultModuleDescriptor. - */ - private def toDefaultModuleDescriptor(md: ModuleDescriptor) = - md match { - case dmd: DefaultModuleDescriptor => dmd - case _ => sys.error("Unknown ModuleDescriptor type.") - } - def getConfigurations( - module: ModuleDescriptor, - configurations: Option[Iterable[Configuration]] - ) = - configurations match { - case Some(confs) => confs.map(_.name).toList.toArray - case None => module.getPublicConfigurationsNames - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala deleted file mode 100644 index 0048a79eb..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala +++ /dev/null @@ -1,599 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt.internal.librarymanagement - -import java.io.File - -import ivyint.CachedResolutionResolveEngine -import org.apache.ivy.Ivy -import org.apache.ivy.core.{ IvyPatternHelper, LogOptions } -import org.apache.ivy.core.deliver.DeliverOptions -import org.apache.ivy.core.install.InstallOptions -import org.apache.ivy.core.module.descriptor.{ - DefaultModuleDescriptor, - MDArtifact, - ModuleDescriptor, - Artifact as IArtifact -} -import org.apache.ivy.core.resolve.ResolveOptions -import org.apache.ivy.plugins.resolver.{ BasicResolver, DependencyResolver } -import org.apache.ivy.util.filter.Filter as IvyFilter -import sbt.io.{ IO, PathFinder } -import sbt.util.Logger -import sbt.librarymanagement.{ ModuleDescriptorConfiguration as InlineConfiguration, * } -import syntax.* -import InternalDefaults.* -import UpdateClassifiersUtil.* -import sbt.internal.librarymanagement.IvyUtil.TransientNetworkException - -object IvyActions { - - /** Installs the dependencies of the given 'module' from the resolver named 'from' to the resolver named 'to'. */ - def install(module: IvySbt#Module, from: String, to: String, log: Logger): Unit = { - module.withModule(log) { (ivy, md, _) => - for (dependency <- md.getDependencies) { - log.info("Installing " + dependency) - val options = new InstallOptions - options.setValidate(module.moduleSettings.validate) - options.setTransitive(dependency.isTransitive) - ivy.install(dependency.getDependencyRevisionId, from, to, options) - } - } - } - - /** Clears the Ivy cache, as configured by 'config'. */ - def cleanCache(ivy: IvySbt, log: Logger) = ivy.withIvy(log) { iv => - iv.getSettings.getResolutionCacheManager.clean() - iv.getSettings.getRepositoryCacheManagers.foreach(_.clean()) - } - - /** - * Cleans the cached resolution cache, if any. - * This is called by clean. - */ - private[sbt] def cleanCachedResolutionCache(module: IvySbt#Module, log: Logger): Unit = - module.withModule(log) { (_, _, _) => - module.owner.cleanCachedResolutionCache() - } - - /** Creates a Maven pom from the given Ivy configuration */ - def makePomFile(module: IvySbt#Module, configuration: MakePomConfiguration, log: Logger): File = { - import configuration.{ - allRepositories, - configurations, - filterRepositories, - process, - includeTypes - } - val file = configuration.file.getOrElse(sys.error("file must be specified.")) - val moduleInfo = configuration.moduleInfo.getOrElse(sys.error("moduleInfo must be specified.")) - val extra = configuration.extra.getOrElse(scala.xml.NodeSeq.Empty) - module.withModule(log) { (ivy, md, _) => - (new MakePom(log)).write( - ivy, - md, - moduleInfo, - configurations, - includeTypes, - extra, - process, - filterRepositories, - allRepositories, - file - ) - log.info("Wrote " + file.getAbsolutePath) - file - } - } - - def deliver(module: IvySbt#Module, configuration: PublishConfiguration, log: Logger): File = { - val deliverIvyPattern = configuration.deliverIvyPattern - .getOrElse(sys.error("deliverIvyPattern must be specified.")) - val status = getDeliverStatus(configuration.status) - module.withModule(log) { case (ivy, md, _) => - val revID = md.getModuleRevisionId - val options = DeliverOptions.newInstance(ivy.getSettings).setStatus(status) - options.setConfs(getConfigurations(md, configuration.configurations)) - ivy.deliver(revID, revID.getRevision, deliverIvyPattern, options) - val file = deliveredFile(ivy, deliverIvyPattern, md) - - // Apply dependency overrides to the delivered Ivy XML - applyOverridesToDeliveredIvy(file, module.moduleSettings, log) - - file - } - } - - /** - * Post-processes the delivered Ivy XML file to apply dependency overrides. - * This is necessary because Ivy's deliver() method doesn't automatically apply - * DependencyDescriptorMediators when writing the XML. - */ - private def applyOverridesToDeliveredIvy( - ivyFile: File, - moduleSettings: ModuleSettings, - log: Logger - ): Unit = { - moduleSettings match { - case ic: InlineConfiguration if ic.overrides.nonEmpty => - val overrideMap = ic.overrides.map(m => (m.organization, m.name) -> m.revision).toMap - if (ivyFile.exists()) { - val xml = scala.xml.XML.loadFile(ivyFile) - val updated = applyDependencyOverrides(xml, overrideMap) - scala.xml.XML.save(ivyFile.getAbsolutePath, updated, "UTF-8", xmlDecl = true, null) - log.debug(s"Applied ${overrideMap.size} dependency override(s) to ${ivyFile.getName}") - } - case _ => // No overrides to apply - } - } - - /** - * Applies dependency overrides to an Ivy XML node by updating the rev attribute - * of dependency elements that match entries in the override map. - * - * @param xml The Ivy XML root node to transform - * @param overrideMap Map from (organization, name) to the overridden revision - * @return The transformed XML node with updated dependency revisions - */ - def applyDependencyOverrides( - xml: scala.xml.Node, - overrideMap: Map[(String, String), String] - ): scala.xml.Node = { - new scala.xml.transform.RuleTransformer(new scala.xml.transform.RewriteRule { - override def transform(n: scala.xml.Node): Seq[scala.xml.Node] = n match { - case e @ scala.xml.Elem(prefix, "dependency", attrs, scope, children*) => - val org = attrs.get("org").map(_.text).getOrElse("") - val name = attrs.get("name").map(_.text).getOrElse("") - overrideMap.get((org, name)) match { - case Some(overrideRev) => - def updateAttrs(metadata: scala.xml.MetaData): scala.xml.MetaData = { - metadata match { - case scala.xml.Null => scala.xml.Null - case attr if attr.key == "rev" => - new scala.xml.UnprefixedAttribute( - "rev", - overrideRev, - updateAttrs(attr.next) - ) - case attr => - attr.copy(next = updateAttrs(attr.next)) - } - } - scala.xml.Elem( - prefix, - "dependency", - updateAttrs(attrs), - scope, - minimizeEmpty = true, - children* - ) - case None => e - } - case other => other - } - }).transform(xml).head - } - - def getConfigurations( - module: ModuleDescriptor, - configurations: Option[Vector[ConfigRef]] - ): Array[String] = - configurations match { - case Some(confs) => (confs map { _.name }).toArray - case None => module.getPublicConfigurationsNames - } - - def deliveredFile(ivy: Ivy, pattern: String, md: ModuleDescriptor): File = - ivy.getSettings.resolveFile( - IvyPatternHelper.substitute(pattern, md.getResolvedModuleRevisionId) - ) - - def publish(module: IvySbt#Module, configuration: PublishConfiguration, log: Logger): Unit = { - val resolverName = configuration.resolverName match { - case Some(x) => x - case _ => sys.error("Resolver name is not specified") - } - - // Todo. Fix publish ordering https://github.com/sbt/sbt/issues/2088#issuecomment-246208872 - val ivyFile: Option[File] = - if (configuration.publishMavenStyle) None - else { - Option(deliver(module, configuration, log)) - } - - val artifacts = Map(configuration.artifacts*) - val checksums = configuration.checksums - module.withModule(log) { case (ivy, md, _) => - val resolver = ivy.getSettings.getResolver(resolverName) - if (resolver eq null) sys.error("Undefined resolver '" + resolverName + "'") - val ivyArtifact = ivyFile map { file => - (MDArtifact.newIvyArtifact(md), file) - } - val cross = crossVersionMap(module.moduleSettings) - val as = mapArtifacts(md, cross, artifacts) ++ ivyArtifact.toList - withChecksums(resolver, checksums) { - publish(md, as, resolver, overwrite = configuration.overwrite) - } - } - } - private def withChecksums[T](resolver: DependencyResolver, checksums: Vector[String])( - act: => T - ): T = - resolver match { case br: BasicResolver => withChecksums(br, checksums)(act); case _ => act } - private def withChecksums[T](resolver: BasicResolver, checksums: Vector[String])( - act: => T - ): T = { - val previous = resolver.getChecksumAlgorithms - resolver.setChecksums(checksums mkString ",") - try { - act - } finally { - resolver.setChecksums(previous mkString ",") - } - } - private def crossVersionMap(moduleSettings: ModuleSettings): Option[String => String] = - moduleSettings match { - case i: InlineConfiguration => CrossVersion(i.module, i.scalaModuleInfo) - case _ => None - } - def mapArtifacts( - module: ModuleDescriptor, - cross: Option[String => String], - artifacts: Map[Artifact, File] - ): Vector[(IArtifact, File)] = { - val rawa = artifacts.keys.toVector - val seqa = CrossVersion.substituteCross(rawa, cross) - val zipped = rawa zip IvySbt.mapArtifacts(module, seqa) - zipped map { (a, ivyA) => (ivyA, artifacts(a)) } - } - - /** - * Updates one module's dependencies performing a dependency resolution and retrieval. - * - * The following mechanism uses ivy under the hood. - * - * @param module The module to be resolved. - * @param configuration The update configuration. - * @param uwconfig The configuration to handle unresolved warnings. - * @param log The logger. - * @return The result, either an unresolved warning or an update report. Note that this - * update report will or will not be successful depending on the `missingOk` option. - */ - private[sbt] def updateEither( - module: IvySbt#Module, - configuration: UpdateConfiguration, - uwconfig: UnresolvedWarningConfiguration, - log: Logger - ): Either[UnresolvedWarning, UpdateReport] = { - module.withModule(log) { case (ivy, moduleDescriptor, _) => - // Warn about duplicated and inconsistent dependencies - val iw = IvySbt.inconsistentDuplicateWarning(moduleDescriptor) - iw.foreach(log.warn(_)) - - val metadataDirectory = configuration.metadataDirectory - - // Create inputs, resolve and retrieve the module descriptor - val inputs = ResolutionInputs(ivy, moduleDescriptor, configuration, log) - val resolutionResult: Either[ResolveException, UpdateReport] = { - if ( - module.owner.configuration.updateOptions.cachedResolution && metadataDirectory.isDefined - ) { - val cache = - metadataDirectory.getOrElse(sys.error("Missing directory for cached resolution.")) - cachedResolveAndRetrieve(inputs, cache) - } else resolveAndRetrieve(inputs) - } - - // Convert to unresolved warning or retrieve update report - resolutionResult.fold( - exception => Left(UnresolvedWarning(exception, uwconfig)), - ur0 => { - val ur = configuration.retrieveManaged match { - case Some(retrieveConf) => retrieve(log, ivy, ur0, retrieveConf) - case _ => ur0 - } - Right(ur) - } - ) - } - } - - def groupedConflicts[T](moduleFilter: ModuleFilter, grouping: ModuleID => T)( - report: UpdateReport - ): Map[T, Set[String]] = - report.configurations.flatMap { confReport => - val evicted = confReport.evicted.filter(moduleFilter) - val evictedSet = evicted.map(m => (m.organization, m.name)).toSet - val conflicted = - confReport.allModules.filter(mod => evictedSet((mod.organization, mod.name))) - grouped(grouping)(conflicted ++ evicted) - }.toMap - - def grouped[T](grouping: ModuleID => T)(mods: Seq[ModuleID]): Map[T, Set[String]] = - mods.groupBy(grouping).view.mapValues(_.map(_.revision).toSet).toMap - - def addExcluded( - report: UpdateReport, - classifiers: Vector[String], - exclude: Map[ModuleID, Set[String]] - ): UpdateReport = - report.addMissing { id => - classifiedArtifacts(id.name, classifiers filter getExcluded(id, exclude)) - } - - private def getExcluded(id: ModuleID, exclude: Map[ModuleID, Set[String]]): Set[String] = - exclude.getOrElse(restrictedCopy(id, false), Set.empty[String]) - - def extractExcludes(report: UpdateReport): Map[ModuleID, Set[String]] = - report.allMissing flatMap { case (_, mod, art) => - art.classifier.map { c => - (restrictedCopy(mod, false), c) - } - } groupBy (_._1) map { (mod, pairs) => (mod, pairs.map(_._2).toSet) } - - /** - * Represents the inputs to pass in to [[resolveAndRetrieve]] and [[cachedResolveAndRetrieve]]. - * - * @param ivy The ivy instance to resolve and retrieve dependencies. - * @param module The module descriptor to be resolved. - * @param updateConfiguration The update configuration for [[ResolveOptions]]. - * @param log The logger. - */ - private case class ResolutionInputs( - ivy: Ivy, - module: DefaultModuleDescriptor, - updateConfiguration: UpdateConfiguration, - log: Logger - ) - - implicit def toIvyFilter(f: ArtifactTypeFilter): IvyFilter = new IvyFilter { - override def accept(o: Object): Boolean = Option(o) exists { case a: IArtifact => - applyFilter(a) - } - - def applyFilter(a: IArtifact): Boolean = - (f.types contains a.getType) ^ f.inverted - } - - /** - * Defines the internal entrypoint of module resolution and retrieval. - * - * This method is the responsible of populating [[ResolveOptions]] and pass - * it in to the ivy instance to perform the module resolution. - * - * It returns an already resolved [[UpdateReport]] instead of a [[ResolveReport]] - * like its counterpart [[CachedResolutionResolveEngine.customResolve]]. - * - * @param inputs The resolution inputs. - * @return The result of the resolution. - */ - private def resolveAndRetrieve( - inputs: ResolutionInputs - ): Either[ResolveException, UpdateReport] = { - // Populate resolve options from the passed arguments - val ivyInstance = inputs.ivy - val moduleDescriptor = inputs.module - val updateConfiguration = inputs.updateConfiguration - val resolveOptions = new ResolveOptions - val resolveId = ResolveOptions.getDefaultResolveId(moduleDescriptor) - val artifactFilter = getArtifactTypeFilter(updateConfiguration.artifactFilter) - import updateConfiguration.* - resolveOptions.setResolveId(resolveId) - resolveOptions.setArtifactFilter(artifactFilter) - resolveOptions.setUseCacheOnly(offline) - resolveOptions.setLog(ivyLogLevel(logging)) - if (frozen) { - resolveOptions.setTransitive(false) - resolveOptions.setCheckIfChanged(false) - } - ResolutionCache.cleanModule( - moduleDescriptor.getModuleRevisionId, - resolveId, - ivyInstance.getSettings.getResolutionCacheManager - ) - - val resolveReport = ivyInstance.resolve(moduleDescriptor, resolveOptions) - if (resolveReport.hasError && !missingOk) { - import scala.jdk.CollectionConverters.* - // If strict error, collect report information and generated UnresolvedWarning - val messages = resolveReport.getAllProblemMessages.asScala.toSeq.map(_.toString).distinct - val failedPaths = resolveReport.getUnresolvedDependencies.map { node => - val moduleID = IvyRetrieve.toModuleID(node.getId) - val path = IvyRetrieve - .findPath(node, moduleDescriptor.getModuleRevisionId) - .map(x => IvyRetrieve.toModuleID(x.getId)) - moduleID -> path - }.toMap - val failedModules = failedPaths.keys.toSeq - Left(new ResolveException(messages, failedModules, failedPaths)) - } else { - // If no strict error, we convert the resolve report into an update report - val cachedDescriptor = ivyInstance.getSettings.getResolutionCacheManager - .getResolvedIvyFileInCache(moduleDescriptor.getModuleRevisionId) - Right(IvyRetrieve.updateReport(resolveReport, cachedDescriptor)) - } - } - - /** - * Resolves and retrieves a module with a cache mechanism defined in - * sbt Cached Resolution. - * - * It's the cached version of [[resolveAndRetrieve]]. - * - * @param inputs The resolution inputs. - * @param cache The optional cache dependency. - * @return The result of the cached resolution. - */ - private def cachedResolveAndRetrieve( - inputs: ResolutionInputs, - cache: File - ): Either[ResolveException, UpdateReport] = { - val log = inputs.log - val descriptor = inputs.module - val updateConfiguration = inputs.updateConfiguration - val resolver = inputs.ivy.getResolveEngine.asInstanceOf[CachedResolutionResolveEngine] - val resolveOptions = new ResolveOptions - val resolveId = ResolveOptions.getDefaultResolveId(descriptor) - val artifactFilter = getArtifactTypeFilter(updateConfiguration.artifactFilter) - import updateConfiguration.* - resolveOptions.setResolveId(resolveId) - resolveOptions.setArtifactFilter(artifactFilter) - resolveOptions.setUseCacheOnly(offline) - resolveOptions.setLog(ivyLogLevel(logging)) - if (frozen) { - resolveOptions.setTransitive(false) - resolveOptions.setCheckIfChanged(false) - } - resolver.customResolve( - descriptor, - missingOk, - updateConfiguration.logicalClock, - resolveOptions, - cache, - log - ) - } - - private def retrieve( - log: Logger, - ivy: Ivy, - report: UpdateReport, - config: RetrieveConfiguration - ): UpdateReport = { - val copyChecksums = - Option(ivy.getVariable(ConvertResolver.ManagedChecksums)) match { - case Some(x) => x.toBoolean - case _ => false - } - val toRetrieve: Option[Vector[ConfigRef]] = config.configurationsToRetrieve - val base = getRetrieveDirectory(config.retrieveDirectory) - val pattern = getRetrievePattern(config.outputPattern) - val existingFiles = PathFinder(base).allPaths.get() filterNot { _.isDirectory } - val toCopy = new collection.mutable.HashSet[(File, File)] - val retReport = report retrieve { (conf: ConfigRef, mid, art, cached) => - toRetrieve match { - case None => performRetrieve(conf, mid, art, base, pattern, cached, copyChecksums, toCopy) - case Some(refs) if refs.contains[ConfigRef](conf) => - performRetrieve(conf, mid, art, base, pattern, cached, copyChecksums, toCopy) - case _ => cached - } - } - IO.copy(toCopy) - val resolvedFiles = toCopy.map(_._2) - if (config.sync) { - val filesToDelete = existingFiles.filterNot(resolvedFiles.contains) - filesToDelete foreach { f => - log.info(s"Deleting old dependency: ${f.getAbsolutePath}") - f.delete() - } - } - - retReport - } - - private def performRetrieve( - conf: ConfigRef, - mid: ModuleID, - art: Artifact, - base: File, - pattern: String, - cached: File, - copyChecksums: Boolean, - toCopy: collection.mutable.HashSet[(File, File)] - ): File = { - val to = retrieveTarget(conf, mid, art, base, pattern) - toCopy += ((cached, to)) - - if (copyChecksums) { - // Copy over to the lib managed directory any checksum for a jar if it exists - // TODO(jvican): Support user-provided checksums - val cachePath = cached.getAbsolutePath - IvySbt.DefaultChecksums.foreach { checksum => - if (cachePath.endsWith(".jar")) { - val cacheChecksum = new File(s"$cachePath.$checksum") - if (cacheChecksum.exists()) { - val toChecksum = new File(s"${to.getAbsolutePath}.$checksum") - toCopy += ((cacheChecksum, toChecksum)) - } - } - } - } - - to - } - - private def retrieveTarget( - conf: ConfigRef, - mid: ModuleID, - art: Artifact, - base: File, - pattern: String - ): File = - new File(base, substitute(conf, mid, art, pattern)) - - private def substitute(conf: ConfigRef, mid: ModuleID, art: Artifact, pattern: String): String = { - val mextra = IvySbt.javaMap(mid.extraAttributes, true) - val aextra = IvySbt.extra(art, true) - IvyPatternHelper.substitute( - pattern, - mid.organization, - mid.name, - mid.branchName.orNull, - mid.revision, - art.name, - art.`type`, - art.extension, - conf.name, - null, - mextra, - aextra - ) - } - - import UpdateLogging.{ Quiet, Full, DownloadOnly, Default } - import LogOptions.{ LOG_QUIET, LOG_DEFAULT, LOG_DOWNLOAD_ONLY } - private def ivyLogLevel(level: UpdateLogging) = - level match { - case Quiet => LOG_QUIET - case DownloadOnly => LOG_DOWNLOAD_ONLY - case Full => LOG_DEFAULT - case Default => LOG_DOWNLOAD_ONLY - } - - def publish( - module: ModuleDescriptor, - artifacts: Seq[(IArtifact, File)], - resolver: DependencyResolver, - overwrite: Boolean - ): Unit = { - if (artifacts.nonEmpty) { - checkFilesPresent(artifacts) - try { - resolver.beginPublishTransaction(module.getModuleRevisionId(), overwrite); - artifacts.foreach { (artifact, file) => - IvyUtil.retryWithBackoff( - resolver.publish(artifact, file, overwrite), - TransientNetworkException.apply, - maxAttempts = LMSysProp.maxPublishAttempts - ) - } - resolver.commitPublishTransaction() - } catch { - case e: Throwable => - try { - resolver.abortPublishTransaction() - } finally { - throw e - } - } - } - } - private def checkFilesPresent(artifacts: Seq[(IArtifact, File)]): Unit = { - val missing = artifacts filter { case (_, file) => !file.exists } - if (missing.nonEmpty) - sys.error( - "Missing files for publishing:\n\t" + missing.map(_._2.getAbsolutePath).mkString("\n\t") - ) - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyCache.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyCache.scala deleted file mode 100644 index 8025d718f..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyCache.scala +++ /dev/null @@ -1,132 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt.internal.librarymanagement - -import java.io.File - -import org.apache.ivy.core.cache.{ - ArtifactOrigin, - CacheDownloadOptions, - DefaultRepositoryCacheManager -} -import org.apache.ivy.core.module.descriptor.{ Artifact as IvyArtifact, DefaultArtifact } -import org.apache.ivy.plugins.repository.file.{ FileRepository as IvyFileRepository, FileResource } -import org.apache.ivy.plugins.repository.{ ArtifactResourceResolver, Resource, ResourceDownloader } -import org.apache.ivy.plugins.resolver.util.ResolvedResource -import org.apache.ivy.util.FileUtil -import sbt.io.Path -import sbt.internal.librarymanagement.ivy.InlineIvyConfiguration -import sbt.librarymanagement.* -import sbt.util.Logger - -class NotInCache(val id: ModuleID, cause: Throwable) - extends RuntimeException(NotInCache(id, cause), cause) { - def this(id: ModuleID) = this(id, null) -} -private object NotInCache { - def apply(id: ModuleID, cause: Throwable) = { - val postfix = if (cause == null) "" else (": " + cause.toString) - "File for " + id + " not in cache" + postfix - } -} - -/** Provides methods for working at the level of a single jar file with the default Ivy cache. */ -class IvyCache(val ivyHome: Option[File]) { - def lockFile = new File(ivyHome getOrElse Path.userHome, ".sbt.cache.lock") - - /** Caches the given 'file' with the given ID. It may be retrieved or cleared using this ID. */ - def cacheJar( - moduleID: ModuleID, - file: File, - lock: Option[xsbti.GlobalLock], - log: Logger - ): Unit = { - val artifact = defaultArtifact(moduleID) - val resolved = - new ResolvedResource(new FileResource(new IvyFileRepository, file), moduleID.revision) - withDefaultCache(lock, log) { cache => - val resolver = new ArtifactResourceResolver { def resolve(artifact: IvyArtifact) = resolved } - cache.download(artifact, resolver, new FileDownloader, new CacheDownloadOptions) - () - } - } - - /** Clears the cache of the jar for the given ID. */ - def clearCachedJar(id: ModuleID, lock: Option[xsbti.GlobalLock], log: Logger): Unit = { - try { - withCachedJar(id, lock, log)(_.delete); () - } catch { - case e: Exception => log.debug("Error cleaning cached jar: " + e.toString) - } - } - - /** Copies the cached jar for the given ID to the directory 'toDirectory'. If the jar is not in the cache, NotInCache is thrown. */ - def retrieveCachedJar( - id: ModuleID, - toDirectory: File, - lock: Option[xsbti.GlobalLock], - log: Logger - ) = - withCachedJar(id, lock, log) { cachedFile => - val copyTo = new File(toDirectory, cachedFile.getName) - FileUtil.copy(cachedFile, copyTo, null) - copyTo - } - - /** Get the location of the cached jar for the given ID in the Ivy cache. If the jar is not in the cache, NotInCache is thrown . */ - def withCachedJar[T](id: ModuleID, lock: Option[xsbti.GlobalLock], log: Logger)( - f: File => T - ): T = { - val cachedFile = - try { - withDefaultCache(lock, log) { cache => - val artifact = defaultArtifact(id) - cache.getArchiveFileInCache(artifact, unknownOrigin(artifact)) - } - } catch { case e: Exception => throw new NotInCache(id, e) } - - if (cachedFile.exists) f(cachedFile) else throw new NotInCache(id) - } - - /** Calls the given function with the default Ivy cache. */ - def withDefaultCache[T](lock: Option[xsbti.GlobalLock], log: Logger)( - f: DefaultRepositoryCacheManager => T - ): T = { - val (ivy, _) = basicLocalIvy(lock, log) - ivy.withIvy(log) { ivy => - val cache = ivy.getSettings.getDefaultRepositoryCacheManager - .asInstanceOf[DefaultRepositoryCacheManager] - cache.setUseOrigin(false) - f(cache) - } - } - private def unknownOrigin(artifact: IvyArtifact) = ArtifactOrigin.unkwnown(artifact) - - /** A minimal Ivy setup with only a local resolver and the current directory as the base directory. */ - private def basicLocalIvy(lock: Option[xsbti.GlobalLock], log: Logger) = { - val local = Resolver.defaultLocal - val paths = IvyPaths(".", ivyHome.map(_.toString)) - val conf = InlineIvyConfiguration() - .withPaths(paths) - .withResolvers(Vector(local)) - .withLock(lock) - .withLog(log) - (new IvySbt(conf), local) - } - - /** Creates a default jar artifact based on the given ID. */ - private def defaultArtifact(moduleID: ModuleID): IvyArtifact = - new DefaultArtifact(IvySbt.toID(moduleID), null, moduleID.name, "jar", "jar") -} - -/** Required by Ivy for copying to the cache. */ -private class FileDownloader extends ResourceDownloader { - def download(artifact: IvyArtifact, resource: Resource, dest: File): Unit = { - if (dest.exists()) dest.delete() - val part = new File(dest.getAbsolutePath + ".part") - FileUtil.copy(resource.openStream, part, null) - if (!part.renameTo(dest)) - sys.error("Could not move temporary file " + part + " to final location " + dest) - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyInternalDefaults.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyInternalDefaults.scala deleted file mode 100644 index 399b13427..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyInternalDefaults.scala +++ /dev/null @@ -1,28 +0,0 @@ -package sbt -package internal.librarymanagement - -import java.io.File -import sbt.librarymanagement.IvyPaths -import sbt.io.syntax.* -import xsbti.Logger as XLogger -import sbt.util.Logger - -/** - * This is a list of functions with default values. - */ -object IvyInternalDefaults { - def defaultBaseDirectory: File = - (new File(".")).getAbsoluteFile / "lib_managed" - - def getBaseDirectory(opt: Option[File]): File = - opt.getOrElse(defaultBaseDirectory) - - def getLog(opt: Option[XLogger]): XLogger = - opt.getOrElse(Logger.Null) - - def defaultIvyPaths: IvyPaths = - IvyPaths(defaultBaseDirectory.toString, None) - - def getIvyPaths(opt: Option[IvyPaths]): IvyPaths = - opt.getOrElse(defaultIvyPaths) -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyLogger.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyLogger.scala deleted file mode 100644 index dd0416399..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyLogger.scala +++ /dev/null @@ -1,58 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt.internal.librarymanagement - -import org.apache.ivy.util.{ Message, MessageLogger, MessageLoggerEngine } -import sbt.util.Logger - -/** Interface to Ivy logging. */ -private[sbt] final class IvyLoggerInterface(logger: Logger) extends MessageLogger { - def rawlog(msg: String, level: Int): Unit = log(msg, level) - def log(msg: String, level: Int): Unit = { - import Message.{ MSG_DEBUG, MSG_VERBOSE, MSG_INFO, MSG_WARN, MSG_ERR } - level match { - case MSG_DEBUG => debug(msg) - case MSG_VERBOSE => verbose(msg) - case MSG_INFO => info(msg) - case MSG_WARN => warn(msg) - case MSG_ERR => error(msg) - } - } - // DEBUG level messages are very verbose and rarely useful to users. - // TODO: provide access to this information some other way - def debug(msg: String): Unit = () - def verbose(msg: String): Unit = logger.verbose(msg) - def deprecated(msg: String): Unit = warn(msg) - def info(msg: String): Unit = if (SbtIvyLogger.acceptInfo(msg)) logger.info(msg) - def rawinfo(msg: String): Unit = info(msg) - def warn(msg: String): Unit = logger.warn(msg) - def error(msg: String): Unit = if (SbtIvyLogger.acceptError(msg)) logger.error(msg) - - private def emptyList = java.util.Collections.emptyList[String] - def getProblems = emptyList - def getWarns = emptyList - def getErrors = emptyList - - def clearProblems(): Unit = () - def sumupProblems(): Unit = clearProblems() - def progress(): Unit = () - def endProgress(): Unit = () - - def endProgress(msg: String): Unit = info(msg) - def isShowProgress = false - def setShowProgress(progress: Boolean): Unit = () -} -private[sbt] final class SbtMessageLoggerEngine extends MessageLoggerEngine { - - /** This is a hack to filter error messages about 'unknown resolver ...'. */ - override def error(msg: String): Unit = if (SbtIvyLogger.acceptError(msg)) super.error(msg) - override def sumupProblems(): Unit = clearProblems() -} -private[sbt] object SbtIvyLogger { - final val unknownResolver = "unknown resolver" - def acceptError(msg: String) = (msg ne null) && !msg.startsWith(unknownResolver) - - final val loadingSettings = ":: loading settings" - def acceptInfo(msg: String) = (msg ne null) && !msg.startsWith(loadingSettings) -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyRetrieve.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyRetrieve.scala deleted file mode 100644 index d2ac42ea5..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyRetrieve.scala +++ /dev/null @@ -1,276 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt.internal.librarymanagement - -import java.io.File -import java.util as ju -import collection.mutable -import collection.immutable.ArraySeq -import org.apache.ivy.core.{ module, report, resolve } -import module.descriptor.{ Artifact as IvyArtifact, License as IvyLicense } -import module.id.{ ModuleRevisionId, ModuleId as IvyModuleId } -import report.{ ArtifactDownloadReport, ConfigurationResolveReport, ResolveReport } -import resolve.{ IvyNode, IvyNodeCallers } -import IvyNodeCallers.Caller as IvyCaller -import ivyint.SbtDefaultDependencyDescriptor -import sbt.librarymanagement.*, syntax.* - -object IvyRetrieve { - def reports(report: ResolveReport): Vector[ConfigurationResolveReport] = - report.getConfigurations.toVector map report.getConfigurationReport - - def moduleReports(confReport: ConfigurationResolveReport): Vector[ModuleReport] = - for { - revId <- confReport.getModuleRevisionIds.toArray.toVector collect { - case revId: ModuleRevisionId => revId - } - } yield moduleRevisionDetail(confReport, confReport.getDependency(revId)) - - private[sbt] def artifacts( - artReport: Seq[ArtifactDownloadReport] - ): (Vector[(Artifact, File)], Vector[Artifact]) = { - val missing = new mutable.ListBuffer[Artifact] - val resolved = new mutable.ListBuffer[(Artifact, File)] - for (r <- artReport) { - val fileOpt = Option(r.getLocalFile) - val art = toArtifact(r.getArtifact) - fileOpt match { - case Some(file) => resolved += ((art, file)) - case None => missing += art - } - } - (resolved.toVector, missing.toVector) - } - - // We need this because current module report used as part of UpdateReport/ConfigurationReport contains - // only the revolved modules. - // Sometimes the entire module can be excluded via rules etc. - private[sbt] def organizationArtifactReports( - confReport: ConfigurationResolveReport - ): Vector[OrganizationArtifactReport] = { - val moduleIds = confReport.getModuleIds.toArray.toVector collect { case mId: IvyModuleId => - mId - } - def organizationArtifact(mid: IvyModuleId): OrganizationArtifactReport = { - val deps = confReport.getNodes(mid).toArray.toVector collect { case node: IvyNode => node } - OrganizationArtifactReport( - mid.getOrganisation, - mid.getName, - deps map { - moduleRevisionDetail(confReport, _) - } - ) - } - moduleIds map { organizationArtifact } - } - - private[sbt] def nonEmptyString(s: String): Option[String] = - s match { - case null => None - case x if x.trim == "" => None - case x => Some(x.trim) - } - - private[sbt] def moduleRevisionDetail( - confReport: ConfigurationResolveReport, - dep: IvyNode - ): ModuleReport = { - def toExtraAttributes(ea: ju.Map[?, ?]): Map[String, String] = - Map(ea.entrySet.toArray collect { - case entry: ju.Map.Entry[?, ?] - if nonEmptyString(entry.getKey.toString).isDefined && nonEmptyString( - entry.getValue.toString - ).isDefined => - (entry.getKey.toString, entry.getValue.toString) - }*) - def toCaller(caller: IvyCaller): Caller = { - val m = toModuleID(caller.getModuleRevisionId) - val callerConfigurations = caller.getCallerConfigurations.toVector collect { - case x if nonEmptyString(x).isDefined => ConfigRef(x) - } - val ddOpt = Option(caller.getDependencyDescriptor) - val (extraAttributes, isForce, isChanging, isTransitive, isDirectlyForce) = ddOpt match { - case Some(dd: SbtDefaultDependencyDescriptor) => - val mod = dd.dependencyModuleId - ( - toExtraAttributes(dd.getExtraAttributes), - mod.isForce, - mod.isChanging, - mod.isTransitive, - mod.isForce - ) - case Some(dd) => - ( - toExtraAttributes(dd.getExtraAttributes), - dd.isForce, - dd.isChanging, - dd.isTransitive, - false - ) - case None => (Map.empty[String, String], false, false, true, false) - } - Caller( - m, - callerConfigurations, - extraAttributes, - isForce, - isChanging, - isTransitive, - isDirectlyForce - ) - } - val revId = dep.getResolvedId - val moduleId = toModuleID(revId) - val branch = nonEmptyString(revId.getBranch) - val (status, publicationDate, resolver, artifactResolver) = dep.isLoaded match { - case true => - val c = new ju.GregorianCalendar() - c.setTimeInMillis(dep.getPublication) - ( - nonEmptyString(dep.getDescriptor.getStatus), - Some(c), - nonEmptyString(dep.getModuleRevision.getResolver.getName), - nonEmptyString(dep.getModuleRevision.getArtifactResolver.getName) - ) - case _ => (None, None, None, None) - } - val (evicted, evictedData, evictedReason) = dep.isEvicted(confReport.getConfiguration) match { - case true => - val edOpt = Option(dep.getEvictedData(confReport.getConfiguration)) - edOpt match { - case Some(ed) => - ( - true, - nonEmptyString(Option(ed.getConflictManager) map { _.toString } getOrElse { - "transitive" - }), - nonEmptyString(ed.getDetail) - ) - case None => (true, None, None) - } - case _ => (false, None, None) - } - val problem = dep.hasProblem match { - case true => nonEmptyString(dep.getProblem.getMessage) - case _ => None - } - val mdOpt = for { - mr <- Option(dep.getModuleRevision) - md <- Option(mr.getDescriptor) - } yield md - val homepage = mdOpt match { - case Some(md) => - nonEmptyString(md.getHomePage) - case _ => None - } - val extraAttributes: Map[String, String] = toExtraAttributes(mdOpt match { - case Some(md) => md.getExtraAttributes - case _ => dep.getResolvedId.getExtraAttributes - }) - val isDefault = Option(dep.getDescriptor) map { _.isDefault } - val configurations = dep.getConfigurations(confReport.getConfiguration).toVector map { - ConfigRef(_) - } - val licenses: Vector[(String, Option[String])] = mdOpt match { - case Some(md) => - md.getLicenses.toVector collect { - case lic: IvyLicense if Option(lic.getName).isDefined => - val temporaryURL = "http://localhost" - (lic.getName, nonEmptyString(lic.getUrl) orElse { Some(temporaryURL) }) - } - case _ => Vector.empty - } - val callers = dep.getCallers(confReport.getConfiguration).toVector map { toCaller } - val (resolved, missing) = artifacts( - ArraySeq.unsafeWrapArray(confReport.getDownloadReports(revId)) - ) - - ModuleReport( - moduleId, - resolved, - missing, - status, - publicationDate, - resolver, - artifactResolver, - evicted, - evictedData, - evictedReason, - problem, - homepage, - extraAttributes, - isDefault, - branch, - configurations, - licenses, - callers - ) - } - - def evicted(confReport: ConfigurationResolveReport): Seq[ModuleID] = - ArraySeq.unsafeWrapArray(confReport.getEvictedNodes).map(node => toModuleID(node.getId)) - - def toModuleID(revID: ModuleRevisionId): ModuleID = - ModuleID(revID.getOrganisation, revID.getName, revID.getRevision) - .withExtraAttributes(IvySbt.getExtraAttributes(revID)) - .branch(nonEmptyString(revID.getBranch)) - - def toArtifact(art: IvyArtifact): Artifact = { - import art.* - Artifact( - getName, - getType, - getExt, - Option(getExtraAttribute("classifier")), - getConfigurations.toVector map { (c: String) => - ConfigRef(c) - }, - Option(getUrl).map(_.toURI) - ) - } - - def updateReport(report: ResolveReport, cachedDescriptor: File): UpdateReport = - UpdateReport( - cachedDescriptor, - reports(report) map configurationReport, - updateStats(report), - Map.empty - ).recomputeStamps() - def updateStats(report: ResolveReport): UpdateStats = - UpdateStats( - report.getResolveTime, - report.getDownloadTime, - report.getDownloadSize, - false, - Some(System.currentTimeMillis().toString) - ) - def configurationReport(confReport: ConfigurationResolveReport): ConfigurationReport = - ConfigurationReport( - ConfigRef(confReport.getConfiguration), - moduleReports(confReport), - organizationArtifactReports(confReport) - ) - - /** - * Tries to find Ivy graph path the from node to target. - */ - def findPath(target: IvyNode, from: ModuleRevisionId): List[IvyNode] = { - def doFindPath(current: IvyNode, path: List[IvyNode]): List[IvyNode] = { - // Ivy actually returns mix of direct and non-direct callers here. - // that's why we have to calculate all possible paths below and pick the longest path. - val callers = current.getAllRealCallers.toList - val callersRevId = (callers map { _.getModuleRevisionId }).distinct - val paths: List[List[IvyNode]] = ((callersRevId map { revId => - val node = current.findNode(revId) - if (revId == from) node :: path - else if (node == node.getRoot) Nil - else if (path.contains[IvyNode](node)) path - else doFindPath(node, node :: path) - }) sortBy { _.size }).reverse - paths.headOption getOrElse Nil - } - if (target.getId == from) List(target) - else doFindPath(target, List(target)) - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyScalaUtil.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyScalaUtil.scala deleted file mode 100644 index aee31c020..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyScalaUtil.scala +++ /dev/null @@ -1,232 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt.internal.librarymanagement - -import java.util.Collections.emptyMap - -import org.apache.ivy.core.module.descriptor.* -import org.apache.ivy.core.module.id.{ ArtifactId, ModuleId, ModuleRevisionId } -import org.apache.ivy.plugins.matcher.ExactPatternMatcher -import org.apache.ivy.plugins.namespace.NamespaceTransformer -import sbt.util.Logger -import sbt.librarymanagement.ScalaArtifacts.* -import sbt.librarymanagement.{ Configuration, CrossVersion, ScalaModuleInfo } - -object IvyScalaUtil { - - /** Performs checks/adds filters on Scala dependencies (if enabled in ScalaModuleInfo). */ - def checkModule( - module: DefaultModuleDescriptor, - scalaVersionConfigs: Vector[String], - log: Logger - )(check: ScalaModuleInfo): Unit = { - if (check.checkExplicit) - checkDependencies( - module, - check.scalaOrganization, - check.scalaArtifacts, - check.scalaBinaryVersion, - scalaVersionConfigs, - log - ) - if (check.filterImplicit) - excludeScalaJars(module, check.configurations) - if (check.overrideScalaVersion) - overrideScalaVersion( - module, - check.scalaOrganization, - check.scalaFullVersion, - scalaVersionConfigs - ) - } - - class OverrideScalaMediator( - scalaOrganization: String, - scalaVersion: String, - scalaVersionConfigs0: Vector[String] - ) extends DependencyDescriptorMediator { - private val scalaVersionConfigs = scalaVersionConfigs0.toSet - private val binaryVersion = CrossVersion.binaryScalaVersion(scalaVersion) - def mediate(dd: DependencyDescriptor): DependencyDescriptor = { - // Mediate only for the dependencies in scalaVersion configurations. https://github.com/sbt/sbt/issues/2786 - def configQualifies: Boolean = - dd.getModuleConfigurations exists { scalaVersionConfigs } - // Do not rewrite the dependencies of Scala dependencies themselves, this prevents bootstrapping - // a Scala compiler using another Scala compiler. - def dependeeQualifies: Boolean = - dd.getParentRevisionId == null || - !isScala2Artifact(dd.getParentRevisionId.getName) || - !isScala3Artifact(dd.getParentRevisionId.getName) - - def matchBinaryVersion(version: String): Boolean = - CrossVersion.binaryScalaVersion(version) == binaryVersion - - val transformer = - new NamespaceTransformer { - def transform(mrid: ModuleRevisionId): ModuleRevisionId = { - if (mrid == null) mrid - else if ( - (isScala2Artifact(mrid.getName) || isScala3Artifact(mrid.getName)) && - configQualifies && - dependeeQualifies - ) { - // do not override the binary incompatible Scala version because: - // - the artifacts compiled with Scala 3 depends on the Scala 2.13 scala-library - // - the Scala 2 TASTy reader can consume the Scala 3 artifacts - val newScalaVersion = - if (matchBinaryVersion(mrid.getRevision)) scalaVersion - else mrid.getRevision - - ModuleRevisionId.newInstance( - scalaOrganization, - mrid.getName, - mrid.getBranch, - newScalaVersion, - mrid.getQualifiedExtraAttributes - ) - } else mrid - } - - def isIdentity: Boolean = false - } - - DefaultDependencyDescriptor.transformInstance(dd, transformer, false) - } - } - - def overrideScalaVersion( - module: DefaultModuleDescriptor, - organization: String, - version: String, - scalaVersionConfigs: Vector[String] - ): Unit = { - val mediator = new OverrideScalaMediator(organization, version, scalaVersionConfigs) - module.addDependencyDescriptorMediator( - new ModuleId(Organization, "*"), - ExactPatternMatcher.INSTANCE, - mediator - ) - if (organization != Organization) - module.addDependencyDescriptorMediator( - new ModuleId(organization, "*"), - ExactPatternMatcher.INSTANCE, - mediator - ) - } - - def overrideVersion( - module: DefaultModuleDescriptor, - org: String, - name: String, - version: String - ): Unit = { - val id = new ModuleId(org, name) - val over = new OverrideDependencyDescriptorMediator(null, version) - module.addDependencyDescriptorMediator(id, ExactPatternMatcher.INSTANCE, over) - } - - /** - * Checks the immediate dependencies of module for dependencies on scala jars and verifies that the version on the - * dependencies matches scalaVersion. - */ - private def checkDependencies( - module: ModuleDescriptor, - scalaOrganization: String, - scalaArtifacts: Vector[String], - scalaBinaryVersion: String, - scalaVersionConfigs0: Vector[String], - log: Logger - ): Unit = { - val scalaVersionConfigs: String => Boolean = - if (scalaVersionConfigs0.isEmpty) (_: String) => false else scalaVersionConfigs0.toSet - def binaryScalaWarning(dep: DependencyDescriptor): Option[String] = { - val id = dep.getDependencyRevisionId - val depBinaryVersion = CrossVersion.binaryScalaVersion(id.getRevision) - def isScalaLangOrg = id.getOrganisation == scalaOrganization - def isScalaArtifact = scalaArtifacts.contains[String](id.getName) - - def hasBinVerMismatch = - depBinaryVersion != scalaBinaryVersion && - // scala 2.13 is compatible with scala 3.x - !Seq(depBinaryVersion, scalaBinaryVersion) - .forall(bv => bv.startsWith("3") || bv.startsWith("2.13")) - - def matchesOneOfTheConfigs = dep.getModuleConfigurations exists { scalaVersionConfigs } - val mismatched = - isScalaLangOrg && isScalaArtifact && hasBinVerMismatch && matchesOneOfTheConfigs - if (mismatched) - Some( - "Binary version (" + depBinaryVersion + ") for dependency " + id + - "\n\tin " + module.getModuleRevisionId + - " differs from Scala binary version in project (" + scalaBinaryVersion + ")." - ) - else - None - } - module.getDependencies.toList.flatMap(binaryScalaWarning).toSet foreach { (s: String) => - log.warn(s) - } - } - private def configurationSet(configurations: Iterable[Configuration]) = - configurations.map(_.toString).toSet - - /** - * Adds exclusions for the scala library and compiler jars so that they are not downloaded. This is - * done because these jars are provided by the ScalaInstance of the project. The version of Scala to use - * is done by setting scalaVersion in the project definition. - */ - private def excludeScalaJars( - module: DefaultModuleDescriptor, - configurations: Iterable[Configuration] - ): Unit = { - val configurationNames = { - val names = module.getConfigurationsNames - if (configurations.isEmpty) names - else { - val configSet = configurationSet(configurations) - configSet.toArray - } - } - def excludeScalaJar(name: String): Unit = - module.addExcludeRule(excludeRule(Organization, name, configurationNames, "jar")) - excludeScalaJar(LibraryID) - excludeScalaJar(CompilerID) - } - - /** - * Creates an ExcludeRule that excludes artifacts with the given module organization and name for - * the given configurations. - */ - private[sbt] def excludeRule( - organization: String, - name: String, - configurationNames: Iterable[String], - excludeTypePattern: String - ): ExcludeRule = { - val artifact = - new ArtifactId(ModuleId.newInstance(organization, name), "*", excludeTypePattern, "*") - val rule = - new DefaultExcludeRule(artifact, ExactPatternMatcher.INSTANCE, emptyMap[AnyRef, AnyRef]) - configurationNames.foreach(rule.addConfiguration) - rule - } - - /** - * Creates an IncludeRule that includes artifacts with the given module organization and name for - * the given configurations. - */ - private[sbt] def includeRule( - organization: String, - name: String, - configurationNames: Iterable[String], - includeTypePattern: String - ): IncludeRule = { - val artifact = - new ArtifactId(ModuleId.newInstance(organization, name), "*", includeTypePattern, "*") - val rule = - new DefaultIncludeRule(artifact, ExactPatternMatcher.INSTANCE, emptyMap[AnyRef, AnyRef]) - configurationNames.foreach(rule.addConfiguration) - rule - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyUtil.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyUtil.scala deleted file mode 100644 index 9b1e43bde..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/IvyUtil.scala +++ /dev/null @@ -1,58 +0,0 @@ -package sbt.internal.librarymanagement - -import java.io.IOException -import java.net.{ SocketException, SocketTimeoutException } - -import scala.annotation.tailrec -import scala.util.{ Failure, Success, Try } - -private[sbt] object IvyUtil { - def separate[A, B](l: Seq[Either[A, B]]): (Seq[A], Seq[B]) = - (l.flatMap(_.left.toOption), l.flatMap(_.toOption)) - - @tailrec - final def retryWithBackoff[T]( - f: => T, - predicate: Throwable => Boolean, - maxAttempts: Int, - retry: Int = 0 - ): T = { - // Using Try helps in catching NonFatal exceptions only - Try { - f - } match { - case Success(value) => value - case Failure(e) if predicate(e) && retry < (maxAttempts - 1) => - // max 8s backoff - val backoff = math.min(math.pow(2d, retry.toDouble).toLong * 1000L, 8000L) - Thread.sleep(backoff) - retryWithBackoff(f, predicate, maxAttempts, retry + 1) - case Failure(e) => throw e - } - } - - /** - * Currently transient network errors are defined as: - * - a network timeout - * - all server errors (response code 5xx) - * - rate limiting (response code 429) - */ - object TransientNetworkException { - private val _r = """.*HTTP response code: (5\d{2}|408|429).*""".r - - @inline private def check(s: String): Boolean = { - if (s == null) { - false - } else { - _r.pattern.matcher(s).matches() - } - } - - def apply(t: Throwable): Boolean = t match { - case _: SocketException | _: SocketTimeoutException => true - case e: IOException if check(e.getMessage) => true - case _ => false - } - } - -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/MakePom.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/MakePom.scala deleted file mode 100644 index 44e90b682..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/MakePom.scala +++ /dev/null @@ -1,485 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ - -// based on Ivy's PomModuleDescriptorWriter, which is Apache Licensed, Version 2.0 -// http://www.apache.org/licenses/LICENSE-2.0 - -package sbt.internal.librarymanagement - -import java.io.File -import sbt.util.Logger -import sbt.librarymanagement.* -import Resolver.* -import mavenint.PomExtraDependencyAttributes - -import scala.collection.immutable.ArraySeq -// Node needs to be renamed to XNode because the task subproject contains a Node type that will shadow -// scala.xml.Node when generating aggregated API documentation -import scala.xml.{ Elem, Node as XNode, NodeSeq, PrettyPrinter, PrefixedAttribute } -import Configurations.Optional - -import org.apache.ivy.Ivy -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.core.module.descriptor.{ - DependencyArtifactDescriptor, - DependencyDescriptor, - License, - ModuleDescriptor, - ExcludeRule -} -import org.apache.ivy.plugins.resolver.{ ChainResolver, DependencyResolver, IBiblioResolver } -import ivyint.CustomRemoteMavenResolver -import sbt.io.IO - -object MakePom { - - /** True if the revision is an ivy-range, not a complete revision. */ - def isDependencyVersionRange(revision: String): Boolean = VersionRange.isVersionRange(revision) - - /** Converts Ivy revision ranges to that of Maven POM */ - def makeDependencyVersion(revision: String): String = - VersionRange.fromIvyToMavenVersion(revision) -} -class MakePom(val log: Logger) { - import MakePom.* - def write( - ivy: Ivy, - module: ModuleDescriptor, - moduleInfo: ModuleInfo, - configurations: Option[Iterable[Configuration]], - includeTypes: Set[String], - extra: NodeSeq, - process: XNode => XNode, - filterRepositories: MavenRepository => Boolean, - allRepositories: Boolean, - output: File - ): Unit = - write( - process( - toPom( - ivy, - module, - moduleInfo, - configurations, - includeTypes, - extra, - filterRepositories, - allRepositories - ) - ), - output - ) - // use \n as newline because toString uses PrettyPrinter, which hard codes line endings to be \n - def write(node: XNode, output: File): Unit = write(toString(node), output, "\n") - def write(xmlString: String, output: File, newline: String): Unit = - IO.write(output, "" + newline + xmlString) - - def toString(node: XNode): String = new PrettyPrinter(1000, 4).format(node) - def toPom( - ivy: Ivy, - module: ModuleDescriptor, - moduleInfo: ModuleInfo, - configurations: Option[Iterable[Configuration]], - includeTypes: Set[String], - extra: NodeSeq, - filterRepositories: MavenRepository => Boolean, - allRepositories: Boolean - ): XNode = - ( - 4.0.0 - {makeModuleID(module)} - {moduleInfo.nameFormal} - {makeStartYear(moduleInfo)} - {makeOrganization(moduleInfo)} - {makeScmInfo(moduleInfo)} - {makeDeveloperInfo(moduleInfo)} - {extra} - { - val deps = depsInConfs(module, configurations) - val (bomDeps, regularDeps) = - deps.partition(d => - d.getAllDependencyArtifacts.nonEmpty && - d.getAllDependencyArtifacts.forall(_.getType == Artifact.PomType) - ) - makeProperties(module, deps) ++ - makeDependencyManagement(bomDeps) ++ - makeDependencies( - regularDeps, - includeTypes, - ArraySeq.unsafeWrapArray(module.getAllExcludeRules) - ) - } - {makeRepositories(ivy.getSettings, allRepositories, filterRepositories)} - ) - - def makeModuleID(module: ModuleDescriptor): NodeSeq = { - val mrid = moduleDescriptor(module) - val a: NodeSeq = - ({mrid.getOrganisation} - {mrid.getName} - {packaging(module)}) - val b: NodeSeq = - ((description(module.getDescription) ++ - homePage(module.getHomePage) ++ - revision(mrid.getRevision) ++ - licenses(module.getLicenses)): NodeSeq) - a ++ b - } - - def makeStartYear(moduleInfo: ModuleInfo): NodeSeq = - moduleInfo.startYear match { - case Some(y) => {y} - case _ => NodeSeq.Empty - } - def makeOrganization(moduleInfo: ModuleInfo): NodeSeq = { - - {moduleInfo.organizationName} - { - moduleInfo.organizationHomepage match { - case Some(h) => {h} - case _ => NodeSeq.Empty - } - } - - } - def makeScmInfo(moduleInfo: ModuleInfo): NodeSeq = { - moduleInfo.scmInfo match { - case Some(s) => - - {s.browseUrl} - {s.connection} - { - s.devConnection match { - case Some(d) => {d} - case _ => NodeSeq.Empty - } - } - - case _ => NodeSeq.Empty - } - } - def makeDeveloperInfo(moduleInfo: ModuleInfo): NodeSeq = { - if (moduleInfo.developers.nonEmpty) { - - { - moduleInfo.developers.map { (developer: Developer) => - - {developer.id} - {developer.name} - {developer.url} - { - developer.email match { - case "" | null => NodeSeq.Empty - case e => {e} - } - } - - } - } - - } else NodeSeq.Empty - } - def makeProperties(module: ModuleDescriptor, dependencies: Seq[DependencyDescriptor]): NodeSeq = { - val extra = IvySbt.getExtraAttributes(module) - val depExtra = PomExtraDependencyAttributes.writeDependencyExtra(dependencies).mkString("\n") - val allExtra = - if (depExtra.isEmpty) extra - else extra.updated(PomExtraDependencyAttributes.ExtraAttributesKey, depExtra) - if (allExtra.isEmpty) NodeSeq.Empty else makeProperties(allExtra) - } - def makeProperties(extra: Map[String, String]): NodeSeq = { - def _extraAttributes(k: String) = - if (k == PomExtraDependencyAttributes.ExtraAttributesKey) xmlSpacePreserve - else scala.xml.Null - { - for ((key, value) <- extra) - yield ({value}).copy(label = key, attributes = _extraAttributes(key)) - } - } - - /** - * Attribute tag that PrettyPrinter won't ignore, saying "don't mess with my spaces" - * Without this, PrettyPrinter will flatten multiple entries for ExtraDependencyAttributes and make them - * unparseable. (e.g. a plugin that depends on multiple plugins will fail) - */ - def xmlSpacePreserve = new PrefixedAttribute("xml", "space", "preserve", scala.xml.Null) - - def description(d: String) = - if ((d eq null) || d.isEmpty) NodeSeq.Empty - else - { - d - } - def licenses(ls: Array[License]) = - if (ls == null || ls.isEmpty) NodeSeq.Empty - else - { - ls.map(license) - } - def license(l: License) = - - {l.getName} - {l.getUrl} - repo - - def homePage(homePage: String) = - if (homePage eq null) NodeSeq.Empty - else - { - homePage - } - def revision(version: String) = - if (version ne null) { - version - } - else NodeSeq.Empty - def packaging(module: ModuleDescriptor) = - module.getAllArtifacts match { - case Array() => "pom" - case Array(x) => x.getType - case xs => - val types = xs.map(_.getType).toList.filterNot(IgnoreTypes) - types match { - case Nil => Artifact.PomType - case xs if xs.contains(Artifact.DefaultType) => Artifact.DefaultType - case x :: (xs @ _) => x - } - } - val IgnoreTypes: Set[String] = Set(Artifact.SourceType, Artifact.DocType, Artifact.PomType) - - /** BOM (Bill of Materials) deps: output under <dependencyManagement> with type pom, scope import (sbt#4531). */ - def makeDependencyManagement(dependencies: Seq[DependencyDescriptor]): NodeSeq = - if (dependencies.isEmpty) - NodeSeq.Empty - else - - - {dependencies.map(makeBomDependencyElem)} - - - - def makeBomDependencyElem(dependency: DependencyDescriptor): Elem = { - val mrid = dependency.getDependencyRevisionId - - {mrid.getOrganisation} - {mrid.getName} - {makeDependencyVersion(mrid.getRevision)} - pom - import - - } - - def makeDependencies( - dependencies: Seq[DependencyDescriptor], - includeTypes: Set[String], - excludes: Seq[ExcludeRule] - ): NodeSeq = - if (dependencies.isEmpty) - NodeSeq.Empty - else - - { - dependencies.map(makeDependency(_, includeTypes, excludes)) - } - - - def makeDependency( - dependency: DependencyDescriptor, - includeTypes: Set[String], - excludes: Seq[ExcludeRule] - ): NodeSeq = { - val artifacts = dependency.getAllDependencyArtifacts - val includeArtifacts = artifacts.filter(d => includeTypes(d.getType)) - if (artifacts.isEmpty) { - val configs = dependency.getModuleConfigurations - if (configs.filterNot(Set("sources", "docs")).nonEmpty) { - val (scope, optional) = getScopeAndOptional(dependency.getModuleConfigurations) - makeDependencyElem(dependency, scope, optional, None, None, excludes) - } else NodeSeq.Empty - } else if (includeArtifacts.isEmpty) - NodeSeq.Empty - else - NodeSeq.fromSeq(artifacts.flatMap(a => makeDependencyElem(dependency, a, excludes))) - } - - def makeDependencyElem( - dependency: DependencyDescriptor, - artifact: DependencyArtifactDescriptor, - excludes: Seq[ExcludeRule] - ): Option[Elem] = { - val configs = artifact.getConfigurations.toList match { - case Nil | "*" :: Nil => dependency.getModuleConfigurations - case x => x.toArray - } - if (!configs.forall(Set("sources", "docs"))) { - val (scope, optional) = getScopeAndOptional(configs) - val classifier = artifactClassifier(artifact) - val baseType = artifactType(artifact) - val tpe = (classifier, baseType) match { - case (Some(c), Some(tpe)) if Artifact.classifierType(c) == tpe => None - case _ => baseType - } - Some(makeDependencyElem(dependency, scope, optional, classifier, tpe, excludes)) - } else None - } - - def makeDependencyElem( - dependency: DependencyDescriptor, - scope: Option[String], - optional: Boolean, - classifier: Option[String], - tpe: Option[String], - excludes: Seq[ExcludeRule] - ): Elem = { - val mrid = dependency.getDependencyRevisionId - val rev = mrid.getRevision - val versionNode: NodeSeq = - if (rev == null || rev == "*" || rev.isEmpty) NodeSeq.Empty - else {makeDependencyVersion(rev)} - val result: Elem = - - {mrid.getOrganisation} - {mrid.getName} - {versionNode} - {scopeElem(scope)} - {optionalElem(optional)} - {classifierElem(classifier)} - {typeElem(tpe)} - {exclusions(dependency, excludes)} - - result - } - - def artifactType(artifact: DependencyArtifactDescriptor): Option[String] = - Option(artifact.getType).flatMap { tpe => - if (tpe == "jar") None else Some(tpe) - } - def typeElem(tpe: Option[String]): NodeSeq = - tpe match { - case Some(t) => {t} - case None => NodeSeq.Empty - } - - def artifactClassifier(artifact: DependencyArtifactDescriptor): Option[String] = - Option(artifact.getExtraAttribute("classifier")) - def classifierElem(classifier: Option[String]): NodeSeq = - classifier match { - case Some(c) => {c} - case None => NodeSeq.Empty - } - - def scopeElem(scope: Option[String]): NodeSeq = scope match { - case None | Some(Configurations.Compile.name) => NodeSeq.Empty - case Some(s) => {s} - } - def optionalElem(opt: Boolean) = if (opt) true else NodeSeq.Empty - def moduleDescriptor(module: ModuleDescriptor) = module.getModuleRevisionId - - def getScopeAndOptional(confs: Array[String]): (Option[String], Boolean) = { - val (opt, notOptional) = confs.partition(_ == Optional.name) - val defaultNotOptional = - Configurations.defaultMavenConfigurations.find({ (c: Configuration) => - notOptional contains c.name - }) - val scope = defaultNotOptional.map(_.name) - (scope, opt.nonEmpty) - } - - def exclusions(dependency: DependencyDescriptor, excludes: Seq[ExcludeRule]): NodeSeq = { - val excl = ArraySeq.unsafeWrapArray( - dependency.getExcludeRules(dependency.getModuleConfigurations) - ) ++ excludes - val (warns, excls) = IvyUtil.separate(excl.map(makeExclusion)) - if (warns.nonEmpty) log.warn(warns.mkString(IO.Newline)) - if (excls.nonEmpty) { - excls - } - else NodeSeq.Empty - } - def makeExclusion(exclRule: ExcludeRule): Either[String, NodeSeq] = { - val m = exclRule.getId.getModuleId - val (g, a) = (m.getOrganisation, m.getName) - if (g == null || g.isEmpty || a == null || a.isEmpty) - Left( - s"Skipped generating '' for ${m}. Dependency exclusion should have both 'org' and 'module' to comply with Maven POM's schema." - ) - else - Right( - - {g} - {a} - - ) - } - - def makeRepositories( - settings: IvySettings, - includeAll: Boolean, - filterRepositories: MavenRepository => Boolean - ) = { - val repositories = - if (includeAll) allResolvers(settings) else resolvers(settings.getDefaultResolver) - val mavenRepositories = - repositories.flatMap { - case m: CustomRemoteMavenResolver if m.repo.root != DefaultMavenRepository.root => - MavenRepository(m.repo.name, m.repo.root) :: Nil - case m: IBiblioResolver if m.isM2compatible && m.getRoot != DefaultMavenRepository.root => - MavenRepository(m.getName, m.getRoot) :: Nil - case _ => Nil - } - val repositoryElements = mavenRepositories.withFilter(filterRepositories).map(mavenRepository) - if (repositoryElements.isEmpty) repositoryElements - else - { - repositoryElements - } - } - def allResolvers(settings: IvySettings): Seq[DependencyResolver] = - flatten(castResolvers(settings.getResolvers)).distinct - def flatten(rs: Seq[DependencyResolver]): Seq[DependencyResolver] = - if (rs eq null) Nil else rs.flatMap(resolvers) - def resolvers(r: DependencyResolver): Seq[DependencyResolver] = - r match { case c: ChainResolver => flatten(castResolvers(c.getResolvers)); case _ => r :: Nil } - - // cast the contents of a pre-generics collection - private def castResolvers(s: java.util.Collection[?]): Seq[DependencyResolver] = { - import scala.jdk.CollectionConverters.* - s.asScala.toSeq.map(_.asInstanceOf[DependencyResolver]) - } - - def toID(name: String) = checkID(name.filter(isValidIDCharacter).mkString, name) - def isValidIDCharacter(c: Char) = !"""\/:"<>|?*""".contains(c) - private def checkID(id: String, name: String) = - if (id.isEmpty) sys.error("Could not convert '" + name + "' to an ID") else id - def mavenRepository(repo: MavenRepository): XNode = - mavenRepository(toID(repo.name), repo.name, repo.root) - def mavenRepository(id: String, name: String, root: String): XNode = - - {id} - {name} - {root} - {"default"} - - - /** - * Retain dependencies only with the configurations given, or all public configurations of `module` if `configurations` is None. - * This currently only preserves the information required by makePom - */ - private def depsInConfs( - module: ModuleDescriptor, - configurations: Option[Iterable[Configuration]] - ): Seq[DependencyDescriptor] = { - val keepConfigurations = IvySbt.getConfigurations(module, configurations) - val keepSet: Set[String] = keepConfigurations.toSet - def translate(dependency: DependencyDescriptor) = { - val keep = dependency.getModuleConfigurations - .filter((conf: String) => keepSet.contains(conf)) - if (keep.isEmpty) - None - else // TODO: translate the dependency to contain only configurations to keep - Some(dependency) - } - ArraySeq.unsafeWrapArray(module.getDependencies) flatMap translate - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ProjectResolver.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ProjectResolver.scala deleted file mode 100644 index f5d4d1d9c..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ProjectResolver.scala +++ /dev/null @@ -1,108 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2011 Mark Harrah - */ -package sbt.internal.librarymanagement - -import java.io.File -import java.util.Date - -import org.apache.ivy.core.{ cache, module, report, resolve, search } -import cache.ArtifactOrigin -import search.{ ModuleEntry, OrganisationEntry, RevisionEntry } -import module.id.ModuleRevisionId -import module.descriptor.{ - Artifact as IArtifact, - DefaultArtifact, - DependencyDescriptor, - ModuleDescriptor -} -import org.apache.ivy.plugins.namespace.Namespace -import org.apache.ivy.plugins.resolver.ResolverSettings -import report.{ - ArtifactDownloadReport, - DownloadReport, - DownloadStatus, - MetadataArtifactDownloadReport -} -import resolve.{ DownloadOptions, ResolveData, ResolvedModuleRevision } - -/** - * A Resolver that uses a predefined mapping from module ids to in-memory descriptors. - * It does not handle artifacts. - */ -class ProjectResolver(name: String, map: Map[ModuleRevisionId, ModuleDescriptor]) - extends ResolverAdapter { - def getName = name - def setName(name: String) = sys.error("Setting name not supported by ProjectResolver") - override def toString = "ProjectResolver(" + name + ", mapped: " + map.keys.mkString(", ") + ")" - - def getDependency(dd: DependencyDescriptor, data: ResolveData): ResolvedModuleRevision = - getDependency(dd.getDependencyRevisionId).orNull - - private def getDependency(revisionId: ModuleRevisionId): Option[ResolvedModuleRevision] = { - def constructResult(descriptor: ModuleDescriptor) = - new ResolvedModuleRevision(this, this, descriptor, report(revisionId), true) - map get revisionId map constructResult - } - - private[sbt] def getModuleDescriptor(revisionId: ModuleRevisionId): Option[ModuleDescriptor] = - map.get(revisionId) - - def report(revisionId: ModuleRevisionId): MetadataArtifactDownloadReport = { - val artifact = DefaultArtifact.newIvyArtifact(revisionId, new Date) - val r = new MetadataArtifactDownloadReport(artifact) - r.setSearched(false) - r.setDownloadStatus(DownloadStatus.FAILED) - r - } - - // this resolver never locates artifacts, only resolves dependencies - def exists(artifact: IArtifact) = false - def locate(artifact: IArtifact) = null - def download(artifacts: Array[IArtifact], options: DownloadOptions): DownloadReport = { - val r = new DownloadReport - for (artifact <- artifacts) - if (getDependency(artifact.getModuleRevisionId).isEmpty) - r.addArtifactReport(notDownloaded(artifact)) - r - } - - def download(artifact: ArtifactOrigin, options: DownloadOptions): ArtifactDownloadReport = - notDownloaded(artifact.getArtifact) - def findIvyFileRef(dd: DependencyDescriptor, data: ResolveData) = null - - def notDownloaded(artifact: IArtifact): ArtifactDownloadReport = { - val r = new ArtifactDownloadReport(artifact) - r.setDownloadStatus(DownloadStatus.FAILED) - r - } - - // doesn't support publishing - def publish(artifact: IArtifact, src: File, overwrite: Boolean) = - sys.error("Publish not supported by ProjectResolver") - def beginPublishTransaction(module: ModuleRevisionId, overwrite: Boolean): Unit = () - def abortPublishTransaction(): Unit = () - def commitPublishTransaction(): Unit = () - - def reportFailure(): Unit = () - def reportFailure(art: IArtifact): Unit = () - - def listOrganisations() = new Array[OrganisationEntry](0) - def listModules(org: OrganisationEntry) = new Array[ModuleEntry](0) - def listRevisions(module: ModuleEntry) = new Array[RevisionEntry](0) - - def getNamespace = Namespace.SYSTEM_NAMESPACE - - private var settings: Option[ResolverSettings] = None - - def dumpSettings(): Unit = () - def setSettings(settings: ResolverSettings): Unit = { this.settings = Some(settings) } - def getRepositoryCacheManager = settings match { - case Some(s) => s.getDefaultRepositoryCacheManager; - case None => sys.error("No settings defined for ProjectResolver") - } -} - -object ProjectResolver { - private[sbt] val InterProject = "inter-project" -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ResolutionCache.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ResolutionCache.scala deleted file mode 100644 index 940098595..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ResolutionCache.scala +++ /dev/null @@ -1,107 +0,0 @@ -package sbt.internal.librarymanagement - -import java.io.File -import org.apache.ivy.core -import org.apache.ivy.plugins.parser -import core.IvyPatternHelper -import core.settings.IvySettings -import core.cache.ResolutionCacheManager -import core.module.id.ModuleRevisionId -import core.module.descriptor.ModuleDescriptor -import ResolutionCache.{ Name, ReportDirectory, ResolvedName, ResolvedPattern } -import parser.xml.XmlModuleDescriptorParser -import sbt.io.IO -import sbt.librarymanagement.* - -/** - * Replaces the standard Ivy resolution cache in order to: - * 1. Separate cached resolved Ivy files from resolution reports, making the resolution reports easier to find. - * 2. Have them per-project for easier cleaning (possible with standard cache, but central to this custom one). - * 3. Cache location includes extra attributes so that cross builds of a plugin do not overwrite each other. - */ -private[sbt] final class ResolutionCache(base: File, settings: IvySettings) - extends ResolutionCacheManager { - private def resolvedFileInCache(m: ModuleRevisionId, name: String, ext: String): File = { - val p = ResolvedPattern - val f = IvyPatternHelper.substitute( - p, - m.getOrganisation, - m.getName, - m.getBranch, - m.getRevision, - name, - name, - ext, - null, - null, - m.getAttributes, - null - ) - new File(base, f) - } - private val reportBase: File = new File(base, ReportDirectory) - - def getResolutionCacheRoot: File = base - def clean(): Unit = IO.delete(base) - override def toString = Name - - def getResolvedIvyFileInCache(mrid: ModuleRevisionId): File = - resolvedFileInCache(mrid, ResolvedName, "xml") - def getResolvedIvyPropertiesInCache(mrid: ModuleRevisionId): File = - resolvedFileInCache(mrid, ResolvedName, "properties") - // name needs to be the same as Ivy's default because the ivy-report.xsl stylesheet assumes this - // when making links to reports for other configurations - def getConfigurationResolveReportInCache(resolveId: String, conf: String): File = - new File(reportBase, resolveId + "-" + conf + ".xml") - def getConfigurationResolveReportsInCache(resolveId: String): Array[File] = - IO.listFiles(reportBase).filter(_.getName.startsWith(resolveId + "-")) - - // XXX: this method is required by ResolutionCacheManager in Ivy 2.3.0 final, - // but it is apparently unused by Ivy as sbt uses Ivy. Therefore, it is - // unexercised in tests. Note that the implementation of this method in Ivy 2.3.0's - // DefaultResolutionCache also resolves parent properties for a given mrid - def getResolvedModuleDescriptor(mrid: ModuleRevisionId): ModuleDescriptor = { - val ivyFile = getResolvedIvyFileInCache(mrid) - if (!ivyFile.exists()) { - throw new IllegalStateException("Ivy file not found in cache for " + mrid + "!") - } - - XmlModuleDescriptorParser.getInstance().parseDescriptor(settings, ivyFile.toURI.toURL, false) - } - - def saveResolvedModuleDescriptor(md: ModuleDescriptor): Unit = { - val mrid = md.getResolvedModuleRevisionId - val cachedIvyFile = getResolvedIvyFileInCache(mrid) - md.toIvyFile(cachedIvyFile) - } -} -private[sbt] object ResolutionCache { - - /** - * Removes cached files from the resolution cache for the module with ID `mrid` - * and the resolveId (as set on `ResolveOptions`). - */ - private[sbt] def cleanModule( - mrid: ModuleRevisionId, - resolveId: String, - manager: ResolutionCacheManager - ): Unit = { - val files = - Option(manager.getResolvedIvyFileInCache(mrid)).toList ::: - Option(manager.getResolvedIvyPropertiesInCache(mrid)).toList ::: - Option(manager.getConfigurationResolveReportsInCache(resolveId)).toList.flatten - IO.delete(files) - } - - private val ReportDirectory = "reports" - - // base name (name except for extension) of resolution report file - private val ResolvedName = "resolved.xml" - - // Cache name - private val Name = "sbt-resolution-cache" - - // use sbt-specific extra attributes so that resolved xml files do not get overwritten when using different Scala/sbt versions - private val ResolvedPattern = - "[organisation]/[module]/" + Resolver.PluginPattern + "([branch]/)[revision]/[artifact].[ext]" -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/CircularDependencyLevel.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/CircularDependencyLevel.scala deleted file mode 100644 index b2acb622b..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/CircularDependencyLevel.scala +++ /dev/null @@ -1,33 +0,0 @@ -package sbt.internal.librarymanagement -package ivy - -import org.apache.ivy.plugins.circular.{ - CircularDependencyStrategy, - WarnCircularDependencyStrategy, - IgnoreCircularDependencyStrategy, - ErrorCircularDependencyStrategy -} - -/** - * Wrapper around circular dependency strategy. - */ -sealed trait CircularDependencyLevel { - private[sbt] def ivyStrategy: CircularDependencyStrategy - private[sbt] def name: String - override def toString: String = name -} - -object CircularDependencyLevel { - val Warn: CircularDependencyLevel = new CircularDependencyLevel { - def ivyStrategy: CircularDependencyStrategy = WarnCircularDependencyStrategy.getInstance - def name: String = "warn" - } - val Ignore: CircularDependencyLevel = new CircularDependencyLevel { - def ivyStrategy: CircularDependencyStrategy = IgnoreCircularDependencyStrategy.getInstance - def name: String = "ignore" - } - val Error: CircularDependencyLevel = new CircularDependencyLevel { - def ivyStrategy: CircularDependencyStrategy = ErrorCircularDependencyStrategy.getInstance - def name: String = "error" - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyCredentials.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyCredentials.scala deleted file mode 100644 index 64cfabdb5..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyCredentials.scala +++ /dev/null @@ -1,74 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package sbt.internal.librarymanagement -package ivy - -import java.io.File -import org.apache.ivy.util.url.CredentialsStore -import sbt.internal.librarymanagement.IvyUtil -import sbt.io.IO -import sbt.librarymanagement.Credentials -import sbt.util.Logger - -object IvyCredentials { - - /** Add the provided credentials to Ivy's credentials cache. */ - def add(realm: String, host: String, userName: String, passwd: String): Unit = - CredentialsStore.INSTANCE.addCredentials(realm, host, userName, passwd) - - /** Load credentials from the given file into Ivy's credentials cache. */ - def add(path: File, log: Logger): Unit = - loadCredentials(path) match { - case Left(err) => log.warn(err) - case Right(dc) => add(dc.realm, dc.host, dc.userName, dc.passwd) - } - - def forHost(sc: Seq[Credentials], host: String) = allDirect(sc) find { _.host == host } - def allDirect(sc: Seq[Credentials]): Seq[Credentials.DirectCredentials] = sc map toDirect - def toDirect(c: Credentials): Credentials.DirectCredentials = c match { - case dc: Credentials.DirectCredentials => dc - case fc: Credentials.FileCredentials => - loadCredentials(fc.path) match { - case Left(err) => sys.error(err) - case Right(dc) => dc - } - } - - def loadCredentials(path: File): Either[String, Credentials.DirectCredentials] = - if (path.exists) { - val properties = read(path) - def get(keys: List[String]): Either[String, String] = - keys - .flatMap(properties.get) - .headOption - .toRight(keys.head + " not specified in credentials file: " + path) - - IvyUtil.separate(List(HostKeys, UserKeys, PasswordKeys).map(get)) match - case (Nil, List(host: String, user: String, pass: String)) => - IvyUtil.separate(List(RealmKeys).map(get)) match - case (_, List(realm: String)) => - Right(new Credentials.DirectCredentials(realm, host, user, pass)) - case _ => Right(new Credentials.DirectCredentials(null, host, user, pass)) - - case (errors, _) => Left(errors.mkString("\n")) - } else Left("Credentials file " + path + " does not exist") - - def register(cs: Seq[Credentials], log: Logger): Unit = - cs foreach { - case f: Credentials.FileCredentials => add(f.path, log) - case d: Credentials.DirectCredentials => add(d.realm, d.host, d.userName, d.passwd) - } - - private val RealmKeys = List("realm") - private val HostKeys = List("host", "hostname") - private val UserKeys = List("user", "user.name", "username") - private val PasswordKeys = List("password", "pwd", "pass", "passwd") - - import scala.jdk.CollectionConverters.* - private def read(from: File): Map[String, String] = { - val properties = new java.util.Properties - IO.load(properties, from) - properties.asScala.map { (k, v) => (k, v.trim) }.toMap - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyDefaults.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyDefaults.scala deleted file mode 100644 index 95819dd0a..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyDefaults.scala +++ /dev/null @@ -1,10 +0,0 @@ -package sbt -package internal -package librarymanagement.ivy - -/** - * This is a list of functions with default values. - */ -object IvyDefaults { - val defaultChecksums: Vector[String] = Vector("sha1", "md5") -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyDependencyResolution.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyDependencyResolution.scala deleted file mode 100644 index d5af49ea1..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyDependencyResolution.scala +++ /dev/null @@ -1,32 +0,0 @@ -package sbt -package internal -package librarymanagement -package ivy - -import sbt.librarymanagement.* -import sbt.util.Logger - -class IvyDependencyResolution private[sbt] (val ivySbt: IvySbt) - extends DependencyResolutionInterface { - type Module = ivySbt.Module - - override def moduleDescriptor(moduleSetting: ModuleDescriptorConfiguration): ModuleDescriptor = { - new Module(moduleSetting) - } - - override def update( - module: ModuleDescriptor, - configuration: UpdateConfiguration, - uwconfig: UnresolvedWarningConfiguration, - log: Logger - ): Either[UnresolvedWarning, UpdateReport] = - IvyActions.updateEither(toModule(module), configuration, uwconfig, log) - - private[sbt] def toModule(module: ModuleDescriptor): Module = - module.asInstanceOf[Module] -} - -object IvyDependencyResolution { - def apply(ivyConfiguration: IvyConfiguration): DependencyResolution = - DependencyResolution(new IvyDependencyResolution(new IvySbt(ivyConfiguration))) -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyLibraryManagementCodec.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyLibraryManagementCodec.scala deleted file mode 100644 index 023ae5bb9..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyLibraryManagementCodec.scala +++ /dev/null @@ -1,17 +0,0 @@ -package sbt.internal.librarymanagement -package ivy - -trait IvyLibraryManagementCodec - extends sjsonnew.BasicJsonProtocol - with sbt.librarymanagement.LibraryManagementCodec - with sbt.internal.librarymanagement.formats.GlobalLockFormat - with sbt.internal.librarymanagement.formats.LoggerFormat - with sbt.internal.librarymanagement.ivy.formats.UpdateOptionsFormat - with sbt.librarymanagement.IvyPathsFormats - with sbt.librarymanagement.ResolverFormats - with sbt.librarymanagement.ModuleConfigurationFormats - with InlineIvyConfigurationFormats - with ExternalIvyConfigurationFormats - with IvyConfigurationFormats - -object IvyLibraryManagementCodec extends IvyLibraryManagementCodec diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyPublisher.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyPublisher.scala deleted file mode 100644 index a7510a16d..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/IvyPublisher.scala +++ /dev/null @@ -1,38 +0,0 @@ -package sbt -package internal -package librarymanagement -package ivy - -import sbt.librarymanagement.* -import sbt.util.Logger -import java.io.File - -class IvyPublisher private[sbt] (val ivySbt: IvySbt) extends PublisherInterface { - type Module = ivySbt.Module - - override def moduleDescriptor(moduleSetting: ModuleDescriptorConfiguration): ModuleDescriptor = { - new Module(moduleSetting) - } - - override def makePomFile( - module: ModuleDescriptor, - configuration: MakePomConfiguration, - log: Logger - ): File = - IvyActions.makePomFile(toModule(module), configuration, log) - - override def publish( - module: ModuleDescriptor, - configuration: PublishConfiguration, - log: Logger - ): Unit = - IvyActions.publish(toModule(module), configuration, log) - - private[sbt] def toModule(module: ModuleDescriptor): Module = - module.asInstanceOf[Module] -} - -object IvyPublisher { - def apply(ivyConfiguration: IvyConfiguration): Publisher = - Publisher(new IvyPublisher(new IvySbt(ivyConfiguration))) -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/UpdateOptions.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/UpdateOptions.scala deleted file mode 100644 index d6a8617c6..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/UpdateOptions.scala +++ /dev/null @@ -1,117 +0,0 @@ -package sbt.internal.librarymanagement -package ivy - -import org.apache.ivy.plugins.resolver.DependencyResolver -import org.apache.ivy.core.settings.IvySettings -import sbt.util.Logger -import sbt.librarymanagement.* - -/** - * Represents configurable options for update task. - * While UpdateConfiguration is passed into update at runtime, - * UpdateOption is intended to be used while setting up the Ivy object. - * - * See also UpdateConfiguration in IvyActions.scala. - */ -final class UpdateOptions private[sbt] ( - // If set to CircularDependencyLevel.Error, halt the dependency resolution. - val circularDependencyLevel: CircularDependencyLevel, - // If set to true, prioritize inter-project resolver - val interProjectFirst: Boolean, - // If set to true, check all resolvers for snapshots. - val latestSnapshots: Boolean, - // If set to true, use cached resolution. - val cachedResolution: Boolean, - // If set to true, use Gigahorse - val gigahorse: Boolean, - // Extension point for an alternative resolver converter. - val resolverConverter: UpdateOptions.ResolverConverter, - // Map the unique resolver to be checked for the module ID - val moduleResolvers: Map[ModuleID, Resolver] -) { - def withCircularDependencyLevel( - circularDependencyLevel: CircularDependencyLevel - ): UpdateOptions = - copy(circularDependencyLevel = circularDependencyLevel) - def withInterProjectFirst(interProjectFirst: Boolean): UpdateOptions = - copy(interProjectFirst = interProjectFirst) - def withLatestSnapshots(latestSnapshots: Boolean): UpdateOptions = - copy(latestSnapshots = latestSnapshots) - def withCachedResolution(cachedResolution: Boolean): UpdateOptions = - copy(cachedResolution = cachedResolution) - - def withGigahorse(gigahorse: Boolean): UpdateOptions = - copy(gigahorse = gigahorse) - - /** Extention point for an alternative resolver converter. */ - def withResolverConverter(resolverConverter: UpdateOptions.ResolverConverter): UpdateOptions = - copy(resolverConverter = resolverConverter) - - def withModuleResolvers(moduleResolvers: Map[ModuleID, Resolver]): UpdateOptions = - copy(moduleResolvers = moduleResolvers) - - private[sbt] def copy( - circularDependencyLevel: CircularDependencyLevel = this.circularDependencyLevel, - interProjectFirst: Boolean = this.interProjectFirst, - latestSnapshots: Boolean = this.latestSnapshots, - cachedResolution: Boolean = this.cachedResolution, - gigahorse: Boolean = this.gigahorse, - resolverConverter: UpdateOptions.ResolverConverter = this.resolverConverter, - moduleResolvers: Map[ModuleID, Resolver] = this.moduleResolvers - ): UpdateOptions = - new UpdateOptions( - circularDependencyLevel, - interProjectFirst, - latestSnapshots, - cachedResolution, - gigahorse, - resolverConverter, - moduleResolvers - ) - - override def toString(): String = - s"""UpdateOptions( - | circularDependencyLevel = $circularDependencyLevel, - | latestSnapshots = $latestSnapshots, - | cachedResolution = $cachedResolution - |)""".stripMargin - - override def equals(o: Any): Boolean = o match { - case o: UpdateOptions => - this.circularDependencyLevel == o.circularDependencyLevel && - this.interProjectFirst == o.interProjectFirst && - this.latestSnapshots == o.latestSnapshots && - this.cachedResolution == o.cachedResolution && - this.gigahorse == o.gigahorse && - this.resolverConverter == o.resolverConverter && - this.moduleResolvers == o.moduleResolvers - case _ => false - } - - override def hashCode: Int = { - var hash = 1 - hash = hash * 31 + this.circularDependencyLevel.## - hash = hash * 31 + this.interProjectFirst.## - hash = hash * 31 + this.latestSnapshots.## - hash = hash * 31 + this.cachedResolution.## - hash = hash * 31 + this.gigahorse.## - hash = hash * 31 + this.resolverConverter.## - hash = hash * 31 + this.moduleResolvers.## - hash - } -} - -object UpdateOptions { - type ResolverConverter = PartialFunction[(Resolver, IvySettings, Logger), DependencyResolver] - - def apply(): UpdateOptions = - new UpdateOptions( - circularDependencyLevel = CircularDependencyLevel.Warn, - interProjectFirst = true, - latestSnapshots = true, - cachedResolution = false, - gigahorse = LMSysProp.useGigahorse, - resolverConverter = PartialFunction.empty, - moduleResolvers = Map.empty - ) -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/formats/UpdateOptionsFormat.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/formats/UpdateOptionsFormat.scala deleted file mode 100644 index ca891f729..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivy/formats/UpdateOptionsFormat.scala +++ /dev/null @@ -1,63 +0,0 @@ -package sbt.internal.librarymanagement.ivy -package formats - -import sjsonnew.* -import sbt.librarymanagement.* - -trait UpdateOptionsFormat { - self: BasicJsonProtocol & ModuleIDFormats & ResolverFormats & - sbt.librarymanagement.ArtifactFormats & sbt.librarymanagement.ConfigRefFormats & - sbt.librarymanagement.ChecksumFormats & sbt.librarymanagement.InclExclRuleFormats & - sbt.librarymanagement.CrossVersionFormats & sbt.librarymanagement.DisabledFormats & - sbt.librarymanagement.BinaryFormats & sbt.librarymanagement.ConstantFormats & - sbt.librarymanagement.PatchFormats & sbt.librarymanagement.FullFormats & - sbt.librarymanagement.For3Use2_13Formats & sbt.librarymanagement.For2_13Use3Formats & - sbt.librarymanagement.ChainedResolverFormats & sbt.librarymanagement.MavenRepoFormats & - sbt.librarymanagement.MavenCacheFormats & sbt.librarymanagement.PatternsFormats & - sbt.librarymanagement.FileConfigurationFormats & sbt.librarymanagement.FileRepositoryFormats & - sbt.librarymanagement.URLRepositoryFormats & sbt.librarymanagement.SshConnectionFormats & - sbt.librarymanagement.SshAuthenticationFormats & sbt.librarymanagement.SshRepositoryFormats & - sbt.librarymanagement.SftpRepositoryFormats & - sbt.librarymanagement.PasswordAuthenticationFormats & - sbt.librarymanagement.KeyFileAuthenticationFormats => - /* This is necessary to serialize/deserialize `directResolvers`. */ - private given moduleIdJsonKeyFormat: sjsonnew.JsonKeyFormat[ModuleID] = { - new sjsonnew.JsonKeyFormat[ModuleID] { - import sjsonnew.support.scalajson.unsafe.* - val moduleIdFormat: JsonFormat[ModuleID] = implicitly[JsonFormat[ModuleID]] - def write(key: ModuleID): String = - CompactPrinter(Converter.toJsonUnsafe(key)(using moduleIdFormat)) - def read(key: String): ModuleID = - Converter.fromJsonUnsafe[ModuleID](Parser.parseUnsafe(key))(using moduleIdFormat) - } - } - - given UpdateOptionsFormat: JsonFormat[UpdateOptions] = - projectFormat( - (uo: UpdateOptions) => - ( - uo.circularDependencyLevel.name, - uo.interProjectFirst, - uo.latestSnapshots, - uo.cachedResolution, - uo.gigahorse, - uo.moduleResolvers - ), - (xs: (String, Boolean, Boolean, Boolean, Boolean, Map[ModuleID, Resolver])) => - new UpdateOptions( - levels(xs._1), - xs._2, - xs._3, - xs._4, - xs._5, - PartialFunction.empty, - xs._6 - ) - ) - - private val levels: Map[String, CircularDependencyLevel] = Map( - "warn" -> CircularDependencyLevel.Warn, - "ignore" -> CircularDependencyLevel.Ignore, - "error" -> CircularDependencyLevel.Error - ) -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/CachedResolutionResolveEngine.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/CachedResolutionResolveEngine.scala deleted file mode 100644 index 0363d7cd3..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/CachedResolutionResolveEngine.scala +++ /dev/null @@ -1,1029 +0,0 @@ -package sbt.internal.librarymanagement -package ivyint - -import java.util.Date -import java.io.File -import java.text.SimpleDateFormat -import collection.concurrent -import collection.mutable -import collection.immutable.ListMap -import org.apache.ivy.Ivy -import org.apache.ivy.core -import core.resolve.* -import core.module.id.{ ModuleRevisionId, ModuleId as IvyModuleId } -import core.report.ResolveReport -import core.module.descriptor.{ - DefaultModuleDescriptor, - ModuleDescriptor, - DefaultDependencyDescriptor, - DependencyDescriptor, - Configuration as IvyConfiguration, - ExcludeRule, - IncludeRule -} -import core.module.descriptor.{ OverrideDependencyDescriptorMediator, DependencyArtifactDescriptor } -import core.IvyPatternHelper -import org.apache.ivy.util.{ Message, MessageLogger } -import org.apache.ivy.plugins.latest.ArtifactInfo as IvyArtifactInfo -import org.apache.ivy.plugins.matcher.{ MapMatcher, PatternMatcher } -import annotation.tailrec -import scala.concurrent.duration.* -import sbt.io.{ DirectoryFilter, Hash, IO } -import sbt.librarymanagement.*, syntax.* -import sbt.util.Logger - -private[sbt] object CachedResolutionResolveCache { - def createID(organization: String, name: String, revision: String) = - ModuleRevisionId.newInstance(organization, name, revision) - def sbtOrgTemp = JsonUtil.sbtOrgTemp - def graphVersion = "0.13.9C" - val buildStartup: Long = System.currentTimeMillis - lazy val todayStr: String = toYyyymmdd(buildStartup) - lazy val tomorrowStr: String = toYyyymmdd(buildStartup + 1.day.toMillis) - lazy val yesterdayStr: String = toYyyymmdd(buildStartup - 1.day.toMillis) - def toYyyymmdd(timeSinceEpoch: Long): String = yyyymmdd.format(new Date(timeSinceEpoch)) - lazy val yyyymmdd: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd") -} - -private[sbt] class CachedResolutionResolveCache { - import CachedResolutionResolveCache.* - val updateReportCache: concurrent.Map[ModuleRevisionId, Either[ResolveException, UpdateReport]] = - concurrent.TrieMap() - // Used for subproject - val projectReportCache - : concurrent.Map[(ModuleRevisionId, LogicalClock), Either[ResolveException, UpdateReport]] = - concurrent.TrieMap() - val resolveReportCache: concurrent.Map[ModuleRevisionId, ResolveReport] = concurrent.TrieMap() - val resolvePropertiesCache: concurrent.Map[ModuleRevisionId, String] = concurrent.TrieMap() - val conflictCache - : concurrent.Map[(ModuleID, ModuleID), (Vector[ModuleID], Vector[ModuleID], String)] = - concurrent.TrieMap() - val maxConflictCacheSize: Int = 1024 - val maxUpdateReportCacheSize: Int = 1024 - - def clean(): Unit = updateReportCache.clear() - - def directDependencies(md0: ModuleDescriptor): Vector[DependencyDescriptor] = - md0.getDependencies.toVector - - // Returns a vector of (module descriptor, changing, dd) - def buildArtificialModuleDescriptors( - md0: ModuleDescriptor, - prOpt: Option[ProjectResolver], - log: Logger - ): Vector[(DefaultModuleDescriptor, Boolean, DependencyDescriptor)] = { - log.debug(s":: building artificial module descriptors from ${md0.getModuleRevisionId}") - // val expanded = expandInternalDependencies(md0, data, prOpt, log) - val rootModuleConfigs = md0.getConfigurations.toVector - directDependencies(md0) map { dd => - val arts = dd.getAllDependencyArtifacts.toVector map { x => - s"""${x.getName}:${x.getType}:${x.getExt}:${x.getExtraAttributes}""" - } - log.debug(s"::: dd: $dd (artifacts: ${arts.mkString(",")})") - buildArtificialModuleDescriptor(dd, rootModuleConfigs, md0, prOpt) - } - } - - def internalDependency( - dd: DependencyDescriptor, - prOpt: Option[ProjectResolver] - ): Option[ModuleDescriptor] = - prOpt match { - case Some(pr) => pr.getModuleDescriptor(dd.getDependencyRevisionId) - case _ => None - } - - def buildArtificialModuleDescriptor( - dd: DependencyDescriptor, - rootModuleConfigs: Vector[IvyConfiguration], - parent: ModuleDescriptor, - prOpt: Option[ProjectResolver] - ): (DefaultModuleDescriptor, Boolean, DependencyDescriptor) = { - def excludeRuleString(rule: ExcludeRule): String = - s"""Exclude(${rule.getId},${rule.getConfigurations.mkString(",")},${rule.getMatcher})""" - def includeRuleString(rule: IncludeRule): String = - s"""Include(${rule.getId},${rule.getConfigurations.mkString(",")},${rule.getMatcher})""" - def artifactString(dad: DependencyArtifactDescriptor): String = - s"""Artifact(${dad.getName},${dad.getType},${dad.getExt},${dad.getUrl},${dad.getConfigurations - .mkString(",")},${dad.getExtraAttributes})""" - val mrid = dd.getDependencyRevisionId - val confMap = (dd.getModuleConfigurations map { conf => - conf + "->(" + dd.getDependencyConfigurations(conf).mkString(",") + ")" - }) - val exclusions = (dd.getModuleConfigurations.toVector flatMap { conf => - dd.getExcludeRules(conf).toVector match { - case Vector() => None - case rules => Some(conf + "->(" + (rules map excludeRuleString).mkString(",") + ")") - } - }) - val inclusions = (dd.getModuleConfigurations.toVector flatMap { conf => - dd.getIncludeRules(conf).toVector match { - case Vector() => None - case rules => Some(conf + "->(" + (rules map includeRuleString).mkString(",") + ")") - } - }) - val explicitArtifacts = dd.getAllDependencyArtifacts.toVector map { artifactString } - val mes = parent.getAllExcludeRules.toVector - val mesStr = (mes map excludeRuleString).mkString(",") - val os = extractOverrides(parent) - val moduleLevel = s"""dependencyOverrides=${os.mkString(",")};moduleExclusions=$mesStr""" - val depsString = s"""$mrid;${confMap.mkString( - "," - )};isForce=${dd.isForce};isChanging=${dd.isChanging};isTransitive=${dd.isTransitive};""" + - s"""exclusions=${exclusions.mkString(",")};inclusions=${inclusions.mkString( - "," - )};explicitArtifacts=${explicitArtifacts - .mkString(",")};$moduleLevel;""" - val sha1 = Hash.toHex( - Hash(s"""graphVersion=${CachedResolutionResolveCache.graphVersion};$depsString""") - ) - val md1 = new DefaultModuleDescriptor( - createID(sbtOrgTemp, "temp-resolve-" + sha1, "1.0"), - "release", - null, - false - ) with ArtificialModuleDescriptor { - def targetModuleRevisionId: ModuleRevisionId = mrid - } - for { - conf <- rootModuleConfigs - } yield md1.addConfiguration(conf) - md1.addDependency(dd) - os foreach { ovr => - md1.addDependencyDescriptorMediator(ovr.moduleId, ovr.pm, ovr.ddm) - } - mes foreach { exclude => - md1.addExcludeRule(exclude) - } - (md1, IvySbt.isChanging(dd) || internalDependency(dd, prOpt).isDefined, dd) - } - def extractOverrides(md0: ModuleDescriptor): Vector[IvyOverride] = { - import scala.jdk.CollectionConverters.* - md0.getAllDependencyDescriptorMediators.getAllRules.asScala.toVector sortBy { case (k, _) => - k.toString - } collect { case (k: MapMatcher, v: OverrideDependencyDescriptorMediator) => - val attr: Map[Any, Any] = k.getAttributes.asScala.toMap - val module = IvyModuleId.newInstance( - attr(IvyPatternHelper.ORGANISATION_KEY).toString, - attr(IvyPatternHelper.MODULE_KEY).toString - ) - val pm = k.getPatternMatcher - IvyOverride(module, pm, v) - } - } - def getOrElseUpdateMiniGraph( - md: ModuleDescriptor, - changing0: Boolean, - logicalClock: LogicalClock, - miniGraphPath: File, - cachedDescriptor: File, - log: Logger - )( - f: => Either[ResolveException, UpdateReport] - ): Either[ResolveException, UpdateReport] = { - import sbt.io.syntax.* - val mrid = md.getResolvedModuleRevisionId - def extraPath(id: ModuleRevisionId, key: String, pattern: String): String = - Option(id.getExtraAttribute(key)).fold(".")(pattern.format(_)) // "." has no affect on paths - def scalaVersion(id: ModuleRevisionId): String = extraPath(id, "e:scalaVersion", "scala_%s") - def sbtVersion(id: ModuleRevisionId): String = extraPath(id, "e:sbtVersion", "sbt_%s") - val (pathOrg, pathName, pathRevision, pathScalaVersion, pathSbtVersion) = md match { - case x: ArtificialModuleDescriptor => - val tmrid = x.targetModuleRevisionId - ( - tmrid.getOrganisation, - tmrid.getName, - tmrid.getRevision + "_" + mrid.getName, - scalaVersion(tmrid), - sbtVersion(tmrid) - ) - case _ => - (mrid.getOrganisation, mrid.getName, mrid.getRevision, scalaVersion(mrid), sbtVersion(mrid)) - } - val staticGraphDirectory = miniGraphPath / "static" - val dynamicGraphDirectory = miniGraphPath / "dynamic" - val staticGraphPath = - staticGraphDirectory / pathScalaVersion / pathSbtVersion / pathOrg / pathName / pathRevision / "graphs" / "graph.json" - val dynamicGraphPath = - dynamicGraphDirectory / todayStr / logicalClock.toString / pathScalaVersion / pathSbtVersion / pathOrg / pathName / pathRevision / "graphs" / "graph.json" - def cleanDynamicGraph(): Unit = { - val list = IO.listFiles(dynamicGraphDirectory, DirectoryFilter).toList - list filterNot { d => - (d.getName == todayStr) || (d.getName == tomorrowStr) || (d.getName == yesterdayStr) - } foreach { d => - log.debug(s"deleting old graphs $d...") - IO.delete(d) - } - } - def loadMiniGraphFromFile: Option[Either[ResolveException, UpdateReport]] = - (if (staticGraphPath.exists) Some(staticGraphPath) - else if (dynamicGraphPath.exists) Some(dynamicGraphPath) - else None) match { - case Some(path) => - log.debug(s"parsing ${path.getAbsolutePath}") - val ur = JsonUtil.parseUpdateReport(path, cachedDescriptor, log) - if (ur.allFiles forall { _.exists }) { - updateReportCache(md.getModuleRevisionId) = Right(ur) - Some(Right(ur)) - } else { - log.debug(s"some files are missing from the cache, so invalidating the minigraph") - IO.delete(path) - None - } - case _ => None - } - (updateReportCache.get(mrid) orElse loadMiniGraphFromFile) match { - case Some(result) => - result match { - case Right(ur) => Right(ur.withStats(ur.stats.withCached(true))) - case x => x - } - case None => - f match { - case Right(ur) => - val changing = changing0 || (ur.configurations exists { cr => - cr.details exists { oar => - oar.modules exists { mr => - IvySbt.isChanging(mr.module) || (mr.callers exists { _.isChangingDependency }) - } - } - }) - IO.createDirectory(miniGraphPath) - val gp = - if (changing) dynamicGraphPath - else staticGraphPath - log.debug(s"saving minigraph to $gp") - if (changing) { - cleanDynamicGraph() - } - JsonUtil.writeUpdateReport(ur, gp) - // limit the update cache size - if (updateReportCache.size > maxUpdateReportCacheSize) { - updateReportCache.remove(updateReportCache.head._1) - } - // don't cache dynamic graphs in memory. - if (!changing) { - updateReportCache(md.getModuleRevisionId) = Right(ur) - } - Right(ur) - case Left(re) => - if (!changing0) { - updateReportCache(md.getModuleRevisionId) = Left(re) - } - Left(re) - } - } - } - - def getOrElseUpdateConflict(cf0: ModuleID, cf1: ModuleID, conflicts: Vector[ModuleReport])( - f: => (Vector[ModuleReport], Vector[ModuleReport], String) - ): (Vector[ModuleReport], Vector[ModuleReport]) = { - def reconstructReports( - surviving: Vector[ModuleID], - evicted: Vector[ModuleID], - mgr: String - ): (Vector[ModuleReport], Vector[ModuleReport]) = { - val moduleIdMap = Map(conflicts map { x => - x.module -> x - }*) - ( - surviving map moduleIdMap, - evicted map moduleIdMap map { - _.withEvicted(true).withEvictedReason(Some(mgr)) - } - ) - } - (conflictCache get ((cf0, cf1))) match { - case Some((surviving, evicted, mgr)) => reconstructReports(surviving, evicted, mgr) - case _ => - (conflictCache get ((cf1, cf0))) match { - case Some((surviving, evicted, mgr)) => reconstructReports(surviving, evicted, mgr) - case _ => - val (surviving, evicted, mgr) = f - if (conflictCache.size > maxConflictCacheSize) { - conflictCache.remove(conflictCache.head._1) - } - conflictCache((cf0, cf1)) = (surviving map { _.module }, evicted map { _.module }, mgr) - (surviving, evicted) - } - } - } - def getOrElseUpdateProjectReport(mrid: ModuleRevisionId, logicalClock: LogicalClock)( - f: => Either[ResolveException, UpdateReport] - ): Either[ResolveException, UpdateReport] = - if (projectReportCache contains (mrid -> logicalClock)) projectReportCache((mrid, logicalClock)) - else { - val oldKeys = projectReportCache.keys filter { case (_, clk) => clk != logicalClock } - projectReportCache --= oldKeys - projectReportCache.getOrElseUpdate((mrid, logicalClock), f) - } -} - -private[sbt] trait ArtificialModuleDescriptor { self: DefaultModuleDescriptor => - def targetModuleRevisionId: ModuleRevisionId -} - -private[sbt] trait CachedResolutionResolveEngine extends ResolveEngine { - private[sbt] def cachedResolutionResolveCache: CachedResolutionResolveCache - private[sbt] def projectResolver: Option[ProjectResolver] - private[sbt] def makeInstance: Ivy - private[sbt] val ignoreTransitiveForce: Boolean = true - - def withIvy[A](log: Logger)(f: Ivy => A): A = - withIvy(new IvyLoggerInterface(log))(f) - def withIvy[A](log: MessageLogger)(f: Ivy => A): A = - withDefaultLogger(log) { - val ivy = makeInstance - ivy.pushContext() - ivy.getLoggerEngine.pushLogger(log) - try { - f(ivy) - } finally { - ivy.getLoggerEngine.popLogger() - ivy.popContext() - } - } - def withDefaultLogger[A](log: MessageLogger)(f: => A): A = { - val originalLogger = Message.getDefaultLogger - Message.setDefaultLogger(log) - try { - f - } finally { - Message.setDefaultLogger(originalLogger) - } - } - - /** - * This returns sbt's UpdateReport structure. - * missingOk allows sbt to call this with classifiers that may or may not exist, and grab the JARs. - */ - def customResolve( - md0: ModuleDescriptor, - missingOk: Boolean, - logicalClock: LogicalClock, - options0: ResolveOptions, - depDir: File, - log: Logger - ): Either[ResolveException, UpdateReport] = - cachedResolutionResolveCache.getOrElseUpdateProjectReport( - md0.getModuleRevisionId, - logicalClock - ) { - import sbt.io.syntax.* - val start = System.currentTimeMillis - val miniGraphPath = depDir / "module" - val cachedDescriptor = - getSettings.getResolutionCacheManager.getResolvedIvyFileInCache(md0.getModuleRevisionId) - val cache = cachedResolutionResolveCache - val os = cache.extractOverrides(md0) - val options1 = new ResolveOptions(options0) - val data = new ResolveData(this, options1) - val mds = cache.buildArtificialModuleDescriptors(md0, projectResolver, log) - - def doWork( - md: ModuleDescriptor, - dd: DependencyDescriptor - ): Either[ResolveException, UpdateReport] = - cache.internalDependency(dd, projectResolver) match { - case Some(md1) => - log.debug(s":: call customResolve recursively: $dd") - customResolve(md1, missingOk, logicalClock, options0, depDir, log) match { - case Right(ur) => - Right(remapInternalProject(new IvyNode(data, md1), ur, md0, dd, os, log)) - case Left(e) => Left(e) - } - case None => - log.debug(s":: call ivy resolution: $dd") - doWorkUsingIvy(md) - } - def doWorkUsingIvy(md: ModuleDescriptor): Either[ResolveException, UpdateReport] = { - import scala.jdk.CollectionConverters.* - val options1 = new ResolveOptions(options0) - val rr = withIvy(log) { ivy => - ivy.resolve(md, options1) - } - if (!rr.hasError || missingOk) Right(IvyRetrieve.updateReport(rr, cachedDescriptor)) - else { - val messages = rr.getAllProblemMessages.asScala.toSeq.map(_.toString).distinct - val failedPaths = ListMap(rr.getUnresolvedDependencies map { node => - val m = IvyRetrieve.toModuleID(node.getId) - val path = IvyRetrieve.findPath(node, md.getModuleRevisionId) map { x => - IvyRetrieve.toModuleID(x.getId) - } - log.debug("- Unresolved path " + path.toString) - m -> path - }*) - val failed = failedPaths.keys.toSeq - Left(new ResolveException(messages, failed, failedPaths)) - } - } - val (internal, external) = mds.partition { case (_, _, dd) => - cache.internalDependency(dd, projectResolver).isDefined - } - val internalResults = internal map { (md, changing, dd) => - cache.getOrElseUpdateMiniGraph( - md, - changing, - logicalClock, - miniGraphPath, - cachedDescriptor, - log - ) { - doWork(md, dd) - } - } - val externalResults = external map { (md0, changing, dd) => - val configurationsInInternal = internalResults flatMap { - case Right(ur) => - ur.allModules.flatMap { case md => - val sameName = md.name == dd.getDependencyId.getName - val sameOrg = md.organization == dd.getDependencyId.getOrganisation - if (sameName && sameOrg) md.configurations - else None - } - case _ => Nil - } - - dd match { - case d: DefaultDependencyDescriptor => - configurationsInInternal foreach { c => - val configurations = c.split(";").map(_.split("->")) - configurations foreach { conf => - try d.addDependencyConfiguration(conf(0), conf(1)) - catch { - case _: Throwable => () - } // An exception will be thrown if `conf(0)` doesn't exist. - } - } - - case _ => () - } - - cache.getOrElseUpdateMiniGraph( - md0, - changing, - logicalClock, - miniGraphPath, - cachedDescriptor, - log - ) { - doWork(md0, dd) - } - } - val results = internalResults ++ externalResults - val uReport = - mergeResults(md0, results, missingOk, System.currentTimeMillis - start, os, log) - val cacheManager = getSettings.getResolutionCacheManager - cacheManager.saveResolvedModuleDescriptor(md0) - val prop0 = "" - val ivyPropertiesInCache0 = - cacheManager.getResolvedIvyPropertiesInCache(md0.getResolvedModuleRevisionId) - IO.write(ivyPropertiesInCache0, prop0) - uReport - } - - def mergeResults( - md0: ModuleDescriptor, - results: Vector[Either[ResolveException, UpdateReport]], - missingOk: Boolean, - resolveTime: Long, - os: Vector[IvyOverride], - log: Logger - ): Either[ResolveException, UpdateReport] = - if (!missingOk && (results exists { _.isLeft })) - Left(mergeErrors(md0, results collect { case Left(re) => re })) - else Right(mergeReports(md0, results collect { case Right(ur) => ur }, resolveTime, os, log)) - - def mergeErrors(md0: ModuleDescriptor, errors: Vector[ResolveException]): ResolveException = { - val messages = errors flatMap { _.messages } - val failed = errors flatMap { _.failed } - val failedPaths = errors flatMap { - _.failedPaths.toList map { (failed, paths) => - if (paths.isEmpty) (failed, paths) - else - ( - failed, - List(IvyRetrieve.toModuleID(md0.getResolvedModuleRevisionId)) ::: paths.toList.tail - ) - } - } - new ResolveException(messages, failed, ListMap(failedPaths*)) - } - - def mergeReports( - md0: ModuleDescriptor, - reports: Vector[UpdateReport], - resolveTime: Long, - os: Vector[IvyOverride], - log: Logger - ): UpdateReport = { - log.debug(s":: merging update reports") - val cachedDescriptor = - getSettings.getResolutionCacheManager.getResolvedIvyFileInCache(md0.getModuleRevisionId) - val rootModuleConfigs = md0.getConfigurations.toVector - val cachedReports = reports filter { !_.stats.cached } - val stats = UpdateStats( - resolveTime, - (cachedReports map { _.stats.downloadTime }).sum, - (cachedReports map { _.stats.downloadSize }).sum, - false, - Some(System.currentTimeMillis().toString) - ) - val configReports = rootModuleConfigs map { conf => - log.debug("::: -----------") - val crs = reports flatMap { - _.configurations filter { _.configuration.name == conf.getName } - } - mergeConfigurationReports(ConfigRef(conf.getName), crs, os, log) - } - UpdateReport(cachedDescriptor, configReports, stats, Map.empty) - } - - // memory usage 62%, of which 58% is in mergeOrganizationArtifactReports - def mergeConfigurationReports( - rootModuleConf: ConfigRef, - reports: Vector[ConfigurationReport], - os: Vector[IvyOverride], - log: Logger - ): ConfigurationReport = { - // get the details right, and the rest could be derived - val details = - mergeOrganizationArtifactReports(rootModuleConf, reports flatMap { _.details }, os, log) - val modules = details flatMap { - _.modules filter { mr => - !mr.evicted && mr.problem.isEmpty - } - } - ConfigurationReport(rootModuleConf, modules, details) - } - - /** - * Returns a tuple of (merged org + name combo, newly evicted modules) - */ - def mergeOrganizationArtifactReports( - rootModuleConf: ConfigRef, - reports0: Vector[OrganizationArtifactReport], - os: Vector[IvyOverride], - log: Logger - ): Vector[OrganizationArtifactReport] = { - // filter out evicted modules from further logic - def filterReports(report0: OrganizationArtifactReport): Option[OrganizationArtifactReport] = - report0.modules flatMap { mr => - if (mr.evicted || mr.problem.nonEmpty) None - else - // https://github.com/sbt/sbt/issues/1763 - Some(mr.withCallers(JsonUtil.filterOutArtificialCallers(mr.callers))) - } match { - case Vector() => None - case ms => Some(OrganizationArtifactReport(report0.organization, report0.name, ms)) - } - - // group by takes up too much memory. trading space with time. - val orgNamePairs: Vector[(String, String)] = (reports0 map { oar => - (oar.organization, oar.name) - }).distinct - // this might take up some memory, but it's limited to a single - val reports1 = reports0 flatMap { filterReports } - val allModules0: Map[(String, String), Vector[OrganizationArtifactReport]] = - Map(orgNamePairs map { (organization, name) => - val xs = reports1 filter { oar => - oar.organization == organization && oar.name == name - } - ((organization, name), xs) - }*) - // this returns a List of Lists of (org, name). should be deterministic - def detectLoops( - allModules: Map[(String, String), Vector[OrganizationArtifactReport]] - ): List[List[(String, String)]] = { - val loopSets: mutable.Set[Set[(String, String)]] = mutable.Set.empty - val loopLists: mutable.ListBuffer[List[(String, String)]] = mutable.ListBuffer.empty - def testLoop( - m: (String, String), - current: (String, String), - history: List[(String, String)] - ): Unit = { - val callers = - (for { - oar <- allModules.getOrElse(current, Vector()) - mr <- oar.modules - c <- mr.callers - } yield (c.caller.organization, c.caller.name)).distinct - callers foreach { c => - if (history.contains[(String, String)](c)) { - val loop = (c :: history.takeWhile(_ != c)) ::: List(c) - if (!loopSets(loop.toSet)) { - loopSets += loop.toSet - loopLists += loop - val loopStr = (loop map { (o, n) => s"$o:$n" }).mkString("->") - log.warn(s"""avoid circular dependency while using cached resolution: $loopStr""") - } - } else testLoop(m, c, c :: history) - } - } - orgNamePairs foreach { orgname => - testLoop(orgname, orgname, List(orgname)) - } - loopLists.toList - } - val allModules2: mutable.Map[(String, String), Vector[OrganizationArtifactReport]] = - allModules0.to(mutable.Map) - @tailrec def breakLoops(loops: List[List[(String, String)]]): Unit = - loops match { - case Nil => () - case loop :: rest => - loop match { - case Nil => - breakLoops(rest) - case loop => - val sortedLoop = loop sortBy { x => - (for { - oar <- allModules0(x) - mr <- oar.modules - c <- mr.callers - } yield c).size - } - val moduleWithMostCallers = sortedLoop.reverse.head - val next: (String, String) = loop(loop.indexOf(moduleWithMostCallers) + 1) - // remove the module with most callers as the caller of next. - // so, A -> C, B -> C, and C -> A. C has the most callers, and C -> A will be removed. - allModules2 foreachEntry { - case (k: (String, String), oars0) if k == next => - val oars: Vector[OrganizationArtifactReport] = oars0 map { oar => - val mrs = oar.modules map { mr => - val callers0 = mr.callers - val callers = callers0 filterNot { c => - (c.caller.organization, c.caller.name) == moduleWithMostCallers - } - if (callers.size == callers0.size) mr - else { - log.debug( - s":: $rootModuleConf: removing caller $moduleWithMostCallers -> $next for sorting" - ) - mr.withCallers(callers) - } - } - OrganizationArtifactReport(oar.organization, oar.name, mrs) - } - allModules2(k) = oars - case (_, _) => // do nothing - } - - breakLoops(rest) - } - } - val loop = detectLoops(allModules0) - log.debug(s":: $rootModuleConf: loop: $loop") - breakLoops(loop) - - // sort the all modules such that less called modules comes earlier - @tailrec - def sortModules( - cs: Vector[(String, String)], - acc: Vector[(String, String)], - extra: Vector[(String, String)], - n: Int, - guard: Int - ): Vector[(String, String)] = { - // println(s"sortModules: $n / $guard") - val keys = cs.toSet - val (called, notCalled) = cs partition { k => - val reports = allModules2(k) - reports exists { - _.modules.exists { - _.callers exists { caller => - val m = caller.caller - keys((m.organization, m.name)) - } - } - } - } - lazy val result0 = acc ++ notCalled ++ called ++ extra - def warnCircular(): Unit = { - log.warn( - s"""unexpected circular dependency while using cached resolution: ${cs.mkString(",")}""" - ) - } - (if (n > guard) { - warnCircular() - result0 - } else if (called.isEmpty) result0 - else if (notCalled.isEmpty) { - warnCircular() - sortModules(cs.tail, acc, extra :+ cs.head, n + 1, guard) - } else sortModules(called, acc ++ notCalled, extra, 0, called.size * called.size + 1)) - } - def resolveConflicts( - cs: List[(String, String)], - allModules: Map[(String, String), Vector[OrganizationArtifactReport]] - ): List[OrganizationArtifactReport] = - cs match { - case Nil => Nil - case (organization, name) :: rest => - val reports = allModules((organization, name)) - reports match { - case Vector() => resolveConflicts(rest, allModules) - case Vector(oa) if (oa.modules.isEmpty) => resolveConflicts(rest, allModules) - case Vector(oa) if (oa.modules.size == 1 && !oa.modules.head.evicted) => - log.debug(s":: no conflict $rootModuleConf: ${oa.organization}:${oa.name}") - oa :: resolveConflicts(rest, allModules) - case oas => - (mergeModuleReports(rootModuleConf, oas flatMap { _.modules }, os, log) match { - case (survivor, newlyEvicted) => - val evicted = (survivor ++ newlyEvicted) filter { m => - m.evicted - } - val notEvicted = (survivor ++ newlyEvicted) filter { m => - !m.evicted - } - log.debug("::: adds " + (notEvicted map { _.module }).mkString(", ")) - log.debug("::: evicted " + (evicted map { _.module }).mkString(", ")) - val x = OrganizationArtifactReport(organization, name, survivor ++ newlyEvicted) - val nextModules = - transitivelyEvict(rootModuleConf, rest, allModules, evicted, log) - x :: resolveConflicts(rest, nextModules) - }) - } - } - val guard0 = (orgNamePairs.size * orgNamePairs.size) + 1 - val sorted: Vector[(String, String)] = sortModules(orgNamePairs, Vector(), Vector(), 0, guard0) - val sortedStr = (sorted map { (o, n) => s"$o:$n" }).mkString(", ") - log.debug(s":: sort result: $sortedStr") - val result = resolveConflicts(sorted.toList, allModules0) - result.toVector - } - - /** - * Merges ModuleReports, which represents organization, name, and version. - * Returns a tuple of (surviving modules ++ non-conflicting modules, newly evicted modules). - */ - def mergeModuleReports( - rootModuleConf: ConfigRef, - modules: Vector[ModuleReport], - os: Vector[IvyOverride], - log: Logger - ): (Vector[ModuleReport], Vector[ModuleReport]) = { - if (modules.nonEmpty) { - log.debug( - s":: merging module reports for $rootModuleConf: ${modules.head.module.organization}:${modules.head.module.name}" - ) - } - def mergeModuleReports(xs: Vector[ModuleReport]): ModuleReport = { - val completelyEvicted = xs forall { _.evicted } - val allCallers = xs flatMap { _.callers } - // Caller info is often repeated across the subprojects. We only need ModuleID info for later, so xs.head is ok. - val distinctByModuleId = allCallers.groupBy({ _.caller }).toVector map { case (_, xs) => - xs.head - } - val allArtifacts = (xs flatMap { _.artifacts }).distinct - xs.head - .withArtifacts(allArtifacts) - .withEvicted(completelyEvicted) - .withCallers(distinctByModuleId) - } - val merged = (modules groupBy { m => - (m.module.organization, m.module.name, m.module.revision) - }).toVector flatMap { case (_, xs) => - if (xs.size < 2) xs - else Vector(mergeModuleReports(xs)) - } - val conflicts = merged filter { m => - !m.evicted && m.problem.isEmpty - } - if (conflicts.size < 2) (merged, Vector()) - else - resolveConflict(rootModuleConf, conflicts, os, log) match { - case (survivor, evicted) => - ( - survivor ++ (merged filter { m => - m.evicted || m.problem.isDefined - }), - evicted - ) - } - } - - /** - * This transitively evicts any non-evicted modules whose only callers are newly evicted. - */ - def transitivelyEvict( - rootModuleConf: ConfigRef, - pairs: List[(String, String)], - reports0: Map[(String, String), Vector[OrganizationArtifactReport]], - evicted0: Vector[ModuleReport], - log: Logger - ): Map[(String, String), Vector[OrganizationArtifactReport]] = { - val em = (evicted0 map { _.module }).toSet - def isTransitivelyEvicted(mr: ModuleReport): Boolean = - mr.callers forall { c => - em(c.caller) - } - val reports: Seq[((String, String), Vector[OrganizationArtifactReport])] = - reports0.toSeq flatMap { - case (k, _) if !(pairs.contains[(String, String)](k)) => Seq() - case ((organization, name), oars0) => - val oars = oars0 map { oar => - val (affected, unaffected) = oar.modules partition { mr => - val x = !mr.evicted && mr.problem.isEmpty && isTransitivelyEvicted(mr) - if (x) { - log.debug(s""":::: transitively evicted $rootModuleConf: ${mr.module}""") - } - x - } - val newlyEvicted = affected map { - _.withEvicted(true).withEvictedReason(Some("transitive-evict")) - } - if (affected.isEmpty) oar - else OrganizationArtifactReport(organization, name, unaffected ++ newlyEvicted) - } - Seq(((organization, name), oars)) - } - Map(reports*) - } - - /** - * resolves dependency resolution conflicts in which multiple candidates are found for organization+name combos. - * The main input is conflicts, which is a Vector of ModuleReport, which contains full info on the modulerevision, including its callers. - * Conflict resolution could be expensive, so this is first cached to `cachedResolutionResolveCache` if the conflict is between 2 modules. - * Otherwise, the default "latest" resolution takes the following precedence: - * 1. overrides passed in to `os`. - * 2. directly forced dependency within the artificial module. - * 3. latest revision. - * Note transitively forced dependencies are not respected. This seems to be the case for stock Ivy's behavior as well, - * which may be because Ivy makes all Maven dependencies as forced="true". - */ - def resolveConflict( - rootModuleConf: ConfigRef, - conflicts: Vector[ModuleReport], - os: Vector[IvyOverride], - log: Logger - ): (Vector[ModuleReport], Vector[ModuleReport]) = { - import org.apache.ivy.plugins.conflict.{ - NoConflictManager, - StrictConflictManager, - LatestConflictManager - } - val head = conflicts.head - val organization = head.module.organization - val name = head.module.name - log.debug(s"::: resolving conflict in $rootModuleConf:$organization:$name " + (conflicts map { - _.module - }).mkString("(", ", ", ")")) - def useLatest( - lcm: LatestConflictManager - ): (Vector[ModuleReport], Vector[ModuleReport], String) = - (conflicts find { m => - m.callers.exists { _.isDirectlyForceDependency } - }) match { - case Some(m) => - log.debug(s"- directly forced dependency: $m ${m.callers}") - ( - Vector(m), - conflicts filterNot { _ == m } map { - _.withEvicted(true).withEvictedReason(Some("direct-force")) - }, - "direct-force" - ) - case None => - (conflicts find { m => - m.callers.exists { _.isForceDependency } - }) match { - // Ivy translates pom.xml dependencies to forced="true", so transitive force is broken. - case Some(m) if !ignoreTransitiveForce => - log.debug(s"- transitively forced dependency: $m ${m.callers}") - ( - Vector(m), - conflicts filterNot { _ == m } map { - _.withEvicted(true).withEvictedReason(Some("transitive-force")) - }, - "transitive-force" - ) - case _ => - val strategy = lcm.getStrategy - val infos = conflicts map { ModuleReportArtifactInfo(_) } - log.debug(s"- Using $strategy with $infos") - Option(strategy.findLatest(infos.toArray, None.orNull)) match { - case Some(ModuleReportArtifactInfo(m)) => - ( - Vector(m), - conflicts filterNot { _ == m } map { - _.withEvicted(true).withEvictedReason(Some(lcm.toString)) - }, - lcm.toString - ) - case _ => (conflicts, Vector(), lcm.toString) - } - } - } - def doResolveConflict: (Vector[ModuleReport], Vector[ModuleReport], String) = - os find { ovr => - ovr.moduleId.getOrganisation == organization && ovr.moduleId.getName == name - } match { - case Some(ovr) if Option(ovr.ddm.getVersion).isDefined => - val ovrVersion = ovr.ddm.getVersion - conflicts find { mr => - mr.module.revision == ovrVersion - } match { - case Some(m) => - ( - Vector(m), - conflicts filterNot { _ == m } map { - _.withEvicted(true).withEvictedReason(Some("override")) - }, - "override" - ) - case None => - sys.error( - s"override dependency specifies $ovrVersion but no candidates were found: " + (conflicts map { - _.module - }).mkString("(", ", ", ")") - ) - } - case _ => - getSettings.getConflictManager(IvyModuleId.newInstance(organization, name)) match { - case ncm: NoConflictManager => (conflicts, Vector(), ncm.toString) - case _: StrictConflictManager => - sys.error( - (s"conflict was found in $rootModuleConf:$organization:$name " + (conflicts map { - _.module - }).mkString("(", ", ", ")")) - ) - case lcm: LatestConflictManager => useLatest(lcm) - case conflictManager => sys.error(s"Unsupported conflict manager $conflictManager") - } - } - if (conflicts.size == 2 && os.isEmpty) { - val (cf0, cf1) = (conflicts(0).module, conflicts(1).module) - val cache = cachedResolutionResolveCache - val (surviving, evicted) = cache.getOrElseUpdateConflict(cf0, cf1, conflicts) { - doResolveConflict - } - (surviving, evicted) - } else { - val (surviving, evicted, _) = doResolveConflict - (surviving, evicted) - } - } - def remapInternalProject( - node: IvyNode, - ur: UpdateReport, - md0: ModuleDescriptor, - dd: DependencyDescriptor, - os: Vector[IvyOverride], - log: Logger - ): UpdateReport = { - def parentConfigs(c: String): Vector[String] = - Option(md0.getConfiguration(c)) match { - case Some(config) => - config.getExtends.toVector ++ - (config.getExtends.toVector flatMap parentConfigs) - case None => Vector() - } - // These are the configurations from the original project we want to resolve. - val rootModuleConfs = md0.getConfigurations.toVector - val configurations0: Vector[ConfigurationReport] = ur.configurations - // This is how md looks from md0 via dd's mapping. - val remappedConfigs0: Map[String, Vector[String]] = Map(rootModuleConfs map { conf0 => - val remapped: Vector[String] = dd - .getDependencyConfigurations(conf0.getName) - .toVector flatMap { conf => - node.getRealConfs(conf).toVector - } - conf0.getName -> remapped - }*) - // This emulates test-internal extending test configuration etc. - val remappedConfigs: Map[String, Vector[String]] = - rootModuleConfs.foldLeft(remappedConfigs0) { (acc0, c) => - val ps = parentConfigs(c.getName) - ps.foldLeft(acc0) { (acc, parent) => - val vs0 = acc.getOrElse(c.getName, Vector()) - val vs = acc.getOrElse(parent, Vector()) - acc.updated(c.getName, (vs0 ++ vs).distinct) - } - } - log.debug(s"::: remapped configs $remappedConfigs") - val configurations = rootModuleConfs map { conf0 => - val remappedCRs: Vector[ConfigurationReport] = configurations0 filter { cr => - remappedConfigs(conf0.getName).contains[String](cr.configuration.name) - } - mergeConfigurationReports(ConfigRef(conf0.getName), remappedCRs, os, log) - } - UpdateReport(ur.cachedDescriptor, configurations, ur.stats, ur.stamps) - } -} - -private[sbt] case class ModuleReportArtifactInfo(moduleReport: ModuleReport) - extends IvyArtifactInfo { - override def getLastModified: Long = - moduleReport.publicationDate map { _.getTimeInMillis } getOrElse 0L - override def getRevision: String = moduleReport.module.revision - override def toString: String = - s"ModuleReportArtifactInfo(${moduleReport.module}, $getRevision, $getLastModified)" -} -private[sbt] case class IvyOverride( - moduleId: IvyModuleId, - pm: PatternMatcher, - ddm: OverrideDependencyDescriptorMediator -) { - override def toString: String = - s"""IvyOverride($moduleId,$pm,${ddm.getVersion},${ddm.getBranch})""" -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/CustomMavenResolver.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/CustomMavenResolver.scala deleted file mode 100644 index ca171a930..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/CustomMavenResolver.scala +++ /dev/null @@ -1,11 +0,0 @@ -package sbt.internal.librarymanagement -package ivyint - -import org.apache.ivy.plugins.resolver.DependencyResolver -import sbt.librarymanagement.* - -// These are placeholder traits for sbt-aether-resolver -trait CustomMavenResolver extends DependencyResolver {} -trait CustomRemoteMavenResolver extends CustomMavenResolver { - def repo: MavenRepository -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ErrorMessageAuthenticator.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ErrorMessageAuthenticator.scala deleted file mode 100644 index c4014bec9..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ErrorMessageAuthenticator.scala +++ /dev/null @@ -1,130 +0,0 @@ -package sbt.internal.librarymanagement -package ivyint - -import java.net.{ Authenticator, PasswordAuthentication } - -import org.apache.ivy.util.Message -import org.apache.ivy.util.url.IvyAuthenticator - -/** - * Helper to install an Authenticator that works with the IvyAuthenticator to provide better error messages when - * credentials don't line up. - */ -object ErrorMessageAuthenticator { - private var securityWarningLogged = false - - private def originalAuthenticator: Option[Authenticator] = - Option(Authenticator.getDefault()) - - private lazy val ivyOriginalField = { - val field = classOf[IvyAuthenticator].getDeclaredField("original") - field.setAccessible(true) - field - } - // Attempts to get the original authenticator form the ivy class or returns null. - private def installIntoIvy(ivy: IvyAuthenticator): Option[Authenticator] = { - // Here we install ourselves as the IvyAuthenticator's default so we get called AFTER Ivy has a chance to run. - def installIntoIvyImpl(original: Option[Authenticator]): Unit = { - val newOriginal = new ErrorMessageAuthenticator(original) - ivyOriginalField.set(ivy, newOriginal) - } - - try - Option(ivyOriginalField.get(ivy).asInstanceOf[Authenticator]) match { - case Some( - _: ErrorMessageAuthenticator - ) => // We're already installed, no need to do the work again. - case originalOpt => installIntoIvyImpl(originalOpt) - } - catch { - case t: Throwable => - Message.debug( - "Error occurred while trying to install debug messages into Ivy Authentication" + t.getMessage - ) - } - Some(ivy) - } - - /** Installs the error message authenticator so we have nicer error messages when using java's URL for downloading. */ - def install(): Unit = { - // Actually installs the error message authenticator. - def doInstall(original: Option[Authenticator]): Unit = - try Authenticator.setDefault(new ErrorMessageAuthenticator(original)) - catch { - case _: SecurityException if !securityWarningLogged => - securityWarningLogged = true - Message.warn( - "Not enough permissions to set the ErrorMessageAuthenticator. " - + "Helpful debug messages disabled!" - ); - } - // We will try to use the original authenticator as backup authenticator. - // Since there is no getter available, so try to use some reflection to - // obtain it. If that doesn't work, assume there is no original authenticator - def doInstallIfIvy(original: Option[Authenticator]): Unit = - original match { - case Some(_: ErrorMessageAuthenticator) => // Ignore, we're already installed - case Some(ivy: IvyAuthenticator) => - installIntoIvy(ivy); () - case original => doInstall(original) - } - doInstallIfIvy(originalAuthenticator) - } -} - -/** - * An authenticator which just delegates to a previous authenticator and issues *nice* - * error messages on failure to find credentials. - * - * Since ivy installs its own credentials handler EVERY TIME it resolves or publishes, we want to - * install this one at some point and eventually ivy will capture it and use it. - */ -private[sbt] final class ErrorMessageAuthenticator(original: Option[Authenticator]) - extends Authenticator { - - protected override def getPasswordAuthentication(): PasswordAuthentication = { - // We're guaranteed to only get here if Ivy's authentication fails - if (!isProxyAuthentication) { - val host = getRequestingHost - // TODO - levenshtein distance "did you mean" message. - Message.error(s"Unable to find credentials for [${getRequestingPrompt} @ ${host}].") - val configuredRealms = IvyCredentialsLookup.realmsForHost.getOrElse(host, Set.empty) - if (configuredRealms.nonEmpty) { - Message.error(s" Is one of these realms misspelled for host [${host}]:") - configuredRealms foreach { realm => - Message.error(s" * ${realm}") - } - } - } - // TODO - Maybe we should work on a helpful proxy message... - - // TODO - To be more maven friendly, we may want to also try to grab the "first" authentication that shows up for a server and try it. - // or maybe allow that behavior to be configured, since maven users aren't used to realms (which they should be). - - // Grabs the authentication that would have been provided had we not been installed... - def originalAuthentication: Option[PasswordAuthentication] = { - Authenticator.setDefault(original.orNull) - try - Option( - Authenticator.requestPasswordAuthentication( - getRequestingHost, - getRequestingSite, - getRequestingPort, - getRequestingProtocol, - getRequestingPrompt, - getRequestingScheme - ) - ) - finally Authenticator.setDefault(this) - } - originalAuthentication.orNull - } - - /** - * Returns true if this authentication if for a proxy and not for an HTTP server. - * We want to display different error messages, depending. - */ - private def isProxyAuthentication: Boolean = - getRequestorType == Authenticator.RequestorType.PROXY - -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/IvyCredentialsLookup.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/IvyCredentialsLookup.scala deleted file mode 100644 index 54f2d07d3..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/IvyCredentialsLookup.scala +++ /dev/null @@ -1,71 +0,0 @@ -package sbt.internal.librarymanagement -package ivyint - -import org.apache.ivy.util.url.CredentialsStore -import scala.jdk.CollectionConverters.* - -/** A key used to store credentials in the ivy credentials store. */ -private[sbt] sealed trait CredentialKey - -/** Represents a key in the ivy credentials store that is only specific to a host. */ -private[sbt] case class Host(name: String) extends CredentialKey - -/** Represents a key in the ivy credentials store that is keyed to both a host and a "realm". */ -private[sbt] case class Realm(host: String, realm: String) extends CredentialKey - -/** - * Helper mechanism to improve credential related error messages. - * - * This evil class exposes to us the necessary information to warn on credential failure and offer - * spelling/typo suggestions. - */ -private[sbt] object IvyCredentialsLookup { - - /** Helper extractor for Ivy's key-value store of credentials. */ - private object KeySplit { - def unapply(key: String): Option[(String, String)] = { - key.indexOf('@') match { - case -1 => None - case n => Some(key.take(n) -> key.drop(n + 1)) - } - } - } - - /** - * Here we cheat runtime private so we can look in the credentials store. - * - * TODO - Don't bomb at class load time... - */ - private val credKeyringField = { - val tmp = classOf[CredentialsStore].getDeclaredField("KEYRING") - tmp.setAccessible(true) - tmp - } - - /** All the keys for credentials in the ivy configuration store. */ - def keyringKeys: Set[CredentialKey] = { - val map = credKeyringField.get(null).asInstanceOf[java.util.HashMap[String, Any]] - // make a clone of the set... - (map.keySet.asScala.map { - case KeySplit(realm, host) => (Realm(host, realm): CredentialKey) - case host => (Host(host): CredentialKey) - }).toSet - } - - /** - * A mapping of host -> realms in the ivy credentials store. - */ - def realmsForHost: Map[String, Set[String]] = - keyringKeys - .collect { case x: Realm => - x - } - .groupBy { realm => - realm.host - } - .view - .mapValues { realms => - realms map (_.realm) - } - .toMap -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/MergeDescriptors.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/MergeDescriptors.scala deleted file mode 100644 index 7b93a7ab5..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/MergeDescriptors.scala +++ /dev/null @@ -1,194 +0,0 @@ -package sbt.internal.librarymanagement -package ivyint - -import scala.collection.immutable.ArraySeq -import org.apache.ivy.core -import core.module.descriptor.{ DependencyArtifactDescriptor, DefaultDependencyArtifactDescriptor } -import core.module.descriptor.DependencyDescriptor -import core.module.id.{ ArtifactId, ModuleRevisionId } - -private[sbt] object MergeDescriptors { - def mergeable(a: DependencyDescriptor, b: DependencyDescriptor): Boolean = - a.isForce == b.isForce && - a.isChanging == b.isChanging && - a.isTransitive == b.isTransitive && - a.getParentRevisionId == b.getParentRevisionId && - a.getNamespace == b.getNamespace && { - val amrid = a.getDependencyRevisionId - val bmrid = b.getDependencyRevisionId - amrid == bmrid - } && { - val adyn = a.getDynamicConstraintDependencyRevisionId - val bdyn = b.getDynamicConstraintDependencyRevisionId - adyn == bdyn - } - - def apply(a: DependencyDescriptor, b: DependencyDescriptor): DependencyDescriptor = { - assert(mergeable(a, b)) - new MergedDescriptors(a, b) - } -} - -// combines the artifacts, configurations, includes, and excludes for DependencyDescriptors `a` and `b` -// that otherwise have equal IDs -private[sbt] final case class MergedDescriptors(a: DependencyDescriptor, b: DependencyDescriptor) - extends DependencyDescriptor { - def getDependencyId = a.getDependencyId - def isForce = a.isForce - def isChanging = a.isChanging - def isTransitive = a.isTransitive - def getNamespace = a.getNamespace - def getParentRevisionId = a.getParentRevisionId - def getDependencyRevisionId = a.getDependencyRevisionId - def getDynamicConstraintDependencyRevisionId = a.getDynamicConstraintDependencyRevisionId - - def getModuleConfigurations = concat(a.getModuleConfigurations, b.getModuleConfigurations) - - def getDependencyConfigurations(moduleConfiguration: String, requestedConfiguration: String) = - concat( - a.getDependencyConfigurations(moduleConfiguration, requestedConfiguration), - b.getDependencyConfigurations(moduleConfiguration) - ) - - def getDependencyConfigurations(moduleConfiguration: String) = - concat( - a.getDependencyConfigurations(moduleConfiguration), - b.getDependencyConfigurations(moduleConfiguration) - ) - - def getDependencyConfigurations(moduleConfigurations: Array[String]) = - concat( - a.getDependencyConfigurations(moduleConfigurations), - b.getDependencyConfigurations(moduleConfigurations) - ) - - def getAllDependencyArtifacts = - concatArtifacts(a, a.getAllDependencyArtifacts, b, b.getAllDependencyArtifacts) - - def getDependencyArtifacts(moduleConfigurations: String) = - concatArtifacts( - a, - a.getDependencyArtifacts(moduleConfigurations), - b, - b.getDependencyArtifacts(moduleConfigurations) - ) - - def getDependencyArtifacts(moduleConfigurations: Array[String]) = - concatArtifacts( - a, - a.getDependencyArtifacts(moduleConfigurations), - b, - b.getDependencyArtifacts(moduleConfigurations) - ) - - def getAllIncludeRules = concat(a.getAllIncludeRules, b.getAllIncludeRules) - - def getIncludeRules(moduleConfigurations: String) = - concat(a.getIncludeRules(moduleConfigurations), b.getIncludeRules(moduleConfigurations)) - - def getIncludeRules(moduleConfigurations: Array[String]) = - concat(a.getIncludeRules(moduleConfigurations), b.getIncludeRules(moduleConfigurations)) - - private def concatArtifacts( - a: DependencyDescriptor, - as: Array[DependencyArtifactDescriptor], - b: DependencyDescriptor, - bs: Array[DependencyArtifactDescriptor] - ) = { - if (as.isEmpty) - if (bs.isEmpty) as - else defaultArtifact(a) ++ explicitConfigurations(b, bs) - else if (bs.isEmpty) explicitConfigurations(a, as) ++ defaultArtifact(b) - else concat(explicitConfigurations(a, as), explicitConfigurations(b, bs)) - } - private def explicitConfigurations( - base: DependencyDescriptor, - arts: Array[DependencyArtifactDescriptor] - ): Array[DependencyArtifactDescriptor] = - arts map { art => - explicitConfigurations(base, art) - } - private def explicitConfigurations( - base: DependencyDescriptor, - art: DependencyArtifactDescriptor - ): DependencyArtifactDescriptor = { - val aConfs = Option(art.getConfigurations) map { _.toList } - // In case configuration list is "*", we should still specify the module configuration of the DependencyDescriptor - // otherwise the explicit specified artifacts from one dd can leak over to the other. - // See gh-1500, gh-2002 - aConfs match { - case None | Some(Nil) | Some(List("*")) => - copyWithConfigurations(art, ArraySeq.unsafeWrapArray(base.getModuleConfigurations)) - case _ => art - } - } - private def defaultArtifact( - a: DependencyDescriptor - ): Array[DependencyArtifactDescriptor] = { - val dd = new DefaultDependencyArtifactDescriptor( - a, - a.getDependencyRevisionId.getName, - "jar", - "jar", - null, - null - ) - addConfigurations(dd, ArraySeq.unsafeWrapArray(a.getModuleConfigurations)) - // If the dependency descriptor is empty, then it means that it has been created from a POM file. In this case, - // it is correct to create a seemingly nonexistent dependency artifact. - if (a.getAllDependencyArtifacts.isEmpty) Array(dd) - else a.getAllDependencyArtifacts filter (_ == dd) - } - private def copyWithConfigurations( - dd: DependencyArtifactDescriptor, - confs: Seq[String] - ): DependencyArtifactDescriptor = { - val dextra = dd.getQualifiedExtraAttributes - val newd = new DefaultDependencyArtifactDescriptor( - dd.getDependencyDescriptor, - dd.getName, - dd.getType, - dd.getExt, - dd.getUrl, - dextra - ) - addConfigurations(newd, confs) - newd - } - private def addConfigurations( - dd: DefaultDependencyArtifactDescriptor, - confs: Seq[String] - ): Unit = - confs foreach dd.addConfiguration - - private def concat[T: reflect.ClassTag](a: Array[T], b: Array[T]): Array[T] = - (a ++ b).distinct - - def getAllExcludeRules = concat(a.getAllExcludeRules, b.getAllExcludeRules) - - def getExcludeRules(moduleConfigurations: String) = - concat(a.getExcludeRules(moduleConfigurations), b.getExcludeRules(moduleConfigurations)) - - def getExcludeRules(moduleConfigurations: Array[String]) = - concat(a.getExcludeRules(moduleConfigurations), b.getExcludeRules(moduleConfigurations)) - - def doesExclude(moduleConfigurations: Array[String], artifactId: ArtifactId) = - a.doesExclude(moduleConfigurations, artifactId) || b.doesExclude( - moduleConfigurations, - artifactId - ) - - def canExclude = a.canExclude || b.canExclude - - def asSystem = this - - def clone(revision: ModuleRevisionId) = - new MergedDescriptors(a.clone(revision), b.clone(revision)) - - def getAttribute(name: String): String = a.getAttribute(name) - def getAttributes = a.getAttributes - def getExtraAttribute(name: String) = a.getExtraAttribute(name) - def getExtraAttributes = a.getExtraAttributes - def getQualifiedExtraAttributes = a.getQualifiedExtraAttributes - def getSourceModule = a.getSourceModule -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ParallelResolveEngine.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ParallelResolveEngine.scala deleted file mode 100644 index fb154a9a2..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ParallelResolveEngine.scala +++ /dev/null @@ -1,114 +0,0 @@ -package sbt.internal.librarymanagement.ivyint - -import java.util.concurrent.Executors - -import org.apache.ivy.core.event.EventManager -import org.apache.ivy.core.event.download.PrepareDownloadEvent -import org.apache.ivy.core.module.descriptor.Artifact -import org.apache.ivy.core.report.* -import org.apache.ivy.core.resolve.* -import org.apache.ivy.core.sort.SortEngine -import org.apache.ivy.util.filter.Filter - -import scala.concurrent.duration.Duration -import scala.concurrent.{ Await, ExecutionContext, Future } - -private[ivyint] case class DownloadResult( - dep: IvyNode, - report: DownloadReport, - totalSizeDownloaded: Long -) - -object ParallelResolveEngine { - private lazy val resolveExecutionContext: ExecutionContext = { - // This throttles the connection number, especially when Gigahorse is not used. - val maxConnectionCount = 6 - val executor = Executors.newFixedThreadPool(maxConnectionCount) - ExecutionContext.fromExecutor(executor) - } -} - -/** Define an ivy [[ResolveEngine]] that resolves dependencies in parallel. */ -private[sbt] class ParallelResolveEngine( - settings: ResolveEngineSettings, - eventManager: EventManager, - sortEngine: SortEngine -) extends ResolveEngine(settings, eventManager, sortEngine) { - - override def downloadArtifacts( - report: ResolveReport, - artifactFilter: Filter, - options: DownloadOptions - ): Unit = { - import scala.jdk.CollectionConverters.* - val start = System.currentTimeMillis - report.getArtifacts match { - case typed: java.util.List[Artifact @unchecked] => - new PrepareDownloadEvent(typed.asScala.toArray) - } - // Farm out the dependencies for parallel download - given ExecutionContext = ParallelResolveEngine.resolveExecutionContext - val allDownloadsFuture = Future.traverse(report.getDependencies.asScala) { case dep: IvyNode => - Future { - if ( - !(dep.isCompletelyEvicted || dep.hasProblem) && - dep.getModuleRevision != null - ) { - Some(downloadNodeArtifacts(dep, artifactFilter, options)) - } else None - } - } - val allDownloads = Await.result(allDownloadsFuture, Duration.Inf) - // compute total downloaded size - val totalSize = allDownloads.foldLeft(0L) { - case (size, Some(download)) => - val dependency = download.dep - val moduleConfigurations = dependency.getRootModuleConfigurations - moduleConfigurations.foreach { configuration => - val configurationReport = report.getConfigurationReport(configuration) - - // Take into account artifacts required by the given configuration - if ( - dependency.isEvicted(configuration) || - dependency.isBlacklisted(configuration) - ) { - configurationReport.addDependency(dependency) - } else configurationReport.addDependency(dependency, download.report) - } - - size + download.totalSizeDownloaded - case (size, None) => size - } - - report.setDownloadTime(System.currentTimeMillis() - start) - report.setDownloadSize(totalSize) - } - - /** - * Download all the artifacts associated with an ivy node. - * - * Return the report and the total downloaded size. - */ - private def downloadNodeArtifacts( - dependency: IvyNode, - artifactFilter: Filter, - options: DownloadOptions - ): DownloadResult = { - - val resolver = dependency.getModuleRevision.getArtifactResolver - val selectedArtifacts = dependency.getSelectedArtifacts(artifactFilter) - val downloadReport = resolver.download(selectedArtifacts, options) - val artifactReports = downloadReport.getArtifactsReports - - val totalSize = artifactReports.foldLeft(0L) { (size, artifactReport) => - // Check download status and report resolution failures - artifactReport.getDownloadStatus match { - case DownloadStatus.SUCCESSFUL => - size + artifactReport.getSize - case _ => size - } - } - - DownloadResult(dependency, downloadReport, totalSize) - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtChainResolver.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtChainResolver.scala deleted file mode 100644 index 85f8964ac..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtChainResolver.scala +++ /dev/null @@ -1,509 +0,0 @@ -package sbt.internal.librarymanagement -package ivyint - -import java.io.{ ByteArrayOutputStream, File, PrintWriter } -import java.text.ParseException -import java.util.Date - -import org.apache.ivy.core.cache.ArtifactOrigin -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.core.{ IvyContext, LogOptions } -import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor -import org.apache.ivy.core.module.descriptor.DependencyDescriptor -import org.apache.ivy.core.module.descriptor.ModuleDescriptor -import org.apache.ivy.core.module.descriptor.Artifact as IArtifact -import org.apache.ivy.core.resolve.{ ResolveData, ResolvedModuleRevision } -import org.apache.ivy.plugins.latest.LatestStrategy -import org.apache.ivy.plugins.repository.file.{ FileResource, FileRepository as IFileRepository } -import org.apache.ivy.plugins.repository.url.URLResource -import org.apache.ivy.plugins.resolver.* -import org.apache.ivy.plugins.resolver.util.{ HasLatestStrategy, ResolvedResource } -import org.apache.ivy.util.{ Message, StringUtils as IvyStringUtils } -import sbt.util.Logger -import sbt.librarymanagement.* -import sbt.internal.librarymanagement.ivy.UpdateOptions - -import scala.util.control.NonFatal - -private[sbt] case class SbtChainResolver( - name: String, - resolvers: Seq[DependencyResolver], - settings: IvySettings, - updateOptions: UpdateOptions, - log: Logger -) extends ChainResolver { - override def setCheckmodified(check: Boolean): Unit = super.setCheckmodified(check) - - override def equals(o: Any): Boolean = o match { - case o: SbtChainResolver => - this.name == o.name && - this.resolvers == o.resolvers && - this.settings == o.settings && - this.updateOptions == o.updateOptions - case _ => false - } - - override def hashCode: Int = { - var hash = 1 - hash = hash * 31 + this.name.## - hash = hash * 31 + this.resolvers.## - hash = hash * 31 + this.settings.## - hash = hash * 31 + this.updateOptions.## - hash - } - - // TODO - We need to special case the project resolver so it always "wins" when resolving with inter-project dependencies. - - def initializeChainResolver(): Unit = { - // Initialize ourselves. - setName(name) - setReturnFirst(true) - setCheckmodified(false) - - /* Append all the resolvers to the extended chain resolvers since we get its value later on */ - resolvers.foreach(add) - } - - initializeChainResolver() - - // Technically, this should be applied to module configurations. - // That would require custom subclasses of all resolver types in ConvertResolver (a delegation approach does not work). - // It would be better to get proper support into Ivy. - // A workaround is to configure the ModuleConfiguration resolver to be a ChainResolver. - // - // This method is only used by the pom parsing code in Ivy to find artifacts it doesn't know about. - // In particular, a) it looks up source and javadoc classifiers b) it looks up a main artifact for packaging="pom" - // sbt now provides the update-classifiers or requires explicitly specifying classifiers explicitly - // Providing a main artifact for packaging="pom" does not seem to be correct and the lookup can be expensive. - // - // Ideally this could just skip the lookup, but unfortunately several artifacts in practice do not follow the - // correct behavior for packaging="pom" and so it is only skipped for source/javadoc classifiers. - override def locate(artifact: IArtifact): ArtifactOrigin = - if (IvySbt.hasImplicitClassifier(artifact)) null else super.locate(artifact) - - override def getDependency( - dd: DependencyDescriptor, - data: ResolveData - ): ResolvedModuleRevision = { - if (data.getOptions.getLog == LogOptions.LOG_DEFAULT) - Message.info("Resolving " + dd.getDependencyRevisionId + " ...") - val gd = CustomSbtResolution.getDependency(dd, data) - val mod = IvySbt.resetArtifactResolver(gd) - mod - } - - /** Implements the custom sbt chain resolution with support for snapshots and caching. */ - private object CustomSbtResolution { - def getCached( - dd: DependencyDescriptor, - data: ResolveData, - resolved0: Option[ResolvedModuleRevision] - ): Option[ResolvedModuleRevision] = { - resolved0.orElse { - val resolverName = getName - Message.verbose(s"$resolverName: Checking cache for: $dd") - Option(findModuleInCache(dd, data, true)).map { moduleRev => - Message.verbose(s"$resolverName: module revision found in cache: ${moduleRev.getId}") - forcedRevision(moduleRev) - } - } - } - - /* Copy pasted from `IvyStringUtils` to handle `Throwable` */ - private def getStackTrace(e: Throwable): String = { - if (e == null) { - "" - } else { - val baos = new ByteArrayOutputStream() - val printWriter = new PrintWriter(baos) - e.printStackTrace(printWriter) - printWriter.flush() - val stackTrace = new String(baos.toByteArray) - printWriter.close() - stackTrace - } - } - - /** If None, module was not found. Otherwise, hit. */ - type TriedResolution = Option[(ResolvedModuleRevision, DependencyResolver)] - - /** - * Attempts to resolve the artifact from each of the resolvers in the chain. - * - * Contract: - * 1. It doesn't resolve anything when there is a resolved module, `isReturnFirst` is - * enabled and `useLatest` is false (meaning that resolution is pure, no SNAPSHOT). - * 2. Otherwise, we try to resolve it. - * - * @param resolved0 The perhaps already resolved module. - * @param useLatest Whether snapshot resolution should be enabled. - * @param data The resolve data to use. - * @param descriptor The dependency descriptor of the in-resolution module. - */ - def getResults( - resolved0: Option[ResolvedModuleRevision], - useLatest: Boolean, - data: ResolveData, - descriptor: DependencyDescriptor, - resolvers: Seq[DependencyResolver] - ): Seq[Either[Throwable, TriedResolution]] = { - var currentlyResolved = resolved0 - - def performResolution( - resolver: DependencyResolver - ): Option[(ResolvedModuleRevision, DependencyResolver)] = { - // Resolve all resolvers when the module is changing - val previouslyResolved = currentlyResolved - if (useLatest) data.setCurrentResolvedModuleRevision(null) - else data.setCurrentResolvedModuleRevision(currentlyResolved.orNull) - currentlyResolved = Option(resolver.getDependency(descriptor, data)) - if (currentlyResolved eq previouslyResolved) None - else if (useLatest) { - currentlyResolved.map(x => - (reparseModuleDescriptor(descriptor, data, resolver, x), resolver) - ) - } else currentlyResolved.map(x => (forcedRevision(x), resolver)) - } - - def reportError(throwable: Throwable, resolver: DependencyResolver): Unit = { - val trace = getStackTrace(throwable) - Message.verbose(s"problem occurred while resolving $descriptor with $resolver: $trace") - } - - resolvers.map { (resolver: DependencyResolver) => - // Return none when revision is cached and `isReturnFirst` is set - if (isReturnFirst && currentlyResolved.isDefined && !useLatest) Right(None) - else { - // We actually do resolution. - val oldLatest: Option[LatestStrategy] = - setLatestIfRequired(resolver, Option(getLatestStrategy)) - try Right(performResolution(resolver)) - catch { case NonFatal(t) => reportError(t, resolver); Left(t) } - finally { - oldLatest.foreach(_ => doSetLatestStrategy(resolver, oldLatest)) - checkInterrupted() - } - } - } - } - - private final val prefix = "Undefined resolution order" - def resolveLatest( - foundRevisions: Seq[(ResolvedModuleRevision, DependencyResolver)], - descriptor: DependencyDescriptor, - data: ResolveData - ): Option[ResolvedModuleRevision] = { - - val sortedRevisions = foundRevisions.sortBy { (rmr, resolver) => - val publicationDate = rmr.getPublicationDate - val descriptorDate = rmr.getDescriptor.getPublicationDate - Message.warn(s"Sorting results from $rmr, using $publicationDate and $descriptorDate.") - // Just issue warning about issues with publication date, and fake one on it for now - val chosenPublicationDate = Option(publicationDate).orElse(Option(descriptorDate)) - chosenPublicationDate match { - case Some(date) => date.getTime - case None => - val id = rmr.getId - val resolvedResource = (resolver.findIvyFileRef(descriptor, data), rmr.getDescriptor) - resolvedResource match { - case (res: ResolvedResource, dmd: DefaultModuleDescriptor) => - val resolvedPublicationDate = new java.util.Date(res.getLastModified) - Message.debug(s"No publication date from resolver $resolver for $id.") - Message.debug(s"Setting publication date to: $resolvedPublicationDate.") - dmd.setPublicationDate(resolvedPublicationDate) - res.getLastModified - case (ivf, dmd) => - // The dependency is specified by a direct URL or some sort of non-ivy file - if (ivf == null && descriptor.isChanging) - Message.warn(s"$prefix: changing dependency $id with no ivy/pom file!") - if (dmd == null) - Message.warn(s"$prefix: no publication date from resolver $resolver for $id") - 0L - } - } - } - - val firstHit = sortedRevisions.reverse.headOption - firstHit.map { hit => - val (resolvedModule, resolver) = hit - - if (resolvedModule.getId.getRevision.contains("SNAPSHOT")) { - - Message.warn( - "Resolving a snapshot version. It's going to be slow unless you use `updateOptions := updateOptions.value.withLatestSnapshots(false)` options." - ) - val resolvers = sortedRevisions.map(_._2.getName) - sortedRevisions.foreach(h => { - val (module, resolver) = h - Message.info( - s"Out of ${sortedRevisions.size} candidates we found for ${module.getId} in ${resolvers - .mkString(" and ")}, we are choosing ${resolver}." - ) - }) - } else { - Message.warn(s"Choosing $resolver for ${resolvedModule.getId}") - } - - // Now that we know the real latest revision, let's force Ivy to use it - val resolvedDescriptor = resolvedModule.getDescriptor - val artifactOpt = findFirstArtifactRef(resolvedDescriptor, data, resolver) - // If `None` do nothing -- modules without artifacts. Otherwise cache. - artifactOpt.foreach { artifactRef => - val dep = toSystem(descriptor) - val first = toSystem(resolvedDescriptor).getAllArtifacts.head - val options = getCacheOptions(data) - val cacheManager = getRepositoryCacheManager - cacheManager.cacheModuleDescriptor(resolver, artifactRef, dep, first, null, options) - } - resolvedModule - } - } - - def resolveByAllMeans( - cachedModule: Option[ResolvedModuleRevision], - useLatest: Boolean, - interResolver: Option[DependencyResolver], - resolveModules: () => Seq[Either[Throwable, TriedResolution]], - dd: DependencyDescriptor, - data: ResolveData - ): Option[ResolvedModuleRevision] = { - val internallyResolved: Option[ResolvedModuleRevision] = { - if (!updateOptions.interProjectFirst) None - else interResolver.flatMap(resolver => Option(resolver.getDependency(dd, data))) - } - val internalOrExternal = internallyResolved.orElse { - val foundRevisions: Seq[(ResolvedModuleRevision, DependencyResolver)] = - resolveModules().collect { case Right(Some(x)) => x } - if (useLatest) resolveLatest(foundRevisions, dd, data) - else foundRevisions.reverse.headOption.map(_._1) // Resolvers are hit in reverse order - } - internalOrExternal.orElse(cachedModule) - } - - /** Cleans unnecessary module id information not provided by [[IvyRetrieve.toModuleID()]]. */ - private final val moduleResolvers = updateOptions.moduleResolvers.map { (key, value) => - val cleanKey = ModuleID(key.organization, key.name, key.revision) - .withExtraAttributes(key.extraAttributes) - .withBranchName(key.branchName) - cleanKey -> value - } - - /** - * Gets the list of resolvers to use for resolving a given descriptor. - * - * NOTE: The ivy implementation guarantees that all resolvers implement dependency resolver. - * @param descriptor The descriptor to be resolved. - */ - def getDependencyResolvers(descriptor: DependencyDescriptor): Vector[DependencyResolver] = { - val moduleRevisionId = descriptor.getDependencyRevisionId - val moduleID = IvyRetrieve.toModuleID(moduleRevisionId) - val resolverForModule = moduleResolvers.get(moduleID) - val ivyResolvers = getResolvers.toArray // Get resolvers from chain resolver directly - val allResolvers = ivyResolvers.collect { case r: DependencyResolver => r }.toVector - // Double check that dependency resolver will always be the super trait of a resolver - assert(ivyResolvers.size == allResolvers.size, "ALERT: Some ivy resolvers were filtered.") - val mappedResolver = resolverForModule.flatMap(r => allResolvers.find(_.getName == r.name)) - mappedResolver match { - case Some(uniqueResolver) => Vector(uniqueResolver) - case None => allResolvers - } - } - - def findInterProjectResolver(resolvers: Seq[DependencyResolver]): Option[DependencyResolver] = - resolvers.find(_.getName == ProjectResolver.InterProject) - - /** - * Gets the dependency for a given descriptor with the pertinent resolve data. - * - * This is a custom sbt chain operation that produces better error output and deals with - * cases that the conventional ivy resolver does not. It accumulates the resolution of - * several resolvers and returns the module which fits the provided resolution strategy. - * - * These are the differences with regard to the default ivy [[ChainResolver]]: - * 1. It skips resolution if "return first" is set to true. - * 2. It skips resolution if a previously resolved or cached resolution is found. - * 3. It always checks all the resolvers and compares timestamps for changing dependencies - * if and only if `latestSnapshots` is enabled in the update options, regardless of what - * the latest strategies are (http://ant.apache.org/ivy/history/2.3.0/settings/latest-strategies.html). - * See https://github.com/sbt/sbt/pull/1520 for more information on this topic. - * - * Note the tradeoff here in SNAPSHOTs: correctness vs slowness. - */ - def getDependency(dd: DependencyDescriptor, data0: ResolveData): ResolvedModuleRevision = { - val isDynamic = dd.isChanging || IvySbt.isChanging(dd.getDependencyRevisionId) - val useLatest = isDynamic && updateOptions.latestSnapshots - if (useLatest) Message.verbose(s"$getName is changing. Checking all resolvers on the chain.") - - /* Get the resolved module descriptor from: - * 1. An already resolved branch of the resolution tree. - * 2. The value from the cache. */ - val data = new ResolveData(data0, doValidate(data0)) - val resolved0 = Option(data.getCurrentResolvedModuleRevision) - val resolvedOrCached = getCached(dd, data0, resolved0) - - val cached: Option[ResolvedModuleRevision] = if (useLatest) None else resolvedOrCached - val resolvers = getDependencyResolvers(dd) - val interResolver = findInterProjectResolver(resolvers) - // TODO: Please, change `Option` return types so that this goes away - lazy val results = getResults(cached, useLatest, data, dd, resolvers) - lazy val errors = results.collect { case Left(t) => t } - val runResolution = () => results - val resolved = resolveByAllMeans(cached, useLatest, interResolver, runResolution, dd, data) - - resolved match { - case None if errors.size == 1 => - errors.head match { - case e: RuntimeException => throw e - case e: ParseException => throw e - case e: Throwable => throw new RuntimeException(e.toString, e) - } - case None if errors.size > 1 => - val traces = errors.toList.map(e => IvyStringUtils.getErrorMessage(e)) - val msg = s"Resolution failed several times for $dd:" - throw new RuntimeException(s"$msg: ${traces.mkString("\n\t", "\n\t", "\n")}") - case _ => - // Can be either `None` with empty error or `Some` - if (resolved0 == resolved) resolved0.orNull - else resolved.map(resolvedRevision).orNull - } - } - } - - /* Ivy keeps module descriptors in memory, so we need to make sure that the - * resolved module revision is in fact the one found in the latest resolver. */ - private def reparseModuleDescriptor( - dd: DependencyDescriptor, - data: ResolveData, - resolver: DependencyResolver, - previouslyResolved: ResolvedModuleRevision - ): ResolvedModuleRevision = { - // TODO: Figure out better alternative or directly attack the - // resolvers ivy uses to get correct behaviour for SNAPSHOT - Option(resolver.findIvyFileRef(dd, data)) flatMap { ivyFile => - ivyFile.getResource match { - case r: FileResource => - val urlDescriptor = r.getFile.toURI.toURL - try { - val parser = previouslyResolved.getDescriptor.getParser - val md = parser.parseDescriptor(settings, urlDescriptor, r, false) - val report = previouslyResolved.getReport - // Note that we always set force for SNAPSHOT resolution... - Some(new ResolvedModuleRevision(resolver, resolver, md, report, true)) - } catch { - case _: ParseException => - Message.warn(s"The descriptor in $urlDescriptor from $resolver could not be parsed.") - Some(previouslyResolved) - } - case unhandledResource => - val unhandledClassName = unhandledResource.getClass.getName - val tip = s"Returning previously resolved $previouslyResolved." - Message.debug(s"Latest snapshots option does not handle `$unhandledClassName`. $tip") - Some(previouslyResolved) - } - } getOrElse { - val previousRevision = dd.getDependencyRevisionId - val date = previouslyResolved.getPublicationDate - // Change from warn to debug -- see https://github.com/sbt/sbt/issues/2650. - Message.debug(s"Unable to find new descriptor for $previousRevision at $date in $resolver.") - previouslyResolved - } - } - - /** Ported from BasicResolver#findFirstArtifactRef. */ - private def findFirstArtifactRef( - md: ModuleDescriptor, - data: ResolveData, - resolver: DependencyResolver - ): Option[ResolvedResource] = { - def artifactRef(artifact: IArtifact, date: Date): Option[ResolvedResource] = - resolver match { - case resolver: BasicResolver => - IvyContext.getContext.set(resolver.getName + ".artifact", artifact) - try { - Option(resolver.doFindArtifactRef(artifact, date)) orElse { - Option(artifact.getUrl) map { url => - Message.verbose("\tusing url for " + artifact + ": " + url) - val resource = - if ("file" == url.getProtocol) - new FileResource(new IFileRepository(), new File(url.getPath)) - else new URLResource(url) - new ResolvedResource(resource, artifact.getModuleRevisionId.getRevision) - } - } - } finally { - IvyContext.getContext.set(resolver.getName + ".artifact", null) - } - case _ => - None - } - val artifactRefs = md.getConfigurations.iterator flatMap { conf => - md.getArtifacts(conf.getName).iterator flatMap { af => - artifactRef(af, data.getDate).iterator - } - } - if (artifactRefs.hasNext) Some(artifactRefs.next()) - else None - } - - /** Ported from ChainResolver#forcedRevision. */ - private def forcedRevision(rmr: ResolvedModuleRevision): ResolvedModuleRevision = - new ResolvedModuleRevision( - rmr.getResolver, - rmr.getArtifactResolver, - rmr.getDescriptor, - rmr.getReport, - true - ) - - /** Ported from ChainResolver#resolvedRevision. */ - private def resolvedRevision(rmr: ResolvedModuleRevision): ResolvedModuleRevision = - if (isDual) - new ResolvedModuleRevision( - rmr.getResolver, - this, - rmr.getDescriptor, - rmr.getReport, - rmr.isForce - ) - else rmr - - /** Ported from ChainResolver#setLatestIfRequired. */ - private def setLatestIfRequired( - resolver: DependencyResolver, - latest: Option[LatestStrategy] - ): Option[LatestStrategy] = - latestStrategyName(resolver) match { - case Some(latestName) if latestName != "default" => - val oldLatest = latestStrategy(resolver) - doSetLatestStrategy(resolver, latest) - oldLatest - case _ => None - } - - /** Ported from ChainResolver#getLatestStrategyName. */ - private def latestStrategyName(resolver: DependencyResolver): Option[String] = - resolver match { - case r: HasLatestStrategy => Some(r.getLatest) - case _ => None - } - - /** Ported from ChainResolver#getLatest. */ - private def latestStrategy(resolver: DependencyResolver): Option[LatestStrategy] = - resolver match { - case r: HasLatestStrategy => Some(r.getLatestStrategy) - case _ => None - } - - /** Ported from ChainResolver#setLatest. */ - private def doSetLatestStrategy( - resolver: DependencyResolver, - latest: Option[LatestStrategy] - ): Option[LatestStrategy] = - resolver match { - case r: HasLatestStrategy => - val oldLatest = latestStrategy(resolver) - r.setLatestStrategy(latest.orNull) - oldLatest - case _ => None - } -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtDefaultDependencyDescriptor.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtDefaultDependencyDescriptor.scala deleted file mode 100644 index bb7eca9ee..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtDefaultDependencyDescriptor.scala +++ /dev/null @@ -1,10 +0,0 @@ -package sbt.internal.librarymanagement -package ivyint - -import org.apache.ivy.core -import core.module.descriptor.DefaultDependencyDescriptor -import sbt.librarymanagement.* - -trait SbtDefaultDependencyDescriptor { self: DefaultDependencyDescriptor => - def dependencyModuleId: ModuleID -} diff --git a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/mavenint/PomExtraDependencyAttributes.scala b/lm-ivy/src/main/scala/sbt/internal/librarymanagement/mavenint/PomExtraDependencyAttributes.scala deleted file mode 100644 index 9b1f6a1c7..000000000 --- a/lm-ivy/src/main/scala/sbt/internal/librarymanagement/mavenint/PomExtraDependencyAttributes.scala +++ /dev/null @@ -1,133 +0,0 @@ -package sbt.internal.librarymanagement -package mavenint - -import scala.collection.immutable.ArraySeq -import java.util.Properties -import java.util.regex.Pattern - -import org.apache.ivy.core.module.descriptor.DependencyDescriptor -import org.apache.ivy.core.module.id.ModuleRevisionId -import org.apache.ivy.util.extendable.ExtendableItem - -/** - * This class contains all the logic for dealing with the extra attributes in pom files relating to extra attributes - * on dependency declarations. - * - * Specifically, if we have a dependency on an sbt plugin, there are two properties that need to propagate: - * - `sbtVersion` - * - `scalaVersion` - * - * These need to exist on the *dependency declaration*. Maven/Aether has no way to inject these into - * the section of pom files, so we use Ivy's Extra attribute hackery to inject a lookup table - * of extra attributes by dependency id into POM files and later we read these back. - */ -object PomExtraDependencyAttributes { - - val ExtraAttributesKey = "extraDependencyAttributes" - val SbtVersionKey = "sbtVersion" - val ScalaVersionKey = "scalaVersion" - - /** - * Reads the extra dependency attributes out of a maven property. - * @param props The properties from an Aether resolution. - * @return - * A map of module id to extra dependency attributes associated with dependencies on that module. - */ - def readFromAether( - props: java.util.Map[String, AnyRef] - ): Map[ModuleRevisionId, Map[String, String]] = { - import scala.jdk.CollectionConverters.* - (props.asScala get ExtraAttributesKey) match { - case None => Map.empty - case Some(str) => - def processDep(m: ModuleRevisionId) = (simplify(m), filterCustomExtra(m, include = true)) - (for { - (id, props) <- readDependencyExtra(str.toString).map(processDep) - } yield id -> props).toMap - } - } - - /** - * Mutates the to collection with the extra dependency attributes from the incoming pom properties list. - * - * @param from The properties directly off a maven POM file - * @param to The aether properties where we can write whatever we want. - * - * TODO - maybe we can just parse this directly here. Note the `readFromAether` method uses - * whatever we set here. - */ - def transferDependencyExtraAttributes( - from: Properties, - to: java.util.Map[String, AnyRef] - ): Unit = - Option(from.getProperty(ExtraAttributesKey, null)) foreach (to.put(ExtraAttributesKey, _)) - - /** - * Reads the extra dependency information out of Ivy's notion of POM properties and returns - * the map of ID -> Extra Properties. - */ - def getDependencyExtra(m: Map[String, String]): Map[ModuleRevisionId, Map[String, String]] = - (m get ExtraAttributesKey) match { - case None => Map.empty - case Some(str) => - def processDep(m: ModuleRevisionId) = (simplify(m), filterCustomExtra(m, include = true)) - readDependencyExtra(str).map(processDep).toMap - } - - def qualifiedExtra(item: ExtendableItem): Map[String, String] = { - import scala.jdk.CollectionConverters.* - item.getQualifiedExtraAttributes.asInstanceOf[java.util.Map[String, String]].asScala.toMap - } - def filterCustomExtra(item: ExtendableItem, include: Boolean): Map[String, String] = - qualifiedExtra(item).view.filterKeys { k => qualifiedIsExtra(k) == include }.toMap - - def qualifiedIsExtra(k: String): Boolean = - k.endsWith(ScalaVersionKey) || k.endsWith(SbtVersionKey) - - // Reduces the id to exclude custom extra attributes - // This makes the id suitable as a key to associate a dependency parsed from a element - // with the extra attributes from the section - def simplify(id: ModuleRevisionId): ModuleRevisionId = { - import scala.jdk.CollectionConverters.* - ModuleRevisionId.newInstance( - id.getOrganisation, - id.getName, - id.getBranch, - id.getRevision, - filterCustomExtra(id, include = false).asJava - ) - } - - /** parses the sequence of dependencies with extra attribute information, with one dependency per line */ - def readDependencyExtra(s: String): Seq[ModuleRevisionId] = ArraySeq.unsafeWrapArray( - LinesP.split(s).map(_.trim).withFilter(!_.isEmpty).map(ModuleRevisionId.decode) - ) - - private val LinesP = Pattern.compile("(?m)^") - - /** - * Creates the "extra" property values for DependencyDescriptors that can be written into a maven pom - * so we don't lose the information. - * @param s - * @return - */ - def writeDependencyExtra(s: Seq[DependencyDescriptor]): Seq[String] = - s.flatMap { dd => - val revId = dd.getDependencyRevisionId - val filteredExtra = filterCustomExtra(revId, include = true) - if (filteredExtra.isEmpty) - Nil - else { - import scala.jdk.CollectionConverters.* - val revId0 = ModuleRevisionId.newInstance( - revId.getOrganisation, - revId.getName, - revId.getBranch, - revId.getRevision, - filteredExtra.asJava - ) - revId0.encodeToString :: Nil - } - } - -} diff --git a/lm-ivy/src/test/resources/artifact1.jar b/lm-ivy/src/test/resources/artifact1.jar deleted file mode 100644 index be043359e..000000000 Binary files a/lm-ivy/src/test/resources/artifact1.jar and /dev/null differ diff --git a/lm-ivy/src/test/resources/artifact2.txt b/lm-ivy/src/test/resources/artifact2.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/lm-ivy/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/ivys/ivy.xml b/lm-ivy/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/ivys/ivy.xml deleted file mode 100755 index ab045d5cb..000000000 --- a/lm-ivy/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/ivys/ivy.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - Just a test module that publishes both a binary jar and a src jar in the 'compile' configuration. - - - - - - - - - - - - - - - - - - diff --git a/lm-ivy/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/jars/libmodule.jar b/lm-ivy/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/jars/libmodule.jar deleted file mode 100644 index b21d53c7b..000000000 Binary files a/lm-ivy/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/jars/libmodule.jar and /dev/null differ diff --git a/lm-ivy/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/srcs/libmodule-source.jar b/lm-ivy/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/srcs/libmodule-source.jar deleted file mode 100644 index b21d53c7b..000000000 Binary files a/lm-ivy/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/srcs/libmodule-source.jar and /dev/null differ diff --git a/lm-ivy/src/test/resources/test-maven-repo/com/test/test-artifact/1.0.0-SNAPSHOT/test-artifact-1.0.0-SNAPSHOT.jar b/lm-ivy/src/test/resources/test-maven-repo/com/test/test-artifact/1.0.0-SNAPSHOT/test-artifact-1.0.0-SNAPSHOT.jar deleted file mode 100644 index e69de29bb..000000000 diff --git a/lm-ivy/src/test/resources/test-maven-repo/com/test/test-artifact/1.0.0-SNAPSHOT/test-artifact-1.0.0-SNAPSHOT.pom b/lm-ivy/src/test/resources/test-maven-repo/com/test/test-artifact/1.0.0-SNAPSHOT/test-artifact-1.0.0-SNAPSHOT.pom deleted file mode 100644 index 7884c5684..000000000 --- a/lm-ivy/src/test/resources/test-maven-repo/com/test/test-artifact/1.0.0-SNAPSHOT/test-artifact-1.0.0-SNAPSHOT.pom +++ /dev/null @@ -1,15 +0,0 @@ - - - 4.0.0 - - com.test - test-artifact - 1.0.0-SNAPSHOT - scala-jar - - - UTF-8 - - - diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/AbstractEngineSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/AbstractEngineSpec.scala deleted file mode 100644 index 9d6e66f3f..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/AbstractEngineSpec.scala +++ /dev/null @@ -1,24 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.librarymanagement.* -import verify.BasicTestSuite - -abstract class AbstractEngineSpec extends BasicTestSuite { - def cleanCache(): Unit - - def module( - moduleId: ModuleID, - deps: Vector[ModuleID], - scalaFullVersion: Option[String] - ): ModuleDescriptor - - def updateEither(module: ModuleDescriptor): Either[UnresolvedWarning, UpdateReport] - - def update(module: ModuleDescriptor) = - updateEither(module) match { - case Right(r) => r - case Left(w) => throw w.resolveException - } - - def cleanCachedResolutionCache(@deprecated("unused", "") module: ModuleDescriptor): Unit = () -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/BaseCachedResolutionSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/BaseCachedResolutionSpec.scala deleted file mode 100644 index acba2315e..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/BaseCachedResolutionSpec.scala +++ /dev/null @@ -1,16 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.librarymanagement.* -import sbt.internal.librarymanagement.ivy.* - -trait BaseCachedResolutionSpec extends BaseIvySpecification { - override def module( - moduleId: ModuleID, - deps: Vector[ModuleID], - scalaFullVersion: Option[String] - ): ModuleDescriptor = { - val uo: UpdateOptions = UpdateOptions() - .withCachedResolution(true) - module(moduleId, deps, scalaFullVersion, uo, true) - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/BaseIvySpecification.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/BaseIvySpecification.scala deleted file mode 100644 index 6f33625b4..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/BaseIvySpecification.scala +++ /dev/null @@ -1,136 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.io.IO -import sbt.io.syntax.* -import java.io.File -import sbt.internal.librarymanagement.ivy.* -import sbt.internal.util.ConsoleLogger -import sbt.librarymanagement.* -import cross.CrossVersionUtil -import Configurations.* - -trait BaseIvySpecification extends AbstractEngineSpec { - def currentBase: File = new File(".") - def label: String = this.getClass.getSimpleName - def currentTarget: File = currentBase / "target" / label / "ivyhome" - def currentManaged: File = currentBase / "target" / label / "lib_managed" - def currentDependency: File = currentBase / "target" / label / "dependency" - def defaultModuleId: ModuleID = - ModuleID("com.example", "foo", "0.1.0").withConfigurations(Some("compile")) - - def scala2_13 = "2.13.10" - - lazy val log = ConsoleLogger() - def lmEngine(uo: UpdateOptions = UpdateOptions()): DependencyResolution = - IvyDependencyResolution(mkIvyConfiguration(uo)) - - def configurations = Vector(Compile, Test, Runtime) - - def module( - moduleId: ModuleID, - deps: Vector[ModuleID], - scalaFullVersion: Option[String] - ): ModuleDescriptor = { - module(moduleId, deps, scalaFullVersion, UpdateOptions(), true) - } - - def module( - moduleId: ModuleID, - deps: Vector[ModuleID], - scalaFullVersion: Option[String], - uo: UpdateOptions = UpdateOptions(), - overrideScalaVersion: Boolean = true, - appendSbtCrossVersion: Boolean = false, - platform: Option[String] = None, - ): IvySbt#Module = { - val scalaModuleInfo = scalaFullVersion map { fv => - ScalaModuleInfo( - scalaFullVersion = fv, - scalaBinaryVersion = CrossVersionUtil.binaryScalaVersion(fv), - configurations = Vector.empty, - checkExplicit = true, - filterImplicit = false, - overrideScalaVersion = overrideScalaVersion - ) - .withPlatform(platform) - } - - val moduleSetting: ModuleSettings = ModuleDescriptorConfiguration(moduleId, ModuleInfo("foo")) - .withDependencies(deps) - .withConfigurations(configurations) - .withScalaModuleInfo(scalaModuleInfo) - val ivySbt = new IvySbt(mkIvyConfiguration(uo)) - new ivySbt.Module(moduleSetting, appendSbtCrossVersion) - } - - def resolvers: Vector[Resolver] = Vector(Resolver.mavenCentral) - - def chainResolver = ChainedResolver("sbt-chain", resolvers) - - def mkIvyConfiguration(uo: UpdateOptions): IvyConfiguration = { - val moduleConfs = Vector(ModuleConfiguration("*", chainResolver)) - val resCacheDir = currentTarget / "resolution-cache" - InlineIvyConfiguration() - .withPaths(IvyPaths(currentBase.toString, Some(currentTarget.toString))) - .withResolvers(resolvers) - .withModuleConfigurations(moduleConfs) - .withChecksums(Vector.empty) - .withResolutionCacheDir(resCacheDir) - .withLog(log) - .withUpdateOptions(uo) - } - - def makeUpdateConfiguration( - offline: Boolean, - metadataDirectory: Option[File] - ): UpdateConfiguration = { - val retrieveConfig = RetrieveConfiguration() - .withRetrieveDirectory(currentManaged) - .withOutputPattern(Resolver.defaultRetrievePattern) - .withSync(false) - - UpdateConfiguration() - .withRetrieveManaged(retrieveConfig) - .withLogging(UpdateLogging.Full) - .withOffline(offline) - .withMetadataDirectory(metadataDirectory) - } - - def updateEither(module: ModuleDescriptor): Either[UnresolvedWarning, UpdateReport] = - ivyUpdateEither(module) - - def ivyUpdateEither(module: ModuleDescriptor): Either[UnresolvedWarning, UpdateReport] = { - module match { - case m: IvySbt#Module => - val config = makeUpdateConfiguration(false, Some(currentDependency)) - IvyActions.updateEither(m, config, UnresolvedWarningConfiguration(), log) - } - } - - def cleanCache(): Unit = cleanIvyCache() - def cleanIvyCache(): Unit = IO.delete(currentTarget / "cache") - - override def cleanCachedResolutionCache(module: ModuleDescriptor): Unit = { - module match { - case m: IvySbt#Module => IvyActions.cleanCachedResolutionCache(m, log) - } - } - - def ivyUpdate(module: ModuleDescriptor): UpdateReport = - update(module) - - def mkPublishConfiguration( - resolver: Resolver, - artifacts: Map[Artifact, File] - ): PublishConfiguration = { - PublishConfiguration() - .withResolverName(resolver.name) - .withArtifacts(artifacts.toVector) - .withChecksums(Vector.empty) - .withOverwrite(true) - } - - def ivyPublish(module: IvySbt#Module, config: PublishConfiguration) = { - IvyActions.publish(module, config, log) - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/CachedResolutionSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/CachedResolutionSpec.scala deleted file mode 100644 index 64d38e5f4..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/CachedResolutionSpec.scala +++ /dev/null @@ -1,10 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.librarymanagement.* - -class CachedResolutionSpec extends ResolutionSpec with BaseCachedResolutionSpec { - override val resolvers = Vector( - Resolver.mavenCentral, - Resolver.sbtPluginRepo("releases") - ) -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ComponentManagerTest.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ComponentManagerTest.scala deleted file mode 100644 index 2dfc4fcbf..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ComponentManagerTest.scala +++ /dev/null @@ -1,165 +0,0 @@ -package sbt.internal.librarymanagement - -import verify.BasicTestSuite - -// TODO - We need to re-enable this test. Right now, we dont' have a "stub" launcher for this. -// This is testing something which uses a launcher interface, but was grabbing the underlying class directly -// when it really should, instead, be stubbing out the underlying class. - -object ComponentManagerTest extends BasicTestSuite { - val TestID = "manager-test" - - /* - test( - "Component manager should throw an exception if 'file' is called for a non-existing component" - ) { - withManager { manager => - intercept[InvalidComponent] { - manager.file(TestID)(Fail) - () - } - } - } - - test("it should throw an exception if 'file' is called for an empty component") { - withManager { manager => - manager.define(TestID, Nil) - intercept[InvalidComponent] { - manager.file(TestID)(Fail) - () - } - } - } - - test("it should return the file for a single-file component") { - withManager { manager => - val hash = defineFile(manager, TestID, "a") - assert(checksum(manager.file(TestID)(Fail)) == hash) - } - } - - test("it should throw an exception if 'file' is called for multi-file component") { - withManager { manager => - defineFiles(manager, TestID, "a", "b") - intercept[InvalidComponent] { - manager.file(TestID)(Fail) - () - } - } - } - - test("it should return the files for a multi-file component") { - withManager { manager => - val hashes = defineFiles(manager, TestID, "a", "b") - assert(checksum(manager.files(TestID)(Fail)).toSet == hashes.toSet) - } - } - - test("it should return the files for a single-file component") { - withManager { manager => - val hashes = defineFiles(manager, TestID, "a") - assert(checksum(manager.files(TestID)(Fail)).toSet == hashes.toSet) - } - } - - test("it should throw an exception if 'files' is called for a non-existing component") { - withManager { manager => - intercept[InvalidComponent] { - manager.files(TestID)(Fail) - () - } - } - } - - test("it should properly cache a file and then retrieve it to an unresolved component") { - withTemporaryDirectory { ivyHome => - withManagerHome(ivyHome) { definingManager => - val hash = defineFile(definingManager, TestID, "a") - try { - definingManager.cache(TestID) - withManagerHome(ivyHome) { usingManager => - assert(checksum(usingManager.file(TestID)(Fail)) == hash) - } - } finally { - definingManager.clearCache(TestID) - } - } - } - } - - private def checksum(files: Iterable[File]): Seq[String] = files.map(checksum).toSeq - private def checksum(file: File): String = - if (file.exists) ChecksumHelper.computeAsString(file, "sha1") else "" - private def defineFile(manager: ComponentManager, id: String, name: String): String = - createFile(manager, id, name)(checksum) - private def defineFiles(manager: ComponentManager, id: String, names: String*): Seq[String] = - createFiles(manager, id, names: _*)(checksum) - private def createFile[T](manager: ComponentManager, id: String, name: String)(f: File => T): T = - createFiles(manager, id, name)(files => f(files.toList.head)) - private def createFiles[T](manager: ComponentManager, id: String, names: String*)( - f: Seq[File] => T - ): T = - withTemporaryDirectory { dir => - val files = names.map(name => new File(dir, name)) - files.foreach(writeRandomContent) - manager.define(id, files) - f(files) - } - private def writeRandomContent(file: File) = IO.write(file, randomString) - private def randomString = "asdf" - private def withManager[T](f: ComponentManager => T): T = - withTemporaryDirectory { ivyHome => - withManagerHome(ivyHome)(f) - } - - private def withManagerHome[T](ivyHome: File)(f: ComponentManager => T): T = - TestLogger { logger => - withTemporaryDirectory { temp => - // The actual classes we'll use at runtime. - // val mgr = new ComponentManager(xsbt.boot.Locks, new xsbt.boot.ComponentProvider(temp, true), Some(ivyHome), logger) - - // A stub component manager - object provider extends ComponentProvider { - override def componentLocation(id: String): File = new File(temp, id) - override def lockFile(): File = { - IO.createDirectory(temp) - new java.io.File(temp, "sbt.components.lock") - } - override def defineComponent(id: String, files: Array[File]): Unit = { - val location = componentLocation(id) - if (location.exists) - throw new RuntimeException( - s"Cannot redefine component. ID: $id, files: ${files.mkString(",")}" - ) - else { - IO.copy(files.map { f => - f -> new java.io.File(location, f.getName) - }) - () - } - } - override def addToComponent(id: String, files: Array[File]): Boolean = { - val location = componentLocation(id) - IO.copy(files.map { f => - f -> new java.io.File(location, f.getName) - }) - true - } - override def component(id: String): Array[File] = - Option(componentLocation(id).listFiles()) - .map(_.filter(_.isFile)) - .getOrElse(Array.empty) - } - // A stubbed locking API. - object locks extends xsbti.GlobalLock { - override def apply[U](lockFile: File, run: Callable[U]): U = { - // TODO - do we need to lock? - run.call() - } - } - val mgr = new ComponentManager(locks, provider, Some(ivyHome), logger) - f(mgr) - } - } - */ -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ConflictWarningSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ConflictWarningSpec.scala deleted file mode 100644 index ec08131e2..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ConflictWarningSpec.scala +++ /dev/null @@ -1,40 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.librarymanagement.* -import sbt.librarymanagement.syntax.* - -object ConflictWarningSpec extends BaseIvySpecification { - - test("it should print out message about the cross-Scala conflict") { - var found = false - val deps = Vector( - `scala2.13.6`, - `cats-effect3.1.1`, - `cats-core2.6.1`.cross(CrossVersion.for3Use2_13), - ) - val m = module(defaultModuleId, deps, Some("3.0.1-RC2")) - val report = ivyUpdate(m) - val w = ConflictWarning.default("foo") - - try { - ConflictWarning(w, report, log) - } catch { - case e: Throwable => - found = true - assert( - e.getMessage.linesIterator.toList.head - .startsWith("Conflicting cross-version suffixes in") - ) - } - if (!found) { - sys.error("conflict warning was expected, but didn't happen sbt/sbt#6578") - } - } - - lazy val `scala2.13.6` = - ModuleID("org.scala-lang", "scala-library", "2.13.6").withConfigurations(Some("compile")) - lazy val `cats-effect3.1.1` = - ("org.typelevel" %% "cats-effect" % "3.1.1").withConfigurations(Some("compile")) - lazy val `cats-core2.6.1` = - ("org.typelevel" %% "cats-core" % "2.6.1").withConfigurations(Some("compile")) -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ConvertResolverSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ConvertResolverSpec.scala deleted file mode 100644 index fcdc79159..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ConvertResolverSpec.scala +++ /dev/null @@ -1,60 +0,0 @@ -/* - * sbt - * Copyright 2023, Scala center - * Copyright 2011 - 2022, Lightbend, Inc. - * Copyright 2008 - 2010, Mark Harrah - * Licensed under Apache License 2.0 (see LICENSE) - */ - -package sbt.internal.librarymanagement - -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.plugins.resolver.AbstractPatternsBasedResolver -import sbt.internal.librarymanagement.ivy.UpdateOptions -import sbt.internal.util.ConsoleLogger -import sbt.librarymanagement.{ Patterns, URLRepository } -import verify.BasicTestSuite - -import scala.jdk.CollectionConverters.* - -// Regression coverage for sbt/sbt#535: the organization token in an Ivy SFTP/SSH/URL resolver pattern -// should be rendered literally unless the resolver is explicitly Maven-compatible. ConvertResolver is the -// boundary sbt owns: it maps Patterns.isMavenCompatible onto Apache Ivy's setM2compatible, which is what -// makes Ivy keep [organisation] literal (false) or rewrite it to slash form (true). -object ConvertResolverSpec extends BasicTestSuite { - private val log = ConsoleLogger() - - // The configuration from issue #535: a custom Ivy pattern using the [organisation] token. - private val orgPatterns = - Patterns() - .withIvyPatterns( - Vector("https://example.org/repo/[organisation]/[module]/ivys/ivy-[revision].xml") - ) - .withArtifactPatterns( - Vector( - "https://example.org/repo/[organisation]/[module]/[type]s/[artifact]-[revision].[ext]" - ) - ) - - private def convert(patterns: Patterns): AbstractPatternsBasedResolver = - ConvertResolver(URLRepository("test-repo", patterns), new IvySettings, UpdateOptions(), log) - .asInstanceOf[AbstractPatternsBasedResolver] - - test("the default Patterns keeps the Ivy resolver non-m2compatible (issue #535)") { - assert(!orgPatterns.isMavenCompatible) - assert(!convert(orgPatterns).isM2compatible) - } - - test("isMavenCompatible = false renders the [organisation] token literally") { - val resolver = convert(orgPatterns) - assert(!resolver.isM2compatible) - // sbt forwards the pattern verbatim; with m2compatible off Ivy does not rewrite the organization. - assert(resolver.getArtifactPatterns.asScala.exists(_.toString.contains("[organisation]"))) - } - - test( - "isMavenCompatible = true makes the Ivy resolver m2compatible (organization rewritten to slash form)" - ) { - assert(convert(orgPatterns.withIsMavenCompatible(true)).isM2compatible) - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/CredentialsSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/CredentialsSpec.scala deleted file mode 100644 index d613a5b6b..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/CredentialsSpec.scala +++ /dev/null @@ -1,51 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.internal.librarymanagement.ivy.IvyCredentials -import sbt.librarymanagement.Credentials - -import java.io.File -import java.nio.file.Files - -import org.scalatest.funsuite.AnyFunSuite - -class CredentialsSpec extends AnyFunSuite { - - test("load credential file without authentication") { - val credentialsFile = File.createTempFile("credentials", "tmp") - - val content = - """|host=example.org - |user=username - |password=password""".stripMargin - - Files.write(credentialsFile.toPath(), content.getBytes()) - - val Right(credentials) = IvyCredentials.loadCredentials(credentialsFile): @unchecked - - assert(credentials.realm == null) - - credentialsFile.delete() - } - - test("DirectCredentials.toString") { - assert( - Credentials( - realm = null, - host = "example.org", - userName = "username", - passwd = "password" - ).toString == - """DirectCredentials(null, "example.org", "username", ****)""" - ) - - assert( - Credentials( - realm = "realm", - host = "example.org", - userName = "username", - passwd = "password" - ).toString == - """DirectCredentials("realm", "example.org", "username", ****)""" - ) - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/CustomPomParserTest.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/CustomPomParserTest.scala deleted file mode 100644 index 08dcf5fb6..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/CustomPomParserTest.scala +++ /dev/null @@ -1,42 +0,0 @@ -package sbt.internal.librarymanagement - -import java.io.File -import org.apache.ivy.core.module.descriptor.Artifact as IvyArtifact -import org.apache.ivy.core.module.id.ModuleRevisionId -import org.apache.ivy.core.resolve.ResolveOptions -import sbt.internal.librarymanagement.ivy.InlineIvyConfiguration -import sbt.librarymanagement.* -import sbt.io.IO.withTemporaryDirectory -import sbt.internal.util.ConsoleLogger -import verify.BasicTestSuite - -object CustomPomParserTest extends BasicTestSuite { - test( - "CustomPomParser should resolve an artifact with packaging 'scala-jar' as a regular jar file." - ) { - val log = ConsoleLogger() - withTemporaryDirectory { cacheDir => - val repoUrl = getClass.getResource("/test-maven-repo") - val local = MavenRepository("Test Repo", repoUrl.toExternalForm) - val paths = IvyPaths(new File(".").toString, Some(cacheDir.toString)) - val conf = InlineIvyConfiguration() - .withPaths(paths) - .withResolvers(Vector(local)) - .withLog(log) - val ivySbt = new IvySbt(conf) - val resolveOpts = new ResolveOptions().setConfs(Array("default")) - val mrid = ModuleRevisionId.newInstance("com.test", "test-artifact", "1.0.0-SNAPSHOT") - - val resolveReport = ivySbt.withIvy(log) { ivy => - ivy.resolve(mrid, resolveOpts, true) - } - - assert(!resolveReport.hasError) - assert(resolveReport.getArtifacts.size() == 1) - val artifact: IvyArtifact = - resolveReport.getArtifacts.asInstanceOf[java.util.List[IvyArtifact]].get(0) - assert(artifact.getModuleRevisionId == mrid) - assert(artifact.getExt == "jar") - } - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/DMSerializationSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/DMSerializationSpec.scala deleted file mode 100644 index b6e221fe7..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/DMSerializationSpec.scala +++ /dev/null @@ -1,110 +0,0 @@ -package sbt.internal.librarymanagement - -import java.net.URI -import java.io.File - -import sbt.librarymanagement.* -import sjsonnew.shaded.scalajson.ast.unsafe.* -import sjsonnew.*, support.scalajson.unsafe.* -import LibraryManagementCodec.given -import verify.BasicTestSuite - -object DMSerializationSpec extends BasicTestSuite { - test("CrossVersion.full should roundtrip") { - roundtripStr(CrossVersion.full: CrossVersion) - } - - test("CrossVersion.binary should roundtrip") { - roundtripStr(CrossVersion.binary: CrossVersion) - } - - test("CrossVersion.for3Use2_13 should roundtrip") { - roundtripStr(CrossVersion.for3Use2_13: CrossVersion) - } - - test("CrossVersion.for2_13Use3 with prefix should roundtrip") { - roundtripStr(CrossVersion.for2_13Use3With("_sjs1", ""): CrossVersion) - } - - test("CrossVersion.Disabled should roundtrip") { - roundtrip(Disabled(): CrossVersion) - } - - test("""Artifact("foo") should roundtrip""") { - roundtrip(Artifact("foo")) - } - - test("""Artifact("foo", "sources") should roundtrip""") { - roundtrip(Artifact("foo", "sources")) - } - - test("""Artifact.pom("foo") should roundtrip""") { - roundtrip(Artifact.pom("foo")) - } - - test("""Artifact("foo", url("http://example.com/")) should roundtrip""") { - roundtrip(Artifact("foo", new URI("http://example.com/"))) - } - - test("""Artifact("foo").extra(("key", "value")) should roundtrip""") { - roundtrip(Artifact("foo").extra(("key", "value"))) - } - - test("""ModuleID("org", "name", "1.0") should roundtrip""") { - roundtrip(ModuleID("org", "name", "1.0")) - } - - test("""ModuleReport(ModuleID("org", "name", "1.0"), Nil, Nil) should roundtrip""") { - roundtripStr(ModuleReport(ModuleID("org", "name", "1.0"), Vector.empty, Vector.empty)) - } - - test("Organization artifact report should roundtrip") { - roundtripStr(organizationArtifactReportExample) - } - - test("Configuration report should roundtrip") { - roundtripStr(configurationReportExample) - } - - test("Update report should roundtrip") { - roundtripStr(updateReportExample) - } - - lazy val updateReportExample = - UpdateReport( - new File("./foo"), - Vector(configurationReportExample), - UpdateStats(0, 0, 0, false), - Map("./foo" -> 0) - ) - lazy val configurationReportExample = - ConfigurationReport( - ConfigRef("compile"), - Vector(moduleReportExample), - Vector(organizationArtifactReportExample) - ) - lazy val organizationArtifactReportExample = - OrganizationArtifactReport("org", "name", Vector(moduleReportExample)) - lazy val moduleReportExample = - ModuleReport(ModuleID("org", "name", "1.0"), Vector.empty, Vector.empty) - - def roundtrip[A: JsonReader: JsonWriter](a: A): Unit = - roundtripBuilder(a) { (x1, x2) => - assert(x1 == x2) - } - - def roundtripStr[A: JsonReader: JsonWriter](a: A): Unit = - roundtripBuilder(a) { (x1, x2) => - assert(x1.toString == x2.toString) - } - - def roundtripBuilder[A: JsonReader: JsonWriter](a: A)(f: (A, A) => Unit): Unit = { - val json = isoString.to(Converter.toJsonUnsafe(a)) - println(json) - val obj = Converter.fromJsonUnsafe[A](isoString.from(json)) - f(a, obj) - } - - given isoString: IsoString[JValue] = - IsoString.iso(CompactPrinter.apply, Parser.parseUnsafe) -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/FakeResolverSpecification.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/FakeResolverSpecification.scala deleted file mode 100644 index 672a3b58a..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/FakeResolverSpecification.scala +++ /dev/null @@ -1,89 +0,0 @@ -package sbt -package internal -package librarymanagement - -import java.io.File - -import sbt.librarymanagement.{ ModuleID, RawRepository, Resolver, UpdateReport, ResolveException } - -object FakeResolverSpecification extends BaseIvySpecification { - import FakeResolver.* - - val myModule = - ModuleID("org.example", "my-module", "0.0.1-SNAPSHOT").withConfigurations(Some("compile")) - val example = ModuleID("com.example", "example", "1.0.0").withConfigurations(Some("compile")) - val anotherExample = - ModuleID("com.example", "another-example", "1.0.0").withConfigurations(Some("compile")) - val nonExisting = - ModuleID("com.example", "does-not-exist", "1.2.3").withConfigurations(Some("compile")) - - test("The FakeResolver should find modules with only one artifact") { - val m = getModule(myModule) - val report = ivyUpdate(m) - val allFiles = getAllFiles(report) - - assert(report.allModules.length == 1) - assert(report.allModuleReports.length == 1) - assert(report.configurations.length == 3) - assert(allFiles.toSet.size == 1) - assert(allFiles(1).getName == "artifact1-0.0.1-SNAPSHOT.jar") - } - - test("it should find modules with more than one artifact") { - val m = getModule(example) - val report = ivyUpdate(m) - val allFiles = getAllFiles(report).toSet - - assert(report.allModules.length == 1) - assert(report.allModuleReports.length == 1) - assert(report.configurations.length == 3) - assert(allFiles.toSet.size == 2) - assert(allFiles.map(_.getName) == Set("artifact1-1.0.0.jar", "artifact2-1.0.0.txt")) - } - - test("it should fail gracefully when asked for unknown modules") { - val m = getModule(nonExisting) - intercept[ResolveException] { - ivyUpdate(m) - () - } - } - - test("it should fail gracefully when some artifacts cannot be found") { - val m = getModule(anotherExample) - intercept[ResolveException] { - ivyUpdate(m) - () - } - } - - private def artifact1 = new File(getClass.getResource("/artifact1.jar").toURI.getPath) - private def artifact2 = new File(getClass.getResource("/artifact2.txt").toURI.getPath) - - private def modules = Map( - ("org.example", "my-module", "0.0.1-SNAPSHOT") -> List( - FakeArtifact("artifact1", "jar", "jar", artifact1) - ), - ("com.example", "example", "1.0.0") -> List( - FakeArtifact("artifact1", "jar", "jar", artifact1), - FakeArtifact("artifact2", "txt", "txt", artifact2) - ), - ("com.example", "another-example", "1.0.0") -> List( - FakeArtifact("artifact1", "jar", "jar", artifact1), - FakeArtifact("non-existing", "txt", "txt", new File("non-existing-file")) - ) - ) - - private def fakeResolver = new FakeResolver("FakeResolver", new File("tmp"), modules) - override def resolvers: Vector[Resolver] = - Vector(new RawRepository(fakeResolver, fakeResolver.getName)) - private def getModule(myModule: ModuleID): IvySbt#Module = - module(defaultModuleId, Vector(myModule), None) - private def getAllFiles(report: UpdateReport) = - for { - conf <- report.configurations - m <- conf.modules - (_, f) <- m.artifacts - } yield f - -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/FrozenModeSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/FrozenModeSpec.scala deleted file mode 100644 index 4cb1ab98b..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/FrozenModeSpec.scala +++ /dev/null @@ -1,77 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.librarymanagement.* -import sbt.internal.librarymanagement.ivy.UpdateOptions -import sbt.librarymanagement.syntax.* - -object FrozenModeSpec extends BaseIvySpecification { - private final val targetDir = Some(currentDependency) - private final val onlineConf = makeUpdateConfiguration(false, targetDir) - private final val frozenConf = makeUpdateConfiguration(false, targetDir).withFrozen(true) - private final val warningConf = UnresolvedWarningConfiguration() - private final val normalOptions = UpdateOptions() - - final val stoml = Vector("me.vican.jorge" % "stoml_2.12" % "0.4" % "compile") - - /* https://repo1.maven.org/maven2/me/vican/jorge/stoml_2.12/0.4/stoml_2.12-0.4.jar - * https://repo1.maven.org/maven2/org/scala-lang/scala-library/2.12.0/scala-library-2.12.0.jar - * https://repo1.maven.org/maven2/com/lihaoyi/fastparse_2.12/0.4.2/fastparse_2.12-0.4.2.jar - * https://repo1.maven.org/maven2/com/lihaoyi/fastparse-utils_2.12/0.4.2/fastparse-utils_2.12-0.4.2.jar - * https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.12/0.1.3/sourcecode_2.12-0.1.3.jar */ - final val explicitStoml = Vector( - "me.vican.jorge" % "stoml_2.12" % "0.4" % "compile", - "org.scala-lang" % "scala-library" % "2.12.0" % "compile", - "com.lihaoyi" % "fastparse_2.12" % "0.4.2" % "compile", - "com.lihaoyi" % "fastparse-utils_2.12" % "0.4.2" % "compile", - "com.lihaoyi" % "sourcecode_2.12" % "0.1.3" % "compile" - ) - - test("fail when artifacts are missing in the cache") { - cleanIvyCache() - def update(module: IvySbt#Module, conf: UpdateConfiguration) = - IvyActions.updateEither(module, conf, warningConf, log) - - val toResolve = module(defaultModuleId, stoml, None, normalOptions) - val onlineResolution = update(toResolve, onlineConf) - assert(onlineResolution.isRight) - val numberResolved = - onlineResolution.fold(e => throw e.resolveException, identity).allModules.size - val numberReportsResolved = - onlineResolution.fold(e => throw e.resolveException, identity).allModuleReports.size - - cleanIvyCache() - val singleFrozenResolution = update(toResolve, frozenConf) - assert(singleFrozenResolution.isRight) - assert( - singleFrozenResolution.fold(e => throw e.resolveException, identity).allModules.size == 1, - s"The number of explicit modules in frozen mode should 1" - ) - assert( - singleFrozenResolution - .fold(e => throw e.resolveException, identity) - .allModuleReports - .size == 1, - s"The number of explicit module reports in frozen mode should 1" - ) - - cleanIvyCache() - // This relies on the fact that stoml has 5 transitive dependencies - val toExplicitResolve = module(defaultModuleId, explicitStoml, None, normalOptions) - val frozenResolution = update(toExplicitResolve, frozenConf) - assert(frozenResolution.isRight) - assert( - frozenResolution - .fold(e => throw e.resolveException, identity) - .allModules - .size == numberResolved, - s"The number of explicit modules in frozen mode should be equal than $numberResolved" - ) - assert( - frozenResolution - .fold(e => throw e.resolveException, identity) - .allModuleReports - .size == numberReportsResolved, - s"The number of explicit module reports in frozen mode should be equal than $numberReportsResolved" - ) - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/InconsistentDuplicateSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/InconsistentDuplicateSpec.scala deleted file mode 100644 index b30c25809..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/InconsistentDuplicateSpec.scala +++ /dev/null @@ -1,44 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.librarymanagement.* -import verify.BasicTestSuite - -// This is a specification to check the inconsistent duplicate warnings -object InconsistentDuplicateSpec extends BasicTestSuite { - test("Duplicate with different version should be warned") { - assert( - IvySbt.inconsistentDuplicateWarning(Seq(akkaActor214, akkaActor230)) == - List( - "Multiple dependencies with the same organization/name but different versions. To avoid conflict, pick one version:", - " * com.typesafe.akka:akka-actor:(2.1.4, 2.3.0)" - ) - ) - } - - test("it should not be warned if in different configurations") { - assert(IvySbt.inconsistentDuplicateWarning(Seq(akkaActor214, akkaActor230Test)) == Nil) - } - - test("Duplicate with same version should not be warned") { - assert(IvySbt.inconsistentDuplicateWarning(Seq(akkaActor230Test, akkaActor230)) == Nil) - } - - def akkaActor214 = - ModuleID("com.typesafe.akka", "akka-actor", "2.1.4") - .withConfigurations( - Some("compile") - ) - .cross(CrossVersion.binary) - def akkaActor230 = - ModuleID("com.typesafe.akka", "akka-actor", "2.3.0") - .withConfigurations( - Some("compile") - ) - .cross(CrossVersion.binary) - def akkaActor230Test = - ModuleID("com.typesafe.akka", "akka-actor", "2.3.0") - .withConfigurations( - Some("test") - ) - .cross(CrossVersion.binary) -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyActionsOverrideSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyActionsOverrideSpec.scala deleted file mode 100644 index 61a51f46b..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyActionsOverrideSpec.scala +++ /dev/null @@ -1,74 +0,0 @@ -package sbt.internal.librarymanagement - -import verify.BasicTestSuite - -object IvyActionsOverrideSpec extends BasicTestSuite: - - test( - "IvyActions.applyDependencyOverrides should replace rev attribute for matching dependencies" - ): - val overrideMap = Map(("org.slf4j", "slf4j-api") -> "2.0.16") - - val sampleXml = - - - - - - - - - val updated = IvyActions.applyDependencyOverrides(sampleXml, overrideMap) - - val dependencies = (updated \\ "dependency") - - // Check slf4j-api has overridden version - val slf4jDep = dependencies.find(d => (d \ "@org").text == "org.slf4j") - assert(slf4jDep.isDefined) - assert((slf4jDep.get \ "@rev").text == "2.0.16") - assert((slf4jDep.get \ "@name").text == "slf4j-api") - assert((slf4jDep.get \ "@conf").text == "compile->default(compile)") - - // Check other-lib is unchanged - val otherDep = dependencies.find(d => (d \ "@org").text == "other.org") - assert(otherDep.isDefined) - assert((otherDep.get \ "@rev").text == "1.0.0") - assert((otherDep.get \ "@name").text == "other-lib") - - test( - "IvyActions.applyDependencyOverrides should preserve all attributes when replacing rev" - ): - val overrideMap = Map(("org.example", "test-lib") -> "3.0.0") - - val sampleXml = - - - - - - - val updated = IvyActions.applyDependencyOverrides(sampleXml, overrideMap) - - val dep = (updated \\ "dependency").head - assert((dep \ "@org").text == "org.example") - assert((dep \ "@name").text == "test-lib") - assert((dep \ "@rev").text == "3.0.0") - assert((dep \ "@conf").text == "compile->default") - assert((dep \ "@transitive").text == "false") - assert((dep \ "@force").text == "true") - - test("IvyActions.applyDependencyOverrides should not modify dependencies without overrides"): - val overrideMap = Map(("org.other", "other-lib") -> "2.0.0") - - val sampleXml = - - - - - - - val updated = IvyActions.applyDependencyOverrides(sampleXml, overrideMap) - - val dep = (updated \\ "dependency").head - assert((dep \ "@rev").text == "1.0.0") // Should remain unchanged -end IvyActionsOverrideSpec diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyModuleSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyModuleSpec.scala deleted file mode 100644 index ac9474912..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyModuleSpec.scala +++ /dev/null @@ -1,38 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.internal.librarymanagement.mavenint.PomExtraDependencyAttributes.{ - SbtVersionKey, - ScalaVersionKey -} -import sbt.librarymanagement.{ CrossVersion, ModuleDescriptorConfiguration } - -object IvyModuleSpec extends BaseIvySpecification { - - test("The Scala binary version of a Scala module should be appended to its name") { - val m = module( - defaultModuleId.withCrossVersion(CrossVersion.Binary()), - Vector.empty, - Some("2.13.10") - ) - m.moduleSettings match { - case configuration: ModuleDescriptorConfiguration => - assert(configuration.module.name == "foo_2.13") - case _ => fail() - } - } - - test("The sbt cross-version should be appended to the name of an sbt plugin") { - val m = module( - defaultModuleId.extra(SbtVersionKey -> "1.0", ScalaVersionKey -> "2.12"), - Vector.empty, - Some("2.12.17"), - appendSbtCrossVersion = true - ) - m.moduleSettings match { - case configuration: ModuleDescriptorConfiguration => - assert(configuration.module.name == "foo_2.12_1.0") - case _ => fail() - } - } - -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyRepoSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyRepoSpec.scala deleted file mode 100644 index 0a1099161..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyRepoSpec.scala +++ /dev/null @@ -1,105 +0,0 @@ -package sbt.internal.librarymanagement - -import org.scalatest.Inside -import sbt.librarymanagement.* -import sbt.librarymanagement.syntax.* -import InternalDefaults.* - -object IvyRepoSpec extends BaseIvySpecification { - - val ourModuleID = ModuleID("com.example", "foo", "0.1.0").withConfigurations(Some("compile")) - - def makeModuleForDepWithSources = { - // By default a module seems to only have [compile, test, runtime], yet deps automatically map to - // default->compile(default) ... so I guess we have to explicitly use e.g. "compile" - val dep = "com.test" % "module-with-srcs" % "0.1.00" % "compile" - - module( - ourModuleID, - Vector(dep), - None // , UpdateOptions().withCachedResolution(true) - ) - } - - test( - "ivyUpdate from ivy repository should resolve only binary artifact from module which also contains a sources artifact under the same configuration." - ) { - cleanIvyCache() - - val m = makeModuleForDepWithSources - - val report = ivyUpdate(m) - - import Inside.* - inside(report.configuration(ConfigRef("compile")).map(_.modules)) { case Some(Seq(mr)) => - inside(mr.artifacts) { case Seq((ar, _)) => - assert(ar.`type` == "jar") - assert(ar.extension == "jar") - } - } - } - - test( - "it should resolve only sources artifact of an acceptable artifact type, \"src\", when calling updateClassifiers." - ) { - cleanIvyCache() - - val m = makeModuleForDepWithSources - - // the "default" configuration used in `update`. - val c = makeUpdateConfiguration(false, None) - - val scalaModuleInfo = m.moduleSettings.scalaModuleInfo - val srcTypes = Vector("src") - val docTypes = Vector("javadoc") - // These will be the default classifiers that SBT should try, in case a dependency is Maven. - // In this case though, they will be tried and should fail gracefully - only the - val attemptedClassifiers = Vector("sources", "javadoc") - - // The dep that we want to get the "classifiers" (i.e. sources / docs) for. - // We know it has only one source artifact in the "compile" configuration. - val dep = "com.test" % "module-with-srcs" % "0.1.00" % "compile" - - val clMod = { - val externalModules = Vector(dep) - // Note: need to extract ourModuleID so we can plug it in here, can't fish it back out of the IvySbt#Module (`m`) - GetClassifiersModule( - ourModuleID, - scalaModuleInfo, - externalModules, - Vector(Configurations.Compile), - attemptedClassifiers - ) - } - - val artifactFilter = getArtifactTypeFilter(c.artifactFilter) - val gcm = GetClassifiersConfiguration( - clMod, - Vector.empty, - c.withArtifactFilter(artifactFilter.invert), - srcTypes, - docTypes - ) - - val report2 = - lmEngine() - .updateClassifiers(gcm, UnresolvedWarningConfiguration(), Vector(), log) - .fold(e => throw e.resolveException, identity) - - import Inside.* - inside(report2.configuration(ConfigRef("compile")).map(_.modules)) { case Some(Seq(mr)) => - inside(mr.artifacts) { case Seq((ar, _)) => - assert(ar.name == "libmodule-source") - assert(ar.`type` == "src") - assert(ar.extension == "jar") - } - } - } - - override lazy val resolvers: Vector[Resolver] = Vector(testIvy) - - lazy val testIvy = { - val repoUrl = getClass.getResource("/test-ivy-repo").toURI() - Resolver.uri("Test Repo", repoUrl)(using Resolver.ivyStylePatterns) - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyResolutionSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyResolutionSpec.scala deleted file mode 100644 index 429de4fa3..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyResolutionSpec.scala +++ /dev/null @@ -1,10 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.librarymanagement.* - -class IvyResolutionSpec extends ResolutionSpec with BaseIvySpecification { - override val resolvers = Vector( - Resolver.mavenCentral, - Resolver.sbtPluginRepo("releases") - ) -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyUtilSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyUtilSpec.scala deleted file mode 100644 index 41b3b2adf..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/IvyUtilSpec.scala +++ /dev/null @@ -1,66 +0,0 @@ -package sbt.internal.librarymanagement - -import java.io.IOException - -import org.scalatest.funsuite.AnyFunSuite -import sbt.internal.librarymanagement.IvyUtil.* - -class IvyUtilSpec extends AnyFunSuite { - test("503 should be a TransientNetworkException") { - val statusCode503Exception = - new IOException("Server returned HTTP response code: 503 for URL:") - assert(TransientNetworkException(statusCode503Exception)) - } - - test("500 should be a TransientNetworkException") { - val statusCode500Exception = - new IOException("Server returned HTTP response code: 500 for URL:") - assert(TransientNetworkException(statusCode500Exception)) - } - - test("408 should be a TransientNetworkException") { - val statusCode408Exception = - new IOException("Server returned HTTP response code: 408 for URL:") - assert(TransientNetworkException(statusCode408Exception)) - } - - test("429 should be a TransientNetworkException") { - val statusCode429Exception = - new IOException(" Server returned HTTP response code: 429 for URL:") - assert(TransientNetworkException(statusCode429Exception)) - } - - test("404 should not be a TransientNetworkException") { - val statusCode404Exception = - new IOException("Server returned HTTP response code: 404 for URL:") - assert(!TransientNetworkException(statusCode404Exception)) - } - - test("IllegalArgumentException should not be a TransientNetworkException") { - val illegalArgumentException = new IllegalArgumentException() - assert(!TransientNetworkException(illegalArgumentException)) - } - - test("it should retry for 3 attempts") { - var i = 0 - def f: Int = { - i += 1 - if (i < 3) throw new RuntimeException() else i - } - // exception predicate retries on all exceptions for this test - val result = retryWithBackoff(f, _ => true, maxAttempts = 3) - assert(result == 3) - } - - test("it should fail after maxAttempts") { - var i = 0 - def f: Int = { - i += 1 - throw new RuntimeException() - } - intercept[RuntimeException] { - retryWithBackoff(f, _ => true, maxAttempts = 3) - } - assert(i == 3) - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/MakePomSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/MakePomSpec.scala deleted file mode 100644 index 6b3aafd17..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/MakePomSpec.scala +++ /dev/null @@ -1,100 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.internal.util.ConsoleLogger -import sbt.librarymanagement.MavenRepository -import verify.BasicTestSuite - -// http://ant.apache.org/ivy/history/2.3.0/ivyfile/dependency.html -// http://maven.apache.org/enforcer/enforcer-rules/versionRanges.html -object MakePomSpec extends BasicTestSuite { - // This is a specification to check the Ivy revision number conversion to pom. - - test("1.0 should convert to 1.0") { - convertTo("1.0", "1.0") - } - - test("[1.0,2.0] should convert to [1.0,2.0]") { - convertTo("[1.0,2.0]", "[1.0,2.0]") - } - - test("[1.0,2.0[ should convert to [1.0,2.0)") { - convertTo("[1.0,2.0[", "[1.0,2.0)") - } - - test("]1.0,2.0] should convert to (1.0,2.0]") { - convertTo("]1.0,2.0]", "(1.0,2.0]") - } - - test("]1.0,2.0[ should convert to (1.0,2.0)") { - convertTo("]1.0,2.0[", "(1.0,2.0)") - } - - test("[1.0,) should convert to [1.0,)") { - convertTo("[1.0,)", "[1.0,)") - } - - test("]1.0,) should convert to (1.0,)") { - convertTo("]1.0,)", "(1.0,)") - } - - test("(,2.0] should convert to (,2.0]") { - convertTo("(,2.0]", "(,2.0]") - } - - test("(,2.0[ should convert to (,2.0)") { - convertTo("(,2.0[", "(,2.0)") - } - - test("1.+ should convert to [1,2)") { - convertTo("1.+", "[1,2)") - } - - test("1.2.3.4.+ should convert to [1.2.3.4,1.2.3.5)") { - convertTo("1.2.3.4.+", "[1.2.3.4,1.2.3.5)") - } - - test("12.31.42.+ should convert to [12.31.42,12.31.43)") { - convertTo("12.31.42.+", "[12.31.42,12.31.43)") - } - - test( - "1.1+ should convert to [1.1,1.2),[1.10,1.20),[1.100,1.200),[1.1000,1.2000),[1.10000,1.20000)" - ) { - convertTo("1.1+", "[1.1,1.2),[1.10,1.20),[1.100,1.200),[1.1000,1.2000),[1.10000,1.20000)") - } - - test("1+ should convert to [1,2),[10,20),[100,200),[1000,2000),[10000,20000)") { - convertTo("1+", "[1,2),[10,20),[100,200),[1000,2000),[10000,20000)") - } - - test("+ should convert to [0,)") { - convertTo("+", "[0,)") - } - - test("foo+ should convert to foo+") { - beParsedAsError("foo+") - } - - test("repository id should not contain maven illegal repo id characters") { - val repository = mp.mavenRepository( - MavenRepository( - """repository-id-\with-/illegal:"<-chars>|?*-others~!@#$%^&`';{}[]=+_,.""", - "uri" - ) - ) - assert( - (repository \ "id").text == "repository-id-with-illegal-chars-others~!@#$%^&`';{}[]=+_,." - ) - } - - val mp = new MakePom(ConsoleLogger()) - def convertTo(s: String, expected: String): Unit = { - assert(MakePom.makeDependencyVersion(s) == expected) - } - def beParsedAsError(s: String): Unit = { - intercept[Throwable] { - MakePom.makeDependencyVersion(s) - () - } - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ManagedChecksumsSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ManagedChecksumsSpec.scala deleted file mode 100644 index 43613964a..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ManagedChecksumsSpec.scala +++ /dev/null @@ -1,59 +0,0 @@ -package sbt.internal.librarymanagement - -import org.apache.ivy.util.Message -import sbt.internal.librarymanagement.ivy.* -import sbt.librarymanagement.* -import sbt.io.IO - -object ManagedChecksumsSpec extends BaseIvySpecification { - private final def targetDir = Some(currentDependency) - private final def onlineConf = makeUpdateConfiguration(false, targetDir) - private final def warningConf = UnresolvedWarningConfiguration() - private final val Checksum = "sha1" - - def avro177 = ModuleID("org.apache.avro", "avro", "1.7.7") - def dataAvro1940 = ModuleID("com.linkedin.pegasus", "data-avro", "1.9.40") - def netty320 = ModuleID("org.jboss.netty", "netty", "3.2.0.Final") - final def dependencies: Vector[ModuleID] = - Vector(avro177, dataAvro1940, netty320).map(_.withConfigurations(Some("compile"))) - - import sbt.io.syntax.* - override def mkIvyConfiguration(uo: UpdateOptions): IvyConfiguration = { - val moduleConfs = Vector(ModuleConfiguration("*", chainResolver)) - val resCacheDir = currentTarget / "resolution-cache" - InlineIvyConfiguration() - .withPaths(IvyPaths(currentBase.toString, Some(currentTarget.toString))) - .withResolvers(resolvers) - .withModuleConfigurations(moduleConfs) - .withChecksums(Vector(Checksum)) - .withResolutionCacheDir(resCacheDir) - .withLog(log) - .withUpdateOptions(uo) - .withManagedChecksums(true) - } - - def cleanAll(): Unit = { - cleanIvyCache() - IO.delete(currentTarget) - IO.delete(currentManaged) - IO.delete(currentDependency) - } - - def assertChecksumExists(file: File) = { - val shaFile = new File(file.getAbsolutePath + s".$Checksum") - Message.info(s"Checking $shaFile exists...") - assert(shaFile.exists(), s"The checksum $Checksum for $file does not exist") - } - - test("Managed checksums should download the checksum files") { - cleanAll() - val updateOptions = UpdateOptions() - val toResolve = module(defaultModuleId, dependencies, None, updateOptions) - val res = IvyActions.updateEither(toResolve, onlineConf, warningConf, log) - assert(res.isRight, s"Resolution with managed checksums failed! $res") - val updateReport = res.fold(e => throw e.resolveException, identity) - val allModuleReports = updateReport.configurations.flatMap(_.modules) - val allArtifacts: Seq[File] = allModuleReports.flatMap(_.artifacts.map(_._2)) - allArtifacts.foreach(assertChecksumExists) - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/MergeDescriptorSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/MergeDescriptorSpec.scala deleted file mode 100644 index 0c106f4cd..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/MergeDescriptorSpec.scala +++ /dev/null @@ -1,40 +0,0 @@ -package sbt.internal.librarymanagement - -import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor -import sbt.librarymanagement.* -import sbt.internal.librarymanagement.ivy.UpdateOptions -import sbt.internal.librarymanagement.ivyint.* - -object MergeDescriptorSpec extends BaseIvySpecification { - test("Merging duplicate dependencies should work") { - cleanIvyCache() - val m = module( - ModuleID("com.example", "foo", "0.1.0").withConfigurations(Some("compile")), - Vector(guavaTest, guavaTestTests), - None, - UpdateOptions() - ) - m.withModule(log) { case (_, md, _) => - val deps = md.getDependencies - assert(deps.size == 1) - deps.headOption.getOrElse(sys.error("Dependencies not found")) match { - case dd @ MergedDescriptors(_, _) => - val arts = dd.getAllDependencyArtifacts - val a0: DependencyArtifactDescriptor = arts.toList(0) - val a1: DependencyArtifactDescriptor = arts.toList(1) - val configs0 = a0.getConfigurations.toList - val configs1 = a1.getConfigurations.toList - assert(configs0 == List("compile")) - assert(configs1 == List("test")) - } - } - } - def guavaTest = - ModuleID("com.google.guava", "guava-tests", "18.0").withConfigurations(Option("compile")) - def guavaTestTests = - ModuleID("com.google.guava", "guava-tests", "18.0") - .withConfigurations(Option("test")) - .classifier("tests") - def defaultOptions = EvictionWarningOptions.default - -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/OfflineModeSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/OfflineModeSpec.scala deleted file mode 100644 index bfd58770f..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/OfflineModeSpec.scala +++ /dev/null @@ -1,74 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.librarymanagement.* -import sbt.internal.librarymanagement.ivy.UpdateOptions -import sbt.io.IO - -object OfflineModeSpec extends BaseIvySpecification { - private final def targetDir = Some(currentDependency) - private final def onlineConf = makeUpdateConfiguration(false, targetDir) - private final def offlineConf = makeUpdateConfiguration(true, targetDir) - private final def warningConf = UnresolvedWarningConfiguration() - private final def normalOptions = UpdateOptions() - private final def cachedOptions = UpdateOptions().withCachedResolution(true) - - def avro177 = ModuleID("org.apache.avro", "avro", "1.7.7") - def dataAvro1940 = ModuleID("com.linkedin.pegasus", "data-avro", "1.9.40") - def netty320 = ModuleID("org.jboss.netty", "netty", "3.2.0.Final") - final def dependencies: Vector[ModuleID] = - Vector(avro177, dataAvro1940, netty320).map(_.withConfigurations(Some("compile"))) - - def cleanAll(): Unit = { - cleanIvyCache() - IO.delete(currentTarget) - IO.delete(currentManaged) - IO.delete(currentDependency) - } - - def checkOnlineAndOfflineResolution(updateOptions: UpdateOptions): Unit = { - cleanAll() - val toResolve = module(defaultModuleId, dependencies, None, updateOptions) - if (updateOptions.cachedResolution) - cleanCachedResolutionCache(toResolve) - - val onlineResolution = - IvyActions.updateEither(toResolve, onlineConf, warningConf, log) - assert(onlineResolution.isRight) - assert(onlineResolution.toOption.exists(report => report.stats.resolveTime > 0)) - - val originalResolveTime = - onlineResolution.fold(e => throw e.resolveException, identity).stats.resolveTime - val offlineResolution = - IvyActions.updateEither(toResolve, offlineConf, warningConf, log) - assert(offlineResolution.isRight) - - val resolveTime = - offlineResolution.fold(e => throw e.resolveException, identity).stats.resolveTime - assert(originalResolveTime > resolveTime) - } - - test("Offline update configuration should reuse the caches when offline is enabled") { - checkOnlineAndOfflineResolution(normalOptions) - } - - test("it should reuse the caches when offline and cached resolution are enabled") { - checkOnlineAndOfflineResolution(cachedOptions) - } - - def checkFailingResolution(updateOptions: UpdateOptions): Unit = { - cleanAll() - val toResolve = module(defaultModuleId, dependencies, None, updateOptions) - if (updateOptions.cachedResolution) cleanCachedResolutionCache(toResolve) - val failedOfflineResolution = - IvyActions.updateEither(toResolve, offlineConf, warningConf, log) - assert(failedOfflineResolution.isLeft) - } - - test("it should fail when artifacts are missing in the cache") { - checkFailingResolution(normalOptions) - } - - test("it should fail when artifacts are missing in the cache for cached resolution") { - checkFailingResolution(cachedOptions) - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/PlatformResolutionSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/PlatformResolutionSpec.scala deleted file mode 100644 index f29873daa..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/PlatformResolutionSpec.scala +++ /dev/null @@ -1,92 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.librarymanagement.* -import sbt.librarymanagement.syntax.* -import sbt.librarymanagement.Platform.* - -object PlatformResolutionSpec extends BaseIvySpecification { - - test("None platform resolves %% as JVM") { - cleanCache() - val m = exampleAutoModule(platform = None) - assert( - update(m).configurations.head.modules - .map(_.toString) - .mkString - .contains("com.github.scopt:scopt_2.13:4.1.0") - ) - } - - test("sjs1 platform resolves %% as sjs1") { - cleanCache() - val m = exampleAutoModule(platform = Some("sjs1")) - assert( - update(m).configurations.head.modules - .map(_.toString) - .mkString - .contains("com.github.scopt:scopt_sjs1_2.13") - ) - } - - test("sjs1 platform resolves % as JVM") { - cleanCache() - val m = module( - exampleModuleId("0.6.0"), - deps = Vector(junit), - Some(scala2_13), - platform = Some(sjs1), - ) - assert( - update(m).configurations.head.modules - .map(_.toString) - .mkString - .contains("junit:junit:4.13.1") - ) - } - - test("None platform can specify .platform(sjs1) dependency") { - cleanCache() - val m = module( - exampleModuleId("0.6.0"), - deps = Vector(scopt.platform(sjs1)), - Some(scala2_13), - platform = None, - ) - assert( - update(m).configurations.head.modules - .map(_.toString) - .mkString - .contains("com.github.scopt:scopt_sjs1_2.13") - ) - } - - test("sjs1 platform can specify .platform(jvm) dependency") { - cleanCache() - val m = module( - exampleModuleId("0.6.0"), - deps = Vector(scopt.platform(jvm)), - Some(scala2_13), - platform = None, - ) - assert( - update(m).configurations.head.modules - .map(_.toString) - .mkString - .contains("com.github.scopt:scopt_2.13:4.1.0") - ) - } - - def exampleAutoModule(platform: Option[String]): ModuleDescriptor = module( - exampleModuleId("0.6.0"), - deps = Vector(scopt), - Some(scala2_13), - platform = platform, - ) - - def exampleModuleId(v: String): ModuleID = ("com.example" % "foo" % v % Compile) - def scopt = ("com.github.scopt" %% "scopt" % "4.1.0" % Compile) - def junit = ("junit" % "junit" % "4.13.1" % Compile) - override val resolvers = Vector( - Resolver.mavenCentral, - ) -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ResolutionSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ResolutionSpec.scala deleted file mode 100644 index 8ef0254ea..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ResolutionSpec.scala +++ /dev/null @@ -1,117 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.util.ShowLines -import sbt.librarymanagement.* -import sbt.librarymanagement.syntax.* - -abstract class ResolutionSpec extends AbstractEngineSpec { - - import TestShowLines.* - - test("Resolving the same module twice should work") { - cleanCache() - val m = module( - exampleModuleId("0.1.0"), - Vector(commonsIo13), - Some("2.10.2") - ) - val report = update(m) - cleanCachedResolutionCache(m) - val _ = update(m) - // first resolution creates the minigraph - println(report) - // second resolution reads from the minigraph - println(report.configurations.head.modules.head.artifacts) - assert(report.configurations.size == 3) - } - - test("Resolving the unsolvable module should not work") { - // log.setLevel(Level.Debug) - val m = module( - exampleModuleId("0.2.0"), - Vector(mavenCayennePlugin302), - Some("2.10.2") - ) - updateEither(m) match { - case Right(_) => sys.error("this should've failed") - case Left(uw) => - println(uw.lines.mkString("\n")) - } - updateEither(m) match { - case Right(_) => sys.error("this should've failed 2") - case Left(uw) => - List( - "\n\tNote: Unresolved dependencies path:", - "\t\tfoundrylogic.vpp:vpp:2.2.1", - "\t\t +- org.apache.cayenne:cayenne-tools:3.0.2", - "\t\t +- org.apache.cayenne.plugins:maven-cayenne-plugin:3.0.2", - "\t\t +- com.example:foo:0.2.0" - ) foreach { line => - assert(uw.lines.contains[String](line)) - } - } - } - - // https://github.com/sbt/sbt/issues/2046 - // data-avro:1.9.40 depends on avro:1.4.0, which depends on netty:3.2.1.Final. - // avro:1.4.0 will be evicted by avro:1.7.7. - // #2046 says that netty:3.2.0.Final is incorrectly evicted by netty:3.2.1.Final - test("Resolving a module with a pseudo-conflict should work") { - // log.setLevel(Level.Debug) - cleanCache() - val m = module( - exampleModuleId("0.3.0"), - Vector(avro177, dataAvro1940, netty320), - Some("2.10.2") - ) - // first resolution creates the minigraph - val _ = update(m) - cleanCachedResolutionCache(m) - // second resolution reads from the minigraph - val report = update(m) - val modules: Seq[String] = report.configurations.head.modules map { _.toString } - assert(modules exists { (x: String) => - x.contains("""org.jboss.netty:netty:3.2.0.Final""") - }) - assert(!(modules exists { (x: String) => - x.contains("""org.jboss.netty:netty:3.2.1.Final""") - })) - } - - test("Resolving a module with sbt cross build should work") { - cleanCache() - val attributes013 = Map("e:sbtVersion" -> "0.13", "e:scalaVersion" -> "2.10") - val attributes10 = Map("e:sbtVersion" -> "1.0", "e:scalaVersion" -> "2.12") - val module013 = module( - exampleModuleId("0.4.0"), - Vector(sbtRelease.withExtraAttributes(attributes013)), - Some("2.10.6") - ) - val module10 = module( - exampleModuleId("0.4.1"), - Vector(sbtRelease.withExtraAttributes(attributes10)), - Some("2.12.3") - ) - assert( - update(module013).configurations.head.modules.map(_.toString) - contains "com.github.gseitz:sbt-release:1.0.6 (scalaVersion=2.10, sbtVersion=0.13)" - ) - assert( - update(module10).configurations.head.modules.map(_.toString) - contains "com.github.gseitz:sbt-release:1.0.6 (scalaVersion=2.12, sbtVersion=1.0)" - ) - } - - def exampleModuleId(v: String): ModuleID = ("com.example" % "foo" % v % Compile) - - def commonsIo13 = ("commons-io" % "commons-io" % "1.3" % Compile) - def mavenCayennePlugin302 = - ("org.apache.cayenne.plugins" % "maven-cayenne-plugin" % "3.0.2" % Compile) - def avro177 = ("org.apache.avro" % "avro" % "1.7.7" % Compile) - def dataAvro1940 = - ("com.linkedin.pegasus" % "data-avro" % "1.9.40" % Compile) - def netty320 = ("org.jboss.netty" % "netty" % "3.2.0.Final" % Compile) - def sbtRelease = ("com.github.gseitz" % "sbt-release" % "1.0.6" % Compile) - - def defaultOptions = EvictionWarningOptions.default -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ResolverSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ResolverSpec.scala deleted file mode 100644 index 504fe2168..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ResolverSpec.scala +++ /dev/null @@ -1,18 +0,0 @@ -package sbttest - -import java.net.URI -import sbt.librarymanagement.* -import sbt.librarymanagement.syntax.* -import verify.BasicTestSuite - -class ResolverSpec extends BasicTestSuite { - test("Resolver.uri") { - Resolver.uri("Test Repo", new URI("http://example.com/"))(using Resolver.ivyStylePatterns) - () - } - - test("at") { - "something" at "http://example.com" - () - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ScalaOverrideTest.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ScalaOverrideTest.scala deleted file mode 100644 index 688f11baf..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/ScalaOverrideTest.scala +++ /dev/null @@ -1,276 +0,0 @@ -package sbt.internal.librarymanagement - -// import org.apache.ivy.core.module.id.ModuleRevisionId -// import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor - -// import sbt.internal.librarymanagement.IvyScalaUtil.OverrideScalaMediator -// import sbt.librarymanagement._ -// import sbt.librarymanagement.ScalaArtifacts._ -import verify.BasicTestSuite - -object ScalaOverrideTest extends BasicTestSuite { - /* - val OtherOrgID = "other.org" - - private val scalaConfigs = - Configurations.default.filter(Configurations.underScalaVersion).map(_.name) - - def checkOrgAndVersion( - org0: String, - version0: String - )(org1: String, name1: String, version1: String): Unit = { - val osm = new OverrideScalaMediator(org0, version0, scalaConfigs) - - val mrid = ModuleRevisionId.newInstance(org1, name1, version1) - val dd = new DefaultDependencyDescriptor(mrid, false) - dd.addDependencyConfiguration("compile", "compile") - - val res = osm.mediate(dd) - assert(res.getDependencyRevisionId == ModuleRevisionId.newInstance(org0, name1, version0)) - } - - def checkOnlyOrg( - org0: String, - version0: String - )(org1: String, name1: String, version1: String): Unit = { - val osm = new OverrideScalaMediator(org0, version0, scalaConfigs) - - val mrid = ModuleRevisionId.newInstance(org1, name1, version1) - val dd = new DefaultDependencyDescriptor(mrid, false) - dd.addDependencyConfiguration("compile", "compile") - - val res = osm.mediate(dd) - assert(res.getDependencyRevisionId == ModuleRevisionId.newInstance(org0, name1, version1)) - } - - def checkNoOverride( - org0: String, - version0: String - )(org1: String, name1: String, version1: String): Unit = { - val osm = new OverrideScalaMediator(org0, version0, scalaConfigs) - - val mrid = ModuleRevisionId.newInstance(org1, name1, version1) - val dd = new DefaultDependencyDescriptor(mrid, false) - dd.addDependencyConfiguration("compile", "compile") - - val res = osm.mediate(dd) - assert(res.getDependencyRevisionId == mrid) - } - - test("OverrideScalaMediator should override compiler version") { - checkOrgAndVersion(Organization, "2.11.8")( - Organization, - CompilerID, - "2.11.9" - ) - } - - test("it should override library version") { - checkOrgAndVersion(Organization, "2.11.8")( - Organization, - LibraryID, - "2.11.8" - ) - } - - test("it should override reflect version") { - checkOrgAndVersion(Organization, "2.11.8")( - Organization, - ReflectID, - "2.11.7" - ) - } - - test("it should override actors version") { - checkOrgAndVersion(Organization, "2.11.8")( - Organization, - ActorsID, - "2.11.6" - ) - } - - test("it should override scalap version") { - checkOrgAndVersion(Organization, "2.11.8")( - Organization, - ScalapID, - "2.11.5" - ) - } - - test("it should override default compiler organization") { - checkOrgAndVersion(OtherOrgID, "2.11.8")( - Organization, - CompilerID, - "2.11.9" - ) - } - - test("it should override default library organization") { - checkOrgAndVersion(OtherOrgID, "2.11.8")( - Organization, - LibraryID, - "2.11.8" - ) - } - - test("it should override default reflect organization") { - checkOrgAndVersion(OtherOrgID, "2.11.8")( - Organization, - ReflectID, - "2.11.7" - ) - } - - test("it should override default actors organization") { - checkOrgAndVersion(OtherOrgID, "2.11.8")( - Organization, - ActorsID, - "2.11.6" - ) - } - - test("it should override default scalap organization") { - checkOrgAndVersion(OtherOrgID, "2.11.8")( - Organization, - ScalapID, - "2.11.5" - ) - } - - test("it should override custom compiler organization") { - checkOrgAndVersion(Organization, "2.11.8")( - OtherOrgID, - CompilerID, - "2.11.9" - ) - } - - test("it should override custom library organization") { - checkOrgAndVersion(Organization, "2.11.8")( - OtherOrgID, - LibraryID, - "2.11.8" - ) - } - - test("it should override custom reflect organization") { - checkOrgAndVersion(Organization, "2.11.8")( - OtherOrgID, - ReflectID, - "2.11.7" - ) - } - - test("it should override custom actors organization") { - checkOrgAndVersion(Organization, "2.11.8")( - OtherOrgID, - ActorsID, - "2.11.6" - ) - } - - test("it should override custom scalap organization") { - checkOrgAndVersion(Organization, "2.11.8")( - OtherOrgID, - ScalapID, - "2.11.5" - ) - } - - test("it should override Scala 3 compiler version") { - checkOrgAndVersion(Organization, "3.1.0")( - Organization, - Scala3CompilerPrefix + "3", - "3.0.0" - ) - } - - test("it should override Scala 3 library version") { - checkOrgAndVersion(Organization, "3.1.0")( - Organization, - Scala3LibraryPrefix + "3", - "3.0.0" - ) - } - - test("it should override Scala 3 interfaces version") { - checkOrgAndVersion(Organization, "3.1.0")( - Organization, - Scala3InterfacesID, - "3.0.0" - ) - } - - test("it should override TASTy core version") { - checkOrgAndVersion(Organization, "3.1.0")( - Organization, - TastyCorePrefix + "3", - "3.0.0" - ) - } - - test("it should not override Scala 2 library version when using Scala 3") { - checkNoOverride(Organization, "3.1.0")( - Organization, - LibraryID, - "2.13.4" - ) - } - - test("it should not override TASTy core version when using Scala 2") { - checkNoOverride(Organization, "2.13.4")( - Organization, - TastyCorePrefix + "3", - "3.0.0" - ) - } - - test("it should override default Scala 3 compiler organization") { - checkOrgAndVersion(OtherOrgID, "3.1.0")( - Organization, - Scala3CompilerPrefix + "3", - "3.0.0" - ) - } - - test("it should override default Scala 3 library organization") { - checkOrgAndVersion(OtherOrgID, "3.1.0")( - Organization, - Scala3LibraryPrefix + "3", - "3.0.0" - ) - } - - test("it should override default Scala 3 interfaces organization") { - checkOrgAndVersion(OtherOrgID, "3.1.0")( - Organization, - Scala3InterfacesID, - "3.0.0" - ) - } - - test("it should override default Scala 3 TASTy core organization") { - checkOrgAndVersion(OtherOrgID, "3.1.0")( - Organization, - TastyCorePrefix + "3", - "3.0.0" - ) - } - - test("it should override default Scala 2 library organization when in Scala 3") { - checkOnlyOrg(OtherOrgID, "3.1.0")( - Organization, - LibraryID, - "2.13.4" - ) - } - - test("it should override default TASTy core organization when in Scala 2") { - checkOnlyOrg(OtherOrgID, "2.13.4")( - Organization, - TastyCorePrefix + "3", - "3.0.0" - ) - } - */ -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/SftpRepoSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/SftpRepoSpec.scala deleted file mode 100644 index 9e3a11ce5..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/SftpRepoSpec.scala +++ /dev/null @@ -1,42 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.io.* -import sbt.io.syntax.* -import sbt.util.Level -import sbt.librarymanagement.* -import sbt.librarymanagement.syntax.* -import java.nio.file.Paths - -//by default this test is ignored -//to run this you need to change "repo" to point to some sftp repository which contains a dependency referring a dependency in same repo -//it will then attempt to authenticate via key file and fetch the dependency specified via "org" and "module" -object SftpRepoSpec extends BaseIvySpecification { - val repo: Option[String] = None -// val repo: Option[String] = Some("some repo") - // a dependency which depends on another in the repo - def org(repo: String) = s"com.${repo}" - def module(org: String) = org % "some-lib" % "version" - - override def resolvers = { - given Patterns = Resolver.defaultIvyPatterns - repo.map { repo => - val privateKeyFile = Paths.get(sys.env("HOME"), ".ssh", s"id_${repo}").toFile - Resolver.sftp(repo, s"repo.${repo}.com", 2222).as(repo, privateKeyFile) - }.toVector ++ super.resolvers - } - - test("resolving multiple deps from sftp repo should not hang or fail") { - repo match { - case Some(repo) => - IO.delete(currentTarget / "cache" / org(repo)) - // log.setLevel(Level.Debug) - lmEngine().retrieve(module(org(repo)), scalaModuleInfo = None, currentTarget, log) match { - case Right(v) => log.debug(v.toString()) - case Left(e) => - log.log(Level.Error, e.failedPaths.toString()) - throw e.resolveException - } - case None => log.info(s"skipped ${getClass}") - } - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/TestLogger.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/TestLogger.scala deleted file mode 100644 index 49f8601c8..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/TestLogger.scala +++ /dev/null @@ -1,14 +0,0 @@ -package sbt -package internal -package librarymanagement - -import sbt.util.* -import sbt.internal.util.* - -object TestLogger { - def apply[T](f: Logger => T): T = { - val log = new BufferedLogger(ConsoleLogger()) - log.setLevel(Level.Debug) - log.bufferQuietly(f(log)) - } -} diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/TestShowLInes.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/TestShowLInes.scala deleted file mode 100644 index 8e318ff53..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/TestShowLInes.scala +++ /dev/null @@ -1,9 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.util.ShowLines - -object TestShowLines: - extension [A: ShowLines](a: A) - inline def lines: Seq[String] = - implicitly[ShowLines[A]].showLines(a) -end TestShowLines diff --git a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/UpdateOptionsSpec.scala b/lm-ivy/src/test/scala/sbt/internal/librarymanagement/UpdateOptionsSpec.scala deleted file mode 100644 index f607edff7..000000000 --- a/lm-ivy/src/test/scala/sbt/internal/librarymanagement/UpdateOptionsSpec.scala +++ /dev/null @@ -1,26 +0,0 @@ -package sbt.internal.librarymanagement - -import sbt.internal.librarymanagement.ivy.* -import verify.BasicTestSuite - -class UpdateOptionsSpec extends BasicTestSuite { - test("UpdateOptions should have proper toString defined") { - assert(UpdateOptions().toString() == """|UpdateOptions( - | circularDependencyLevel = warn, - | latestSnapshots = true, - | cachedResolution = false - |)""".stripMargin) - - assert( - UpdateOptions() - .withCircularDependencyLevel(CircularDependencyLevel.Error) - .withCachedResolution(true) - .withLatestSnapshots(false) - .toString() == """|UpdateOptions( - | circularDependencyLevel = error, - | latestSnapshots = false, - | cachedResolution = true - |)""".stripMargin - ) - } -} diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 576980af0..3ea33bd30 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -30,10 +30,7 @@ import sbt.internal.* import sbt.internal.classpath.AlternativeZincUtil import sbt.internal.inc.classpath.ClasspathFilter import sbt.internal.inc.{ CompileOutput, MappedFileConverter, Stamps, ZincLmUtil, ZincUtil } -import sbt.internal.librarymanagement.mavenint.{ - PomExtraDependencyAttributes, - SbtPomExtraProperties -} +import sbt.internal.librarymanagement.mavenint.{ PomExtraAttributeKeys, SbtPomExtraProperties } import sbt.internal.librarymanagement.* import sbt.internal.nio.{ CheckBuildSources, Globs } import sbt.internal.server.{ @@ -265,7 +262,7 @@ object Defaults extends BuildCommon with DefExtra { "bundle", "maven-plugin", "test-jar" - ) ++ CustomPomParser.JarPackagings, + ) ++ PomExtraAttributeKeys.JarPackagings, artifactClassifier :== None, checksums := Classpaths.bootChecksums(appConfiguration.value), conflictManager := ConflictManager.default, @@ -2622,8 +2619,8 @@ object Defaults extends BuildCommon with DefExtra { partialVersion(sbtV) match case Some((0, _)) | Some((1, _)) => m.extra( - PomExtraDependencyAttributes.SbtVersionKey -> sbtV, - PomExtraDependencyAttributes.ScalaVersionKey -> scalaV + PomExtraAttributeKeys.SbtVersionKey -> sbtV, + PomExtraAttributeKeys.ScalaVersionKey -> scalaV ).withCrossVersion(Disabled()) case Some(_) => // this produces a normal suffix like _sjs1_2.13 @@ -4031,7 +4028,7 @@ object Classpaths { def deliverTask(config: TaskKey[PublishConfiguration]): Initialize[Task[File]] = Def.task { sys.error( - "deliver/makeIvyXml requires the sbt-ivy plugin. Add IvyDependencyPlugin to your project." + "deliver/makeIvyXml requires an Ivy-based publishing plugin, which is not part of this sbt distribution." ) } diff --git a/main/src/main/scala/sbt/internal/PomGenerator.scala b/main/src/main/scala/sbt/internal/PomGenerator.scala index 5fc7c1ed8..7d8cc293e 100644 --- a/main/src/main/scala/sbt/internal/PomGenerator.scala +++ b/main/src/main/scala/sbt/internal/PomGenerator.scala @@ -14,7 +14,6 @@ import scala.xml.{ Elem, Node, NodeSeq } /** * Generates Maven POM XML from sbt's own types, without requiring Ivy. - * This is used by the default publisher when the sbt-ivy plugin is not loaded. */ private[sbt] object PomGenerator: diff --git a/main/src/main/scala/sbt/internal/librarymanagement/GenericPublisher.scala b/main/src/main/scala/sbt/internal/librarymanagement/GenericPublisher.scala index 927ec9ec0..4094c8838 100644 --- a/main/src/main/scala/sbt/internal/librarymanagement/GenericPublisher.scala +++ b/main/src/main/scala/sbt/internal/librarymanagement/GenericPublisher.scala @@ -16,7 +16,7 @@ import java.util.regex.Matcher import gigahorse.AuthScheme import gigahorse.support.apachehttp.Gigahorse -import sbt.internal.librarymanagement.mavenint.PomExtraDependencyAttributes +import sbt.internal.librarymanagement.mavenint.PomExtraAttributeKeys import sbt.librarymanagement.* import sbt.util.Logger import sbt.io.IO @@ -170,8 +170,8 @@ class GenericPublisher private[sbt] ( private def pluginCrossPath: Seq[String] = val attrs = project.module.attributes - attrs.get(PomExtraDependencyAttributes.ScalaVersionKey).map("scala_" + _).toSeq ++ - attrs.get(PomExtraDependencyAttributes.SbtVersionKey).map("sbt_" + _).toSeq + attrs.get(PomExtraAttributeKeys.ScalaVersionKey).map("scala_" + _).toSeq ++ + attrs.get(PomExtraAttributeKeys.SbtVersionKey).map("sbt_" + _).toSeq private def typeToFolder(tpe: String): String = tpe match case "jar" => "jars" @@ -266,8 +266,8 @@ class GenericPublisher private[sbt] ( else s = s.replace("(-[classifier])", "") // Substitute or drop optional Ivy pattern parts (scala/sbt version), remove branch for ivyless layout val attrs = project.module.attributes - val scalaV = attrs.get(PomExtraDependencyAttributes.ScalaVersionKey) - val sbtV = attrs.get(PomExtraDependencyAttributes.SbtVersionKey) + val scalaV = attrs.get(PomExtraAttributeKeys.ScalaVersionKey) + val sbtV = attrs.get(PomExtraAttributeKeys.SbtVersionKey) s = s.replaceAll( "\\(scala_[^)]+/\\)", scalaV.map(v => Matcher.quoteReplacement(s"scala_$v/")).getOrElse("") diff --git a/project/DatatypeConfig.scala b/project/DatatypeConfig.scala index 845bcdd47..f643166b2 100644 --- a/project/DatatypeConfig.scala +++ b/project/DatatypeConfig.scala @@ -23,10 +23,6 @@ object DatatypeConfig { "sbt.internal.librarymanagement.formats.NodeSeqFormat" :: Nil } - case "org.apache.ivy.plugins.resolver.DependencyResolver" => { _ => - "sbt.internal.librarymanagement.formats.DependencyResolverFormat" :: Nil - } - case "xsbti.GlobalLock" => { _ => "sbt.internal.librarymanagement.formats.GlobalLockFormat" :: Nil } @@ -38,10 +34,6 @@ object DatatypeConfig { "sbt.librarymanagement.IvyPathsFormats" :: Nil } - case "sbt.internal.librarymanagement.ivy.UpdateOptions" => { _ => - "sbt.internal.librarymanagement.ivy.formats.UpdateOptionsFormat" :: Nil - } - case "sbt.librarymanagement.LogicalClock" => { _ => "sbt.internal.librarymanagement.formats.LogicalClockFormats" :: Nil } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index dd553a385..05f8234eb 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -113,7 +113,6 @@ object Dependencies { val hedgehog = "qa.hedgehog" %% "hedgehog-sbt" % "0.13.0" val disruptor = "com.lmax" % "disruptor" % "3.4.2" - val ivy = "org.scala-sbt.ivy" % "ivy" % "2.3.0-sbt-f686954b0021a5c3245766ced0cdaeca8ba2fd7a" // lm dependencies val jsch = ("com.github.mwiede" % "jsch" % "0.2.23").intransitive() diff --git a/sbt-app/src/main/scala/sbt/Import.scala b/sbt-app/src/main/scala/sbt/Import.scala index 228d08faa..dc3106baa 100644 --- a/sbt-app/src/main/scala/sbt/Import.scala +++ b/sbt-app/src/main/scala/sbt/Import.scala @@ -357,8 +357,6 @@ trait Import { val URLRepository = sbt.librarymanagement.URLRepository type URLRepository = sbt.librarymanagement.URLRepository val UpdateLogging = sbt.librarymanagement.UpdateLogging - val UpdateOptions = sbt.internal.librarymanagement.ivy.UpdateOptions - type UpdateOptions = sbt.internal.librarymanagement.ivy.UpdateOptions val UpdateReport = sbt.librarymanagement.UpdateReport type UpdateReport = sbt.librarymanagement.UpdateReport val UpdateStats = sbt.librarymanagement.UpdateStats diff --git a/sbt-app/src/sbt-test/dependency-management/platform-publish/build.sbt b/sbt-app/src/sbt-test/dependency-management/platform-publish/build.sbt index 7ad0b638b..0bec6ae61 100644 --- a/sbt-app/src/sbt-test/dependency-management/platform-publish/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/platform-publish/build.sbt @@ -1,5 +1,5 @@ // sbt/sbt#9117: published artifact names must carry the platform suffix, matching the -// coordinate, on both publish backends. `platform` is set directly, as sbt-scala-native does. +// coordinate. `platform` is set directly, as sbt-scala-native does. ThisBuild / organization := "com.example" ThisBuild / version := "0.1.0" @@ -11,42 +11,35 @@ ThisBuild / mavenRepo := (ThisBuild / baseDirectory).value / "maven-repo" def expected(name: String) = s"${name}_native0.5_3" -def producer(useIvyFlag: Boolean): Seq[Setting[?]] = Seq( - platform := "native0.5", - crossVersion := CrossVersion.binary, - useIvy := useIvyFlag, - publishMavenStyle := true, - ivyPaths := IvyPaths(baseDirectory.value.toString, Some((target.value / "ivy2").toString)), - publishTo := Some(MavenCache("platform-publish-local", (ThisBuild / mavenRepo).value)) -) - -// checkPomArtifactId only for ivyless: there the artifactId comes from PomGenerator (and -// resolution does not validate POM content); the Ivy backend's is correct regardless. -def ivyLayoutCheck(base: String, checkPomArtifactId: Boolean): Setting[?] = - TaskKey[Unit]("check") := { - val nm = expected(base) - val dir = target.value / "ivy2" / "local" / organization.value / nm / version.value - def req(f: File): Unit = assert(f.exists, s"expected $f to exist") - req(dir / "jars" / s"$nm.jar") - req(dir / "srcs" / s"$nm-sources.jar") - val pom = dir / "poms" / s"$nm.pom" - req(pom) - if (checkPomArtifactId) - assert(IO.read(pom).contains(s"$nm"), s"POM artifactId must be $nm: ${IO.read(pom)}") - } - lazy val ivyless = (project in file("ivyless")) - .settings(name := "libivyless", producer(false), ivyLayoutCheck("libivyless", checkPomArtifactId = true)) + .settings( + name := "libivyless", + platform := "native0.5", + crossVersion := CrossVersion.binary, + useIvy := false, + publishMavenStyle := true, + ivyPaths := IvyPaths(baseDirectory.value.toString, Some((target.value / "ivy2").toString)), + publishTo := Some(MavenCache("platform-publish-local", (ThisBuild / mavenRepo).value)), + TaskKey[Unit]("check") := { + val nm = expected("libivyless") + val dir = target.value / "ivy2" / "local" / organization.value / nm / version.value + def req(f: File): Unit = assert(f.exists, s"expected $f to exist") + req(dir / "jars" / s"$nm.jar") + req(dir / "srcs" / s"$nm-sources.jar") + val pom = dir / "poms" / s"$nm.pom" + req(pom) + assert( + IO.read(pom).contains(s"$nm"), + s"POM artifactId must be $nm: ${IO.read(pom)}" + ) + } + ) -lazy val ivyfull = (project in file("ivyfull")) - .settings(name := "libivyfull", producer(true), ivyLayoutCheck("libivyfull", checkPomArtifactId = false)) - -// Must not dependsOn the producers, so the coordinates resolve from the Maven repo rather +// Must not dependsOn the producer, so the coordinate resolves from the Maven repo rather // than inter-project - otherwise a suffix-dropped published name would not be caught. lazy val consumer = (project in file("consumer")) .settings( publish / skip := true, resolvers += MavenCache("platform-publish-local", (ThisBuild / mavenRepo).value), - libraryDependencies += organization.value % expected("libivyless") % version.value, - libraryDependencies += organization.value % expected("libivyfull") % version.value + libraryDependencies += organization.value % expected("libivyless") % version.value ) diff --git a/sbt-app/src/sbt-test/dependency-management/platform-publish/ivyfull/src/main/scala/Lib.scala b/sbt-app/src/sbt-test/dependency-management/platform-publish/ivyfull/src/main/scala/Lib.scala deleted file mode 100644 index 4da7a9970..000000000 --- a/sbt-app/src/sbt-test/dependency-management/platform-publish/ivyfull/src/main/scala/Lib.scala +++ /dev/null @@ -1,4 +0,0 @@ -package lib - -object Lib: - def greeting: String = "hi" diff --git a/sbt-app/src/sbt-test/dependency-management/platform-publish/project/plugins.sbt b/sbt-app/src/sbt-test/dependency-management/platform-publish/project/plugins.sbt deleted file mode 100644 index 938089c9a..000000000 --- a/sbt-app/src/sbt-test/dependency-management/platform-publish/project/plugins.sbt +++ /dev/null @@ -1,6 +0,0 @@ -// sbt-ivy provides the Ivy publish backend exercised by the `ivyfull` project. -libraryDependencies += Defaults.sbtPluginExtra( - "org.scala-sbt" % "sbt-ivy" % sbtVersion.value, - sbtVersion.value, - scalaVersion.value, -) diff --git a/sbt-app/src/sbt-test/dependency-management/platform-publish/test b/sbt-app/src/sbt-test/dependency-management/platform-publish/test index bd3213bee..0a800d46f 100644 --- a/sbt-app/src/sbt-test/dependency-management/platform-publish/test +++ b/sbt-app/src/sbt-test/dependency-management/platform-publish/test @@ -1,14 +1,11 @@ # sbt/sbt#9117: published artifact names must carry the platform suffix (_native0.5), -# matching the coordinate, on both the ivyless and Ivy backends. +# matching the coordinate. # publishLocal (ivy layout): filenames + POM artifactId > ivyless/publishLocal > ivyless/check -> ivyfull/publishLocal -> ivyfull/check -# publish to a local Maven repo, then resolve the artifacts back under their platform +# publish to a local Maven repo, then resolve the artifact back under its platform # coordinate > ivyless/publish -> ivyfull/publish > consumer/update 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 413e6b850..4cd908207 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 @@ -63,14 +63,17 @@ lazy val root = (project in file(".")) "org.scala-lang:scala3-library_3", "org.scala-lang:tasty-core_3", "org.scala-sbt.ipcsocket:ipcsocket", - "org.scala-sbt.ivy:ivy", "org.scala-sbt.jline:jline", "org.scala-sbt.gson:shaded-gson", "org.slf4j:slf4j-api", ) def assertCollectionsEqual(message: String, expected: Seq[String], actual: Seq[String]): Unit = // using the new line for a more readable comparison failure output - assert(expected.mkString("\n") == actual.mkString("\n"), message + ": " + actual) + val diff = ((expected.toVector diff actual.toVector) + .map("-" + _) ++ + (actual.toVector diff expected.toVector).map("+" + _)) + .mkString("\n") + assert(expected.mkString("\n") == actual.mkString("\n"), message + ": " + diff) assertCollectionsEqual( "Unexpected module ids in updateSbtClassifiers", diff --git a/sbt-app/src/sbt-test/ivy/build-deps/a/A.scala b/sbt-app/src/sbt-test/ivy/build-deps/a/A.scala deleted file mode 100644 index 0153a3d4a..000000000 --- a/sbt-app/src/sbt-test/ivy/build-deps/a/A.scala +++ /dev/null @@ -1,3 +0,0 @@ -object A { - val x = B.x -} diff --git a/sbt-app/src/sbt-test/ivy/build-deps/b/B.scala b/sbt-app/src/sbt-test/ivy/build-deps/b/B.scala deleted file mode 100644 index 10357cf39..000000000 --- a/sbt-app/src/sbt-test/ivy/build-deps/b/B.scala +++ /dev/null @@ -1,3 +0,0 @@ -object B { - val x = 3 -} diff --git a/sbt-app/src/sbt-test/ivy/build-deps/build.sbt b/sbt-app/src/sbt-test/ivy/build-deps/build.sbt deleted file mode 100644 index 853e3673c..000000000 --- a/sbt-app/src/sbt-test/ivy/build-deps/build.sbt +++ /dev/null @@ -1,4 +0,0 @@ -ThisBuild / useIvy := true -lazy val root = (project in file(".")) -lazy val a = project -lazy val b = project diff --git a/sbt-app/src/sbt-test/ivy/build-deps/changes/b.sbt b/sbt-app/src/sbt-test/ivy/build-deps/changes/b.sbt deleted file mode 100644 index 80ff9307e..000000000 --- a/sbt-app/src/sbt-test/ivy/build-deps/changes/b.sbt +++ /dev/null @@ -1,5 +0,0 @@ -Global / buildDependencies := - (Global / buildDependencies).value.addClasspath( - (LocalProject("a") / thisProjectRef).value, - ResolvedClasspathDependency(thisProjectRef.value, None) - ) diff --git a/sbt-app/src/sbt-test/ivy/build-deps/project/plugins.sbt b/sbt-app/src/sbt-test/ivy/build-deps/project/plugins.sbt deleted file mode 100644 index 15c7fdd35..000000000 --- a/sbt-app/src/sbt-test/ivy/build-deps/project/plugins.sbt +++ /dev/null @@ -1,5 +0,0 @@ -libraryDependencies += Defaults.sbtPluginExtra( - "org.scala-sbt" % "sbt-ivy" % sbtVersion.value, - sbtVersion.value, - scalaVersion.value, -) diff --git a/sbt-app/src/sbt-test/ivy/build-deps/test b/sbt-app/src/sbt-test/ivy/build-deps/test deleted file mode 100644 index e72a50750..000000000 --- a/sbt-app/src/sbt-test/ivy/build-deps/test +++ /dev/null @@ -1,4 +0,0 @@ --> a/compile -$ copy-file changes/b.sbt b/build.sbt -> reload -> a/compile diff --git a/sbt-app/src/sbt-test/ivy/deliver-artifacts/a/A.java b/sbt-app/src/sbt-test/ivy/deliver-artifacts/a/A.java deleted file mode 100644 index c668f8fb1..000000000 --- a/sbt-app/src/sbt-test/ivy/deliver-artifacts/a/A.java +++ /dev/null @@ -1,3 +0,0 @@ -public class A { - public static final int x = 3; -} diff --git a/sbt-app/src/sbt-test/ivy/deliver-artifacts/b/B.java b/sbt-app/src/sbt-test/ivy/deliver-artifacts/b/B.java deleted file mode 100644 index 6b0375d92..000000000 --- a/sbt-app/src/sbt-test/ivy/deliver-artifacts/b/B.java +++ /dev/null @@ -1,5 +0,0 @@ -public final class B { - public static void main(String[] args) { - System.out.println(A.x); - } -} diff --git a/sbt-app/src/sbt-test/ivy/deliver-artifacts/build.sbt b/sbt-app/src/sbt-test/ivy/deliver-artifacts/build.sbt deleted file mode 100644 index b826fa328..000000000 --- a/sbt-app/src/sbt-test/ivy/deliver-artifacts/build.sbt +++ /dev/null @@ -1,25 +0,0 @@ -ThisBuild / csrCacheDirectory := (ThisBuild / baseDirectory).value / "coursier-cache" -ThisBuild / organization := "org.example" -ThisBuild / version := "1.0" -ThisBuild / useIvy := true - -lazy val a = project.settings(common).settings( - // verifies that a can be published as an ivy.xml file and preserve the extra artifact information, - // such as a classifier - libraryDependencies := Seq(("net.sf.json-lib" % "json-lib" % "2.4").classifier("jdk15").intransitive()), - // verifies that an artifact without an explicit configuration gets published in all public configurations - (Compile / packageBin / artifact) := Artifact("demo") -) - -lazy val b = project.settings(common).settings( - libraryDependencies := Seq(organization.value %% "a" % version.value) -) - -def localCache = - ivyPaths := IvyPaths(baseDirectory.value.toString, Some(((ThisBuild / baseDirectory).value / "ivy" / "cache").toString)) - -lazy val common = Seq( - localCache, - autoScalaLibrary := false, // avoid downloading fresh scala-library/scala-compiler - managedScalaInstance := false, -) diff --git a/sbt-app/src/sbt-test/ivy/deliver-artifacts/project/plugins.sbt b/sbt-app/src/sbt-test/ivy/deliver-artifacts/project/plugins.sbt deleted file mode 100644 index 15c7fdd35..000000000 --- a/sbt-app/src/sbt-test/ivy/deliver-artifacts/project/plugins.sbt +++ /dev/null @@ -1,5 +0,0 @@ -libraryDependencies += Defaults.sbtPluginExtra( - "org.scala-sbt" % "sbt-ivy" % sbtVersion.value, - sbtVersion.value, - scalaVersion.value, -) diff --git a/sbt-app/src/sbt-test/ivy/deliver-artifacts/test b/sbt-app/src/sbt-test/ivy/deliver-artifacts/test deleted file mode 100644 index e6adf0b4c..000000000 --- a/sbt-app/src/sbt-test/ivy/deliver-artifacts/test +++ /dev/null @@ -1,4 +0,0 @@ -> a/publishLocal -> b/update -# verify that A's artifact was published and available for B to compile/run against -> b/run diff --git a/sbt-app/src/sbt-test/ivy/exclude-dependencies/build.sbt b/sbt-app/src/sbt-test/ivy/exclude-dependencies/build.sbt deleted file mode 100644 index e0c75bbdf..000000000 --- a/sbt-app/src/sbt-test/ivy/exclude-dependencies/build.sbt +++ /dev/null @@ -1,66 +0,0 @@ -import scala.xml.{ Node, _ } -import scala.xml.Utility.trim -import sbt.internal.librarymanagement.{ IvySbt, MakePom } - -ThisBuild / useIvy := true - -lazy val check = taskKey[Unit]("check") - -val dispatch = "net.databinder.dispatch" %% "dispatch-core" % "0.11.2" -val repatchTwitter = "com.eed3si9n" %% "repatch-twitter-core" % "dispatch0.11.1_0.1.0" - -lazy val a = (project in file("a")). - settings( - scalaVersion := "2.11.12", - libraryDependencies += dispatch, - excludeDependencies += "org.slf4j" - ) - -lazy val b = (project in file("b")). - settings( - scalaVersion := "2.11.12", - libraryDependencies += repatchTwitter, - excludeDependencies += "net.databinder.dispatch" %% "dispatch-core" - ) - -lazy val root = (project in file(".")). - settings( - check := Def.uncached { - (a / update).value - (b / update).value - val acp = (a / Compile / externalDependencyClasspath).value.sortBy {_.data.name} - val bcp = (b / Compile / externalDependencyClasspath).value.sortBy {_.data.name} - - if (acp exists { _.data.name.contains("slf4j-api-1.7.5.jar") }) { - sys.error("slf4j-api-1.7.5.jar found when it should NOT be included: " + acp.toString) - } - if (bcp exists { _.data.name.contains("dispatch-core_2.11-0.11.1.jar") }) { - sys.error("dispatch-core_2.11-0.11.1.jar found when it should NOT be included: " + bcp.toString) - } - - val bPomXml = makePomXml(streams.value.log, (b / makePomConfiguration).value, (b / ivyModule).value.asInstanceOf[IvySbt#Module]) - - val repatchTwitterXml = bPomXml \ "dependencies" \ "dependency" find { d => - (d \ "groupId").text == "com.eed3si9n" && (d \ "artifactId").text == "repatch-twitter-core_2.11" - } getOrElse (sys error s"Missing repatch-twitter-core dependency: $bPomXml") - - val excludeDispatchCoreXml = - - net.databinder.dispatch - dispatch-core_2.11 - - - if (trim((repatchTwitterXml \ "exclusions" \ "exclusion").head) != trim(excludeDispatchCoreXml)) - sys error s"Missing dispatch-core exclusion: $repatchTwitterXml" - - () - } - ) - -def makePomXml(log: Logger, makePomConfig: MakePomConfiguration, ivyModule: IvySbt#Module): Node = { - ivyModule.withModule[Node](log) { (ivy, md, default) => - import makePomConfig._ - new MakePom(log).toPom( - ivy, md, moduleInfo.get, configurations, includeTypes, extra.get, filterRepositories, allRepositories) - } -} diff --git a/sbt-app/src/sbt-test/ivy/exclude-dependencies/project/plugins.sbt b/sbt-app/src/sbt-test/ivy/exclude-dependencies/project/plugins.sbt deleted file mode 100644 index 15c7fdd35..000000000 --- a/sbt-app/src/sbt-test/ivy/exclude-dependencies/project/plugins.sbt +++ /dev/null @@ -1,5 +0,0 @@ -libraryDependencies += Defaults.sbtPluginExtra( - "org.scala-sbt" % "sbt-ivy" % sbtVersion.value, - sbtVersion.value, - scalaVersion.value, -) diff --git a/sbt-app/src/sbt-test/ivy/exclude-dependencies/test b/sbt-app/src/sbt-test/ivy/exclude-dependencies/test deleted file mode 100644 index 15675b169..000000000 --- a/sbt-app/src/sbt-test/ivy/exclude-dependencies/test +++ /dev/null @@ -1 +0,0 @@ -> check diff --git a/sbt-app/src/sbt-test/ivy/make-ivy-xml/build.sbt b/sbt-app/src/sbt-test/ivy/make-ivy-xml/build.sbt deleted file mode 100644 index 76de7b65b..000000000 --- a/sbt-app/src/sbt-test/ivy/make-ivy-xml/build.sbt +++ /dev/null @@ -1,25 +0,0 @@ -import scala.xml.XML - -val descriptionValue = "This is just a test" -val homepageValue = "http://example.com" - -lazy val root = (project in file(".")) settings( - useIvy := true, - name := "ivy-xml-test", - description := descriptionValue, - homepage := Some(uri(homepageValue)), - - TaskKey[Unit]("checkIvyXml") := { - val ivyXml = XML.loadFile(makeIvyXml.value) - val description = (ivyXml \ "info" \ "description").head - val homepage = (description \ "@homepage").head - - if (description.text != descriptionValue) - sys.error(s"Unexpected description: ${description.text}") - - if (homepage.text != homepageValue) - sys.error(s"Unexpected homepage: ${homepage.text}") - - () - } -) diff --git a/sbt-app/src/sbt-test/ivy/make-ivy-xml/project/plugins.sbt b/sbt-app/src/sbt-test/ivy/make-ivy-xml/project/plugins.sbt deleted file mode 100644 index 15c7fdd35..000000000 --- a/sbt-app/src/sbt-test/ivy/make-ivy-xml/project/plugins.sbt +++ /dev/null @@ -1,5 +0,0 @@ -libraryDependencies += Defaults.sbtPluginExtra( - "org.scala-sbt" % "sbt-ivy" % sbtVersion.value, - sbtVersion.value, - scalaVersion.value, -) diff --git a/sbt-app/src/sbt-test/ivy/make-ivy-xml/test b/sbt-app/src/sbt-test/ivy/make-ivy-xml/test deleted file mode 100644 index 0c84f997a..000000000 --- a/sbt-app/src/sbt-test/ivy/make-ivy-xml/test +++ /dev/null @@ -1 +0,0 @@ -> checkIvyXml diff --git a/sbt-app/src/sbt-test/ivy/provided-multi/changes/A.scala b/sbt-app/src/sbt-test/ivy/provided-multi/changes/A.scala deleted file mode 100644 index 40190d644..000000000 --- a/sbt-app/src/sbt-test/ivy/provided-multi/changes/A.scala +++ /dev/null @@ -1,6 +0,0 @@ -import sbinary._ - -trait A -{ - def format: Format[A] -} \ No newline at end of file diff --git a/sbt-app/src/sbt-test/ivy/provided-multi/changes/B.scala b/sbt-app/src/sbt-test/ivy/provided-multi/changes/B.scala deleted file mode 100644 index 3519cbacb..000000000 --- a/sbt-app/src/sbt-test/ivy/provided-multi/changes/B.scala +++ /dev/null @@ -1,6 +0,0 @@ -import sbinary._ - -trait B -{ - def format(a: A): Format[A] -} \ No newline at end of file diff --git a/sbt-app/src/sbt-test/ivy/provided-multi/changes/p.sbt b/sbt-app/src/sbt-test/ivy/provided-multi/changes/p.sbt deleted file mode 100644 index 15f3c2626..000000000 --- a/sbt-app/src/sbt-test/ivy/provided-multi/changes/p.sbt +++ /dev/null @@ -1,23 +0,0 @@ -ThisBuild / scalaVersion := "2.12.21" -ThisBuild / useIvy := true -def configIvyScala = - scalaModuleInfo ~= (_ map (_ withCheckExplicit false)) - -val declared = SettingKey[Boolean]("declared") -lazy val a = project - .settings( - libraryDependencies += "org.scala-tools.sbinary" %% "sbinary" % "0.4.0" % "provided", - configIvyScala, - update / scalaBinaryVersion := "2.9.0", - ) - -lazy val b = project - .dependsOn(a) - .settings( - libraryDependencies := declared((d) => - if (d) Seq("org.scala-tools.sbinary" %% "sbinary" % "0.4.0" % "provided") - else Nil).value, - declared := baseDirectory((dir) => (dir / "declare.lib").exists).value, - configIvyScala, - update / scalaBinaryVersion := "2.9.0" - ) diff --git a/sbt-app/src/sbt-test/ivy/provided-multi/project/plugins.sbt b/sbt-app/src/sbt-test/ivy/provided-multi/project/plugins.sbt deleted file mode 100644 index 15c7fdd35..000000000 --- a/sbt-app/src/sbt-test/ivy/provided-multi/project/plugins.sbt +++ /dev/null @@ -1,5 +0,0 @@ -libraryDependencies += Defaults.sbtPluginExtra( - "org.scala-sbt" % "sbt-ivy" % sbtVersion.value, - sbtVersion.value, - scalaVersion.value, -) diff --git a/sbt-app/src/sbt-test/ivy/provided-multi/test b/sbt-app/src/sbt-test/ivy/provided-multi/test deleted file mode 100644 index 211dd1327..000000000 --- a/sbt-app/src/sbt-test/ivy/provided-multi/test +++ /dev/null @@ -1,11 +0,0 @@ -$ copy-file changes/p.sbt p.sbt -$ copy-file changes/A.scala a/src/main/scala/A.scala -$ copy-file changes/B.scala b/src/main/scala/B.scala -> reload - -> a/compile --> b/compile - -$ touch b/declare.lib -> reload -> compile diff --git a/sbt-app/src/sbt-test/ivy/publish-asc-no-checksum/build.sbt b/sbt-app/src/sbt-test/ivy/publish-asc-no-checksum/build.sbt deleted file mode 100644 index dfdfa9a65..000000000 --- a/sbt-app/src/sbt-test/ivy/publish-asc-no-checksum/build.sbt +++ /dev/null @@ -1,25 +0,0 @@ -// `signTask` stands in for sbt-pgp's signing task: it just writes a fake signature file -// and publishes it as an extra artifact with an ".asc" extension. - -useIvy := true - -organization := "com.example" -name := "foo" -version := "1.0.0" -scalaVersion := "2.12.21" -autoScalaLibrary := false -crossPaths := false -Compile / packageDoc / publishArtifact := false -Compile / packageSrc / publishArtifact := false -publishTo := localStaging.value - -lazy val signTask = taskKey[HashedVirtualFileRef]("Emulates sbt-pgp's signing task") - -signTask := { - val conv = fileConverter.value - val out = target.value / "foo-1.0.0.jar.asc" - IO.write(out, "fake-signature") - conv.toVirtualFile(out.toPath) -} - -addArtifact(Artifact("foo", "asc", "jar.asc"), signTask) diff --git a/sbt-app/src/sbt-test/ivy/publish-asc-no-checksum/project/plugins.sbt b/sbt-app/src/sbt-test/ivy/publish-asc-no-checksum/project/plugins.sbt deleted file mode 100644 index 15c7fdd35..000000000 --- a/sbt-app/src/sbt-test/ivy/publish-asc-no-checksum/project/plugins.sbt +++ /dev/null @@ -1,5 +0,0 @@ -libraryDependencies += Defaults.sbtPluginExtra( - "org.scala-sbt" % "sbt-ivy" % sbtVersion.value, - sbtVersion.value, - scalaVersion.value, -) diff --git a/sbt-app/src/sbt-test/ivy/publish-asc-no-checksum/test b/sbt-app/src/sbt-test/ivy/publish-asc-no-checksum/test deleted file mode 100644 index fab6a6f32..000000000 --- a/sbt-app/src/sbt-test/ivy/publish-asc-no-checksum/test +++ /dev/null @@ -1,16 +0,0 @@ -# useIvy := true forces the Ivy-backed publisher (ConvertResolver), which is what generates -# checksums via the ChecksumFriendlyURLResolver shim. -> publish - -# ordinary artifacts and their checksums are published as usual -$ exists target/sona-staging/com/example/foo/1.0.0/foo-1.0.0.jar -$ exists target/sona-staging/com/example/foo/1.0.0/foo-1.0.0.jar.md5 -$ exists target/sona-staging/com/example/foo/1.0.0/foo-1.0.0.jar.sha1 -$ exists target/sona-staging/com/example/foo/1.0.0/foo-1.0.0.pom -$ exists target/sona-staging/com/example/foo/1.0.0/foo-1.0.0.pom.md5 -$ exists target/sona-staging/com/example/foo/1.0.0/foo-1.0.0.pom.sha1 - -# the .asc signature artifact is published, but must NOT get its own checksum files -$ exists target/sona-staging/com/example/foo/1.0.0/foo-1.0.0.jar.asc --$ exists target/sona-staging/com/example/foo/1.0.0/foo-1.0.0.jar.asc.md5 --$ exists target/sona-staging/com/example/foo/1.0.0/foo-1.0.0.jar.asc.sha1 diff --git a/sbt-app/src/sbt-test/plugins/pgp/project/plugins.sbt b/sbt-app/src/sbt-test/plugins/pgp/project/plugins.sbt index 267a6dd6d..62c4971a1 100644 --- a/sbt-app/src/sbt-test/plugins/pgp/project/plugins.sbt +++ b/sbt-app/src/sbt-test/plugins/pgp/project/plugins.sbt @@ -1 +1 @@ -addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1") +addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.2") diff --git a/sbt-app/src/sbt-test/project/source-plugins/project/plugin.sbt b/sbt-app/src/sbt-test/project/source-plugins/project/plugin.sbt index b4391883f..3da15b22f 100644 --- a/sbt-app/src/sbt-test/project/source-plugins/project/plugin.sbt +++ b/sbt-app/src/sbt-test/project/source-plugins/project/plugin.sbt @@ -1,4 +1,4 @@ -lazy val git = RootProject(uri("https://github.com/sbt/sbt-git.git#66bf7f0bd51629deb0c4283cddbe8af8c30af0de")) +lazy val git = RootProject(uri("https://github.com/sbt/sbt-git.git#047e2800186c82c1c8c65e130542be46c9ce3223")) lazy val root = (project in file(".")). dependsOn(git) diff --git a/sbt-app/src/test/scala/sbt/RunFromSourceMain.scala b/sbt-app/src/test/scala/sbt/RunFromSourceMain.scala index 1e11f0cad..c8040d65d 100644 --- a/sbt-app/src/test/scala/sbt/RunFromSourceMain.scala +++ b/sbt-app/src/test/scala/sbt/RunFromSourceMain.scala @@ -142,34 +142,26 @@ object RunFromSourceMain { val scalaHome1 = fakeboot / s"scala-$scalaVersion" val scalaHome1Lib = scalaHome1 / "lib" val scalaHome1Temp = scalaHome1 / "temp" - if (scalaHome1Lib.exists) log.info(s"""using $scalaHome1 that was found""") + if scalaHome1Lib.exists then log.info(s"""using $scalaHome1 that was found""") else { log.info(s"""creating $scalaHome1 by downloading scala-compiler $scalaVersion""") IO.createDirectories(List(scalaHome1Lib, scalaHome1Temp)) val lm = { - import sbt.internal.librarymanagement.ivy.{ - InlineIvyConfiguration, - IvyDependencyResolution - } - val ivyConfig = InlineIvyConfiguration().withLog(log) - IvyDependencyResolution(ivyConfig) + import lmcoursier.{ CoursierConfiguration, CoursierDependencyResolution } + CoursierDependencyResolution(CoursierConfiguration()) } val Name = """(.*)(?:\-[\d.]+)\.jar""".r val BinPre = """(.*)(?:\-[\d.]+)-(?:bin|pre)-.*\.jar""".r val module = "org.scala-lang" % "scala3-compiler_3" % scalaVersion - lm.retrieve(module, scalaModuleInfo = None, scalaHome1Temp, log) match { - case Left(w) => throw w.resolveException - case Right(_) => - val jars = (scalaHome1Temp ** "*.jar").get() + lm.retrieve(module, scalaModuleInfo = None, scalaHome1Temp, log) match + case Left(w) => throw w.resolveException + case Right(jars) => assert(jars.nonEmpty, s"no jars for scala $scalaVersion") - jars.foreach { f => - val name = f.getName match { + jars.foreach: f => + val name = f.getName match case Name(name) => name case BinPre(name) => name - } IO.copyFile(f, scalaHome1Lib / s"$name.jar") - } - } } scalaHome1 } diff --git a/sbt-ivy/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala b/sbt-ivy/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala deleted file mode 100644 index d1817ae34..000000000 --- a/sbt-ivy/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala +++ /dev/null @@ -1,246 +0,0 @@ -/* - * sbt - * Copyright 2023, Scala center - * Copyright 2011 - 2022, Lightbend, Inc. - * Copyright 2008 - 2010, Mark Harrah - * Licensed under Apache License 2.0 (see LICENSE) - */ - -package sbt -package internal -package librarymanagement - -import java.nio.file.Files - -import lmcoursier.definitions.{ Configuration, Project } -import org.apache.ivy.core.module.id.ModuleRevisionId -import Def.Setting -import sbt.Keys.{ csrProject, csrPublications, publishLocalConfiguration, publishConfiguration } -import sbt.ProjectExtra.* -import scala.jdk.CollectionConverters.* -import scala.xml.{ Node, PrefixedAttribute } - -object IvyXml { - - private def rawContent( - currentProject: Project, - shadedConfigOpt: Option[Configuration], - bomForcedDeps: Seq[(String, String, String)] - ): String = { - - // Important: width = Int.MaxValue, so that no tag gets truncated. - // In particular, that prevents things like to be split to - // - // - // by the pretty-printer. - // See https://github.com/sbt/sbt/issues/3412. - val printer = new scala.xml.PrettyPrinter(Int.MaxValue, 2) - - """""" + '\n' + - printer.format(content(currentProject, shadedConfigOpt, bomForcedDeps)) - } - - // These are required for publish to be fine, later on. - private[sbt] def writeFiles( - currentProject: Project, - shadedConfigOpt: Option[Configuration], - ivySbt: IvySbt, - log: sbt.util.Logger, - resolvedDeps: Seq[sbt.librarymanagement.ModuleID] - ): Unit = { - val bomForcedDeps = resolvedDeps.map(m => (m.organization, m.name, m.revision)) - - val ivyCacheManager = ivySbt.withIvy(log)(ivy => ivy.getResolutionCacheManager) - - val ivyModule = ModuleRevisionId.newInstance( - currentProject.module.organization.value, - currentProject.module.name.value, - currentProject.version, - currentProject.module.attributes.asJava - ) - - val cacheIvyFile = ivyCacheManager.getResolvedIvyFileInCache(ivyModule) - val cacheIvyPropertiesFile = ivyCacheManager.getResolvedIvyPropertiesInCache(ivyModule) - - val content0 = rawContent(currentProject, shadedConfigOpt, bomForcedDeps) - cacheIvyFile.getParentFile.mkdirs() - log.debug(s"writing Ivy file $cacheIvyFile") - Files.writeString(cacheIvyFile.toPath, content0) - - // Just writing an empty file here... Are these only used? - cacheIvyPropertiesFile.getParentFile.mkdirs() - Files.write(cacheIvyPropertiesFile.toPath, Array.emptyByteArray) - () - } - - private def content( - project0: Project, - shadedConfigOpt: Option[Configuration], - bomForcedDeps: Seq[(String, String, String)] - ): Node = { - - val filterOutDependencies = - shadedConfigOpt.toSet[Configuration].flatMap { shadedConfig => - project0.dependencies - .collect { case (conf, dep) if conf.value == shadedConfig.value => dep } - } - - val project: Project = project0.withDependencies(project0.dependencies.collect { - case p @ (_, dep) if !filterOutDependencies(dep) => p - }) - - val infoAttrs = - (project.module.attributes.toSeq ++ project.properties).foldLeft[xml.MetaData](xml.Null) { - case (acc, (k, v)) => - new PrefixedAttribute("e", k, v, acc) - } - - val licenseElems = project.info.licenses.map { (name, urlOpt) => - val n = - - urlOpt.fold(n) { url => - n % .attributes - } - } - - val descriptionElem = { - val n = {project.info.description} - if (project.info.homePage.nonEmpty) - n % .attributes - else - n - } - - val infoElem = { - - {licenseElems} - {descriptionElem} - - } % infoAttrs - - val confElems = project.configurations.toVector.collect { - case (name, extends0) if !shadedConfigOpt.exists(_.value == name.value) => - val extends1 = shadedConfigOpt.fold(extends0)(c => extends0.filter(_.value != c.value)) - val visibility = - if (project.privateConfigs.contains(name)) "private" else "public" - val n = - if (extends1.nonEmpty) - n % .attributes - else - n - } - - val publications = project.publications - .groupBy { case (_, p) => p } - .view - .mapValues { _.map { case (cfg, _) => cfg } } - - val publicationElems = publications.map { (pub, configs) => - val n = - - - if (pub.classifier.value.nonEmpty) - n % .attributes - else - n - } - - val bomForcedSet = bomForcedDeps.toSet - val dependencyElems = project.dependencies.toVector.map { (conf, dep) => - val classifier = { - val pub = dep.publication - if (pub.classifier.value.nonEmpty) - Seq( - - ) - else - Seq.empty - } - - val excludes = dep.exclusions.toSeq.map { (org, name) => - - } - - val org0 = dep.module.organization.value - val name0 = dep.module.name.value - val rev0 = dep.version - val forced = bomForcedSet((org0, name0, rev0)) - val forceAttr = - if (forced) new scala.xml.UnprefixedAttribute("force", "true", scala.xml.Null) - else scala.xml.Null - - val n = - ${dep.configuration.value}" - }> - {classifier} - {excludes} - - - val moduleAttrs = dep.module.attributes.foldLeft[xml.MetaData](xml.Null) { - case (acc, (k, v)) => - new PrefixedAttribute("e", k, v, acc) - } - - n % moduleAttrs % forceAttr - } - - - {infoElem} - {confElems} - {publicationElems} - {dependencyElems} - - } - - private def makeIvyXmlBefore[T]( - task: TaskKey[T], - shadedConfigOpt: Option[Configuration] - ): Setting[Task[T]] = - task := Def.uncached(task.dependsOnTask { - Def.ifS(Def.task { sbt.Keys.useIvy.value })( - Def.task { - val currentProject = { - val proj = csrProject.value - val publications = csrPublications.value - proj.withPublications(publications) - } - val resolved = sbt.Keys.resolvedDependencies.value - IvyXml.writeFiles( - currentProject, - shadedConfigOpt, - sbt.Keys.ivySbt.value.asInstanceOf[IvySbt], - sbt.Keys.streams.value.log, - resolved - ) - } - )( - Def.task { () } - ) - }.value) - - private lazy val needsIvyXmlLocal = Seq(publishLocalConfiguration) ++ List( - sbt.Keys.makeIvyXmlLocalConfiguration - ) - private lazy val needsIvyXml = Seq(publishConfiguration) ++ List( - sbt.Keys.makeIvyXmlConfiguration - ) - - def generateIvyXmlSettings( - shadedConfigOpt: Option[Configuration] = None - ): Seq[Setting[?]] = - (needsIvyXml ++ needsIvyXmlLocal).map(makeIvyXmlBefore(_, shadedConfigOpt)) - -} diff --git a/sbt-ivy/src/main/scala/sbt/plugins/IvyDependencyPlugin.scala b/sbt-ivy/src/main/scala/sbt/plugins/IvyDependencyPlugin.scala deleted file mode 100644 index 77929acc9..000000000 --- a/sbt-ivy/src/main/scala/sbt/plugins/IvyDependencyPlugin.scala +++ /dev/null @@ -1,283 +0,0 @@ -/* - * sbt - * Copyright 2023, Scala center - * Copyright 2011 - 2022, Lightbend, Inc. - * Copyright 2008 - 2010, Mark Harrah - * Licensed under Apache License 2.0 (see LICENSE) - */ - -package sbt -package plugins - -import java.io.File -import org.apache.ivy.core.module.descriptor.{ DependencyDescriptor, ModuleDescriptor } -import org.apache.ivy.core.module.id.ModuleRevisionId -import sbt.Def.{ Initialize, Setting } -import sbt.Keys.* -import sbt.ProjectExtra.* -import sbt.internal.LibraryManagement -import sbt.internal.librarymanagement.{ - GenericPublisher, - IvyActions, - IvySbt, - IvyXml, - ProjectResolver -} -import sbt.internal.librarymanagement.ivy.* -import sbt.io.syntax.* -import sbt.librarymanagement.* -import sbt.std.TaskExtra.* -import lmcoursier.definitions.{ - Classifier as CClassifier, - Configuration as CConfiguration, - Dependency as CDependency, - Extension as CExtension, - Info as CInfo, - Module as CModule, - ModuleName as CModuleName, - Organization as COrganization, - Project as CProject, - Publication as CPublication, - Type as CType, -} -import lmcoursier.Inputs -import scala.jdk.CollectionConverters.* - -/** - * AutoPlugin that provides all Ivy-specific functionality. - * This plugin overrides the stub defaults in main/ with real Ivy implementations. - */ -object IvyDependencyPlugin extends AutoPlugin: - override def requires = IvyPlugin - override def trigger = allRequirements - - override lazy val globalSettings: Seq[Setting[?]] = Seq( - updateOptions := UpdateOptions(), - ) - - override lazy val projectSettings: Seq[Setting[?]] = Seq( - ivyConfiguration := Def.uncached( - Def - .ifS(Def.task { useIvy.value })( - Def.task { mkIvyConfiguration.value: Any } - )( - Def.task { (): Any } - ) - .value - ), - publisher := Def.uncached( - Def - .ifS(Def.task { useIvy.value })( - Def.task { - IvyPublisher(ivyConfiguration.value.asInstanceOf[IvyConfiguration]) - } - )( - Def.task { - val ivyHome = ivyPaths.value.ivyHome.map(new File(_)).getOrElse { - new File(System.getProperty("user.home")) / ".ivy2" - } - val localResolver = - Resolver.file("local", ivyHome / "local")(using Resolver.ivyStylePatterns) - // otherResolvers already has Resolver.publishMavenLocal +: publishTo.value.toVector - val knownResolvers = localResolver +: otherResolvers.value - Publisher( - GenericPublisher( - dependencyResolution.value, - fullResolvers.value.toVector, - csrProject.value.withPublications(csrPublications.value), - allCredentials.value, - knownResolvers - ) - ) - } - ) - .value - ), - ivySbt := Def.uncached( - Def - .ifS(Def.task { useIvy.value })( - Def.task { ivySbt0.value: Any } - )( - Def.task { (): Any } - ) - .value - ), - ivyModule := Def.uncached( - Def - .ifS(Def.task { useIvy.value })( - Def.task { - val is = ivySbt.value.asInstanceOf[IvySbt] - new is.Module(moduleSettings.value): Any - } - )( - Def.task { (): Any } - ) - .value - ), - projectResolver := Def.uncached( - Def - .ifS(Def.task { useIvy.value })( - projectResolverTask - )( - Classpaths.projectResolverTask - ) - .value - ), - csrExtraProjects := Def.uncached( - Def - .ifS(Def.task { useIvy.value })( - coursierExtraProjectsTask - )( - Def.task { Nil } - ) - .value - ), - ) ++ IvyXml.generateIvyXmlSettings() ++ ivyPublishOrSkipSettings - - private lazy val ivySbt0: Initialize[Task[IvySbt]] = - Def.task { - IvyCredentials.register(credentials.value, streams.value.log) - new IvySbt(ivyConfiguration.value.asInstanceOf[IvyConfiguration]) - } - - private lazy val mkIvyConfiguration: Initialize[Task[InlineIvyConfiguration]] = - Def.task { - val (rs, other) = (fullResolvers.value.toVector, otherResolvers.value.toVector) - val s = streams.value - Classpaths.warnResolversConflict(rs ++: other, s.log) - Classpaths.errorInsecureProtocol(rs ++: other, s.log) - InlineIvyConfiguration() - .withPaths(ivyPaths.value) - .withResolvers(rs) - .withOtherResolvers(other) - .withModuleConfigurations(moduleConfigurations.value.toVector) - .withLock(LibraryManagement.lock(appConfiguration.value)) - .withChecksums((update / checksums).value.toVector) - .withResolutionCacheDir(target.value / "resolution-cache") - .withUpdateOptions(updateOptions.value.asInstanceOf[UpdateOptions]) - .withLog(s.log) - } - - private def depMap: Initialize[Task[Map[ModuleRevisionId, ModuleDescriptor]]] = - import sbt.TupleSyntax.* - (buildDependencies.toTaskable, thisProjectRef.toTaskable, settingsData, streams) - .flatMapN { (bd, thisProj, data, s) => - depMap(bd.classpathTransitiveRefs(thisProj), data, s.log) - } - - private def depMap( - projects: Seq[ProjectRef], - data: Def.Settings, - log: sbt.util.Logger - ): Task[Map[ModuleRevisionId, ModuleDescriptor]] = - val ivyModules = projects.flatMap { proj => - (proj / ivyModule).get(data) - }.join - ivyModules.mapN { mod => - mod.map { m => m.asInstanceOf[IvySbt#Module].dependencyMapping(log) }.toMap - } - - private def projectResolverTask: Initialize[Task[Resolver]] = - depMap.map { m => - val resolver = new ProjectResolver(ProjectResolver.InterProject, m) - new RawRepository(resolver, resolver.getName) - } - - private def ivyPublishOrSkipSettings: Seq[Setting[?]] = - Seq( - deliver := deliverTask(makeIvyXmlConfiguration).value, - deliverLocal := deliverTask(makeIvyXmlLocalConfiguration).value, - makeIvyXml := deliverTask(makeIvyXmlConfiguration).value, - ) - - private def deliverTask(config: TaskKey[PublishConfiguration]): Initialize[Task[File]] = - Def.task { - Def.unit(update.value) - if !useIvy.value then sys.error("deliver/makeIvyXml requires useIvy := true") - IvyActions.deliver( - ivyModule.value.asInstanceOf[IvySbt#Module], - config.value, - streams.value.log - ) - } - - private lazy val coursierExtraProjectsTask: Initialize[Task[Seq[CProject]]] = - Def.task { - val projects = csrInterProjectDependencies.value - val projectModules = projects.map(_.module).toSet - depMap.value - .map { (id, desc) => - moduleFromIvy(id) -> desc - } - .filter { case (module, _) => - !projectModules(module) - } - .toVector - .map { (module, v) => - val configurations = v.getConfigurations.map { c => - CConfiguration(c.getName) -> c.getExtends.map(CConfiguration(_)).toSeq - }.toMap - val deps = v.getDependencies.flatMap(dependencyFromIvy) - CProject( - module, - v.getModuleRevisionId.getRevision, - deps.toSeq, - configurations, - Nil, - None, - Nil, - CInfo("", "", Nil, Nil, None) - ) - } - } - - private def moduleFromIvy(id: ModuleRevisionId): CModule = - CModule( - COrganization(id.getOrganisation), - CModuleName(id.getName), - id.getExtraAttributes.asScala.map { (k0, v0) => - k0.asInstanceOf[String] -> v0.asInstanceOf[String] - }.toMap - ) - - private def dependencyFromIvy( - desc: DependencyDescriptor - ): Seq[(CConfiguration, CDependency)] = - val id = desc.getDependencyRevisionId - val module = moduleFromIvy(id) - val exclusions = desc.getAllExcludeRules.map { rule => - val modId = rule.getId.getModuleId - (COrganization(modId.getOrganisation), CModuleName(modId.getName)) - }.toSet - val configurations = desc.getModuleConfigurations.toVector - .flatMap(Inputs.ivyXmlMappings) - - def dependency(conf: CConfiguration, pub: CPublication) = CDependency( - module, - id.getRevision, - conf, - exclusions, - pub, - optional = false, - desc.isTransitive - ) - - val publications: CConfiguration => CPublication = - val artifacts = desc.getAllDependencyArtifacts - val m = artifacts.toVector.flatMap { art => - val pub = CPublication( - art.getName, - CType(art.getType), - CExtension(art.getExt()), - CClassifier("") - ) - art.getConfigurations.map(CConfiguration(_)).toVector.map { conf => - conf -> pub - } - }.toMap - c => m.getOrElse(c, CPublication("", CType(""), CExtension(""), CClassifier(""))) - - configurations.map { (from, to) => - from -> dependency(to, publications(to)) - } -end IvyDependencyPlugin