mirror of https://github.com/sbt/sbt.git
Refactor sbt-coursier, move bits of it to new lm-coursier module (#6)
This commit is contained in:
parent
58e8dae7e3
commit
af041251c1
20
build.sbt
20
build.sbt
|
|
@ -17,37 +17,33 @@ inThisBuild(List(
|
|||
|
||||
val coursierVersion = "1.1.0-M8"
|
||||
|
||||
lazy val `sbt-shared` = project
|
||||
.in(file("modules/sbt-shared"))
|
||||
lazy val `lm-coursier` = project
|
||||
.in(file("modules/lm-coursier"))
|
||||
.settings(
|
||||
shared,
|
||||
libraryDependencies ++= Seq(
|
||||
"io.get-coursier" %% "coursier" % coursierVersion,
|
||||
"io.get-coursier" %% "coursier-cache" % coursierVersion,
|
||||
"org.scala-sbt" %% "librarymanagement-ivy" % "1.0.2"
|
||||
"io.get-coursier" %% "coursier-extra" % coursierVersion,
|
||||
"org.scala-sbt" %% "librarymanagement-core" % "1.0.2"
|
||||
)
|
||||
)
|
||||
|
||||
lazy val `sbt-coursier` = project
|
||||
.in(file("modules/sbt-coursier"))
|
||||
.enablePlugins(ScriptedPlugin)
|
||||
.dependsOn(`sbt-shared`)
|
||||
.dependsOn(`lm-coursier`)
|
||||
.settings(
|
||||
plugin,
|
||||
libraryDependencies += "com.lihaoyi" %% "utest" % "0.6.4" % Test,
|
||||
testFrameworks += new TestFramework("utest.runner.Framework"),
|
||||
libraryDependencies ++= Seq(
|
||||
"io.get-coursier" %% "coursier" % coursierVersion,
|
||||
"io.get-coursier" %% "coursier-cache" % coursierVersion,
|
||||
"io.get-coursier" %% "coursier-extra" % coursierVersion,
|
||||
"io.get-coursier" %% "coursier-scalaz-interop" % coursierVersion
|
||||
),
|
||||
libraryDependencies +="io.get-coursier" %% "coursier-scalaz-interop" % coursierVersion,
|
||||
scriptedDependencies := {
|
||||
scriptedDependencies.value
|
||||
|
||||
// TODO Get dependency projects automatically
|
||||
// (but shouldn't scripted itself handle that…?)
|
||||
publishLocal.in(`sbt-shared`).value
|
||||
publishLocal.in(`lm-coursier`).value
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -95,7 +91,7 @@ lazy val `sbt-shading` = project
|
|||
lazy val `sbt-coursier-root` = project
|
||||
.in(file("."))
|
||||
.aggregate(
|
||||
`sbt-shared`,
|
||||
`lm-coursier`,
|
||||
`sbt-coursier`,
|
||||
`sbt-pgp-coursier`,
|
||||
`sbt-shading`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import java.io.File
|
||||
|
||||
import coursier.{Cache, CachePolicy}
|
||||
import coursier.core.{Classifier, Resolution}
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
final case class ArtifactsParams(
|
||||
classifiers: Option[Seq[Classifier]],
|
||||
res: Seq[Resolution],
|
||||
includeSignatures: Boolean,
|
||||
parallelDownloads: Int,
|
||||
createLogger: () => Cache.Logger,
|
||||
cache: File,
|
||||
artifactsChecksums: Seq[Option[String]],
|
||||
ttl: Option[Duration],
|
||||
cachePolicies: Seq[CachePolicy],
|
||||
projectName: String,
|
||||
sbtClassifiers: Boolean
|
||||
)
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import java.io.File
|
||||
import java.util.concurrent.ExecutorService
|
||||
|
||||
import coursier.{Artifact, Cache, CachePolicy, FileError}
|
||||
import coursier.util.{Schedulable, Task}
|
||||
import sbt.util.Logger
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
object ArtifactsRun {
|
||||
|
||||
def artifacts(
|
||||
params: ArtifactsParams,
|
||||
verbosityLevel: Int,
|
||||
log: Logger
|
||||
): Either[ResolutionError.UnknownDownloadException, Map[Artifact, Either[FileError, File]]] = {
|
||||
|
||||
val allArtifacts0 = params.res.flatMap(_.dependencyArtifacts(params.classifiers)).map(_._3)
|
||||
|
||||
val allArtifacts =
|
||||
if (params.includeSignatures)
|
||||
allArtifacts0.flatMap { a =>
|
||||
val sigOpt = a.extra.get("sig")
|
||||
Seq(a) ++ sigOpt.toSeq
|
||||
}
|
||||
else
|
||||
allArtifacts0
|
||||
|
||||
// let's update only one module at once, for a better output
|
||||
// Downloads are already parallel, no need to parallelize further anyway
|
||||
Lock.lock.synchronized {
|
||||
|
||||
var pool: ExecutorService = null
|
||||
var artifactsLogger: Cache.Logger = null
|
||||
|
||||
val printOptionalMessage = verbosityLevel >= 0 && verbosityLevel <= 1
|
||||
|
||||
val artifactFilesOrErrors = try {
|
||||
pool = Schedulable.fixedThreadPool(params.parallelDownloads)
|
||||
artifactsLogger = params.createLogger()
|
||||
|
||||
val artifactFileOrErrorTasks = allArtifacts.toVector.distinct.map { a =>
|
||||
def f(p: CachePolicy) =
|
||||
Cache.file[Task](
|
||||
a,
|
||||
params.cache,
|
||||
p,
|
||||
checksums = params.artifactsChecksums,
|
||||
logger = Some(artifactsLogger),
|
||||
pool = pool,
|
||||
ttl = params.ttl
|
||||
)
|
||||
|
||||
params.cachePolicies.tail
|
||||
.foldLeft(f(params.cachePolicies.head))(_ orElse f(_))
|
||||
.run
|
||||
.map((a, _))
|
||||
}
|
||||
|
||||
val artifactInitialMessage =
|
||||
if (verbosityLevel >= 0)
|
||||
s"Fetching artifacts of ${params.projectName}" +
|
||||
(if (params.sbtClassifiers) " (sbt classifiers)" else "")
|
||||
else
|
||||
""
|
||||
|
||||
if (verbosityLevel >= 2)
|
||||
log.info(artifactInitialMessage)
|
||||
|
||||
artifactsLogger.init(if (printOptionalMessage) log.info(artifactInitialMessage))
|
||||
|
||||
Task.gather.gather(artifactFileOrErrorTasks).attempt.unsafeRun()(ExecutionContext.fromExecutorService(pool)) match {
|
||||
case Left(ex) =>
|
||||
Left(ResolutionError.UnknownDownloadException(ex))
|
||||
case Right(l) =>
|
||||
Right(l.toMap)
|
||||
}
|
||||
} finally {
|
||||
if (pool != null)
|
||||
pool.shutdown()
|
||||
if (artifactsLogger != null)
|
||||
if ((artifactsLogger.stopDidPrintSomething() && printOptionalMessage) || verbosityLevel >= 2)
|
||||
log.info(
|
||||
s"Fetched artifacts of ${params.projectName}" +
|
||||
(if (params.sbtClassifiers) " (sbt classifiers)" else "")
|
||||
)
|
||||
}
|
||||
|
||||
artifactFilesOrErrors
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
package coursier
|
||||
package coursier.sbtcoursier
|
||||
|
||||
import coursier.ivy.IvyRepository
|
||||
import coursier.ivy.IvyXml.{mappings => ivyXmlMappings}
|
||||
import java.net.{MalformedURLException, URL}
|
||||
|
||||
import coursier.core.{Authentication, Classifier, Configuration, Type}
|
||||
import coursier.{Attributes, Cache, Dependency, Module}
|
||||
import coursier.core._
|
||||
import coursier.maven.MavenRepository
|
||||
import sbt.internal.librarymanagement.mavenint.SbtPomExtraProperties
|
||||
import sbt.librarymanagement.{CrossVersion, FileRepository, GetClassifiersModule, ModuleID, Patterns, RawRepository, Resolver, URLRepository}
|
||||
import sbt.librarymanagement.{Configuration => _, MavenRepository => _, _}
|
||||
import sbt.util.Logger
|
||||
|
||||
object FromSbt {
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import coursier.core.{Configuration, ModuleName, Organization, Project}
|
||||
import sbt.librarymanagement.{InclExclRule, ModuleID}
|
||||
import sbt.util.Logger
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
object Inputs {
|
||||
|
||||
def configExtends(configurations: Seq[sbt.librarymanagement.Configuration]): Map[Configuration, Seq[Configuration]] =
|
||||
configurations
|
||||
.map(cfg => Configuration(cfg.name) -> cfg.extendsConfigs.map(c => Configuration(c.name)))
|
||||
.toMap
|
||||
|
||||
def coursierConfigurations(
|
||||
configurations: Seq[sbt.librarymanagement.Configuration],
|
||||
shadedConfig: Option[(String, Configuration)] = None
|
||||
): Map[Configuration, Set[Configuration]] = {
|
||||
|
||||
val configs0 = Inputs.configExtends(configurations)
|
||||
|
||||
def allExtends(c: Configuration) = {
|
||||
// possibly bad complexity
|
||||
def helper(current: Set[Configuration]): Set[Configuration] = {
|
||||
val newSet = current ++ current.flatMap(configs0.getOrElse(_, Nil))
|
||||
if ((newSet -- current).nonEmpty)
|
||||
helper(newSet)
|
||||
else
|
||||
newSet
|
||||
}
|
||||
|
||||
helper(Set(c))
|
||||
}
|
||||
|
||||
val map = configs0.map {
|
||||
case (config, _) =>
|
||||
config -> allExtends(config)
|
||||
}
|
||||
|
||||
map ++ shadedConfig.toSeq.flatMap {
|
||||
case (baseConfig, shadedConfig) =>
|
||||
val baseConfig0 = Configuration(baseConfig)
|
||||
Seq(
|
||||
baseConfig0 -> (map.getOrElse(baseConfig0, Set(baseConfig0)) + shadedConfig),
|
||||
shadedConfig -> map.getOrElse(shadedConfig, Set(shadedConfig))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def ivyGraphs(configurations: Map[Configuration, Seq[Configuration]]): Seq[Set[Configuration]] = {
|
||||
|
||||
// probably bad complexity, but that shouldn't matter given the size of the graphs involved...
|
||||
|
||||
final class Wrapper(val set: mutable.HashSet[Configuration]) {
|
||||
def ++=(other: Wrapper): this.type = {
|
||||
set ++= other.set
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
val sets =
|
||||
new mutable.HashMap[Configuration, Wrapper] ++= configurations.map {
|
||||
case (k, l) =>
|
||||
val s = new mutable.HashSet[Configuration]
|
||||
s ++= l
|
||||
s += k
|
||||
k -> new Wrapper(s)
|
||||
}
|
||||
|
||||
for (k <- configurations.keys) {
|
||||
val s = sets(k)
|
||||
|
||||
var foundNew = true
|
||||
while (foundNew) {
|
||||
foundNew = false
|
||||
for (other <- s.set.toVector) {
|
||||
val otherS = sets(other)
|
||||
if (!otherS.eq(s)) {
|
||||
s ++= otherS
|
||||
sets += other -> s
|
||||
foundNew = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sets.values.toVector.distinct.map(_.set.toSet)
|
||||
}
|
||||
|
||||
def coursierProject(
|
||||
projId: ModuleID,
|
||||
dependencies: Seq[ModuleID],
|
||||
excludeDeps: Seq[InclExclRule],
|
||||
configurations: Seq[sbt.librarymanagement.Configuration],
|
||||
sv: String,
|
||||
sbv: String,
|
||||
log: Logger
|
||||
): Project = {
|
||||
|
||||
val exclusions = {
|
||||
|
||||
var anyNonSupportedExclusionRule = false
|
||||
|
||||
val res = excludeDeps
|
||||
.flatMap { rule =>
|
||||
if (rule.artifact != "*" || rule.configurations.nonEmpty) {
|
||||
log.warn(s"Unsupported exclusion rule $rule")
|
||||
anyNonSupportedExclusionRule = true
|
||||
Nil
|
||||
} else
|
||||
Seq(
|
||||
(Organization(rule.organization), ModuleName(FromSbt.sbtCrossVersionName(rule.name, rule.crossVersion, sv, sbv)))
|
||||
)
|
||||
}
|
||||
.toSet
|
||||
|
||||
if (anyNonSupportedExclusionRule)
|
||||
log.warn("Only supported exclusion rule fields: organization, name")
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
val configMap = configExtends(configurations)
|
||||
|
||||
val proj = FromSbt.project(
|
||||
projId,
|
||||
dependencies,
|
||||
configMap,
|
||||
sv,
|
||||
sbv
|
||||
)
|
||||
|
||||
proj.copy(
|
||||
dependencies = proj.dependencies.map {
|
||||
case (config, dep) =>
|
||||
(config, dep.copy(exclusions = dep.exclusions ++ exclusions))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package coursier
|
||||
package coursier.sbtcoursier
|
||||
|
||||
import coursier.core.Classifier
|
||||
import coursier.Fetch
|
||||
import coursier.core._
|
||||
import coursier.util.{EitherT, Monad}
|
||||
|
||||
final case class InterProjectRepository(projects: Seq[Project]) extends Repository {
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
object Lock {
|
||||
|
||||
// Wrap blocks downloading stuff (resolution / artifact downloads) in lock.synchronized.
|
||||
// Downloads are already parallel, no need to parallelize further, and this results in
|
||||
// a clearer output.
|
||||
val lock = new Object
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
package coursier
|
||||
package coursier.sbtcoursier
|
||||
|
||||
import coursier.FileError
|
||||
import coursier.core.Module
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package coursier
|
||||
package coursier.sbtcoursier
|
||||
|
||||
final class ResolutionException(
|
||||
val error: ResolutionError
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
|
||||
import coursier.{Cache, CachePolicy, FallbackDependenciesRepository, ProjectCache, Resolution, moduleNameString}
|
||||
import coursier.core._
|
||||
import coursier.extra.Typelevel
|
||||
import coursier.ivy.PropertiesPattern
|
||||
import sbt.librarymanagement.{Resolver, URLRepository}
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
final case class ResolutionParams(
|
||||
dependencies: Seq[(Configuration, Dependency)],
|
||||
fallbackDependencies: Seq[(Module, String, URL, Boolean)],
|
||||
configGraphs: Seq[Set[Configuration]],
|
||||
autoScalaLib: Boolean,
|
||||
mainRepositories: Seq[Repository],
|
||||
parentProjectCache: ProjectCache,
|
||||
interProjectDependencies: Seq[Project],
|
||||
internalRepositories: Seq[Repository],
|
||||
userEnabledProfiles: Set[String],
|
||||
userForceVersions: Map[Module, String],
|
||||
typelevel: Boolean,
|
||||
so: Organization,
|
||||
sv: String,
|
||||
sbtClassifiers: Boolean,
|
||||
parallelDownloads: Int,
|
||||
projectName: String,
|
||||
maxIterations: Int,
|
||||
createLogger: () => Cache.Logger,
|
||||
cache: File,
|
||||
cachePolicies: Seq[CachePolicy],
|
||||
ttl: Option[Duration],
|
||||
checksums: Seq[Option[String]]
|
||||
) {
|
||||
|
||||
val fallbackDependenciesRepositories =
|
||||
if (fallbackDependencies.isEmpty)
|
||||
Nil
|
||||
else {
|
||||
val map = fallbackDependencies.map {
|
||||
case (mod, ver, url, changing) =>
|
||||
(mod, ver) -> ((url, changing))
|
||||
}.toMap
|
||||
|
||||
Seq(
|
||||
FallbackDependenciesRepository(map)
|
||||
)
|
||||
}
|
||||
|
||||
val repositories =
|
||||
internalRepositories ++
|
||||
mainRepositories ++
|
||||
fallbackDependenciesRepositories
|
||||
|
||||
private val noOptionalFilter: Option[Dependency => Boolean] = Some(dep => !dep.optional)
|
||||
private val typelevelOrgSwap: Option[Dependency => Dependency] = Some(Typelevel.swap(_))
|
||||
|
||||
private def forcedScalaModules(
|
||||
scalaOrganization: Organization,
|
||||
scalaVersion: String
|
||||
): Map[Module, String] =
|
||||
Map(
|
||||
Module(scalaOrganization, name"scala-library", Map.empty) -> scalaVersion,
|
||||
Module(scalaOrganization, name"scala-compiler", Map.empty) -> scalaVersion,
|
||||
Module(scalaOrganization, name"scala-reflect", Map.empty) -> scalaVersion,
|
||||
Module(scalaOrganization, name"scalap", Map.empty) -> scalaVersion
|
||||
)
|
||||
|
||||
private def startRes(configs: Set[Configuration]) = Resolution(
|
||||
dependencies
|
||||
.collect {
|
||||
case (config, dep) if configs(config) =>
|
||||
dep
|
||||
}
|
||||
.toSet,
|
||||
filter = noOptionalFilter,
|
||||
userActivations =
|
||||
if (userEnabledProfiles.isEmpty)
|
||||
None
|
||||
else
|
||||
Some(userEnabledProfiles.iterator.map(_ -> true).toMap),
|
||||
forceVersions =
|
||||
// order matters here
|
||||
userForceVersions ++
|
||||
(if (autoScalaLib && (configs(Configuration.compile) || configs(Configuration("scala-tool")))) forcedScalaModules(so, sv) else Map()) ++
|
||||
interProjectDependencies.map(_.moduleVersion),
|
||||
projectCache = parentProjectCache,
|
||||
mapDependencies = if (typelevel && (configs(Configuration.compile) || configs(Configuration("scala-tool")))) typelevelOrgSwap else None
|
||||
)
|
||||
|
||||
lazy val allStartRes = configGraphs.map(configs => configs -> startRes(configs)).toMap
|
||||
|
||||
lazy val resolutionKey = SbtCoursierCache.ResolutionKey(
|
||||
dependencies,
|
||||
repositories,
|
||||
userEnabledProfiles,
|
||||
allStartRes,
|
||||
sbtClassifiers
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
object ResolutionParams {
|
||||
|
||||
def defaultIvyProperties(): Map[String, String] = {
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
Map(
|
||||
"ivy.home" -> ivyHome,
|
||||
"sbt.ivy.home" -> sbtIvyHome
|
||||
) ++ sys.props
|
||||
}
|
||||
|
||||
private def exceptionPatternParser(): String => coursier.ivy.Pattern = {
|
||||
|
||||
val props = sys.props.toMap
|
||||
|
||||
val extraProps = new ArrayBuffer[(String, String)]
|
||||
|
||||
def addUriProp(key: String): Unit =
|
||||
for (b <- props.get(key)) {
|
||||
val uri = new File(b).toURI.toString
|
||||
extraProps += s"$key.uri" -> uri
|
||||
}
|
||||
|
||||
addUriProp("sbt.global.base")
|
||||
addUriProp("user.home")
|
||||
|
||||
{
|
||||
s =>
|
||||
val p = PropertiesPattern.parse(s) match {
|
||||
case Left(err) =>
|
||||
throw new Exception(s"Cannot parse pattern $s: $err")
|
||||
case Right(p) =>
|
||||
p
|
||||
}
|
||||
|
||||
p.substituteProperties(props ++ extraProps) match {
|
||||
case Left(err) =>
|
||||
throw new Exception(err)
|
||||
case Right(p) =>
|
||||
p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def globalPluginPatterns(sbtVersion: String): Seq[coursier.ivy.Pattern] = {
|
||||
|
||||
val defaultRawPattern = s"$${sbt.global.base.uri-$${user.home.uri}/.sbt/$sbtVersion}/plugins/target" +
|
||||
"/resolution-cache/" +
|
||||
"[organization]/[module](/scala_[scalaVersion])(/sbt_[sbtVersion])/[revision]/resolved.xml.[ext]"
|
||||
|
||||
// seems to be required in more recent versions of sbt (since 0.13.16?)
|
||||
val extraRawPattern = s"$${sbt.global.base.uri-$${user.home.uri}/.sbt/$sbtVersion}/plugins/target" +
|
||||
"(/scala-[scalaVersion])(/sbt-[sbtVersion])" +
|
||||
"/resolution-cache/" +
|
||||
"[organization]/[module](/scala_[scalaVersion])(/sbt_[sbtVersion])/[revision]/resolved.xml.[ext]"
|
||||
|
||||
val p = exceptionPatternParser()
|
||||
|
||||
Seq(
|
||||
defaultRawPattern,
|
||||
extraRawPattern
|
||||
).map(p)
|
||||
}
|
||||
|
||||
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: sbt.librarymanagement.MavenRepository =>
|
||||
Some(m.root)
|
||||
case u: URLRepository =>
|
||||
u.patterns.artifactPatterns.headOption
|
||||
.orElse(u.patterns.ivyPatterns.headOption)
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
|
||||
private def fastRepo(res: Resolver): Boolean =
|
||||
url(res).exists(u => fastReposBase.exists(u.startsWith))
|
||||
|
||||
private def slowRepo(res: Resolver): Boolean =
|
||||
url(res).exists(u => slowReposBase.exists(u.startsWith))
|
||||
|
||||
def reorderResolvers(resolvers: Seq[Resolver]): Seq[Resolver] =
|
||||
if (resolvers.exists(fastRepo) && resolvers.exists(slowRepo)) {
|
||||
val (slow, other) = resolvers.partition(slowRepo)
|
||||
other ++ slow
|
||||
} else
|
||||
resolvers
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import java.util.concurrent.ExecutorService
|
||||
|
||||
import coursier.{Cache, Fetch, Resolution}
|
||||
import coursier.core._
|
||||
import coursier.ivy.IvyRepository
|
||||
import coursier.maven.MavenRepository
|
||||
import coursier.util.{Print, Schedulable, Task}
|
||||
import sbt.util.Logger
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
object ResolutionRun {
|
||||
|
||||
def resolution(
|
||||
params: ResolutionParams,
|
||||
verbosityLevel: Int,
|
||||
log: Logger,
|
||||
startRes: Resolution
|
||||
): Either[ResolutionError, Resolution] = {
|
||||
|
||||
// TODO Re-use the thread pool across resolutions / downloads?
|
||||
var pool: ExecutorService = null
|
||||
|
||||
var resLogger: Cache.Logger = null
|
||||
|
||||
val printOptionalMessage = verbosityLevel >= 0 && verbosityLevel <= 1
|
||||
|
||||
val resOrError: Either[ResolutionError, Resolution] = try {
|
||||
pool = Schedulable.fixedThreadPool(params.parallelDownloads)
|
||||
resLogger = params.createLogger()
|
||||
|
||||
val fetch = Fetch.from(
|
||||
params.repositories,
|
||||
Cache.fetch[Task](params.cache, params.cachePolicies.head, checksums = params.checksums, logger = Some(resLogger), pool = pool, ttl = params.ttl),
|
||||
params.cachePolicies.tail.map(p =>
|
||||
Cache.fetch[Task](params.cache, p, checksums = params.checksums, logger = Some(resLogger), pool = pool, ttl = params.ttl)
|
||||
): _*
|
||||
)
|
||||
|
||||
def depsRepr(deps: Seq[(Configuration, Dependency)]) =
|
||||
deps.map { case (config, dep) =>
|
||||
s"${dep.module}:${dep.version}:${config.value}->${dep.configuration.value}"
|
||||
}.sorted.distinct
|
||||
|
||||
if (verbosityLevel >= 2) {
|
||||
val repoReprs = params.repositories.map {
|
||||
case r: IvyRepository =>
|
||||
s"ivy:${r.pattern}"
|
||||
case _: InterProjectRepository =>
|
||||
"inter-project"
|
||||
case r: MavenRepository =>
|
||||
r.root
|
||||
case r =>
|
||||
// should not happen
|
||||
r.toString
|
||||
}
|
||||
|
||||
log.info(
|
||||
"Repositories:\n" +
|
||||
repoReprs.map(" " + _).mkString("\n")
|
||||
)
|
||||
}
|
||||
|
||||
val initialMessage =
|
||||
Seq(
|
||||
if (verbosityLevel >= 0)
|
||||
Seq(s"Updating ${params.projectName}" + (if (params.sbtClassifiers) " (sbt classifiers)" else ""))
|
||||
else
|
||||
Nil,
|
||||
if (verbosityLevel >= 2)
|
||||
depsRepr(params.dependencies).map(depRepr =>
|
||||
s" $depRepr"
|
||||
)
|
||||
else
|
||||
Nil
|
||||
).flatten.mkString("\n")
|
||||
|
||||
if (verbosityLevel >= 2)
|
||||
log.info(initialMessage)
|
||||
|
||||
resLogger.init(if (printOptionalMessage) log.info(initialMessage))
|
||||
|
||||
startRes
|
||||
.process
|
||||
.run(fetch, params.maxIterations)
|
||||
.attempt
|
||||
.unsafeRun()(ExecutionContext.fromExecutorService(pool))
|
||||
.left
|
||||
.map(ex =>
|
||||
ResolutionError.UnknownException(ex)
|
||||
)
|
||||
} finally {
|
||||
if (pool != null)
|
||||
pool.shutdown()
|
||||
if (resLogger != null)
|
||||
if ((resLogger.stopDidPrintSomething() && printOptionalMessage) || verbosityLevel >= 2)
|
||||
log.info(s"Resolved ${params.projectName} dependencies")
|
||||
}
|
||||
|
||||
resOrError.flatMap { res =>
|
||||
if (!res.isDone)
|
||||
Left(
|
||||
ResolutionError.MaximumIterationsReached
|
||||
)
|
||||
else if (res.conflicts.nonEmpty) {
|
||||
val projCache = res.projectCache.mapValues { case (_, p) => p }
|
||||
|
||||
Left(
|
||||
ResolutionError.Conflicts(
|
||||
"Conflict(s) in dependency resolution:\n " +
|
||||
Print.dependenciesUnknownConfigs(res.conflicts.toVector, projCache)
|
||||
)
|
||||
)
|
||||
} else if (res.errors.nonEmpty) {
|
||||
val internalRepositoriesLen = params.internalRepositories.length
|
||||
val errors =
|
||||
if (params.repositories.length > internalRepositoriesLen)
|
||||
// drop internal repository errors
|
||||
res.errors.map {
|
||||
case (dep, errs) =>
|
||||
dep -> errs.drop(internalRepositoriesLen)
|
||||
}
|
||||
else
|
||||
res.errors
|
||||
|
||||
Left(
|
||||
ResolutionError.MetadataDownloadErrors(errors)
|
||||
)
|
||||
} else
|
||||
Right(res)
|
||||
}
|
||||
}
|
||||
|
||||
def resolutions(
|
||||
params: ResolutionParams,
|
||||
verbosityLevel: Int,
|
||||
log: Logger
|
||||
): Either[ResolutionError, Map[Set[Configuration], Resolution]] = {
|
||||
|
||||
// TODO Warn about possible duplicated modules from source repositories?
|
||||
|
||||
if (verbosityLevel >= 2) {
|
||||
log.info("InterProjectRepository")
|
||||
for (p <- params.interProjectDependencies)
|
||||
log.info(s" ${p.module}:${p.version}")
|
||||
}
|
||||
|
||||
SbtCoursierCache.default.resolutionOpt(params.resolutionKey).map(Right(_)).getOrElse {
|
||||
// Let's update only one module at once, for a better output.
|
||||
// Downloads are already parallel, no need to parallelize further, anyway.
|
||||
val resOrError =
|
||||
Lock.lock.synchronized {
|
||||
params.allStartRes.foldLeft[Either[ResolutionError, Map[Set[Configuration], Resolution]]](Right(Map())) {
|
||||
case (acc, (config, startRes)) =>
|
||||
for {
|
||||
m <- acc
|
||||
res <- resolution(params, verbosityLevel, log, startRes)
|
||||
} yield m + (config -> res)
|
||||
}
|
||||
}
|
||||
for (res <- resOrError)
|
||||
SbtCoursierCache.default.putResolution(params.resolutionKey, res)
|
||||
resOrError
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package coursier
|
||||
package coursier.sbtcoursier
|
||||
|
||||
import java.io.File
|
||||
|
||||
import coursier.core.{Module, ModuleName, Organization}
|
||||
|
||||
object SbtBootJars {
|
||||
def apply(
|
||||
scalaOrg: Organization,
|
||||
|
|
@ -12,7 +14,7 @@ object SbtBootJars {
|
|||
.collect {
|
||||
case jar if jar.getName.endsWith(".jar") =>
|
||||
val name = ModuleName(jar.getName.stripSuffix(".jar"))
|
||||
val mod = Module(scalaOrg, name)
|
||||
val mod = Module(scalaOrg, name, Map.empty)
|
||||
|
||||
(mod, scalaVersion) -> jar
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
import coursier.core._
|
||||
import sbt.librarymanagement.UpdateReport
|
||||
|
||||
class SbtCoursierCache {
|
||||
|
||||
import SbtCoursierCache._
|
||||
|
||||
private val resolutionsCache = new ConcurrentHashMap[ResolutionKey, Map[Set[Configuration], Resolution]]
|
||||
// these may actually not need to be cached any more, now that the resolutions
|
||||
// are cached
|
||||
private val reportsCache = new ConcurrentHashMap[ReportKey, UpdateReport]
|
||||
|
||||
|
||||
def resolutionOpt(key: ResolutionKey): Option[Map[Set[Configuration], Resolution]] =
|
||||
Option(resolutionsCache.get(key))
|
||||
def putResolution(key: ResolutionKey, res: Map[Set[Configuration], Resolution]): Unit =
|
||||
resolutionsCache.put(key, res)
|
||||
|
||||
def reportOpt(key: ReportKey): Option[UpdateReport] =
|
||||
Option(reportsCache.get(key))
|
||||
def putReport(key: ReportKey, report: UpdateReport): Unit =
|
||||
reportsCache.put(key, report)
|
||||
|
||||
def clear(): Unit = {
|
||||
resolutionsCache.clear()
|
||||
reportsCache.clear()
|
||||
}
|
||||
|
||||
def isEmpty: Boolean =
|
||||
resolutionsCache.isEmpty && reportsCache.isEmpty
|
||||
|
||||
}
|
||||
|
||||
object SbtCoursierCache {
|
||||
|
||||
final case class ResolutionKey(
|
||||
dependencies: Seq[(Configuration, Dependency)],
|
||||
repositories: Seq[Repository],
|
||||
userEnabledProfiles: Set[String],
|
||||
resolution: Map[Set[Configuration], Resolution],
|
||||
sbtClassifiers: Boolean
|
||||
)
|
||||
|
||||
final case class ReportKey(
|
||||
dependencies: Seq[(Configuration, Dependency)],
|
||||
resolution: Map[Set[Configuration], Resolution],
|
||||
withClassifiers: Boolean,
|
||||
sbtClassifiers: Boolean,
|
||||
ignoreArtifactErrors: Boolean
|
||||
)
|
||||
|
||||
|
||||
private[coursier] val default = new SbtCoursierCache
|
||||
|
||||
}
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
package coursier
|
||||
package coursier.sbtcoursier
|
||||
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.util.GregorianCalendar
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
import coursier.{Artifact, Attributes, Dependency, Module, Project, Resolution}
|
||||
import coursier.core.{Classifier, Configuration, Type}
|
||||
import coursier.maven.MavenAttributes
|
||||
import sbt.librarymanagement.{Configuration => _, _}
|
||||
import sbt.librarymanagement.{Artifact => _, Configuration => _, _}
|
||||
import sbt.util.Logger
|
||||
|
||||
object ToSbt {
|
||||
|
|
@ -253,7 +254,7 @@ object ToSbt {
|
|||
}
|
||||
|
||||
UpdateReport(
|
||||
null,
|
||||
new File("."),
|
||||
configReports.toVector,
|
||||
UpdateStats(-1L, -1L, -1L, cached = false),
|
||||
Map.empty
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import java.io.File
|
||||
|
||||
import coursier.FileError
|
||||
import coursier.core._
|
||||
|
||||
final case class UpdateParams(
|
||||
shadedConfigOpt: Option[(String, Configuration)],
|
||||
artifacts: Map[Artifact, Either[FileError, File]],
|
||||
classifiers: Option[Seq[Classifier]],
|
||||
configs: Map[Configuration, Set[Configuration]],
|
||||
dependencies: Seq[(Configuration, Dependency)],
|
||||
res: Map[Set[Configuration], Resolution],
|
||||
ignoreArtifactErrors: Boolean,
|
||||
includeSignatures: Boolean,
|
||||
sbtBootJarOverrides: Map[(Module, String), File]
|
||||
)
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import java.io.File
|
||||
|
||||
import coursier.core.Resolution.ModuleVersion
|
||||
import coursier.core._
|
||||
import coursier.util.Print
|
||||
import sbt.librarymanagement.UpdateReport
|
||||
import sbt.util.Logger
|
||||
|
||||
object UpdateRun {
|
||||
|
||||
private def artifactFileOpt(
|
||||
sbtBootJarOverrides: Map[(Module, String), File],
|
||||
artifactFiles: Map[Artifact, File],
|
||||
erroredArtifacts: Set[Artifact]
|
||||
)(
|
||||
module: Module,
|
||||
version: String,
|
||||
attributes: Attributes,
|
||||
artifact: Artifact
|
||||
): Option[File] = {
|
||||
|
||||
// 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 (attributes.classifier.isEmpty && attributes.`type` == Type.jar)
|
||||
sbtBootJarOverrides.get((module, version))
|
||||
else
|
||||
None
|
||||
|
||||
val res = fromBootJars.orElse(artifactFiles.get(artifact))
|
||||
|
||||
if (res.isEmpty && !erroredArtifacts(artifact))
|
||||
sys.error(s"${artifact.url} not downloaded (should not happen)")
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
// Move back to coursier.util (in core module) after 1.0?
|
||||
private def allDependenciesByConfig(
|
||||
res: Map[Configuration, Resolution],
|
||||
depsByConfig: Map[Configuration, Set[Dependency]],
|
||||
configs: Map[Configuration, Set[Configuration]]
|
||||
): Map[Configuration, Set[Dependency]] = {
|
||||
|
||||
val allDepsByConfig = depsByConfig.map {
|
||||
case (config, deps) =>
|
||||
config -> res(config).subset(deps).minDependencies
|
||||
}
|
||||
|
||||
val filteredAllDepsByConfig = allDepsByConfig.map {
|
||||
case (config, allDeps) =>
|
||||
val allExtendedConfigs = configs.getOrElse(config, Set.empty) - config
|
||||
val inherited = allExtendedConfigs
|
||||
.flatMap(allDepsByConfig.getOrElse(_, Set.empty))
|
||||
|
||||
config -> (allDeps -- inherited)
|
||||
}
|
||||
|
||||
filteredAllDepsByConfig
|
||||
}
|
||||
|
||||
// Move back to coursier.util (in core module) after 1.0?
|
||||
private def dependenciesWithConfig(
|
||||
res: Map[Configuration, Resolution],
|
||||
depsByConfig: Map[Configuration, Set[Dependency]],
|
||||
configs: Map[Configuration, Set[Configuration]]
|
||||
): Set[Dependency] =
|
||||
allDependenciesByConfig(res, depsByConfig, configs)
|
||||
.flatMap {
|
||||
case (config, deps) =>
|
||||
deps.map(dep => dep.copy(configuration = config --> dep.configuration))
|
||||
}
|
||||
.groupBy(_.copy(configuration = Configuration.empty))
|
||||
.map {
|
||||
case (dep, l) =>
|
||||
dep.copy(configuration = Configuration.join(l.map(_.configuration).toSeq: _*))
|
||||
}
|
||||
.toSet
|
||||
|
||||
def update(
|
||||
params: UpdateParams,
|
||||
verbosityLevel: Int,
|
||||
log: Logger
|
||||
): Either[ResolutionError.DownloadErrors, UpdateReport] = {
|
||||
|
||||
val configResolutions = params.res.flatMap {
|
||||
case (configs, r) =>
|
||||
configs.iterator.map((_, r))
|
||||
}
|
||||
|
||||
val depsByConfig = grouped(params.dependencies)(
|
||||
config =>
|
||||
params.shadedConfigOpt match {
|
||||
case Some((baseConfig, `config`)) =>
|
||||
Configuration(baseConfig)
|
||||
case _ =>
|
||||
config
|
||||
}
|
||||
)
|
||||
|
||||
if (verbosityLevel >= 2) {
|
||||
val finalDeps = dependenciesWithConfig(
|
||||
configResolutions,
|
||||
depsByConfig.map { case (k, l) => k -> l.toSet },
|
||||
params.configs
|
||||
)
|
||||
|
||||
val projCache = params.res.values.foldLeft(Map.empty[ModuleVersion, Project])(_ ++ _.projectCache.mapValues(_._2))
|
||||
val repr = Print.dependenciesUnknownConfigs(finalDeps.toVector, projCache)
|
||||
log.info(repr.split('\n').map(" " + _).mkString("\n"))
|
||||
}
|
||||
|
||||
val artifactFiles = params.artifacts.collect {
|
||||
case (artifact, Right(file)) =>
|
||||
artifact -> file
|
||||
}
|
||||
|
||||
val artifactErrors = params
|
||||
.artifacts
|
||||
.toVector
|
||||
.collect {
|
||||
case (a, Left(err)) if !a.optional || !err.notFound =>
|
||||
a -> err
|
||||
}
|
||||
|
||||
// can be non empty only if ignoreArtifactErrors is true or some optional artifacts are not found
|
||||
val erroredArtifacts = params
|
||||
.artifacts
|
||||
.collect {
|
||||
case (artifact, Left(_)) =>
|
||||
artifact
|
||||
}
|
||||
.toSet
|
||||
|
||||
def report =
|
||||
ToSbt.updateReport(
|
||||
depsByConfig,
|
||||
configResolutions,
|
||||
params.configs,
|
||||
params.classifiers,
|
||||
artifactFileOpt(
|
||||
params.sbtBootJarOverrides,
|
||||
artifactFiles,
|
||||
erroredArtifacts
|
||||
),
|
||||
log,
|
||||
includeSignatures = params.includeSignatures
|
||||
)
|
||||
|
||||
if (artifactErrors.isEmpty)
|
||||
Right(report)
|
||||
else {
|
||||
val error = ResolutionError.DownloadErrors(artifactErrors.map(_._2))
|
||||
|
||||
if (params.ignoreArtifactErrors) {
|
||||
log.warn(error.description(verbosityLevel >= 1))
|
||||
Right(report)
|
||||
} else
|
||||
Left(error)
|
||||
}
|
||||
}
|
||||
|
||||
private def grouped[K, V](map: Seq[(K, V)])(mapKey: K => K): Map[K, Seq[V]] =
|
||||
map
|
||||
.groupBy(t => mapKey(t._1))
|
||||
.mapValues(_.map(_._2))
|
||||
.iterator
|
||||
.toMap
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,215 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import java.io.File
|
||||
|
||||
import coursier.{Artifact, FileError}
|
||||
import coursier.core._
|
||||
import coursier.sbtcoursier.Keys._
|
||||
import coursier.sbtcoursier.Structure._
|
||||
import sbt.librarymanagement.{Artifact => _, Configuration => _, _}
|
||||
import sbt.Def
|
||||
import sbt.Keys._
|
||||
|
||||
object ArtifactsTasks {
|
||||
|
||||
def coursierPublicationsTask(
|
||||
configsMap: (sbt.librarymanagement.Configuration, Configuration)*
|
||||
): Def.Initialize[sbt.Task[Seq[(Configuration, Publication)]]] =
|
||||
Def.task {
|
||||
|
||||
val state = sbt.Keys.state.value
|
||||
val projectRef = sbt.Keys.thisProjectRef.value
|
||||
val projId = sbt.Keys.projectID.value
|
||||
val sv = sbt.Keys.scalaVersion.value
|
||||
val sbv = sbt.Keys.scalaBinaryVersion.value
|
||||
val ivyConfs = sbt.Keys.ivyConfigurations.value
|
||||
|
||||
val sourcesConfigOpt =
|
||||
if (ivyConfigurations.value.exists(_.name == "sources"))
|
||||
Some(Configuration("sources"))
|
||||
else
|
||||
None
|
||||
|
||||
val docsConfigOpt =
|
||||
if (ivyConfigurations.value.exists(_.name == "docs"))
|
||||
Some(Configuration("docs"))
|
||||
else
|
||||
None
|
||||
|
||||
val sbtBinArtifacts =
|
||||
for ((config, targetConfig) <- configsMap) yield {
|
||||
|
||||
val publish = publishArtifact
|
||||
.in(projectRef)
|
||||
.in(packageBin)
|
||||
.in(config)
|
||||
.getOrElse(state, false)
|
||||
|
||||
if (publish)
|
||||
artifact
|
||||
.in(projectRef)
|
||||
.in(packageBin)
|
||||
.in(config)
|
||||
.find(state)
|
||||
.map(targetConfig -> _)
|
||||
else
|
||||
None
|
||||
}
|
||||
|
||||
val sbtSourceArtifacts =
|
||||
for ((config, targetConfig) <- configsMap) yield {
|
||||
|
||||
val publish = publishArtifact
|
||||
.in(projectRef)
|
||||
.in(packageSrc)
|
||||
.in(config)
|
||||
.getOrElse(state, false)
|
||||
|
||||
if (publish)
|
||||
artifact
|
||||
.in(projectRef)
|
||||
.in(packageSrc)
|
||||
.in(config)
|
||||
.find(state)
|
||||
.map(sourcesConfigOpt.getOrElse(targetConfig) -> _)
|
||||
else
|
||||
None
|
||||
}
|
||||
|
||||
val sbtDocArtifacts =
|
||||
for ((config, targetConfig) <- configsMap) yield {
|
||||
|
||||
val publish = publishArtifact
|
||||
.in(projectRef)
|
||||
.in(packageDoc)
|
||||
.in(config)
|
||||
.getOrElse(state, false)
|
||||
|
||||
if (publish)
|
||||
artifact
|
||||
.in(projectRef)
|
||||
.in(packageDoc)
|
||||
.in(config)
|
||||
.find(state)
|
||||
.map(docsConfigOpt.getOrElse(targetConfig) -> _)
|
||||
else
|
||||
None
|
||||
}
|
||||
|
||||
val sbtArtifacts = sbtBinArtifacts ++ sbtSourceArtifacts ++ sbtDocArtifacts
|
||||
|
||||
def artifactPublication(artifact: sbt.Artifact) = {
|
||||
|
||||
val name = FromSbt.sbtCrossVersionName(
|
||||
artifact.name,
|
||||
projId.crossVersion,
|
||||
sv,
|
||||
sbv
|
||||
)
|
||||
|
||||
Publication(
|
||||
name,
|
||||
Type(artifact.`type`),
|
||||
Extension(artifact.extension),
|
||||
artifact.classifier.fold(Classifier.empty)(Classifier(_))
|
||||
)
|
||||
}
|
||||
|
||||
val sbtArtifactsPublication = sbtArtifacts.collect {
|
||||
case Some((config, artifact)) =>
|
||||
config -> artifactPublication(artifact)
|
||||
}
|
||||
|
||||
val stdArtifactsSet = sbtArtifacts.flatMap(_.map { case (_, a) => a }.toSeq).toSet
|
||||
|
||||
// Second-way of getting artifacts from SBT
|
||||
// No obvious way of getting the corresponding publishArtifact value for the ones
|
||||
// only here, it seems.
|
||||
val extraSbtArtifacts = sbt.Keys.artifacts.in(projectRef).getOrElse(state, Nil)
|
||||
.filterNot(stdArtifactsSet)
|
||||
|
||||
// Seems that SBT does that - if an artifact has no configs,
|
||||
// it puts it in all of them. See for example what happens to
|
||||
// the standalone JAR artifact of the coursier cli module.
|
||||
def allConfigsIfEmpty(configs: Iterable[ConfigRef]): Iterable[ConfigRef] =
|
||||
if (configs.isEmpty) ivyConfs.filter(_.isPublic).map(c => ConfigRef(c.name)) else configs
|
||||
|
||||
val extraSbtArtifactsPublication = for {
|
||||
artifact <- extraSbtArtifacts
|
||||
config <- allConfigsIfEmpty(artifact.configurations.map(x => ConfigRef(x.name)))
|
||||
// FIXME If some configurations from artifact.configurations are not public, they may leak here :\
|
||||
} yield Configuration(config.name) -> artifactPublication(artifact)
|
||||
|
||||
sbtArtifactsPublication ++ extraSbtArtifactsPublication
|
||||
}
|
||||
|
||||
def artifactsTask(
|
||||
withClassifiers: Boolean,
|
||||
sbtClassifiers: Boolean = false,
|
||||
ignoreArtifactErrors: Boolean = false,
|
||||
includeSignatures: Boolean = false
|
||||
): Def.Initialize[sbt.Task[Map[Artifact, Either[FileError, File]]]] = {
|
||||
|
||||
val resTask: sbt.Def.Initialize[sbt.Task[Seq[Resolution]]] =
|
||||
if (withClassifiers && sbtClassifiers)
|
||||
Def.task(Seq(coursierSbtClassifiersResolution.value))
|
||||
else
|
||||
Def.task(coursierResolutions.value.values.toVector)
|
||||
|
||||
val classifiersTask: sbt.Def.Initialize[sbt.Task[Option[Seq[Classifier]]]] =
|
||||
if (withClassifiers) {
|
||||
if (sbtClassifiers)
|
||||
Def.task(Some(coursierSbtClassifiersModule.value.classifiers.map(Classifier(_))))
|
||||
else
|
||||
Def.task(Some(transitiveClassifiers.value.map(Classifier(_))))
|
||||
} else
|
||||
Def.task(None)
|
||||
|
||||
Def.task {
|
||||
|
||||
val projectName = thisProjectRef.value.project
|
||||
|
||||
val parallelDownloads = coursierParallelDownloads.value
|
||||
val artifactsChecksums = coursierArtifactsChecksums.value
|
||||
val cachePolicies = coursierCachePolicies.value
|
||||
val ttl = coursierTtl.value
|
||||
val cache = coursierCache.value
|
||||
val createLogger = coursierCreateLogger.value
|
||||
|
||||
val log = streams.value.log
|
||||
|
||||
val verbosityLevel = coursierVerbosity.value
|
||||
|
||||
val classifiers = classifiersTask.value
|
||||
val res = resTask.value
|
||||
|
||||
val params = ArtifactsParams(
|
||||
classifiers,
|
||||
res,
|
||||
includeSignatures,
|
||||
parallelDownloads,
|
||||
createLogger,
|
||||
cache,
|
||||
artifactsChecksums,
|
||||
ttl,
|
||||
cachePolicies,
|
||||
projectName,
|
||||
sbtClassifiers
|
||||
)
|
||||
|
||||
val resOrError = ArtifactsRun.artifacts(
|
||||
params,
|
||||
verbosityLevel,
|
||||
log
|
||||
)
|
||||
|
||||
resOrError match {
|
||||
case Left(err) =>
|
||||
err.throwException()
|
||||
case Right(res) =>
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
package coursier
|
||||
package coursier.sbtcoursier
|
||||
|
||||
import java.io.OutputStreamWriter
|
||||
|
||||
import coursier.core.Configuration
|
||||
import coursier.{Cache, CachePolicy, TermDisplay}
|
||||
import coursier.core.{Configuration, ResolutionProcess}
|
||||
import sbt.librarymanagement.{Configuration => _, Resolver => _, _}
|
||||
import sbt.{Configuration => _, _}
|
||||
import sbt.{Cache => _, Configuration => _, _}
|
||||
import sbt.Keys._
|
||||
|
||||
object CoursierPlugin extends AutoPlugin {
|
||||
|
|
@ -64,16 +65,16 @@ object CoursierPlugin extends AutoPlugin {
|
|||
import autoImport._
|
||||
|
||||
lazy val treeSettings = Seq(
|
||||
coursierDependencyTree := Tasks.coursierDependencyTreeTask(
|
||||
coursierDependencyTree := DisplayTasks.coursierDependencyTreeTask(
|
||||
inverse = false
|
||||
).value,
|
||||
coursierDependencyInverseTree := Tasks.coursierDependencyTreeTask(
|
||||
coursierDependencyInverseTree := DisplayTasks.coursierDependencyTreeTask(
|
||||
inverse = true
|
||||
).value,
|
||||
coursierWhatDependsOn := Def.inputTaskDyn {
|
||||
import sbt.complete.DefaultParsers._
|
||||
val input = token(SpaceClass ~ NotQuoted, "<arg>").parsed._2
|
||||
Tasks.coursierWhatDependsOnTask(input)
|
||||
DisplayTasks.coursierWhatDependsOnTask(input)
|
||||
}.evaluated
|
||||
)
|
||||
|
||||
|
|
@ -165,11 +166,10 @@ object CoursierPlugin extends AutoPlugin {
|
|||
) = hackHack ++ Seq(
|
||||
clean := {
|
||||
val noWarningPlz = clean.value
|
||||
Tasks.resolutionsCache.clear()
|
||||
Tasks.reportsCache.clear()
|
||||
SbtCoursierCache.default.clear()
|
||||
},
|
||||
coursierResolvers := Tasks.coursierResolversTask.value,
|
||||
coursierRecursiveResolvers := Tasks.coursierRecursiveResolversTask.value,
|
||||
coursierResolvers := RepositoriesTasks.coursierResolversTask.value,
|
||||
coursierRecursiveResolvers := RepositoriesTasks.coursierRecursiveResolversTask.value,
|
||||
coursierSbtResolvers := {
|
||||
|
||||
// TODO Add docker-based integration test for that, see https://github.com/coursier/coursier/issues/632
|
||||
|
|
@ -201,39 +201,39 @@ object CoursierPlugin extends AutoPlugin {
|
|||
!r.name.startsWith("local-preloaded")
|
||||
}
|
||||
},
|
||||
coursierFallbackDependencies := Tasks.coursierFallbackDependenciesTask.value,
|
||||
coursierArtifacts := Tasks.artifactFilesOrErrors(withClassifiers = false).value,
|
||||
coursierSignedArtifacts := Tasks.artifactFilesOrErrors(withClassifiers = false, includeSignatures = true).value,
|
||||
coursierClassifiersArtifacts := Tasks.artifactFilesOrErrors(
|
||||
coursierFallbackDependencies := InputsTasks.coursierFallbackDependenciesTask.value,
|
||||
coursierArtifacts := ArtifactsTasks.artifactsTask(withClassifiers = false).value,
|
||||
coursierSignedArtifacts := ArtifactsTasks.artifactsTask(withClassifiers = false, includeSignatures = true).value,
|
||||
coursierClassifiersArtifacts := ArtifactsTasks.artifactsTask(
|
||||
withClassifiers = true
|
||||
).value,
|
||||
coursierSbtClassifiersArtifacts := Tasks.artifactFilesOrErrors(
|
||||
coursierSbtClassifiersArtifacts := ArtifactsTasks.artifactsTask(
|
||||
withClassifiers = true,
|
||||
sbtClassifiers = true
|
||||
).value,
|
||||
update := Tasks.updateTask(
|
||||
update := UpdateTasks.updateTask(
|
||||
shadedConfigOpt,
|
||||
withClassifiers = false
|
||||
).value,
|
||||
updateClassifiers := Tasks.updateTask(
|
||||
updateClassifiers := UpdateTasks.updateTask(
|
||||
shadedConfigOpt,
|
||||
withClassifiers = true,
|
||||
ignoreArtifactErrors = true
|
||||
).value,
|
||||
updateSbtClassifiers.in(Defaults.TaskGlobal) := Tasks.updateTask(
|
||||
updateSbtClassifiers.in(Defaults.TaskGlobal) := UpdateTasks.updateTask(
|
||||
shadedConfigOpt,
|
||||
withClassifiers = true,
|
||||
sbtClassifiers = true,
|
||||
ignoreArtifactErrors = true
|
||||
).value,
|
||||
coursierProject := Tasks.coursierProjectTask.value,
|
||||
coursierConfigGraphs := Tasks.ivyGraphsTask.value,
|
||||
coursierInterProjectDependencies := Tasks.coursierInterProjectDependenciesTask.value,
|
||||
coursierPublications := Tasks.coursierPublicationsTask(packageConfigs: _*).value,
|
||||
coursierProject := InputsTasks.coursierProjectTask.value,
|
||||
coursierConfigGraphs := InputsTasks.ivyGraphsTask.value,
|
||||
coursierInterProjectDependencies := InputsTasks.coursierInterProjectDependenciesTask.value,
|
||||
coursierPublications := ArtifactsTasks.coursierPublicationsTask(packageConfigs: _*).value,
|
||||
coursierSbtClassifiersModule := classifiersModule.in(updateSbtClassifiers).value,
|
||||
coursierConfigurations := Tasks.coursierConfigurationsTask(None).value,
|
||||
coursierParentProjectCache := Tasks.parentProjectCacheTask.value,
|
||||
coursierResolutions := Tasks.resolutionsTask().value,
|
||||
coursierConfigurations := InputsTasks.coursierConfigurationsTask(None).value,
|
||||
coursierParentProjectCache := InputsTasks.parentProjectCacheTask.value,
|
||||
coursierResolutions := ResolutionTasks.resolutionsTask().value,
|
||||
Keys.actualCoursierResolution := {
|
||||
|
||||
val config = Configuration(Compile.name)
|
||||
|
|
@ -248,7 +248,7 @@ object CoursierPlugin extends AutoPlugin {
|
|||
sys.error(s"Resolution for configuration $config not found")
|
||||
}
|
||||
},
|
||||
coursierSbtClassifiersResolution := Tasks.resolutionsTask(
|
||||
coursierSbtClassifiersResolution := ResolutionTasks.resolutionsTask(
|
||||
sbtClassifiers = true
|
||||
).value.head._2,
|
||||
ivyConfigurations := {
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import coursier.core._
|
||||
import coursier.sbtcoursier.Keys._
|
||||
import coursier.util.Print.Colors
|
||||
import coursier.util.{Parse, Print}
|
||||
import sbt.Def
|
||||
import sbt.Keys._
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
object DisplayTasks {
|
||||
|
||||
private case class ResolutionResult(configs: Set[Configuration], resolution: Resolution, dependencies: Seq[Dependency])
|
||||
|
||||
private def coursierResolutionTask(
|
||||
sbtClassifiers: Boolean = false,
|
||||
ignoreArtifactErrors: Boolean = false
|
||||
): Def.Initialize[sbt.Task[Seq[ResolutionResult]]] = {
|
||||
|
||||
val currentProjectTask =
|
||||
if (sbtClassifiers)
|
||||
Def.task {
|
||||
val sv = scalaVersion.value
|
||||
val sbv = scalaBinaryVersion.value
|
||||
val cm = coursierSbtClassifiersModule.value
|
||||
FromSbt.sbtClassifiersProject(cm, sv, sbv)
|
||||
}
|
||||
else
|
||||
Def.task {
|
||||
val proj = coursierProject.value
|
||||
val publications = coursierPublications.value
|
||||
proj.copy(publications = publications)
|
||||
}
|
||||
|
||||
Def.taskDyn {
|
||||
|
||||
val config = Configuration(configuration.value.name)
|
||||
val configs = coursierConfigurations.value
|
||||
|
||||
val includedConfigs = configs.getOrElse(config, Set.empty) + config
|
||||
|
||||
Def.taskDyn {
|
||||
val currentProject = currentProjectTask.value
|
||||
|
||||
val resolutionsTask =
|
||||
if (sbtClassifiers)
|
||||
Def.task {
|
||||
val classifiersRes = coursierSbtClassifiersResolution.value
|
||||
Map(currentProject.configurations.keySet -> classifiersRes)
|
||||
}
|
||||
else
|
||||
Def.task(coursierResolutions.value)
|
||||
|
||||
Def.task {
|
||||
val resolutions = resolutionsTask.value
|
||||
|
||||
for {
|
||||
(subGraphConfigs, res) <- resolutions.toSeq
|
||||
if subGraphConfigs.exists(includedConfigs)
|
||||
} yield {
|
||||
|
||||
val dependencies0 = currentProject.dependencies.collect {
|
||||
case (cfg, dep) if includedConfigs(cfg) && subGraphConfigs(cfg) => dep
|
||||
}.sortBy { dep =>
|
||||
(dep.module.organization, dep.module.name, dep.version)
|
||||
}
|
||||
|
||||
val subRes = res.subset(dependencies0.toSet)
|
||||
|
||||
ResolutionResult(subGraphConfigs, subRes, dependencies0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def coursierDependencyTreeTask(
|
||||
inverse: Boolean,
|
||||
sbtClassifiers: Boolean = false,
|
||||
ignoreArtifactErrors: Boolean = false
|
||||
) = Def.task {
|
||||
val projectName = thisProjectRef.value.project
|
||||
|
||||
val resolutions = coursierResolutionTask(sbtClassifiers, ignoreArtifactErrors).value
|
||||
for (ResolutionResult(subGraphConfigs, resolution, dependencies) <- resolutions) {
|
||||
// use sbt logging?
|
||||
println(
|
||||
s"$projectName (configurations ${subGraphConfigs.toVector.sorted.mkString(", ")})" + "\n" +
|
||||
Print.dependencyTree(
|
||||
dependencies,
|
||||
resolution,
|
||||
printExclusions = true,
|
||||
inverse,
|
||||
colors = !sys.props.get("sbt.log.noformat").toSeq.contains("true")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def coursierWhatDependsOnTask(
|
||||
moduleName: String,
|
||||
sbtClassifiers: Boolean = false,
|
||||
ignoreArtifactErrors: Boolean = false
|
||||
) = Def.task {
|
||||
val module = Parse.module(moduleName, scalaVersion.value)
|
||||
.right
|
||||
.getOrElse(throw new RuntimeException(s"Could not parse module `$moduleName`"))
|
||||
|
||||
val projectName = thisProjectRef.value.project
|
||||
|
||||
val resolutions = coursierResolutionTask(sbtClassifiers, ignoreArtifactErrors).value
|
||||
val result = new mutable.StringBuilder
|
||||
for (ResolutionResult(subGraphConfigs, resolution, _) <- resolutions) {
|
||||
val roots: Seq[Dependency] = resolution.transitiveDependencies.filter(f => f.module == module)
|
||||
val strToPrint = s"$projectName (configurations ${subGraphConfigs.toVector.sorted.map(_.value).mkString(", ")})" + "\n" +
|
||||
Print.reverseTree(roots, resolution, withExclusions = true)
|
||||
.render(_.repr(Colors.get(!sys.props.get("sbt.log.noformat").toSeq.contains("true"))))
|
||||
println(strToPrint)
|
||||
result.append(strToPrint)
|
||||
result.append("\n")
|
||||
}
|
||||
|
||||
result.toString
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import java.net.URL
|
||||
|
||||
import coursier.ProjectCache
|
||||
import coursier.core._
|
||||
import coursier.sbtcoursier.Keys._
|
||||
import coursier.sbtcoursier.Structure._
|
||||
import sbt.librarymanagement.{Configuration => _, _}
|
||||
import sbt.Def
|
||||
import sbt.Keys._
|
||||
|
||||
object InputsTasks {
|
||||
|
||||
def coursierFallbackDependenciesTask: Def.Initialize[sbt.Task[Seq[(Module, String, URL, Boolean)]]] =
|
||||
Def.taskDyn {
|
||||
|
||||
val state = sbt.Keys.state.value
|
||||
val projectRef = sbt.Keys.thisProjectRef.value
|
||||
|
||||
val projects = Structure.allRecursiveInterDependencies(state, projectRef)
|
||||
|
||||
val allDependenciesTask = allDependencies
|
||||
.forAllProjects(state, projectRef +: projects)
|
||||
.map(_.values.toVector.flatten)
|
||||
|
||||
Def.task {
|
||||
val allDependencies = allDependenciesTask.value
|
||||
|
||||
FromSbt.fallbackDependencies(
|
||||
allDependencies,
|
||||
scalaVersion.in(projectRef).get(state),
|
||||
scalaBinaryVersion.in(projectRef).get(state)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def coursierProjectTask: Def.Initialize[sbt.Task[Project]] =
|
||||
Def.taskDyn {
|
||||
|
||||
val state = sbt.Keys.state.value
|
||||
val projectRef = sbt.Keys.thisProjectRef.value
|
||||
|
||||
val allDependenciesTask = allDependencies.in(projectRef).get(state)
|
||||
|
||||
Def.task {
|
||||
Inputs.coursierProject(
|
||||
projectID.in(projectRef).get(state),
|
||||
allDependenciesTask.value,
|
||||
excludeDependencies.in(projectRef).get(state),
|
||||
// should projectID.configurations be used instead?
|
||||
ivyConfigurations.in(projectRef).get(state),
|
||||
scalaVersion.in(projectRef).get(state),
|
||||
scalaBinaryVersion.in(projectRef).get(state),
|
||||
state.log
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def coursierInterProjectDependenciesTask: Def.Initialize[sbt.Task[Seq[Project]]] =
|
||||
Def.taskDyn {
|
||||
|
||||
val state = sbt.Keys.state.value
|
||||
val projectRef = sbt.Keys.thisProjectRef.value
|
||||
|
||||
val projects = Structure.allRecursiveInterDependencies(state, projectRef)
|
||||
|
||||
val t = coursierProject.forAllProjects(state, projects).map(_.values.toVector)
|
||||
|
||||
Def.task(t.value)
|
||||
}
|
||||
|
||||
def coursierConfigurationsTask(
|
||||
shadedConfig: Option[(String, Configuration)]
|
||||
): Def.Initialize[sbt.Task[Map[Configuration, Set[Configuration]]]] =
|
||||
Def.task {
|
||||
Inputs.coursierConfigurations(ivyConfigurations.value, shadedConfig)
|
||||
}
|
||||
|
||||
def ivyGraphsTask: Def.Initialize[sbt.Task[Seq[Set[Configuration]]]] =
|
||||
Def.task {
|
||||
val p = coursierProject.value
|
||||
Inputs.ivyGraphs(p.configurations)
|
||||
}
|
||||
|
||||
def parentProjectCacheTask: Def.Initialize[sbt.Task[Map[Seq[sbt.librarymanagement.Resolver], Seq[coursier.ProjectCache]]]] =
|
||||
Def.taskDyn {
|
||||
|
||||
val state = sbt.Keys.state.value
|
||||
val projectRef = sbt.Keys.thisProjectRef.value
|
||||
|
||||
val projectDeps = structure(state).allProjects
|
||||
.find(_.id == projectRef.project)
|
||||
.map(_.dependencies.map(_.project.project).toSet)
|
||||
.getOrElse(Set.empty)
|
||||
|
||||
val projects = structure(state).allProjectRefs.filter(p => projectDeps(p.project))
|
||||
|
||||
val t =
|
||||
for {
|
||||
m <- coursierRecursiveResolvers.forAllProjects(state, projects)
|
||||
n <- coursierResolutions.forAllProjects(state, m.keys.toSeq)
|
||||
} yield
|
||||
n.foldLeft(Map.empty[Seq[Resolver], Seq[ProjectCache]]) {
|
||||
case (caches, (ref, resolutions)) =>
|
||||
val mainResOpt = resolutions.collectFirst {
|
||||
case (k, v) if k(Configuration.compile) => v
|
||||
}
|
||||
|
||||
val r = for {
|
||||
resolvers <- m.get(ref)
|
||||
resolution <- mainResOpt
|
||||
} yield
|
||||
caches.updated(resolvers, resolution.projectCache +: caches.getOrElse(resolvers, Seq.empty))
|
||||
|
||||
r.getOrElse(caches)
|
||||
}
|
||||
|
||||
Def.task(t.value)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
package coursier
|
||||
package coursier.sbtcoursier
|
||||
|
||||
import java.nio.charset.StandardCharsets.UTF_8
|
||||
import java.nio.file.Files
|
||||
|
||||
import coursier.core.Configuration
|
||||
import coursier.core.{Configuration, Project}
|
||||
import org.apache.ivy.core.module.id.ModuleRevisionId
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
package coursier
|
||||
package coursier.sbtcoursier
|
||||
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
|
||||
import coursier.core.{Configuration, Publication}
|
||||
import sbt.librarymanagement.GetClassifiersModule
|
||||
import sbt.{InputKey, Resolver, SettingKey, TaskKey}
|
||||
import coursier.{Cache, CachePolicy, Credentials, FileError, ProjectCache}
|
||||
import coursier.core._
|
||||
import sbt.librarymanagement.{GetClassifiersModule, Resolver}
|
||||
import sbt.{InputKey, SettingKey, TaskKey}
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import coursier.sbtcoursier.Keys._
|
||||
import coursier.sbtcoursier.Structure._
|
||||
import sbt.{Classpaths, Def}
|
||||
import sbt.Keys._
|
||||
import sbt.librarymanagement.Resolver
|
||||
|
||||
object RepositoriesTasks {
|
||||
|
||||
private def resultTask(bootResOpt: Option[Seq[Resolver]], overrideFlag: Boolean): Def.Initialize[sbt.Task[Seq[Resolver]]] =
|
||||
bootResOpt.filter(_ => overrideFlag) match {
|
||||
case Some(r) => Def.task(r)
|
||||
case None =>
|
||||
Def.taskDyn {
|
||||
val extRes = externalResolvers.value
|
||||
val isSbtPlugin = sbtPlugin.value
|
||||
if (isSbtPlugin)
|
||||
Def.task {
|
||||
Seq(
|
||||
sbtResolver.value,
|
||||
Classpaths.sbtPluginReleases
|
||||
) ++ extRes
|
||||
}
|
||||
else
|
||||
Def.task(extRes)
|
||||
}
|
||||
}
|
||||
|
||||
def coursierResolversTask: Def.Initialize[sbt.Task[Seq[Resolver]]] =
|
||||
Def.taskDyn {
|
||||
|
||||
val bootResOpt = bootResolvers.value
|
||||
val overrideFlag = overrideBuildResolvers.value
|
||||
|
||||
Def.task {
|
||||
val result = resultTask(bootResOpt, overrideFlag).value
|
||||
val reorderResolvers = coursierReorderResolvers.value
|
||||
val keepPreloaded = coursierKeepPreloaded.value
|
||||
|
||||
val result0 =
|
||||
if (reorderResolvers)
|
||||
ResolutionParams.reorderResolvers(result)
|
||||
else
|
||||
result
|
||||
|
||||
if (keepPreloaded)
|
||||
result0
|
||||
else
|
||||
result0.filter { r =>
|
||||
!r.name.startsWith("local-preloaded")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def coursierRecursiveResolversTask: Def.Initialize[sbt.Task[Seq[Resolver]]] =
|
||||
Def.taskDyn {
|
||||
|
||||
val state = sbt.Keys.state.value
|
||||
val projectRef = sbt.Keys.thisProjectRef.value
|
||||
|
||||
val projects = Structure.allRecursiveInterDependencies(state, projectRef)
|
||||
|
||||
val t = coursierResolvers
|
||||
.forAllProjects(state, projectRef +: projects)
|
||||
.map(_.values.toVector.flatten)
|
||||
|
||||
Def.task(t.value)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import java.net.URL
|
||||
|
||||
import coursier.{Cache, ProjectCache}
|
||||
import coursier.core._
|
||||
import coursier.extra.Typelevel
|
||||
import coursier.ivy.IvyRepository
|
||||
import coursier.maven.MavenRepository
|
||||
import coursier.sbtcoursier.Keys._
|
||||
import sbt.Def
|
||||
import sbt.Keys._
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
object ResolutionTasks {
|
||||
|
||||
def resolutionsTask(
|
||||
sbtClassifiers: Boolean = false
|
||||
): Def.Initialize[sbt.Task[Map[Set[Configuration], coursier.Resolution]]] = {
|
||||
|
||||
val currentProjectTask: sbt.Def.Initialize[sbt.Task[(Project, Seq[(Module, String, URL, Boolean)], Seq[Set[Configuration]])]] =
|
||||
if (sbtClassifiers)
|
||||
Def.task {
|
||||
val sv = scalaVersion.value
|
||||
val sbv = scalaBinaryVersion.value
|
||||
val cm = coursierSbtClassifiersModule.value
|
||||
val proj = FromSbt.sbtClassifiersProject(cm, sv, sbv)
|
||||
|
||||
val fallbackDeps = FromSbt.fallbackDependencies(
|
||||
cm.dependencies,
|
||||
sv,
|
||||
sbv
|
||||
)
|
||||
|
||||
(proj, fallbackDeps, Vector(cm.configurations.map(c => Configuration(c.name)).toSet))
|
||||
}
|
||||
else
|
||||
Def.task {
|
||||
val baseConfigGraphs = coursierConfigGraphs.value
|
||||
(coursierProject.value.copy(publications = coursierPublications.value), coursierFallbackDependencies.value, baseConfigGraphs)
|
||||
}
|
||||
|
||||
val resolversTask =
|
||||
if (sbtClassifiers)
|
||||
Def.task(coursierSbtResolvers.value)
|
||||
else
|
||||
Def.task(coursierRecursiveResolvers.value.distinct)
|
||||
|
||||
val authenticationByHostTask = Def.taskDyn {
|
||||
|
||||
val useSbtCredentials = coursierUseSbtCredentials.value
|
||||
|
||||
if (useSbtCredentials)
|
||||
Def.task {
|
||||
val log = streams.value.log
|
||||
|
||||
sbt.Keys.credentials.value
|
||||
.flatMap {
|
||||
case dc: sbt.DirectCredentials => List(dc)
|
||||
case fc: sbt.FileCredentials =>
|
||||
sbt.Credentials.loadCredentials(fc.path) match {
|
||||
case Left(err) =>
|
||||
log.warn(s"$err, ignoring it")
|
||||
Nil
|
||||
case Right(dc) => List(dc)
|
||||
}
|
||||
}
|
||||
.map { c =>
|
||||
c.host -> Authentication(c.userName, c.passwd)
|
||||
}
|
||||
.toMap
|
||||
}
|
||||
else
|
||||
Def.task(Map.empty[String, Authentication])
|
||||
}
|
||||
|
||||
Def.task {
|
||||
val projectName = thisProjectRef.value.project
|
||||
|
||||
val sv = scalaVersion.value
|
||||
val sbv = scalaBinaryVersion.value
|
||||
|
||||
val interProjectDependencies = coursierInterProjectDependencies.value
|
||||
|
||||
val parallelDownloads = coursierParallelDownloads.value
|
||||
val checksums = coursierChecksums.value
|
||||
val maxIterations = coursierMaxIterations.value
|
||||
val cachePolicies = coursierCachePolicies.value
|
||||
val ttl = coursierTtl.value
|
||||
val cache = coursierCache.value
|
||||
val createLogger = coursierCreateLogger.value
|
||||
|
||||
val log = streams.value.log
|
||||
|
||||
// are these always defined? (e.g. for Java only projects?)
|
||||
val so = Organization(scalaOrganization.value)
|
||||
|
||||
val userForceVersions = dependencyOverrides
|
||||
.value
|
||||
.map(FromSbt.moduleVersion(_, sv, sbv))
|
||||
.toMap
|
||||
|
||||
val verbosityLevel = coursierVerbosity.value
|
||||
|
||||
val userEnabledProfiles = mavenProfiles.value
|
||||
|
||||
val typelevel = Organization(scalaOrganization.value) == Typelevel.typelevelOrg
|
||||
|
||||
val globalPluginsRepos =
|
||||
for (p <- ResolutionParams.globalPluginPatterns(sbtBinaryVersion.value))
|
||||
yield IvyRepository.fromPattern(
|
||||
p,
|
||||
withChecksums = false,
|
||||
withSignatures = false,
|
||||
withArtifacts = false
|
||||
)
|
||||
|
||||
val interProjectRepo = InterProjectRepository(interProjectDependencies)
|
||||
|
||||
val ivyProperties = ResolutionParams.defaultIvyProperties()
|
||||
|
||||
val authenticationByRepositoryId = coursierCredentials.value.mapValues(_.authentication)
|
||||
|
||||
val (currentProject, fallbackDependencies, configGraphs) = currentProjectTask.value
|
||||
|
||||
val autoScalaLib = autoScalaLibrary.value
|
||||
|
||||
val resolvers = resolversTask.value
|
||||
|
||||
// TODO Warn about possible duplicated modules from source repositories?
|
||||
|
||||
val authenticationByHost = authenticationByHostTask.value
|
||||
|
||||
val internalRepositories = globalPluginsRepos :+ interProjectRepo
|
||||
|
||||
val parentProjectCache: ProjectCache = coursierParentProjectCache.value
|
||||
.get(resolvers)
|
||||
.map(_.foldLeft[ProjectCache](Map.empty)(_ ++ _))
|
||||
.getOrElse(Map.empty)
|
||||
|
||||
def withAuthenticationByHost(repo: Repository, credentials: Map[String, Authentication]): Repository = {
|
||||
|
||||
def httpHost(s: String) =
|
||||
if (s.startsWith("http://") || s.startsWith("https://"))
|
||||
Try(Cache.url(s).getHost).toOption
|
||||
else
|
||||
None
|
||||
|
||||
repo match {
|
||||
case m: MavenRepository =>
|
||||
if (m.authentication.isEmpty)
|
||||
httpHost(m.root).flatMap(credentials.get).fold(m) { auth =>
|
||||
m.copy(authentication = Some(auth))
|
||||
}
|
||||
else
|
||||
m
|
||||
case i: IvyRepository =>
|
||||
if (i.authentication.isEmpty) {
|
||||
val base = i.pattern.chunks.takeWhile {
|
||||
case _: coursier.ivy.Pattern.Chunk.Const => true
|
||||
case _ => false
|
||||
}.map(_.string).mkString
|
||||
|
||||
httpHost(base).flatMap(credentials.get).fold(i) { auth =>
|
||||
i.copy(authentication = Some(auth))
|
||||
}
|
||||
} else
|
||||
i
|
||||
case _ =>
|
||||
repo
|
||||
}
|
||||
}
|
||||
|
||||
val mainRepositories = resolvers
|
||||
.flatMap { resolver =>
|
||||
FromSbt.repository(
|
||||
resolver,
|
||||
ivyProperties,
|
||||
log,
|
||||
authenticationByRepositoryId.get(resolver.name)
|
||||
)
|
||||
}
|
||||
.map(withAuthenticationByHost(_, authenticationByHost))
|
||||
|
||||
val resOrError = ResolutionRun.resolutions(
|
||||
ResolutionParams(
|
||||
currentProject.dependencies,
|
||||
fallbackDependencies,
|
||||
configGraphs,
|
||||
autoScalaLib,
|
||||
mainRepositories,
|
||||
parentProjectCache,
|
||||
interProjectDependencies,
|
||||
internalRepositories,
|
||||
userEnabledProfiles,
|
||||
userForceVersions,
|
||||
typelevel,
|
||||
so,
|
||||
sv,
|
||||
sbtClassifiers,
|
||||
parallelDownloads,
|
||||
projectName,
|
||||
maxIterations,
|
||||
createLogger,
|
||||
cache,
|
||||
cachePolicies,
|
||||
ttl,
|
||||
checksums
|
||||
),
|
||||
verbosityLevel,
|
||||
log
|
||||
)
|
||||
|
||||
resOrError match {
|
||||
case Left(err) =>
|
||||
err.throwException()
|
||||
case Right(res) =>
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package coursier
|
||||
package coursier.sbtcoursier
|
||||
|
||||
import sbt.Logger
|
||||
|
||||
|
|
@ -1,10 +1,34 @@
|
|||
package coursier
|
||||
package coursier.sbtcoursier
|
||||
|
||||
import sbt._
|
||||
|
||||
// things from sbt-structure
|
||||
object Structure {
|
||||
|
||||
def allRecursiveInterDependencies(state: sbt.State, projectRef: sbt.ProjectRef) = {
|
||||
|
||||
def dependencies(map: Map[String, Seq[String]], id: String): Set[String] = {
|
||||
|
||||
def helper(map: Map[String, Seq[String]], acc: Set[String]): Set[String] =
|
||||
if (acc.exists(map.contains)) {
|
||||
val (kept, rem) = map.partition { case (k, _) => acc(k) }
|
||||
helper(rem, acc ++ kept.valuesIterator.flatten)
|
||||
} else
|
||||
acc
|
||||
|
||||
helper(map - id, map.getOrElse(id, Nil).toSet)
|
||||
}
|
||||
|
||||
val allProjectsDeps =
|
||||
for (p <- structure(state).allProjects)
|
||||
yield p.id -> p.dependencies.map(_.project.project)
|
||||
|
||||
val deps = dependencies(allProjectsDeps.toMap, projectRef.project)
|
||||
|
||||
structure(state).allProjectRefs.filter(p => deps(p.project))
|
||||
}
|
||||
|
||||
// vv things from sbt-structure vv
|
||||
|
||||
def structure(state: State) =
|
||||
sbt.Project.structure(state)
|
||||
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
package coursier.sbtcoursier
|
||||
|
||||
import coursier.core._
|
||||
import coursier.sbtcoursier.Keys._
|
||||
import sbt.Def
|
||||
import sbt.Keys._
|
||||
import sbt.librarymanagement.UpdateReport
|
||||
|
||||
object UpdateTasks {
|
||||
|
||||
def updateTask(
|
||||
shadedConfigOpt: Option[(String, Configuration)],
|
||||
withClassifiers: Boolean,
|
||||
sbtClassifiers: Boolean = false,
|
||||
ignoreArtifactErrors: Boolean = false,
|
||||
includeSignatures: Boolean = false
|
||||
): Def.Initialize[sbt.Task[UpdateReport]] = {
|
||||
|
||||
val currentProjectTask =
|
||||
if (sbtClassifiers)
|
||||
Def.task {
|
||||
val sv = scalaVersion.value
|
||||
val sbv = scalaBinaryVersion.value
|
||||
FromSbt.sbtClassifiersProject(coursierSbtClassifiersModule.value, sv, sbv)
|
||||
}
|
||||
else
|
||||
Def.task {
|
||||
val proj = coursierProject.value
|
||||
val publications = coursierPublications.value
|
||||
|
||||
proj.copy(publications = publications)
|
||||
}
|
||||
|
||||
val resTask =
|
||||
if (withClassifiers && sbtClassifiers)
|
||||
Def.task {
|
||||
val cm = coursierSbtClassifiersModule.value
|
||||
val classifiersRes = coursierSbtClassifiersResolution.value
|
||||
Map(cm.configurations.map(c => Configuration(c.name)).toSet -> classifiersRes)
|
||||
}
|
||||
else
|
||||
Def.task(coursierResolutions.value)
|
||||
|
||||
// we should be able to call .value on that one here, its conditions don't originate from other tasks
|
||||
val artifactFilesOrErrors0Task =
|
||||
if (withClassifiers) {
|
||||
if (sbtClassifiers)
|
||||
Keys.coursierSbtClassifiersArtifacts
|
||||
else
|
||||
Keys.coursierClassifiersArtifacts
|
||||
} else if (includeSignatures)
|
||||
Keys.coursierSignedArtifacts
|
||||
else
|
||||
Keys.coursierArtifacts
|
||||
|
||||
val configsTask: sbt.Def.Initialize[sbt.Task[Map[Configuration, Set[Configuration]]]] =
|
||||
if (withClassifiers && sbtClassifiers)
|
||||
Def.task {
|
||||
val cm = coursierSbtClassifiersModule.value
|
||||
cm.configurations.map(c => Configuration(c.name) -> Set(Configuration(c.name))).toMap
|
||||
}
|
||||
else
|
||||
Def.task {
|
||||
val configs0 = coursierConfigurations.value
|
||||
|
||||
shadedConfigOpt.fold(configs0) {
|
||||
case (baseConfig, shadedConfig) =>
|
||||
val baseConfig0 = Configuration(baseConfig)
|
||||
(configs0 - shadedConfig) + (
|
||||
baseConfig0 -> (configs0.getOrElse(baseConfig0, Set()) - shadedConfig)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val classifiersTask: sbt.Def.Initialize[sbt.Task[Option[Seq[Classifier]]]] =
|
||||
if (withClassifiers) {
|
||||
if (sbtClassifiers)
|
||||
Def.task {
|
||||
val cm = coursierSbtClassifiersModule.value
|
||||
Some(cm.classifiers.map(Classifier(_)))
|
||||
}
|
||||
else
|
||||
Def.task(Some(transitiveClassifiers.value.map(Classifier(_))))
|
||||
} else
|
||||
Def.task(None)
|
||||
|
||||
Def.taskDyn {
|
||||
|
||||
val so = Organization(scalaOrganization.value)
|
||||
val internalSbtScalaProvider = appConfiguration.value.provider.scalaProvider
|
||||
val sbtBootJarOverrides = SbtBootJars(
|
||||
so, // this seems plain wrong - this assumes that the scala org of the project is the same
|
||||
// as the one that started SBT. This will scrap the scala org specific JARs by the ones
|
||||
// that booted SBT, even if the latter come from the standard org.scala-lang org.
|
||||
// But SBT itself does it this way, and not doing so may make two different versions
|
||||
// of the scala JARs land in the classpath...
|
||||
internalSbtScalaProvider.version(),
|
||||
internalSbtScalaProvider.jars()
|
||||
)
|
||||
|
||||
val log = streams.value.log
|
||||
|
||||
val verbosityLevel = coursierVerbosity.value
|
||||
|
||||
val dependencies = currentProjectTask.value.dependencies
|
||||
val res = resTask.value
|
||||
|
||||
val key = SbtCoursierCache.ReportKey(
|
||||
dependencies,
|
||||
res,
|
||||
withClassifiers,
|
||||
sbtClassifiers,
|
||||
ignoreArtifactErrors
|
||||
)
|
||||
|
||||
SbtCoursierCache.default.reportOpt(key) match {
|
||||
case Some(report) =>
|
||||
Def.task(report)
|
||||
case None =>
|
||||
Def.task {
|
||||
|
||||
val artifactFilesOrErrors0 = artifactFilesOrErrors0Task.value
|
||||
val classifiers = classifiersTask.value
|
||||
val configs = configsTask.value
|
||||
|
||||
val params = UpdateParams(
|
||||
shadedConfigOpt,
|
||||
artifactFilesOrErrors0,
|
||||
classifiers,
|
||||
configs,
|
||||
dependencies,
|
||||
res,
|
||||
ignoreArtifactErrors,
|
||||
includeSignatures,
|
||||
sbtBootJarOverrides
|
||||
)
|
||||
|
||||
val repOrError =
|
||||
Lock.lock.synchronized {
|
||||
UpdateRun.update(params, verbosityLevel, log)
|
||||
}
|
||||
for (rep <- repOrError)
|
||||
SbtCoursierCache.default.putReport(key, rep)
|
||||
repOrError match {
|
||||
case Left(err) => err.throwException()
|
||||
case Right(rep) => rep
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,8 +2,7 @@ package coursier
|
|||
|
||||
object Helper {
|
||||
|
||||
def checkEmpty(): Boolean = {
|
||||
Tasks.resolutionsCache.isEmpty && Tasks.reportsCache.isEmpty
|
||||
}
|
||||
def checkEmpty(): Boolean =
|
||||
coursier.sbtcoursier.SbtCoursierCache.default.isEmpty
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package coursier
|
||||
package coursier.sbtcoursier
|
||||
|
||||
import coursier.core.Configuration
|
||||
import coursier.{Info, Module, Project, moduleNameString, organizationString}
|
||||
import utest._
|
||||
|
||||
object IvyXmlTests extends TestSuite {
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
package coursier
|
||||
|
||||
import com.typesafe.sbt.pgp.PgpKeys.updatePgpSignatures
|
||||
import coursier.sbtcoursier.UpdateTasks
|
||||
import sbt.AutoPlugin
|
||||
|
||||
object CoursierSbtPgpPlugin extends AutoPlugin {
|
||||
|
||||
override def trigger = allRequirements
|
||||
|
||||
override def requires = com.typesafe.sbt.SbtPgp && coursier.CoursierPlugin
|
||||
override def requires = com.typesafe.sbt.SbtPgp && coursier.sbtcoursier.CoursierPlugin
|
||||
|
||||
override val projectSettings = Seq(
|
||||
updatePgpSignatures := {
|
||||
Tasks.updateTask(
|
||||
UpdateTasks.updateTask(
|
||||
None,
|
||||
withClassifiers = false,
|
||||
includeSignatures = true
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import java.io.File
|
|||
|
||||
import coursier.core.{Configuration, Type}
|
||||
import coursier.ivy.IvyXml.{mappings => ivyXmlMappings}
|
||||
import coursier.sbtcoursier.{CoursierPlugin, InputsTasks, Keys}
|
||||
import sbt.librarymanagement._
|
||||
import sbt.Keys._
|
||||
import sbt.{AutoPlugin, Compile, SettingKey, TaskKey, inConfig}
|
||||
|
|
@ -71,7 +72,7 @@ object ShadingPlugin extends AutoPlugin {
|
|||
packagedArtifacts := sbt.Classpaths.packaged(shadingDefaultArtifactTasks).value
|
||||
)
|
||||
|
||||
import CoursierPlugin.autoImport._
|
||||
import coursier.sbtcoursier.CoursierPlugin.autoImport._
|
||||
|
||||
override lazy val buildSettings = super.buildSettings ++ Seq(
|
||||
shadeNamespaces := Set()
|
||||
|
|
@ -79,7 +80,7 @@ object ShadingPlugin extends AutoPlugin {
|
|||
|
||||
override lazy val projectSettings =
|
||||
Seq(
|
||||
coursierConfigurations := Tasks.coursierConfigurationsTask(
|
||||
coursierConfigurations := InputsTasks.coursierConfigurationsTask(
|
||||
Some(baseDependencyConfiguration.value -> Configuration(Shaded.name))
|
||||
).value,
|
||||
ivyConfigurations := Shaded +: ivyConfigurations.value.map {
|
||||
|
|
|
|||
Loading…
Reference in New Issue