mirror of https://github.com/sbt/sbt.git
Merge pull request #270 from andreaTP/coursierAgain
Add Coursier as a library management implementation
This commit is contained in:
commit
4b179e526d
|
|
@ -3,3 +3,4 @@ target/
|
||||||
__pycache__
|
__pycache__
|
||||||
|
|
||||||
scripted-test/src/sbt-test/*/*/project/build.properties
|
scripted-test/src/sbt-test/*/*/project/build.properties
|
||||||
|
scripted-test/src/sbt-test/*/*/project/plugins.sbt
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ script: sbt -Dfile.encoding=UTF8 -J-XX:ReservedCodeCacheSize=256M
|
||||||
whitesourceCheckPolicies
|
whitesourceCheckPolicies
|
||||||
test
|
test
|
||||||
scriptedIvy
|
scriptedIvy
|
||||||
|
scriptedCoursier
|
||||||
packagedArtifacts # ensure that all artifacts for publish package without failure
|
packagedArtifacts # ensure that all artifacts for publish package without failure
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
|
|
||||||
34
build.sbt
34
build.sbt
|
|
@ -12,6 +12,7 @@ def commonSettings: Seq[Setting[_]] = Def.settings(
|
||||||
// publishArtifact in packageDoc := false,
|
// publishArtifact in packageDoc := false,
|
||||||
resolvers += Resolver.typesafeIvyRepo("releases"),
|
resolvers += Resolver.typesafeIvyRepo("releases"),
|
||||||
resolvers += Resolver.sonatypeRepo("snapshots"),
|
resolvers += Resolver.sonatypeRepo("snapshots"),
|
||||||
|
resolvers += Resolver.sbtPluginRepo("releases"),
|
||||||
resolvers += "bintray-sbt-maven-releases" at "https://dl.bintray.com/sbt/maven-releases/",
|
resolvers += "bintray-sbt-maven-releases" at "https://dl.bintray.com/sbt/maven-releases/",
|
||||||
// concurrentRestrictions in Global += Util.testExclusiveRestriction,
|
// concurrentRestrictions in Global += Util.testExclusiveRestriction,
|
||||||
testOptions += Tests.Argument(TestFrameworks.ScalaCheck, "-w", "1"),
|
testOptions += Tests.Argument(TestFrameworks.ScalaCheck, "-w", "1"),
|
||||||
|
|
@ -45,7 +46,7 @@ val mimaSettings = Def settings (
|
||||||
)
|
)
|
||||||
|
|
||||||
lazy val lmRoot = (project in file("."))
|
lazy val lmRoot = (project in file("."))
|
||||||
.aggregate(lmCore, lmIvy)
|
.aggregate(lmCore, lmIvy, lmCoursier)
|
||||||
.settings(
|
.settings(
|
||||||
inThisBuild(
|
inThisBuild(
|
||||||
Seq(
|
Seq(
|
||||||
|
|
@ -259,6 +260,26 @@ lazy val lmIvy = (project in file("ivy"))
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
lazy val lmCoursier = (project in file("coursier"))
|
||||||
|
.enablePlugins(ContrabandPlugin, JsonCodecPlugin)
|
||||||
|
.dependsOn(lmIvy)
|
||||||
|
.settings(
|
||||||
|
commonSettings,
|
||||||
|
crossScalaVersions := Seq(scala212, scala211),
|
||||||
|
name := "librarymanagement-coursier",
|
||||||
|
libraryDependencies ++= Seq(coursier,
|
||||||
|
coursierCache,
|
||||||
|
scalaTest % Test,
|
||||||
|
scalaCheck % Test
|
||||||
|
),
|
||||||
|
managedSourceDirectories in Compile +=
|
||||||
|
baseDirectory.value / "src" / "main" / "contraband-scala",
|
||||||
|
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala",
|
||||||
|
contrabandFormatsForType in generateContrabands in Compile := DatatypeConfig.getFormats,
|
||||||
|
scalacOptions in (Compile, console) --=
|
||||||
|
Vector("-Ywarn-unused-import", "-Ywarn-unused", "-Xlint")
|
||||||
|
)
|
||||||
|
|
||||||
lazy val lmScriptedTest = (project in file("scripted-test"))
|
lazy val lmScriptedTest = (project in file("scripted-test"))
|
||||||
.enablePlugins(SbtPlugin)
|
.enablePlugins(SbtPlugin)
|
||||||
.settings(
|
.settings(
|
||||||
|
|
@ -276,7 +297,16 @@ addCommandAlias("scriptedIvy", Seq(
|
||||||
"lmIvy/publishLocal",
|
"lmIvy/publishLocal",
|
||||||
"lmScriptedTest/clean",
|
"lmScriptedTest/clean",
|
||||||
"""set ThisBuild / scriptedTestLMImpl := "ivy"""",
|
"""set ThisBuild / scriptedTestLMImpl := "ivy"""",
|
||||||
"""set ThisBuild / scriptedLaunchOpts += "-Ddependency.resolution=set ThisBuild / dependencyResolution := sbt.librarymanagement.ivy.IvyDependencyResolution(ivyConfiguration.value)" """,
|
"""set ThisBuild / scriptedLaunchOpts += "-Ddependency.resolution=ivy" """,
|
||||||
|
"lmScriptedTest/scripted").mkString(";",";",""))
|
||||||
|
|
||||||
|
addCommandAlias("scriptedCoursier", Seq(
|
||||||
|
"lmCore/publishLocal",
|
||||||
|
"lmIvy/publishLocal",
|
||||||
|
"lmCoursier/publishLocal",
|
||||||
|
"lmScriptedTest/clean",
|
||||||
|
"""set ThisBuild / scriptedTestLMImpl := "coursier"""",
|
||||||
|
"""set ThisBuild / scriptedLaunchOpts += "-Ddependency.resolution=coursier" """,
|
||||||
"lmScriptedTest/scripted").mkString(";",";",""))
|
"lmScriptedTest/scripted").mkString(";",";",""))
|
||||||
|
|
||||||
def customCommands: Seq[Setting[_]] = Seq(
|
def customCommands: Seq[Setting[_]] = Seq(
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ trait PublisherInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decribes the representation of a module, inclding its dependencies
|
* Decribes the representation of a module, including its dependencies
|
||||||
* and the version of Scala it uses, if any.
|
* and the version of Scala it uses, if any.
|
||||||
*/
|
*/
|
||||||
trait ModuleDescriptor {
|
trait ModuleDescriptor {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
|
*/
|
||||||
|
|
||||||
|
// DO NOT EDIT MANUALLY
|
||||||
|
package sbt.librarymanagement.coursier
|
||||||
|
final class CoursierConfiguration private (
|
||||||
|
val log: Option[xsbti.Logger],
|
||||||
|
val resolvers: Vector[sbt.librarymanagement.Resolver],
|
||||||
|
val otherResolvers: Vector[sbt.librarymanagement.Resolver],
|
||||||
|
val reorderResolvers: Boolean,
|
||||||
|
val parallelDownloads: Int,
|
||||||
|
val maxIterations: Int) extends Serializable {
|
||||||
|
|
||||||
|
private def this() = this(None, sbt.librarymanagement.Resolver.defaults, Vector.empty, true, 6, 100)
|
||||||
|
|
||||||
|
override def equals(o: Any): Boolean = o match {
|
||||||
|
case x: CoursierConfiguration => (this.log == x.log) && (this.resolvers == x.resolvers) && (this.otherResolvers == x.otherResolvers) && (this.reorderResolvers == x.reorderResolvers) && (this.parallelDownloads == x.parallelDownloads) && (this.maxIterations == x.maxIterations)
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
override def hashCode: Int = {
|
||||||
|
37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.librarymanagement.coursier.CoursierConfiguration".##) + log.##) + resolvers.##) + otherResolvers.##) + reorderResolvers.##) + parallelDownloads.##) + maxIterations.##)
|
||||||
|
}
|
||||||
|
override def toString: String = {
|
||||||
|
"CoursierConfiguration(" + log + ", " + resolvers + ", " + otherResolvers + ", " + reorderResolvers + ", " + parallelDownloads + ", " + maxIterations + ")"
|
||||||
|
}
|
||||||
|
private[this] def copy(log: Option[xsbti.Logger] = log, resolvers: Vector[sbt.librarymanagement.Resolver] = resolvers, otherResolvers: Vector[sbt.librarymanagement.Resolver] = otherResolvers, reorderResolvers: Boolean = reorderResolvers, parallelDownloads: Int = parallelDownloads, maxIterations: Int = maxIterations): CoursierConfiguration = {
|
||||||
|
new CoursierConfiguration(log, resolvers, otherResolvers, reorderResolvers, parallelDownloads, maxIterations)
|
||||||
|
}
|
||||||
|
def withLog(log: Option[xsbti.Logger]): CoursierConfiguration = {
|
||||||
|
copy(log = log)
|
||||||
|
}
|
||||||
|
def withLog(log: xsbti.Logger): CoursierConfiguration = {
|
||||||
|
copy(log = Option(log))
|
||||||
|
}
|
||||||
|
def withResolvers(resolvers: Vector[sbt.librarymanagement.Resolver]): CoursierConfiguration = {
|
||||||
|
copy(resolvers = resolvers)
|
||||||
|
}
|
||||||
|
def withOtherResolvers(otherResolvers: Vector[sbt.librarymanagement.Resolver]): CoursierConfiguration = {
|
||||||
|
copy(otherResolvers = otherResolvers)
|
||||||
|
}
|
||||||
|
def withReorderResolvers(reorderResolvers: Boolean): CoursierConfiguration = {
|
||||||
|
copy(reorderResolvers = reorderResolvers)
|
||||||
|
}
|
||||||
|
def withParallelDownloads(parallelDownloads: Int): CoursierConfiguration = {
|
||||||
|
copy(parallelDownloads = parallelDownloads)
|
||||||
|
}
|
||||||
|
def withMaxIterations(maxIterations: Int): CoursierConfiguration = {
|
||||||
|
copy(maxIterations = maxIterations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object CoursierConfiguration {
|
||||||
|
|
||||||
|
def apply(): CoursierConfiguration = new CoursierConfiguration()
|
||||||
|
def apply(log: Option[xsbti.Logger], resolvers: Vector[sbt.librarymanagement.Resolver], otherResolvers: Vector[sbt.librarymanagement.Resolver], reorderResolvers: Boolean, parallelDownloads: Int, maxIterations: Int): CoursierConfiguration = new CoursierConfiguration(log, resolvers, otherResolvers, reorderResolvers, parallelDownloads, maxIterations)
|
||||||
|
def apply(log: xsbti.Logger, resolvers: Vector[sbt.librarymanagement.Resolver], otherResolvers: Vector[sbt.librarymanagement.Resolver], reorderResolvers: Boolean, parallelDownloads: Int, maxIterations: Int): CoursierConfiguration = new CoursierConfiguration(Option(log), resolvers, otherResolvers, reorderResolvers, parallelDownloads, maxIterations)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
|
*/
|
||||||
|
|
||||||
|
// DO NOT EDIT MANUALLY
|
||||||
|
package sbt.librarymanagement.coursier
|
||||||
|
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||||
|
trait CoursierConfigurationFormats { self: sbt.internal.librarymanagement.formats.LoggerFormat with sbt.librarymanagement.ResolverFormats with sjsonnew.BasicJsonProtocol =>
|
||||||
|
implicit lazy val CoursierConfigurationFormat: JsonFormat[sbt.librarymanagement.coursier.CoursierConfiguration] = new JsonFormat[sbt.librarymanagement.coursier.CoursierConfiguration] {
|
||||||
|
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.librarymanagement.coursier.CoursierConfiguration = {
|
||||||
|
jsOpt match {
|
||||||
|
case Some(js) =>
|
||||||
|
unbuilder.beginObject(js)
|
||||||
|
val log = unbuilder.readField[Option[xsbti.Logger]]("log")
|
||||||
|
val resolvers = unbuilder.readField[Vector[sbt.librarymanagement.Resolver]]("resolvers")
|
||||||
|
val otherResolvers = unbuilder.readField[Vector[sbt.librarymanagement.Resolver]]("otherResolvers")
|
||||||
|
val reorderResolvers = unbuilder.readField[Boolean]("reorderResolvers")
|
||||||
|
val parallelDownloads = unbuilder.readField[Int]("parallelDownloads")
|
||||||
|
val maxIterations = unbuilder.readField[Int]("maxIterations")
|
||||||
|
unbuilder.endObject()
|
||||||
|
sbt.librarymanagement.coursier.CoursierConfiguration(log, resolvers, otherResolvers, reorderResolvers, parallelDownloads, maxIterations)
|
||||||
|
case None =>
|
||||||
|
deserializationError("Expected JsObject but found None")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override def write[J](obj: sbt.librarymanagement.coursier.CoursierConfiguration, builder: Builder[J]): Unit = {
|
||||||
|
builder.beginObject()
|
||||||
|
builder.addField("log", obj.log)
|
||||||
|
builder.addField("resolvers", obj.resolvers)
|
||||||
|
builder.addField("otherResolvers", obj.otherResolvers)
|
||||||
|
builder.addField("reorderResolvers", obj.reorderResolvers)
|
||||||
|
builder.addField("parallelDownloads", obj.parallelDownloads)
|
||||||
|
builder.addField("maxIterations", obj.maxIterations)
|
||||||
|
builder.endObject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"codecNamespace": "sbt.librarymanagement.coursier",
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"name": "CoursierConfiguration",
|
||||||
|
"namespace": "sbt.librarymanagement.coursier",
|
||||||
|
"target": "Scala",
|
||||||
|
"type": "record",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "log",
|
||||||
|
"type": "xsbti.Logger?",
|
||||||
|
"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": "reorderResolvers",
|
||||||
|
"type": "Boolean",
|
||||||
|
"default": "true",
|
||||||
|
"since": "0.0.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "parallelDownloads",
|
||||||
|
"type": "Int",
|
||||||
|
"default": "6",
|
||||||
|
"since": "0.0.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "maxIterations",
|
||||||
|
"type": "Int",
|
||||||
|
"default": "100",
|
||||||
|
"since": "0.0.1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,283 @@
|
||||||
|
package coursier
|
||||||
|
|
||||||
|
import coursier.ivy.IvyRepository
|
||||||
|
import coursier.ivy.IvyXml.{ mappings => ivyXmlMappings }
|
||||||
|
import java.net.{ MalformedURLException, URL }
|
||||||
|
|
||||||
|
import coursier.core.Authentication
|
||||||
|
import sbt.internal.librarymanagement.mavenint.SbtPomExtraProperties
|
||||||
|
import sbt.librarymanagement._
|
||||||
|
import sbt.librarymanagement.Resolver
|
||||||
|
import sbt.util.Logger
|
||||||
|
|
||||||
|
object FromSbt {
|
||||||
|
|
||||||
|
def sbtModuleIdName(
|
||||||
|
moduleId: ModuleID,
|
||||||
|
scalaVersion: => String,
|
||||||
|
scalaBinaryVersion: => String
|
||||||
|
): String =
|
||||||
|
sbtCrossVersionName(moduleId.name, moduleId.crossVersion, scalaVersion, scalaBinaryVersion)
|
||||||
|
|
||||||
|
def sbtCrossVersionName(
|
||||||
|
name: String,
|
||||||
|
crossVersion: CrossVersion,
|
||||||
|
scalaVersion: => String,
|
||||||
|
scalaBinaryVersion: => String
|
||||||
|
): String =
|
||||||
|
CrossVersion(crossVersion, scalaVersion, scalaBinaryVersion)
|
||||||
|
.fold(name)(_(name))
|
||||||
|
|
||||||
|
def attributes(attr: Map[String, String]): Map[String, String] =
|
||||||
|
attr
|
||||||
|
.map {
|
||||||
|
case (k, v) =>
|
||||||
|
k.stripPrefix("e:") -> v
|
||||||
|
}
|
||||||
|
.filter {
|
||||||
|
case (k, _) =>
|
||||||
|
!k.startsWith(SbtPomExtraProperties.POM_INFO_KEY_PREFIX)
|
||||||
|
}
|
||||||
|
|
||||||
|
def moduleVersion(
|
||||||
|
module: ModuleID,
|
||||||
|
scalaVersion: String,
|
||||||
|
scalaBinaryVersion: String
|
||||||
|
): (Module, String) = {
|
||||||
|
|
||||||
|
val fullName = sbtModuleIdName(module, scalaVersion, scalaBinaryVersion)
|
||||||
|
|
||||||
|
val module0 =
|
||||||
|
Module(module.organization, fullName, FromSbt.attributes(module.extraDependencyAttributes))
|
||||||
|
val version = module.revision
|
||||||
|
|
||||||
|
(module0, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
def dependencies(
|
||||||
|
module: ModuleID,
|
||||||
|
scalaVersion: String,
|
||||||
|
scalaBinaryVersion: String
|
||||||
|
): Seq[(String, Dependency)] = {
|
||||||
|
|
||||||
|
// TODO Warn about unsupported properties in `module`
|
||||||
|
|
||||||
|
val (module0, version) = moduleVersion(module, scalaVersion, scalaBinaryVersion)
|
||||||
|
|
||||||
|
val dep = Dependency(
|
||||||
|
module0,
|
||||||
|
version,
|
||||||
|
exclusions = module.exclusions.map { rule =>
|
||||||
|
// FIXME Other `rule` fields are ignored here
|
||||||
|
(rule.organization, rule.name)
|
||||||
|
}.toSet,
|
||||||
|
transitive = module.isTransitive
|
||||||
|
)
|
||||||
|
|
||||||
|
val mapping = module.configurations.getOrElse("compile")
|
||||||
|
val allMappings = ivyXmlMappings(mapping)
|
||||||
|
|
||||||
|
val attributes =
|
||||||
|
if (module.explicitArtifacts.isEmpty)
|
||||||
|
Seq(Attributes("", ""))
|
||||||
|
else
|
||||||
|
module.explicitArtifacts.map { a =>
|
||||||
|
Attributes(`type` = a.`type`, classifier = a.classifier.getOrElse(""))
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
(from, to) <- allMappings
|
||||||
|
attr <- attributes
|
||||||
|
} yield from -> dep.copy(configuration = to, attributes = attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
def fallbackDependencies(
|
||||||
|
allDependencies: Seq[ModuleID],
|
||||||
|
scalaVersion: String,
|
||||||
|
scalaBinaryVersion: String
|
||||||
|
): Seq[(Module, String, URL, Boolean)] =
|
||||||
|
for {
|
||||||
|
module <- allDependencies
|
||||||
|
artifact <- module.explicitArtifacts
|
||||||
|
url <- artifact.url.toSeq
|
||||||
|
} yield {
|
||||||
|
val (module0, version) = moduleVersion(module, scalaVersion, scalaBinaryVersion)
|
||||||
|
(module0, version, url, module.isChanging)
|
||||||
|
}
|
||||||
|
|
||||||
|
def sbtClassifiersProject(
|
||||||
|
cm: GetClassifiersModule,
|
||||||
|
scalaVersion: String,
|
||||||
|
scalaBinaryVersion: String
|
||||||
|
) = {
|
||||||
|
|
||||||
|
val p = FromSbt.project(
|
||||||
|
cm.id,
|
||||||
|
cm.dependencies,
|
||||||
|
cm.configurations.map(cfg => cfg.name -> cfg.extendsConfigs.map(_.name)).toMap,
|
||||||
|
scalaVersion,
|
||||||
|
scalaBinaryVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
// for w/e reasons, the dependencies sometimes don't land in the right config above
|
||||||
|
// this is a loose attempt at fixing that
|
||||||
|
cm.configurations match {
|
||||||
|
case Seq(cfg) =>
|
||||||
|
p.copy(
|
||||||
|
dependencies = p.dependencies.map {
|
||||||
|
case (_, d) => (cfg.name, d)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
|
p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def project(
|
||||||
|
projectID: ModuleID,
|
||||||
|
allDependencies: Seq[ModuleID],
|
||||||
|
ivyConfigurations: Map[String, Seq[String]],
|
||||||
|
scalaVersion: String,
|
||||||
|
scalaBinaryVersion: String
|
||||||
|
): Project = {
|
||||||
|
|
||||||
|
val deps = allDependencies.flatMap(dependencies(_, scalaVersion, scalaBinaryVersion))
|
||||||
|
|
||||||
|
Project(
|
||||||
|
Module(
|
||||||
|
projectID.organization,
|
||||||
|
sbtModuleIdName(projectID, scalaVersion, scalaBinaryVersion),
|
||||||
|
FromSbt.attributes(projectID.extraDependencyAttributes)
|
||||||
|
),
|
||||||
|
projectID.revision,
|
||||||
|
deps,
|
||||||
|
ivyConfigurations,
|
||||||
|
None,
|
||||||
|
Nil,
|
||||||
|
Nil,
|
||||||
|
Nil,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Nil,
|
||||||
|
Info.empty
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def mavenCompatibleBaseOpt(patterns: Patterns): Option[String] =
|
||||||
|
if (patterns.isMavenCompatible) {
|
||||||
|
val baseIvyPattern = patterns.ivyPatterns.head.takeWhile(c => c != '[' && c != '(')
|
||||||
|
val baseArtifactPattern = patterns.ivyPatterns.head.takeWhile(c => c != '[' && c != '(')
|
||||||
|
|
||||||
|
if (baseIvyPattern == baseArtifactPattern)
|
||||||
|
Some(baseIvyPattern)
|
||||||
|
else
|
||||||
|
None
|
||||||
|
} else
|
||||||
|
None
|
||||||
|
|
||||||
|
private def mavenRepositoryOpt(
|
||||||
|
root: String,
|
||||||
|
log: Logger,
|
||||||
|
authentication: Option[Authentication]
|
||||||
|
): Option[MavenRepository] =
|
||||||
|
try {
|
||||||
|
Cache.url(root) // ensure root is a URL whose protocol can be handled here
|
||||||
|
val root0 = if (root.endsWith("/")) root else root + "/"
|
||||||
|
Some(
|
||||||
|
MavenRepository(
|
||||||
|
root0,
|
||||||
|
authentication = authentication
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
case e: MalformedURLException =>
|
||||||
|
log.warn(
|
||||||
|
"Error parsing Maven repository base " +
|
||||||
|
root +
|
||||||
|
Option(e.getMessage).fold("")(" (" + _ + ")") +
|
||||||
|
", ignoring it"
|
||||||
|
)
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
def repository(
|
||||||
|
resolver: Resolver,
|
||||||
|
ivyProperties: Map[String, String],
|
||||||
|
log: Logger,
|
||||||
|
authentication: Option[Authentication]
|
||||||
|
): Option[Repository] =
|
||||||
|
resolver match {
|
||||||
|
case r: sbt.librarymanagement.MavenRepository =>
|
||||||
|
mavenRepositoryOpt(r.root, log, authentication)
|
||||||
|
|
||||||
|
case r: FileRepository
|
||||||
|
if r.patterns.ivyPatterns.lengthCompare(1) == 0 &&
|
||||||
|
r.patterns.artifactPatterns.lengthCompare(1) == 0 =>
|
||||||
|
val mavenCompatibleBaseOpt0 = mavenCompatibleBaseOpt(r.patterns)
|
||||||
|
|
||||||
|
mavenCompatibleBaseOpt0 match {
|
||||||
|
case None =>
|
||||||
|
val repo = IvyRepository.parse(
|
||||||
|
"file://" + r.patterns.artifactPatterns.head,
|
||||||
|
metadataPatternOpt = Some("file://" + r.patterns.ivyPatterns.head),
|
||||||
|
changing = Some(true),
|
||||||
|
properties = ivyProperties,
|
||||||
|
dropInfoAttributes = true,
|
||||||
|
authentication = authentication
|
||||||
|
) match {
|
||||||
|
case Left(err) =>
|
||||||
|
sys.error(
|
||||||
|
s"Cannot parse Ivy patterns ${r.patterns.artifactPatterns.head} and ${r.patterns.ivyPatterns.head}: $err"
|
||||||
|
)
|
||||||
|
case Right(repo) =>
|
||||||
|
repo
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(repo)
|
||||||
|
|
||||||
|
case Some(mavenCompatibleBase) =>
|
||||||
|
mavenRepositoryOpt("file://" + mavenCompatibleBase, log, authentication)
|
||||||
|
}
|
||||||
|
|
||||||
|
case r: URLRepository
|
||||||
|
if r.patterns.ivyPatterns.lengthCompare(1) == 0 &&
|
||||||
|
r.patterns.artifactPatterns.lengthCompare(1) == 0 =>
|
||||||
|
val mavenCompatibleBaseOpt0 = mavenCompatibleBaseOpt(r.patterns)
|
||||||
|
|
||||||
|
mavenCompatibleBaseOpt0 match {
|
||||||
|
case None =>
|
||||||
|
val repo = IvyRepository.parse(
|
||||||
|
r.patterns.artifactPatterns.head,
|
||||||
|
metadataPatternOpt = Some(r.patterns.ivyPatterns.head),
|
||||||
|
changing = None,
|
||||||
|
properties = ivyProperties,
|
||||||
|
dropInfoAttributes = true,
|
||||||
|
authentication = authentication
|
||||||
|
) match {
|
||||||
|
case Left(err) =>
|
||||||
|
sys.error(
|
||||||
|
s"Cannot parse Ivy patterns ${r.patterns.artifactPatterns.head} and ${r.patterns.ivyPatterns.head}: $err"
|
||||||
|
)
|
||||||
|
case Right(repo) =>
|
||||||
|
repo
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(repo)
|
||||||
|
|
||||||
|
case Some(mavenCompatibleBase) =>
|
||||||
|
mavenRepositoryOpt(mavenCompatibleBase, log, authentication)
|
||||||
|
}
|
||||||
|
|
||||||
|
case raw: RawRepository
|
||||||
|
if raw.name == "inter-project" => // sbt.RawRepository.equals just compares names anyway
|
||||||
|
None
|
||||||
|
|
||||||
|
case other =>
|
||||||
|
log.warn(s"Unrecognized repository ${other.name}, ignoring it")
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package coursier
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object SbtBootJars {
|
||||||
|
def apply(
|
||||||
|
scalaOrg: String,
|
||||||
|
scalaVersion: String,
|
||||||
|
jars: Seq[File]
|
||||||
|
): Map[(Module, String), File] =
|
||||||
|
jars.collect {
|
||||||
|
case jar if jar.getName.endsWith(".jar") =>
|
||||||
|
val name = jar.getName.stripSuffix(".jar")
|
||||||
|
val mod = Module(scalaOrg, name)
|
||||||
|
|
||||||
|
(mod, scalaVersion) -> jar
|
||||||
|
}.toMap
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,275 @@
|
||||||
|
package coursier
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.GregorianCalendar
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
import coursier.maven.MavenSource
|
||||||
|
import sbt.librarymanagement._
|
||||||
|
import sbt.util.Logger
|
||||||
|
|
||||||
|
object ToSbt {
|
||||||
|
|
||||||
|
private def caching[K, V](f: K => V): K => V = {
|
||||||
|
|
||||||
|
val cache = new ConcurrentHashMap[K, V]
|
||||||
|
|
||||||
|
key =>
|
||||||
|
val previousValueOpt = Option(cache.get(key))
|
||||||
|
|
||||||
|
previousValueOpt.getOrElse {
|
||||||
|
val value = f(key)
|
||||||
|
val concurrentValueOpt = Option(cache.putIfAbsent(key, value))
|
||||||
|
concurrentValueOpt.getOrElse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val moduleId = caching[(Dependency, Map[String, String]), ModuleID] {
|
||||||
|
case (dependency, extraProperties) =>
|
||||||
|
sbt.librarymanagement
|
||||||
|
.ModuleID(
|
||||||
|
dependency.module.organization,
|
||||||
|
dependency.module.name,
|
||||||
|
dependency.version
|
||||||
|
)
|
||||||
|
.withConfigurations(
|
||||||
|
Some(dependency.configuration)
|
||||||
|
)
|
||||||
|
.withExtraAttributes(
|
||||||
|
dependency.module.attributes ++ extraProperties
|
||||||
|
)
|
||||||
|
.withExclusions(
|
||||||
|
dependency.exclusions.toVector
|
||||||
|
.map {
|
||||||
|
case (org, name) =>
|
||||||
|
sbt.librarymanagement
|
||||||
|
.InclExclRule()
|
||||||
|
.withOrganization(org)
|
||||||
|
.withName(name)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withIsTransitive(
|
||||||
|
dependency.transitive
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val artifact = caching[(Module, Map[String, String], Artifact), sbt.librarymanagement.Artifact] {
|
||||||
|
case (module, extraProperties, artifact) =>
|
||||||
|
sbt.librarymanagement
|
||||||
|
.Artifact(module.name)
|
||||||
|
// FIXME Get these two from publications
|
||||||
|
.withType(artifact.attributes.`type`)
|
||||||
|
.withExtension(MavenSource.typeExtension(artifact.attributes.`type`))
|
||||||
|
.withClassifier(
|
||||||
|
Some(artifact.attributes.classifier)
|
||||||
|
.filter(_.nonEmpty)
|
||||||
|
.orElse(MavenSource.typeDefaultClassifierOpt(artifact.attributes.`type`))
|
||||||
|
)
|
||||||
|
// .withConfigurations(Vector())
|
||||||
|
.withUrl(Some(new URL(artifact.url)))
|
||||||
|
.withExtraAttributes(module.attributes ++ extraProperties)
|
||||||
|
}
|
||||||
|
|
||||||
|
val moduleReport =
|
||||||
|
caching[(Dependency, Seq[(Dependency, Project)], Project, Seq[(Artifact, Option[File])]),
|
||||||
|
ModuleReport] {
|
||||||
|
case (dependency, dependees, project, artifacts) =>
|
||||||
|
val sbtArtifacts = artifacts.collect {
|
||||||
|
case (artifact, Some(file)) =>
|
||||||
|
(ToSbt.artifact((dependency.module, project.properties.toMap, artifact)), file)
|
||||||
|
}
|
||||||
|
val sbtMissingArtifacts = artifacts.collect {
|
||||||
|
case (artifact, None) =>
|
||||||
|
ToSbt.artifact((dependency.module, project.properties.toMap, artifact))
|
||||||
|
}
|
||||||
|
|
||||||
|
val publicationDate = project.info.publication.map { dt =>
|
||||||
|
new GregorianCalendar(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
val callers = dependees.map {
|
||||||
|
case (dependee, dependeeProj) =>
|
||||||
|
Caller(
|
||||||
|
ToSbt.moduleId((dependee, dependeeProj.properties.toMap)),
|
||||||
|
dependeeProj.configurations.keys.toVector.map(ConfigRef(_)),
|
||||||
|
dependee.module.attributes ++ dependeeProj.properties,
|
||||||
|
// FIXME Set better values here
|
||||||
|
isForceDependency = false,
|
||||||
|
isChangingDependency = false,
|
||||||
|
isTransitiveDependency = false,
|
||||||
|
isDirectlyForceDependency = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleReport(
|
||||||
|
ToSbt.moduleId((dependency, project.properties.toMap)),
|
||||||
|
sbtArtifacts.toVector,
|
||||||
|
sbtMissingArtifacts.toVector
|
||||||
|
)
|
||||||
|
// .withStatus(None)
|
||||||
|
.withPublicationDate(publicationDate)
|
||||||
|
// .withResolver(None)
|
||||||
|
// .withArtifactResolver(None)
|
||||||
|
// .withEvicted(false)
|
||||||
|
// .withEvictedData(None)
|
||||||
|
// .withEvictedReason(None)
|
||||||
|
// .withProblem(None)
|
||||||
|
.withHomepage(Some(project.info.homePage).filter(_.nonEmpty))
|
||||||
|
.withExtraAttributes(dependency.module.attributes ++ project.properties)
|
||||||
|
// .withIsDefault(None)
|
||||||
|
// .withBranch(None)
|
||||||
|
.withConfigurations(project.configurations.keys.toVector.map(ConfigRef(_)))
|
||||||
|
.withLicenses(project.info.licenses.toVector)
|
||||||
|
.withCallers(callers.toVector)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def grouped[K, V](map: Seq[(K, V)]): Map[K, Seq[V]] =
|
||||||
|
map.groupBy { case (k, _) => k }.map {
|
||||||
|
case (k, l) =>
|
||||||
|
k -> l.map { case (_, v) => v }
|
||||||
|
}
|
||||||
|
|
||||||
|
def moduleReports(
|
||||||
|
res: Resolution,
|
||||||
|
classifiersOpt: Option[Seq[String]],
|
||||||
|
artifactFileOpt: (Module, String, Artifact) => Option[File],
|
||||||
|
log: Logger,
|
||||||
|
keepPomArtifact: Boolean = false,
|
||||||
|
includeSignatures: Boolean = false
|
||||||
|
) = {
|
||||||
|
val depArtifacts1 =
|
||||||
|
classifiersOpt match {
|
||||||
|
case None => res.dependencyArtifacts(withOptional = true)
|
||||||
|
case Some(cl) => res.dependencyClassifiersArtifacts(cl)
|
||||||
|
}
|
||||||
|
|
||||||
|
val depArtifacts0 =
|
||||||
|
if (keepPomArtifact)
|
||||||
|
depArtifacts1
|
||||||
|
else
|
||||||
|
depArtifacts1.filter {
|
||||||
|
case (_, a) => a.attributes != Attributes("pom", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
val depArtifacts =
|
||||||
|
if (includeSignatures) {
|
||||||
|
|
||||||
|
val notFound = depArtifacts0.filter(!_._2.extra.contains("sig"))
|
||||||
|
|
||||||
|
if (notFound.isEmpty)
|
||||||
|
depArtifacts0.flatMap {
|
||||||
|
case (dep, a) =>
|
||||||
|
Seq(dep -> a) ++ a.extra.get("sig").toSeq.map(dep -> _)
|
||||||
|
} else {
|
||||||
|
for ((_, a) <- notFound)
|
||||||
|
log.error(s"No signature found for ${a.url}")
|
||||||
|
sys.error(s"${notFound.length} signature(s) not found")
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
depArtifacts0
|
||||||
|
|
||||||
|
val groupedDepArtifacts = grouped(depArtifacts)
|
||||||
|
|
||||||
|
val versions = res.dependencies.toVector.map { dep =>
|
||||||
|
dep.module -> dep.version
|
||||||
|
}.toMap
|
||||||
|
|
||||||
|
def clean(dep: Dependency): Dependency =
|
||||||
|
dep.copy(configuration = "", exclusions = Set.empty, optional = false)
|
||||||
|
|
||||||
|
val reverseDependencies = res.reverseDependencies.toVector
|
||||||
|
.map {
|
||||||
|
case (k, v) =>
|
||||||
|
clean(k) -> v.map(clean)
|
||||||
|
}
|
||||||
|
.groupBy { case (k, _) => k }
|
||||||
|
.mapValues { v =>
|
||||||
|
v.flatMap {
|
||||||
|
case (_, l) => l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toVector
|
||||||
|
.toMap
|
||||||
|
|
||||||
|
groupedDepArtifacts.map {
|
||||||
|
case (dep, artifacts) =>
|
||||||
|
val (_, proj) = res.projectCache(dep.moduleVersion)
|
||||||
|
|
||||||
|
// FIXME Likely flaky...
|
||||||
|
val dependees = reverseDependencies
|
||||||
|
.getOrElse(clean(dep.copy(version = "")), Vector.empty)
|
||||||
|
.map { dependee0 =>
|
||||||
|
val version = versions(dependee0.module)
|
||||||
|
val dependee = dependee0.copy(version = version)
|
||||||
|
val (_, dependeeProj) = res.projectCache(dependee.moduleVersion)
|
||||||
|
(dependee, dependeeProj)
|
||||||
|
}
|
||||||
|
|
||||||
|
ToSbt.moduleReport(
|
||||||
|
(
|
||||||
|
dep,
|
||||||
|
dependees,
|
||||||
|
proj,
|
||||||
|
artifacts.map(a => a -> artifactFileOpt(proj.module, proj.version, a))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateReport(
|
||||||
|
configDependencies: Map[String, Seq[Dependency]],
|
||||||
|
resolutions: Map[String, Resolution],
|
||||||
|
configs: Map[String, Set[String]],
|
||||||
|
classifiersOpt: Option[Seq[String]],
|
||||||
|
artifactFileOpt: (Module, String, Artifact) => Option[File],
|
||||||
|
log: Logger,
|
||||||
|
keepPomArtifact: Boolean = false,
|
||||||
|
includeSignatures: Boolean = false
|
||||||
|
): UpdateReport = {
|
||||||
|
|
||||||
|
val configReports = configs.map {
|
||||||
|
case (config, extends0) =>
|
||||||
|
val configDeps = extends0.flatMap(configDependencies.getOrElse(_, Nil))
|
||||||
|
val subRes = resolutions(config).subset(configDeps)
|
||||||
|
|
||||||
|
val reports = ToSbt.moduleReports(
|
||||||
|
subRes,
|
||||||
|
classifiersOpt,
|
||||||
|
artifactFileOpt,
|
||||||
|
log,
|
||||||
|
keepPomArtifact = keepPomArtifact,
|
||||||
|
includeSignatures = includeSignatures
|
||||||
|
)
|
||||||
|
|
||||||
|
val reports0 =
|
||||||
|
if (subRes.rootDependencies.size == 1) {
|
||||||
|
// quick hack ensuring the module for the only root dependency
|
||||||
|
// appears first in the update report, see https://github.com/coursier/coursier/issues/650
|
||||||
|
val dep = subRes.rootDependencies.head
|
||||||
|
val (_, proj) = subRes.projectCache(dep.moduleVersion)
|
||||||
|
val mod = ToSbt.moduleId((dep, proj.properties.toMap))
|
||||||
|
val (main, other) = reports.partition { r =>
|
||||||
|
r.module.organization == mod.organization &&
|
||||||
|
r.module.name == mod.name &&
|
||||||
|
r.module.crossVersion == mod.crossVersion
|
||||||
|
}
|
||||||
|
main.toVector ++ other.toVector
|
||||||
|
} else
|
||||||
|
reports.toVector
|
||||||
|
|
||||||
|
ConfigurationReport(
|
||||||
|
ConfigRef(config),
|
||||||
|
reports0,
|
||||||
|
Vector()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateReport(
|
||||||
|
File.createTempFile("fake-update-report", "json"),
|
||||||
|
configReports.toVector,
|
||||||
|
UpdateStats(-1L, -1L, -1L, cached = false),
|
||||||
|
Map.empty
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,382 @@
|
||||||
|
package sbt.librarymanagement.coursier
|
||||||
|
|
||||||
|
import java.io.{ File, OutputStreamWriter }
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
import scala.util.{ Failure, Success }
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
import coursier.{ Artifact, Resolution, _ }
|
||||||
|
import coursier.util.{ Gather, Task }
|
||||||
|
import sbt.internal.librarymanagement.IvySbt
|
||||||
|
import sbt.librarymanagement.Configurations.{ CompilerPlugin, Component, ScalaTool }
|
||||||
|
import sbt.librarymanagement._
|
||||||
|
import sbt.util.Logger
|
||||||
|
import sjsonnew.JsonFormat
|
||||||
|
import sjsonnew.support.murmurhash.Hasher
|
||||||
|
|
||||||
|
case class CoursierModuleDescriptor(
|
||||||
|
directDependencies: Vector[ModuleID],
|
||||||
|
scalaModuleInfo: Option[ScalaModuleInfo],
|
||||||
|
moduleSettings: ModuleSettings,
|
||||||
|
configurations: Seq[String],
|
||||||
|
extraInputHash: Long
|
||||||
|
) extends ModuleDescriptor
|
||||||
|
|
||||||
|
case class CoursierModuleSettings() extends ModuleSettings
|
||||||
|
|
||||||
|
private[sbt] class CoursierDependencyResolution(coursierConfiguration: CoursierConfiguration)
|
||||||
|
extends DependencyResolutionInterface {
|
||||||
|
|
||||||
|
// keep the pool alive while the class is loaded
|
||||||
|
private lazy val pool =
|
||||||
|
ExecutionContext.fromExecutor(
|
||||||
|
Executors.newFixedThreadPool(coursierConfiguration.parallelDownloads)
|
||||||
|
)
|
||||||
|
|
||||||
|
private[coursier] val reorderedResolvers = {
|
||||||
|
val resolvers0 =
|
||||||
|
coursierConfiguration.resolvers ++ coursierConfiguration.otherResolvers
|
||||||
|
|
||||||
|
if (coursierConfiguration.reorderResolvers) {
|
||||||
|
Resolvers.reorder(resolvers0)
|
||||||
|
} else resolvers0
|
||||||
|
}
|
||||||
|
|
||||||
|
private[sbt] object AltLibraryManagementCodec extends CoursierLibraryManagementCodec {
|
||||||
|
type CoursierHL = (
|
||||||
|
Vector[Resolver],
|
||||||
|
Vector[Resolver],
|
||||||
|
Boolean,
|
||||||
|
Int,
|
||||||
|
Int
|
||||||
|
)
|
||||||
|
|
||||||
|
def coursierToHL(c: CoursierConfiguration): CoursierHL =
|
||||||
|
(
|
||||||
|
c.resolvers,
|
||||||
|
c.otherResolvers,
|
||||||
|
c.reorderResolvers,
|
||||||
|
c.parallelDownloads,
|
||||||
|
c.maxIterations
|
||||||
|
)
|
||||||
|
// Redefine to use a subset of properties, that are serialisable
|
||||||
|
override implicit lazy val CoursierConfigurationFormat: JsonFormat[CoursierConfiguration] = {
|
||||||
|
def hlToCoursier(c: CoursierHL): CoursierConfiguration = {
|
||||||
|
val (
|
||||||
|
resolvers,
|
||||||
|
otherResolvers,
|
||||||
|
reorderResolvers,
|
||||||
|
parallelDownloads,
|
||||||
|
maxIterations
|
||||||
|
) = c
|
||||||
|
CoursierConfiguration()
|
||||||
|
.withResolvers(resolvers)
|
||||||
|
.withOtherResolvers(otherResolvers)
|
||||||
|
.withReorderResolvers(reorderResolvers)
|
||||||
|
.withParallelDownloads(parallelDownloads)
|
||||||
|
.withMaxIterations(maxIterations)
|
||||||
|
}
|
||||||
|
projectFormat[CoursierConfiguration, CoursierHL](coursierToHL, hlToCoursier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def extraInputHash: Long = {
|
||||||
|
import AltLibraryManagementCodec._
|
||||||
|
Hasher.hash(coursierConfiguration) match {
|
||||||
|
case Success(keyHash) => keyHash.toLong
|
||||||
|
case Failure(_) => 0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a ModuleDescriptor that describes a subproject with dependencies.
|
||||||
|
*
|
||||||
|
* @param moduleSetting It contains the information about the module including the dependencies.
|
||||||
|
* @return A `ModuleDescriptor` describing a subproject and its dependencies.
|
||||||
|
*/
|
||||||
|
override def moduleDescriptor(
|
||||||
|
moduleSetting: ModuleDescriptorConfiguration): CoursierModuleDescriptor = {
|
||||||
|
CoursierModuleDescriptor(
|
||||||
|
moduleSetting.dependencies,
|
||||||
|
moduleSetting.scalaModuleInfo,
|
||||||
|
CoursierModuleSettings(),
|
||||||
|
moduleSetting.configurations.map(_.name),
|
||||||
|
extraInputHash
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val ivyHome = sys.props.getOrElse(
|
||||||
|
"ivy.home",
|
||||||
|
new File(sys.props("user.home")).toURI.getPath + ".ivy2"
|
||||||
|
)
|
||||||
|
|
||||||
|
val sbtIvyHome = sys.props.getOrElse(
|
||||||
|
"sbt.ivy.home",
|
||||||
|
ivyHome
|
||||||
|
)
|
||||||
|
|
||||||
|
val ivyProperties = Map(
|
||||||
|
"ivy.home" -> ivyHome,
|
||||||
|
"sbt.ivy.home" -> sbtIvyHome
|
||||||
|
) ++ sys.props
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the given module's dependencies performing a retrieval.
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
override def update(module: ModuleDescriptor,
|
||||||
|
configuration: UpdateConfiguration,
|
||||||
|
uwconfig: UnresolvedWarningConfiguration,
|
||||||
|
log: Logger): Either[UnresolvedWarning, UpdateReport] = {
|
||||||
|
|
||||||
|
// not sure what DependencyResolutionInterface.moduleDescriptor is for, we're handled ivy stuff anyway...
|
||||||
|
val module0 = module match {
|
||||||
|
case c: CoursierModuleDescriptor => c
|
||||||
|
// This shouldn't happen at best of my understanding
|
||||||
|
case i: IvySbt#Module =>
|
||||||
|
moduleDescriptor(
|
||||||
|
i.moduleSettings match {
|
||||||
|
case c: ModuleDescriptorConfiguration => c
|
||||||
|
case other => sys.error(s"unrecognized module settings: $other")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
|
sys.error(s"unrecognized ModuleDescriptor type: $module")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reorderedResolvers.isEmpty) {
|
||||||
|
log.error(
|
||||||
|
"Dependency resolution is configured with an empty list of resolvers. This is unlikely to work.")
|
||||||
|
}
|
||||||
|
|
||||||
|
val dependencies = module.directDependencies.map(toCoursierDependency).flatten.toSet
|
||||||
|
val start = Resolution(dependencies.map(_._1))
|
||||||
|
val authentication = None // TODO: get correct value
|
||||||
|
val ivyConfiguration = ivyProperties // TODO: is it enough?
|
||||||
|
|
||||||
|
val repositories =
|
||||||
|
reorderedResolvers.flatMap(r => FromSbt.repository(r, ivyConfiguration, log, authentication)) ++ Seq(
|
||||||
|
Cache.ivy2Local,
|
||||||
|
Cache.ivy2Cache
|
||||||
|
)
|
||||||
|
|
||||||
|
implicit val ec = pool
|
||||||
|
|
||||||
|
val coursierLogger = createLogger()
|
||||||
|
try {
|
||||||
|
val fetch = Fetch.from(
|
||||||
|
repositories,
|
||||||
|
Cache.fetch[Task](logger = Some(coursierLogger))
|
||||||
|
)
|
||||||
|
val resolution = start.process
|
||||||
|
.run(fetch, coursierConfiguration.maxIterations)
|
||||||
|
.unsafeRun()
|
||||||
|
|
||||||
|
def updateReport() = {
|
||||||
|
val localArtifacts: Map[Artifact, Either[FileError, File]] = Gather[Task]
|
||||||
|
.gather(
|
||||||
|
resolution.artifacts.map { a =>
|
||||||
|
Cache
|
||||||
|
.file[Task](a, logger = Some(coursierLogger))
|
||||||
|
.run
|
||||||
|
.map((a, _))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.unsafeRun()
|
||||||
|
.toMap
|
||||||
|
|
||||||
|
toUpdateReport(resolution,
|
||||||
|
(module0.configurations ++ dependencies.map(_._2)).distinct,
|
||||||
|
localArtifacts,
|
||||||
|
log)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolution.isDone &&
|
||||||
|
resolution.errors.isEmpty &&
|
||||||
|
resolution.conflicts.isEmpty) {
|
||||||
|
updateReport()
|
||||||
|
} else if (resolution.isDone &&
|
||||||
|
(!resolution.errors.isEmpty && configuration.missingOk)
|
||||||
|
&& resolution.conflicts.isEmpty) {
|
||||||
|
log.warn(s"""Failed to download artifacts: ${resolution.errors
|
||||||
|
.map(_._2)
|
||||||
|
.flatten
|
||||||
|
.mkString(", ")}""")
|
||||||
|
updateReport()
|
||||||
|
} else {
|
||||||
|
toSbtError(log, uwconfig, resolution)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
coursierLogger.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// utilities
|
||||||
|
private def createLogger() = {
|
||||||
|
val t = new TermDisplay(new OutputStreamWriter(System.out))
|
||||||
|
t.init()
|
||||||
|
t
|
||||||
|
}
|
||||||
|
|
||||||
|
private def toCoursierDependency(moduleID: ModuleID) = {
|
||||||
|
val attributes =
|
||||||
|
if (moduleID.explicitArtifacts.isEmpty)
|
||||||
|
Seq(Attributes("", ""))
|
||||||
|
else
|
||||||
|
moduleID.explicitArtifacts.map { a =>
|
||||||
|
Attributes(`type` = a.`type`, classifier = a.classifier.getOrElse(""))
|
||||||
|
}
|
||||||
|
|
||||||
|
val extraAttrs = FromSbt.attributes(moduleID.extraDependencyAttributes)
|
||||||
|
|
||||||
|
val mapping = moduleID.configurations.getOrElse("compile")
|
||||||
|
|
||||||
|
import _root_.coursier.ivy.IvyXml.{ mappings => ivyXmlMappings }
|
||||||
|
val allMappings = ivyXmlMappings(mapping)
|
||||||
|
|
||||||
|
for {
|
||||||
|
(from, to) <- allMappings
|
||||||
|
attr <- attributes
|
||||||
|
} yield {
|
||||||
|
Dependency(
|
||||||
|
Module(moduleID.organization, moduleID.name, extraAttrs),
|
||||||
|
moduleID.revision,
|
||||||
|
configuration = to,
|
||||||
|
attributes = attr,
|
||||||
|
exclusions = moduleID.exclusions.map { rule =>
|
||||||
|
(rule.organization, rule.name)
|
||||||
|
}.toSet,
|
||||||
|
transitive = moduleID.isTransitive
|
||||||
|
) -> from
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def toUpdateReport(resolution: Resolution,
|
||||||
|
configurations: Seq[String],
|
||||||
|
artifactFilesOrErrors0: Map[Artifact, Either[FileError, File]],
|
||||||
|
log: Logger): Either[UnresolvedWarning, UpdateReport] = {
|
||||||
|
|
||||||
|
val artifactFiles = artifactFilesOrErrors0.collect {
|
||||||
|
case (artifact, Right(file)) =>
|
||||||
|
artifact -> file
|
||||||
|
}
|
||||||
|
|
||||||
|
val artifactErrors = artifactFilesOrErrors0.toVector
|
||||||
|
.collect {
|
||||||
|
case (a, Left(err)) if !a.isOptional || !err.notFound =>
|
||||||
|
a -> err
|
||||||
|
}
|
||||||
|
|
||||||
|
val erroredArtifacts = artifactFilesOrErrors0.collect {
|
||||||
|
case (a, Left(_)) => a
|
||||||
|
}.toSet
|
||||||
|
|
||||||
|
val depsByConfig = {
|
||||||
|
val deps = resolution.dependencies.toVector
|
||||||
|
configurations
|
||||||
|
.map((_, deps))
|
||||||
|
.toMap
|
||||||
|
}
|
||||||
|
|
||||||
|
val configurations0 = extractConfigurationTree(configurations)
|
||||||
|
|
||||||
|
val configResolutions =
|
||||||
|
(depsByConfig.keys ++ configurations0.keys).map(k => (k, resolution)).toMap
|
||||||
|
|
||||||
|
val sbtBootJarOverrides = Map.empty[(Module, String), File]
|
||||||
|
val classifiers = None // TODO: get correct values
|
||||||
|
|
||||||
|
if (artifactErrors.isEmpty) {
|
||||||
|
Right(
|
||||||
|
ToSbt.updateReport(
|
||||||
|
depsByConfig,
|
||||||
|
configResolutions,
|
||||||
|
configurations0,
|
||||||
|
classifiers,
|
||||||
|
artifactFileOpt(
|
||||||
|
sbtBootJarOverrides,
|
||||||
|
artifactFiles,
|
||||||
|
erroredArtifacts,
|
||||||
|
log,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_
|
||||||
|
),
|
||||||
|
log
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException(s"Could not save downloaded dependencies: $erroredArtifacts")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigurationName = String
|
||||||
|
type ConfigurationDependencyTree = Map[ConfigurationName, Set[ConfigurationName]]
|
||||||
|
|
||||||
|
// Key is the name of the configuration (i.e. `compile`) and the values are the name itself plus the
|
||||||
|
// names of the configurations that this one depends on.
|
||||||
|
private def extractConfigurationTree(available: Seq[String]): ConfigurationDependencyTree = {
|
||||||
|
(Configurations.default ++
|
||||||
|
Configurations.defaultInternal ++
|
||||||
|
Seq(ScalaTool, CompilerPlugin, Component))
|
||||||
|
.filter(c => available.contains(c.name))
|
||||||
|
.map(c => (c.name, c.extendsConfigs.map(_.name) :+ c.name))
|
||||||
|
.toMap
|
||||||
|
.mapValues(_.toSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def artifactFileOpt(
|
||||||
|
sbtBootJarOverrides: Map[(Module, String), File],
|
||||||
|
artifactFiles: Map[Artifact, File],
|
||||||
|
erroredArtifacts: Set[Artifact],
|
||||||
|
log: Logger,
|
||||||
|
module: Module,
|
||||||
|
version: String,
|
||||||
|
artifact: Artifact
|
||||||
|
) = {
|
||||||
|
|
||||||
|
val artifact0 = artifact
|
||||||
|
.copy(attributes = Attributes()) // temporary hack :-(
|
||||||
|
|
||||||
|
// Under some conditions, SBT puts the scala JARs of its own classpath
|
||||||
|
// in the application classpath. Ensuring we return SBT's jars rather than
|
||||||
|
// JARs from the coursier cache, so that a same JAR doesn't land twice in the
|
||||||
|
// application classpath (once via SBT jars, once via coursier cache).
|
||||||
|
val fromBootJars =
|
||||||
|
if (artifact.classifier.isEmpty && artifact.`type` == "jar")
|
||||||
|
sbtBootJarOverrides.get((module, version))
|
||||||
|
else
|
||||||
|
None
|
||||||
|
|
||||||
|
val res = fromBootJars.orElse(artifactFiles.get(artifact0))
|
||||||
|
if (res.isEmpty && !erroredArtifacts(artifact0))
|
||||||
|
log.error(s"${artifact.url} not downloaded (should not happen)")
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
private def toSbtError(log: Logger,
|
||||||
|
uwconfig: UnresolvedWarningConfiguration,
|
||||||
|
resolution: Resolution) = {
|
||||||
|
val failedResolution = resolution.errors.map {
|
||||||
|
case ((failedModule, failedVersion), _) =>
|
||||||
|
ModuleID(failedModule.organization, failedModule.name, failedVersion)
|
||||||
|
}
|
||||||
|
val msgs = resolution.errors.flatMap(_._2)
|
||||||
|
log.debug(s"Failed resolution: $msgs")
|
||||||
|
log.debug(s"Missing artifacts: $failedResolution")
|
||||||
|
val ex = new ResolveException(msgs, failedResolution)
|
||||||
|
Left(UnresolvedWarning(ex, uwconfig))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object CoursierDependencyResolution {
|
||||||
|
def apply(configuration: CoursierConfiguration) =
|
||||||
|
DependencyResolution(new CoursierDependencyResolution(configuration))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package sbt.librarymanagement
|
||||||
|
package coursier
|
||||||
|
|
||||||
|
trait CoursierLibraryManagementCodec
|
||||||
|
extends sjsonnew.BasicJsonProtocol
|
||||||
|
with LibraryManagementCodec
|
||||||
|
// with sbt.internal.librarymanagement.formats.GlobalLockFormat
|
||||||
|
with sbt.internal.librarymanagement.formats.LoggerFormat
|
||||||
|
with sbt.librarymanagement.ResolverFormats
|
||||||
|
with CoursierConfigurationFormats
|
||||||
|
|
||||||
|
object CoursierLibraryManagementCodec extends CoursierLibraryManagementCodec
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package sbt.librarymanagement.coursier
|
||||||
|
|
||||||
|
import sbt.librarymanagement.{ MavenRepository, Resolver, URLRepository }
|
||||||
|
|
||||||
|
object Resolvers {
|
||||||
|
|
||||||
|
private val slowReposBase = Seq(
|
||||||
|
"https://repo.typesafe.com/",
|
||||||
|
"https://repo.scala-sbt.org/",
|
||||||
|
"http://repo.typesafe.com/",
|
||||||
|
"http://repo.scala-sbt.org/"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val fastReposBase = Seq(
|
||||||
|
"http://repo1.maven.org/",
|
||||||
|
"https://repo1.maven.org/"
|
||||||
|
)
|
||||||
|
|
||||||
|
private def url(res: Resolver): Option[String] =
|
||||||
|
res match {
|
||||||
|
case m: MavenRepository =>
|
||||||
|
Some(m.root)
|
||||||
|
case u: URLRepository =>
|
||||||
|
u.patterns.artifactPatterns.headOption
|
||||||
|
.orElse(u.patterns.ivyPatterns.headOption)
|
||||||
|
case _ =>
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
private def filterResolvers(bases: Seq[String],
|
||||||
|
resolvers: Seq[(Resolver, Option[String])]): Seq[Resolver] =
|
||||||
|
resolvers
|
||||||
|
.filter(tuple => tuple._2.exists(url => bases.exists(base => url.startsWith(base))))
|
||||||
|
.map(_._1)
|
||||||
|
|
||||||
|
def reorder(resolvers: Seq[Resolver]): Seq[Resolver] = {
|
||||||
|
|
||||||
|
val byUrl = resolvers.map(r => (r, url(r)))
|
||||||
|
|
||||||
|
val fast = filterResolvers(fastReposBase, byUrl)
|
||||||
|
val slow = filterResolvers(slowReposBase, byUrl)
|
||||||
|
val rest = resolvers.diff(fast).diff(slow)
|
||||||
|
|
||||||
|
val reordered = fast ++ rest ++ slow
|
||||||
|
assert(reordered.size == resolvers.size,
|
||||||
|
"Reordered resolvers should be the same size as the unordered ones.")
|
||||||
|
|
||||||
|
reordered
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package sbt.librarymanagement.coursier
|
||||||
|
|
||||||
|
import sbt.internal.librarymanagement.cross.CrossVersionUtil
|
||||||
|
import sbt.internal.util.ConsoleLogger
|
||||||
|
import sbt.librarymanagement.Configurations._
|
||||||
|
import sbt.librarymanagement._
|
||||||
|
|
||||||
|
trait BaseCoursierSpecification extends UnitSpec {
|
||||||
|
lazy val log = ConsoleLogger()
|
||||||
|
val lmEngine: CoursierDependencyResolution
|
||||||
|
|
||||||
|
def configurations = Vector(Compile, Test, Runtime)
|
||||||
|
def module(moduleId: ModuleID,
|
||||||
|
deps: Vector[ModuleID],
|
||||||
|
scalaFullVersion: Option[String],
|
||||||
|
overrideScalaVersion: Boolean = true): ModuleDescriptor = {
|
||||||
|
val scalaModuleInfo = scalaFullVersion map { fv =>
|
||||||
|
ScalaModuleInfo(
|
||||||
|
scalaFullVersion = fv,
|
||||||
|
scalaBinaryVersion = CrossVersionUtil.binaryScalaVersion(fv),
|
||||||
|
configurations = configurations,
|
||||||
|
checkExplicit = true,
|
||||||
|
filterImplicit = false,
|
||||||
|
overrideScalaVersion = overrideScalaVersion
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val moduleSetting = ModuleDescriptorConfiguration(moduleId, ModuleInfo("foo"))
|
||||||
|
.withDependencies(deps)
|
||||||
|
.withConfigurations(configurations)
|
||||||
|
.withScalaModuleInfo(scalaModuleInfo)
|
||||||
|
lmEngine.moduleDescriptor(moduleSetting)
|
||||||
|
}
|
||||||
|
|
||||||
|
def resolvers: Vector[Resolver]
|
||||||
|
|
||||||
|
def configuration(res: Vector[Resolver] = resolvers) =
|
||||||
|
CoursierConfiguration().withResolvers(res)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
package sbt.librarymanagement.coursier
|
||||||
|
|
||||||
|
import sbt.librarymanagement.Configurations.Component
|
||||||
|
import sbt.librarymanagement.Resolver.{
|
||||||
|
DefaultMavenRepository,
|
||||||
|
JCenterRepository,
|
||||||
|
JavaNet2Repository
|
||||||
|
}
|
||||||
|
import sbt.librarymanagement.syntax._
|
||||||
|
import sbt.librarymanagement.{ Resolver, UnresolvedWarningConfiguration, UpdateConfiguration }
|
||||||
|
|
||||||
|
class ResolutionSpec extends BaseCoursierSpecification {
|
||||||
|
override def resolvers = Vector(
|
||||||
|
DefaultMavenRepository,
|
||||||
|
JavaNet2Repository,
|
||||||
|
JCenterRepository,
|
||||||
|
Resolver.sbtPluginRepo("releases")
|
||||||
|
)
|
||||||
|
|
||||||
|
val lmEngine = new CoursierDependencyResolution(configuration())
|
||||||
|
|
||||||
|
private final val stubModule = "com.example" % "foo" % "0.1.0" % "compile"
|
||||||
|
|
||||||
|
"Coursier dependency resolution" should "resolve very simple module" in {
|
||||||
|
val dependencies = Vector(
|
||||||
|
"com.typesafe.scala-logging" % "scala-logging_2.12" % "3.7.2" % "compile",
|
||||||
|
"org.scalatest" % "scalatest_2.12" % "3.0.4" % "test"
|
||||||
|
).map(_.withIsTransitive(false))
|
||||||
|
|
||||||
|
val coursierModule = module(stubModule, dependencies, Some("2.12.4"))
|
||||||
|
val resolution =
|
||||||
|
lmEngine.update(coursierModule, UpdateConfiguration(), UnresolvedWarningConfiguration(), log)
|
||||||
|
|
||||||
|
resolution should be('right)
|
||||||
|
val r = resolution.right.get
|
||||||
|
r.configurations.map(_.configuration) should have size 3
|
||||||
|
|
||||||
|
val compileConfig = r.configurations.find(_.configuration == Compile.toConfigRef).get
|
||||||
|
compileConfig.modules should have size 2
|
||||||
|
|
||||||
|
val runtimeConfig = r.configurations.find(_.configuration == Runtime.toConfigRef).get
|
||||||
|
runtimeConfig.modules should have size 2
|
||||||
|
|
||||||
|
val testConfig = r.configurations.find(_.configuration == Test.toConfigRef).get
|
||||||
|
testConfig.modules should have size 2
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "resolve compiler bridge" in {
|
||||||
|
val dependencies =
|
||||||
|
Vector(("org.scala-sbt" % "compiler-interface" % "1.0.4" % "component").sources())
|
||||||
|
val coursierModule = module(stubModule, dependencies, Some("2.12.4"))
|
||||||
|
val resolution =
|
||||||
|
lmEngine.update(coursierModule, UpdateConfiguration(), UnresolvedWarningConfiguration(), log)
|
||||||
|
|
||||||
|
val r = resolution.right.get
|
||||||
|
|
||||||
|
val componentConfig = r.configurations.find(_.configuration == Component.toConfigRef).get
|
||||||
|
componentConfig.modules should have size 2
|
||||||
|
componentConfig.modules.head.artifacts should have size 1
|
||||||
|
componentConfig.modules.head.artifacts.head._1.classifier should contain("sources")
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "resolve sbt jars" in {
|
||||||
|
val dependencies =
|
||||||
|
Vector(("org.scala-sbt" % "sbt" % "1.1.0" % "provided"))
|
||||||
|
val coursierModule = module(stubModule, dependencies, Some("2.12.4"))
|
||||||
|
val resolution =
|
||||||
|
lmEngine.update(coursierModule, UpdateConfiguration(), UnresolvedWarningConfiguration(), log)
|
||||||
|
|
||||||
|
val r = resolution.right.get
|
||||||
|
|
||||||
|
val modules = r.configurations.flatMap(_.modules)
|
||||||
|
modules.map(_.module.name) should contain("main_2.12")
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "resolve with default resolvers" in {
|
||||||
|
val dependencies =
|
||||||
|
Vector(("org.scala-sbt" % "compiler-interface" % "1.0.4" % "component").sources())
|
||||||
|
val lmEngine =
|
||||||
|
CoursierDependencyResolution.apply(
|
||||||
|
configuration(Resolver.combineDefaultResolvers(Vector.empty))
|
||||||
|
)
|
||||||
|
val coursierModule = module(stubModule, dependencies, Some("2.12.4"))
|
||||||
|
val resolution =
|
||||||
|
lmEngine.update(coursierModule, UpdateConfiguration(), UnresolvedWarningConfiguration(), log)
|
||||||
|
|
||||||
|
resolution should be('right)
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "resolve plugin" in {
|
||||||
|
val pluginAttributes = Map("scalaVersion" -> "2.12", "sbtVersion" -> "1.0")
|
||||||
|
val dependencies =
|
||||||
|
Vector(("org.xerial.sbt" % "sbt-sonatype" % "2.0").withExtraAttributes(pluginAttributes))
|
||||||
|
val coursierModule = module(stubModule, dependencies, Some("2.12.4"))
|
||||||
|
val resolution =
|
||||||
|
lmEngine.update(coursierModule, UpdateConfiguration(), UnresolvedWarningConfiguration(), log)
|
||||||
|
|
||||||
|
val r = resolution.right.get
|
||||||
|
|
||||||
|
val componentConfig = r.configurations.find(_.configuration == Compile.toConfigRef).get
|
||||||
|
componentConfig.modules.map(_.module.name) should have size 5
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "strip e: prefix from plugin attributes" in {
|
||||||
|
val pluginAttributes = Map("e:scalaVersion" -> "2.12", "e:sbtVersion" -> "1.0")
|
||||||
|
val dependencies =
|
||||||
|
Vector(("org.xerial.sbt" % "sbt-sonatype" % "2.0").withExtraAttributes(pluginAttributes))
|
||||||
|
val coursierModule = module(stubModule, dependencies, Some("2.12.4"))
|
||||||
|
val resolution =
|
||||||
|
lmEngine.update(coursierModule, UpdateConfiguration(), UnresolvedWarningConfiguration(), log)
|
||||||
|
|
||||||
|
resolution should be('right)
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "resolve plugins hosted on repo.typesafe.com" in {
|
||||||
|
val pluginAttributes = Map("e:scalaVersion" -> "2.12", "e:sbtVersion" -> "1.0")
|
||||||
|
val dependencies =
|
||||||
|
Vector(("com.typesafe.sbt" % "sbt-git" % "0.9.3").withExtraAttributes(pluginAttributes))
|
||||||
|
val coursierModule = module(stubModule, dependencies, Some("2.12.4"))
|
||||||
|
val resolution =
|
||||||
|
lmEngine.update(coursierModule, UpdateConfiguration(), UnresolvedWarningConfiguration(), log)
|
||||||
|
|
||||||
|
resolution should be('right)
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "reorder fast and slow resolvers" in {
|
||||||
|
val resolvers = Vector(
|
||||||
|
JavaNet2Repository,
|
||||||
|
Resolver.sbtPluginRepo("releases"),
|
||||||
|
DefaultMavenRepository
|
||||||
|
)
|
||||||
|
val engine = new CoursierDependencyResolution(configuration(resolvers))
|
||||||
|
engine.reorderedResolvers.head.name should be("public")
|
||||||
|
engine.reorderedResolvers.last.name should be("sbt-plugin-releases")
|
||||||
|
engine.reorderedResolvers should have size 3
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "reorder default resolvers" in {
|
||||||
|
val resolvers = Resolver.combineDefaultResolvers(Vector.empty)
|
||||||
|
val engine = new CoursierDependencyResolution(configuration(resolvers))
|
||||||
|
engine.reorderedResolvers should not be 'empty
|
||||||
|
engine.reorderedResolvers.head.name should be("public")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package sbt.librarymanagement.coursier
|
||||||
|
|
||||||
|
import org.scalatest.{ FlatSpec, Matchers }
|
||||||
|
|
||||||
|
abstract class UnitSpec extends FlatSpec with Matchers
|
||||||
|
|
@ -9,6 +9,8 @@ object Dependencies {
|
||||||
private val ioVersion = "1.2.1"
|
private val ioVersion = "1.2.1"
|
||||||
private val utilVersion = "1.2.2"
|
private val utilVersion = "1.2.2"
|
||||||
|
|
||||||
|
private val coursierVersion = "1.1.0-M7"
|
||||||
|
|
||||||
private val sbtIO = "org.scala-sbt" %% "io" % ioVersion
|
private val sbtIO = "org.scala-sbt" %% "io" % ioVersion
|
||||||
|
|
||||||
private val utilPosition = "org.scala-sbt" %% "util-position" % utilVersion
|
private val utilPosition = "org.scala-sbt" %% "util-position" % utilVersion
|
||||||
|
|
@ -41,7 +43,13 @@ object Dependencies {
|
||||||
|
|
||||||
val launcherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.0"
|
val launcherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.0"
|
||||||
val ivy = "org.scala-sbt.ivy" % "ivy" % "2.3.0-sbt-b18f59ea3bc914a297bb6f1a4f7fb0ace399e310"
|
val ivy = "org.scala-sbt.ivy" % "ivy" % "2.3.0-sbt-b18f59ea3bc914a297bb6f1a4f7fb0ace399e310"
|
||||||
val jsch = "com.jcraft" % "jsch" % "0.1.54"
|
val coursier = "io.get-coursier" %% "coursier" % coursierVersion
|
||||||
|
val coursierCache = "io.get-coursier" %% "coursier-cache" % coursierVersion
|
||||||
|
|
||||||
|
val sbtV = "1.0"
|
||||||
|
val scalaV = "2.12"
|
||||||
|
|
||||||
|
val jsch = "com.jcraft" % "jsch" % "0.1.54" intransitive ()
|
||||||
val scalaReflect = Def.setting { "org.scala-lang" % "scala-reflect" % scalaVersion.value }
|
val scalaReflect = Def.setting { "org.scala-lang" % "scala-reflect" % scalaVersion.value }
|
||||||
val scalaCompiler = Def.setting { "org.scala-lang" % "scala-compiler" % scalaVersion.value }
|
val scalaCompiler = Def.setting { "org.scala-lang" % "scala-compiler" % scalaVersion.value }
|
||||||
val scalaXml = scala211Module("scala-xml", "1.0.5")
|
val scalaXml = scala211Module("scala-xml", "1.0.5")
|
||||||
|
|
|
||||||
|
|
@ -85,15 +85,17 @@ object SbtScriptedIT extends AutoPlugin {
|
||||||
scriptedTests := {
|
scriptedTests := {
|
||||||
val targetDir = target.value / "sbt"
|
val targetDir = target.value / "sbt"
|
||||||
|
|
||||||
cloneSbt(targetDir, scriptedTestSbtRepo.value, scriptedTestSbtRef.value)
|
if (!targetDir.exists) {
|
||||||
|
cloneSbt(targetDir, scriptedTestSbtRepo.value, scriptedTestSbtRef.value)
|
||||||
|
|
||||||
publishLocalSbt(
|
publishLocalSbt(
|
||||||
targetDir,
|
targetDir,
|
||||||
version.value,
|
version.value,
|
||||||
organization.value,
|
organization.value,
|
||||||
s"librarymanagement-${scriptedTestLMImpl.value}",
|
s"librarymanagement-${scriptedTestLMImpl.value}",
|
||||||
scriptedSbtVersion.value
|
scriptedSbtVersion.value
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
setScriptedTestsSbtVersion(
|
setScriptedTestsSbtVersion(
|
||||||
sbtTestDirectory.value / thisProject.value.id,
|
sbtTestDirectory.value / thisProject.value.id,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
lazy val root = (project in file(".")).aggregate(parent, child)
|
||||||
|
lazy val parent = project
|
||||||
|
lazy val child = project.dependsOn(parent)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
class Bar extends Foo
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
class Foo
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
> compile
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
object Main {
|
||||||
|
|
||||||
|
println("hello, world!")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
|
||||||
|
{
|
||||||
|
def writePluginsSbt(str: String) = {
|
||||||
|
val pluginsSbt = file(".") / "project" / "plugins.sbt"
|
||||||
|
if (!pluginsSbt.exists)
|
||||||
|
IO.write(
|
||||||
|
pluginsSbt,
|
||||||
|
s"""$str
|
||||||
|
|addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.8")
|
||||||
|
|""".stripMargin
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val dr = sys.props.get("dependency.resolution") match {
|
||||||
|
case Some("ivy") =>
|
||||||
|
"""dependencyResolution := sbt.librarymanagement.ivy.IvyDependencyResolution(ivyConfiguration.value)"""
|
||||||
|
case Some("coursier") =>
|
||||||
|
"""dependencyResolution := sbt.librarymanagement.coursier.CoursierDependencyResolution(sbt.librarymanagement.coursier.CoursierConfiguration())"""
|
||||||
|
case _ => sys.error("""|The system property 'dependency.resolution' is not defined.
|
||||||
|
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
|
||||||
|
}
|
||||||
|
|
||||||
|
writePluginsSbt(dr)
|
||||||
|
addCommandAlias(
|
||||||
|
"setDependencyResolution",
|
||||||
|
s"""set $dr"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
> reload
|
||||||
|
> assembly
|
||||||
|
|
@ -1,8 +1,19 @@
|
||||||
|
|
||||||
addCommandAlias("setDependencyResolution", sys.props.get("dependency.resolution") match {
|
sys.props.get("dependency.resolution") match {
|
||||||
case Some(x) => x
|
case Some("ivy") =>
|
||||||
|
addCommandAlias(
|
||||||
|
"setDependencyResolution",
|
||||||
|
"""set dependencyResolution := sbt.librarymanagement.ivy.IvyDependencyResolution(ivyConfiguration.value)"""
|
||||||
|
)
|
||||||
|
case Some("coursier") =>
|
||||||
|
addCommandAlias(
|
||||||
|
"setDependencyResolution",
|
||||||
|
"""set dependencyResolution := sbt.librarymanagement.coursier.CoursierDependencyResolution(sbt.librarymanagement.coursier.CoursierConfiguration())"""
|
||||||
|
)
|
||||||
case _ => sys.error("""|The system property 'dependency.resolution' is not defined.
|
case _ => sys.error("""|The system property 'dependency.resolution' is not defined.
|
||||||
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
|
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
|
||||||
})
|
}
|
||||||
|
|
||||||
libraryDependencies += "com.typesafe" % "config" % "1.3.2"
|
libraryDependencies ++= Seq(
|
||||||
|
"com.typesafe" % "config" % "1.3.3"
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1 @@
|
||||||
> sbtVersion
|
|
||||||
> setDependencyResolution
|
|
||||||
> show ThisBuild/dependencyResolution
|
|
||||||
> clean
|
|
||||||
> compile
|
> compile
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
{
|
||||||
|
def writePluginsSbt(str: String) = {
|
||||||
|
val pluginsSbt = file(".") / "project" / "plugins.sbt"
|
||||||
|
if (!pluginsSbt.exists)
|
||||||
|
IO.write(
|
||||||
|
pluginsSbt,
|
||||||
|
str
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val dr = sys.props.get("dependency.resolution") match {
|
||||||
|
case Some("ivy") =>
|
||||||
|
"""dependencyResolution := sbt.librarymanagement.ivy.IvyDependencyResolution(ivyConfiguration.value)"""
|
||||||
|
case Some("coursier") =>
|
||||||
|
"""dependencyResolution := sbt.librarymanagement.coursier.CoursierDependencyResolution(sbt.librarymanagement.coursier.CoursierConfiguration())"""
|
||||||
|
case _ => sys.error("""|The system property 'dependency.resolution' is not defined.
|
||||||
|
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
|
||||||
|
}
|
||||||
|
|
||||||
|
writePluginsSbt(dr)
|
||||||
|
addCommandAlias(
|
||||||
|
"setDependencyResolution",
|
||||||
|
s"""set $dr"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import sbt._
|
||||||
|
import Keys._
|
||||||
|
|
||||||
|
object Dependencies {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
> reload
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
object Main {
|
||||||
|
|
||||||
|
import akka.actor._
|
||||||
|
|
||||||
|
val system = ActorSystem()
|
||||||
|
|
||||||
|
system.terminate()
|
||||||
|
|
||||||
|
import com.typesafe.config.ConfigFactory
|
||||||
|
|
||||||
|
val x = ConfigFactory.load()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
sys.props.get("dependency.resolution") match {
|
||||||
|
case Some("ivy") =>
|
||||||
|
addCommandAlias(
|
||||||
|
"setDependencyResolution",
|
||||||
|
"""set dependencyResolution := sbt.librarymanagement.ivy.IvyDependencyResolution(ivyConfiguration.value)"""
|
||||||
|
)
|
||||||
|
case Some("coursier") =>
|
||||||
|
addCommandAlias(
|
||||||
|
"setDependencyResolution",
|
||||||
|
"""set dependencyResolution := sbt.librarymanagement.coursier.CoursierDependencyResolution(sbt.librarymanagement.coursier.CoursierConfiguration())"""
|
||||||
|
)
|
||||||
|
case _ => sys.error("""|The system property 'dependency.resolution' is not defined.
|
||||||
|
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
|
||||||
|
}
|
||||||
|
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
"com.typesafe.akka" %% "akka-actor" % "2.5.17"
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
> compile
|
||||||
Loading…
Reference in New Issue