This commit is contained in:
Adrien Piquerez 2024-10-09 09:49:30 +02:00
parent b71a412203
commit 30248a4513
41 changed files with 1114 additions and 894 deletions

View File

@ -1,64 +1,83 @@
package lmcoursier
import java.io.File
import dataclass.{data, since}
import dataclass.{ data, since }
import coursier.cache.CacheDefaults
import coursier.params.rule.{Rule, RuleResolution}
import coursier.params.rule.{ Rule, RuleResolution }
import lmcoursier.credentials.Credentials
import lmcoursier.definitions.{Authentication, CacheLogger, CachePolicy, FromCoursier, Module, ModuleMatchers, Project, Reconciliation, Strict}
import sbt.librarymanagement.{CrossVersion, InclExclRule, ModuleDescriptorConfiguration, ModuleID, ModuleInfo, Resolver, UpdateConfiguration}
import lmcoursier.definitions.{
Authentication,
CacheLogger,
CachePolicy,
FromCoursier,
Module,
ModuleMatchers,
Project,
Reconciliation,
Strict
}
import sbt.librarymanagement.{
CrossVersion,
InclExclRule,
ModuleDescriptorConfiguration,
ModuleID,
ModuleInfo,
Resolver,
UpdateConfiguration
}
import xsbti.Logger
import scala.concurrent.duration.{Duration, FiniteDuration}
import scala.concurrent.duration.{ Duration, FiniteDuration }
import java.net.URL
import java.net.URLClassLoader
@data class CoursierConfiguration(
log: Option[Logger] = None,
resolvers: Vector[Resolver] = Resolver.defaults,
parallelDownloads: Int = 6,
maxIterations: Int = 100,
sbtScalaOrganization: Option[String] = None,
sbtScalaVersion: Option[String] = None,
sbtScalaJars: Vector[File] = Vector.empty,
interProjectDependencies: Vector[Project] = Vector.empty,
excludeDependencies: Vector[(String, String)] = Vector.empty,
fallbackDependencies: Vector[FallbackDependency] = Vector.empty,
autoScalaLibrary: Boolean = true,
hasClassifiers: Boolean = false,
classifiers: Vector[String] = Vector.empty,
mavenProfiles: Vector[String] = Vector.empty,
scalaOrganization: Option[String] = None,
scalaVersion: Option[String] = None,
authenticationByRepositoryId: Vector[(String, Authentication)] = Vector.empty,
credentials: Seq[Credentials] = Vector.empty,
logger: Option[CacheLogger] = None,
cache: Option[File] = None,
@since
ivyHome: Option[File] = None,
@since
followHttpToHttpsRedirections: Option[Boolean] = None,
@since
strict: Option[Strict] = None,
extraProjects: Vector[Project] = Vector.empty,
forceVersions: Vector[(Module, String)] = Vector.empty,
@since
reconciliation: Vector[(ModuleMatchers, Reconciliation)] = Vector.empty,
@since
classpathOrder: Boolean = true,
@since
verbosityLevel: Int = 0,
ttl: Option[Duration] = CacheDefaults.ttl,
checksums: Vector[Option[String]] = CacheDefaults.checksums.toVector,
cachePolicies: Vector[CachePolicy] = CacheDefaults.cachePolicies.toVector.map(FromCoursier.cachePolicy),
@since
missingOk: Boolean = false,
@since
sbtClassifiers: Boolean = false,
@since
providedInCompile: Boolean = false, // unused, kept for binary compatibility
@since
protocolHandlerDependencies: Seq[ModuleID] = Vector.empty,
retry: Option[(FiniteDuration, Int)] = None,
sameVersions: Seq[Set[InclExclRule]] = Nil,
log: Option[Logger] = None,
resolvers: Vector[Resolver] = Resolver.defaults,
parallelDownloads: Int = 6,
maxIterations: Int = 100,
sbtScalaOrganization: Option[String] = None,
sbtScalaVersion: Option[String] = None,
sbtScalaJars: Vector[File] = Vector.empty,
interProjectDependencies: Vector[Project] = Vector.empty,
excludeDependencies: Vector[(String, String)] = Vector.empty,
fallbackDependencies: Vector[FallbackDependency] = Vector.empty,
autoScalaLibrary: Boolean = true,
hasClassifiers: Boolean = false,
classifiers: Vector[String] = Vector.empty,
mavenProfiles: Vector[String] = Vector.empty,
scalaOrganization: Option[String] = None,
scalaVersion: Option[String] = None,
authenticationByRepositoryId: Vector[(String, Authentication)] = Vector.empty,
credentials: Seq[Credentials] = Vector.empty,
logger: Option[CacheLogger] = None,
cache: Option[File] = None,
@since
ivyHome: Option[File] = None,
@since
followHttpToHttpsRedirections: Option[Boolean] = None,
@since
strict: Option[Strict] = None,
extraProjects: Vector[Project] = Vector.empty,
forceVersions: Vector[(Module, String)] = Vector.empty,
@since
reconciliation: Vector[(ModuleMatchers, Reconciliation)] = Vector.empty,
@since
classpathOrder: Boolean = true,
@since
verbosityLevel: Int = 0,
ttl: Option[Duration] = CacheDefaults.ttl,
checksums: Vector[Option[String]] = CacheDefaults.checksums.toVector,
cachePolicies: Vector[CachePolicy] =
CacheDefaults.cachePolicies.toVector.map(FromCoursier.cachePolicy),
@since
missingOk: Boolean = false,
@since
sbtClassifiers: Boolean = false,
@since
providedInCompile: Boolean = false, // unused, kept for binary compatibility
@since
protocolHandlerDependencies: Seq[ModuleID] = Vector.empty,
retry: Option[(FiniteDuration, Int)] = None,
sameVersions: Seq[Set[InclExclRule]] = Nil,
)

View File

@ -6,8 +6,8 @@ import dataclass.data
import lmcoursier.definitions.Module
//FIXME use URI instead of URL
@data class FallbackDependency(
module: Module,
version: String,
url: URL,
changing: Boolean
module: Module,
version: String,
url: URL,
changing: Boolean
)

View File

@ -3,17 +3,17 @@ package lmcoursier.credentials
import dataclass._
@data class DirectCredentials(
host: String = "",
username: String = "",
password: String = "",
@since("1.0")
realm: Option[String] = None,
@since("1.1")
optional: Boolean = true,
@since("1.2")
matchHost: Boolean = false,
@since("1.3")
httpsOnly: Boolean = true
host: String = "",
username: String = "",
password: String = "",
@since("1.0")
realm: Option[String] = None,
@since("1.1")
optional: Boolean = true,
@since("1.2")
matchHost: Boolean = false,
@since("1.3")
httpsOnly: Boolean = true
) extends Credentials {
override def toString(): String = s"DirectCredentials(host=$host, username=$username)"

View File

@ -3,6 +3,6 @@ package lmcoursier.credentials
import dataclass.data
@data class FileCredentials(
path: String,
optional: Boolean = true
path: String,
optional: Boolean = true
) extends Credentials

View File

@ -3,6 +3,6 @@ package lmcoursier.definitions
import dataclass.data
@data class Attributes(
`type`: Type,
classifier: Classifier
`type`: Type,
classifier: Classifier
)

View File

@ -3,16 +3,16 @@ package lmcoursier.definitions
import dataclass._
@data class Authentication(
user: String,
password: String,
optional: Boolean = false,
realmOpt: Option[String] = None,
@since("1.0")
headers: Seq[(String,String)] = Nil,
@since("1.1")
httpsOnly: Boolean = true,
@since("1.2")
passOnRedirect: Boolean = false
user: String,
password: String,
optional: Boolean = false,
realmOpt: Option[String] = None,
@since("1.0")
headers: Seq[(String, String)] = Nil,
@since("1.1")
httpsOnly: Boolean = true,
@since("1.2")
passOnRedirect: Boolean = false
) {
override def toString(): String =
s"Authentication(user=$user)"

View File

@ -3,10 +3,10 @@ package lmcoursier.definitions
import dataclass.data
@data class DateTime(
year: Int,
month: Int,
day: Int,
hour: Int,
minute: Int,
second: Int
year: Int,
month: Int,
day: Int,
hour: Int,
minute: Int,
second: Int
)

View File

@ -3,11 +3,11 @@ package lmcoursier.definitions
import dataclass.data
@data class Dependency(
module: Module,
version: String,
configuration: Configuration,
exclusions: Set[(Organization, ModuleName)],
publication: Publication,
optional: Boolean,
transitive: Boolean
module: Module,
version: String,
configuration: Configuration,
exclusions: Set[(Organization, ModuleName)],
publication: Publication,
optional: Boolean,
transitive: Boolean
)

View File

@ -3,7 +3,7 @@ package lmcoursier.definitions
import dataclass.data
@data class Developer(
id: String,
name: String,
url: String
id: String,
name: String,
url: String
)

View File

@ -3,9 +3,9 @@ package lmcoursier.definitions
import dataclass.data
@data class Info(
description: String,
homePage: String,
licenses: Seq[(String, Option[String])],
developers: Seq[Developer],
publication: Option[DateTime]
description: String,
homePage: String,
licenses: Seq[(String, Option[String])],
developers: Seq[Developer],
publication: Option[DateTime]
)

View File

@ -2,7 +2,7 @@ package lmcoursier.definitions
import dataclass.data
@data class Module(
organization: Organization,
name: ModuleName,
attributes: Map[String, String]
organization: Organization,
name: ModuleName,
attributes: Map[String, String]
)

View File

@ -7,7 +7,7 @@ import dataclass.data
* @param include Use "*" in either organization or name to match any.
*/
@data class ModuleMatchers(
exclude: Set[Module],
include: Set[Module],
includeByDefault: Boolean = true
exclude: Set[Module],
include: Set[Module],
includeByDefault: Boolean = true
)

View File

@ -3,12 +3,12 @@ package lmcoursier.definitions
import dataclass.data
@data class Project(
module: Module,
version: String,
dependencies: Seq[(Configuration, Dependency)],
configurations: Map[Configuration, Seq[Configuration]],
properties: Seq[(String, String)],
packagingOpt: Option[Type],
publications: Seq[(Configuration, Publication)],
info: Info
module: Module,
version: String,
dependencies: Seq[(Configuration, Dependency)],
configurations: Map[Configuration, Seq[Configuration]],
properties: Seq[(String, String)],
packagingOpt: Option[Type],
publications: Seq[(Configuration, Publication)],
info: Info
)

View File

@ -3,8 +3,8 @@ package lmcoursier.definitions
import dataclass.data
@data class Publication(
name: String,
`type`: Type,
ext: Extension,
classifier: Classifier
name: String,
`type`: Type,
ext: Extension,
classifier: Classifier
)

View File

@ -3,10 +3,10 @@ package lmcoursier.definitions
import dataclass._
@data class Strict(
include: Set[(String, String)] = Set(("*", "*")),
exclude: Set[(String, String)] = Set.empty,
ignoreIfForcedVersion: Boolean = true,
@since
includeByDefault: Boolean = false,
semVer: Boolean = false
include: Set[(String, String)] = Set(("*", "*")),
exclude: Set[(String, String)] = Set.empty,
ignoreIfForcedVersion: Boolean = true,
@since
includeByDefault: Boolean = false,
semVer: Boolean = false
)

View File

@ -1,15 +1,26 @@
package lmcoursier
import java.io.File
import java.net.{URL, URLClassLoader, URLConnection, MalformedURLException}
import java.net.{ URL, URLClassLoader, URLConnection, MalformedURLException }
import coursier.{Organization, Resolution, organizationString}
import coursier.core.{Classifier, Configuration}
import coursier.cache.{CacheDefaults, CachePolicy}
import coursier.{ Organization, Resolution, organizationString }
import coursier.core.{ Classifier, Configuration }
import coursier.cache.{ CacheDefaults, CachePolicy }
import coursier.util.Artifact
import coursier.internal.Typelevel
import lmcoursier.definitions.ToCoursier
import lmcoursier.internal.{ArtifactsParams, ArtifactsRun, CoursierModuleDescriptor, InterProjectRepository, ResolutionParams, ResolutionRun, Resolvers, SbtBootJars, UpdateParams, UpdateRun}
import lmcoursier.internal.{
ArtifactsParams,
ArtifactsRun,
CoursierModuleDescriptor,
InterProjectRepository,
ResolutionParams,
ResolutionRun,
Resolvers,
SbtBootJars,
UpdateParams,
UpdateRun
}
import lmcoursier.syntax._
import sbt.internal.librarymanagement.IvySbt
import sbt.librarymanagement._
@ -17,12 +28,12 @@ import sbt.util.Logger
import coursier.core.Dependency
import coursier.core.Publication
import scala.util.{Try, Failure}
import scala.util.{ Try, Failure }
class CoursierDependencyResolution(
conf: CoursierConfiguration,
protocolHandlerConfiguration: Option[CoursierConfiguration],
bootstrappingProtocolHandler: Boolean
conf: CoursierConfiguration,
protocolHandlerConfiguration: Option[CoursierConfiguration],
bootstrappingProtocolHandler: Boolean
) extends DependencyResolutionInterface {
def this(conf: CoursierConfiguration) =
@ -36,9 +47,9 @@ class CoursierDependencyResolution(
private val protocolHandlerClassLoaderLock = new Object
private def fetchProtocolHandlerClassLoader(
configuration: UpdateConfiguration,
uwconfig: UnresolvedWarningConfiguration,
log: Logger
configuration: UpdateConfiguration,
uwconfig: UnresolvedWarningConfiguration,
log: Logger
): ClassLoader = {
val conf0 = protocolHandlerConfiguration.getOrElse(conf)
@ -46,7 +57,7 @@ class CoursierDependencyResolution(
def isUnknownProtocol(rawURL: String): Boolean = {
Try(new URL(rawURL)) match {
case Failure(ex) if ex.getMessage.startsWith("unknown protocol: ") => true
case _ => false
case _ => false
}
}
@ -71,9 +82,10 @@ class CoursierDependencyResolution(
ModuleID("lmcoursier", "lmcoursier", "0.1.0"),
ModuleInfo("protocol-handler")
)
.withDependencies(conf0.protocolHandlerDependencies.toVector)
.withDependencies(conf0.protocolHandlerDependencies.toVector)
val reportOrUnresolved = resolution.update(moduleDescriptor(fakeModule), configuration, uwconfig, log)
val reportOrUnresolved =
resolution.update(moduleDescriptor(fakeModule), configuration, uwconfig, log)
val report = reportOrUnresolved match {
case Right(report0) =>
@ -105,10 +117,10 @@ class CoursierDependencyResolution(
CoursierModuleDescriptor(moduleSetting, conf)
def update(
module: ModuleDescriptor,
configuration: UpdateConfiguration,
uwconfig: UnresolvedWarningConfiguration,
log: Logger
module: ModuleDescriptor,
configuration: UpdateConfiguration,
uwconfig: UnresolvedWarningConfiguration,
log: Logger
): Either[UnresolvedWarning, UpdateReport] = {
if (bootstrappingProtocolHandler && protocolHandlerClassLoader.isEmpty)
@ -136,7 +148,8 @@ class CoursierDependencyResolution(
sys.error(s"unrecognized ModuleDescriptor type: $module")
}
val so = conf.scalaOrganization.map(Organization(_))
val so = conf.scalaOrganization
.map(Organization(_))
.orElse(module0.scalaModuleInfo.map(m => Organization(m.scalaOrganization)))
.getOrElse(Organization("org.scala-lang"))
val sv = conf.scalaVersion
@ -148,7 +161,13 @@ class CoursierDependencyResolution(
sv.split('.').take(2).mkString(".")
}
val projectPlatform = module0.scalaModuleInfo.flatMap(_.platform)
val (mod, ver) = FromSbt.moduleVersion(module0.module, sv, sbv, optionalCrossVer = true, projectPlatform = projectPlatform)
val (mod, ver) = FromSbt.moduleVersion(
module0.module,
sv,
sbv,
optionalCrossVer = true,
projectPlatform = projectPlatform
)
val interProjectDependencies = {
val needed = conf.interProjectDependencies.exists { p =>
p.module == mod && p.version == ver
@ -181,8 +200,7 @@ class CoursierDependencyResolution(
val authenticationByRepositoryId = conf.authenticationByRepositoryId.toMap
val mainRepositories = conf
.resolvers
val mainRepositories = conf.resolvers
.flatMap { resolver =>
Resolvers.repository(
resolver,
@ -196,27 +214,26 @@ class CoursierDependencyResolution(
val interProjectRepo = InterProjectRepository(interProjectDependencies)
val extraProjectsRepo = InterProjectRepository(extraProjects)
val dependencies = module0
.dependencies
val dependencies = module0.dependencies
.flatMap { d =>
// crossVersion sometimes already taken into account (when called via the update task), sometimes not
// (e.g. sbt-dotty 0.13.0-RC1)
FromSbt.dependencies(d, sv, sbv, optionalCrossVer = true)
}
.map {
case (config, dep) =>
(ToCoursier.configuration(config), ToCoursier.dependency(dep))
.map { case (config, dep) =>
(ToCoursier.configuration(config), ToCoursier.dependency(dep))
}
val orderedConfigs = Inputs.orderedConfigurations(Inputs.configExtendsSeq(module0.configurations))
.map {
case (config, extends0) =>
(ToCoursier.configuration(config), extends0.map(ToCoursier.configuration))
val orderedConfigs = Inputs
.orderedConfigurations(Inputs.configExtendsSeq(module0.configurations))
.map { case (config, extends0) =>
(ToCoursier.configuration(config), extends0.map(ToCoursier.configuration))
}
val typelevel = so == Typelevel.typelevelOrg
val cache0 = coursier.cache.FileCache()
val cache0 = coursier.cache
.FileCache()
.withLocation(cache)
.withCachePolicies(cachePolicies)
.withTtl(ttl)
@ -224,13 +241,9 @@ class CoursierDependencyResolution(
.withCredentials(conf.credentials.map(ToCoursier.credentials))
.withFollowHttpToHttpsRedirections(conf.followHttpToHttpsRedirections.getOrElse(true))
val excludeDependencies = conf
.excludeDependencies
.map {
case (strOrg, strName) =>
(coursier.Organization(strOrg), coursier.ModuleName(strName))
}
.toSet
val excludeDependencies = conf.excludeDependencies.map { case (strOrg, strName) =>
(coursier.Organization(strOrg), coursier.ModuleName(strName))
}.toSet
val resolutionParams = ResolutionParams(
dependencies = dependencies,
@ -246,7 +259,8 @@ class CoursierDependencyResolution(
loggerOpt = loggerOpt,
cache = cache0,
parallel = conf.parallelDownloads,
params = coursier.params.ResolutionParams()
params = coursier.params
.ResolutionParams()
.withMaxIterations(conf.maxIterations)
.withProfiles(conf.mavenProfiles.toSet)
.withForceVersion(conf.forceVersions.map { case (k, v) => (ToCoursier.module(k), v) }.toMap)
@ -279,14 +293,13 @@ class CoursierDependencyResolution(
conf.sbtScalaJars
)
val configs = Inputs.coursierConfigurationsMap(module0.configurations).map {
case (k, l) =>
ToCoursier.configuration(k) -> l.map(ToCoursier.configuration)
val configs = Inputs.coursierConfigurationsMap(module0.configurations).map { case (k, l) =>
ToCoursier.configuration(k) -> l.map(ToCoursier.configuration)
}
def updateParams(
resolutions: Map[Configuration, Resolution],
artifacts: Seq[(Dependency, Publication, Artifact, Option[File])]
resolutions: Map[Configuration, Resolution],
artifacts: Seq[(Dependency, Publication, Artifact, Option[File])]
) =
UpdateParams(
thisModule = (ToCoursier.module(mod), ver),
@ -317,16 +330,16 @@ class CoursierDependencyResolution(
}
private def unresolvedWarningOrThrow(
uwconfig: UnresolvedWarningConfiguration,
ex: coursier.error.CoursierError
uwconfig: UnresolvedWarningConfiguration,
ex: coursier.error.CoursierError
): UnresolvedWarning = {
// TODO Take coursier.error.FetchError.DownloadingArtifacts into account
val downloadErrors = ex match {
case ex0: coursier.error.ResolutionError =>
ex0.errors.collect {
case err: coursier.error.ResolutionError.CantDownloadModule => err
ex0.errors.collect { case err: coursier.error.ResolutionError.CantDownloadModule =>
err
}
case _ =>
Nil
@ -335,21 +348,21 @@ class CoursierDependencyResolution(
case ex0: coursier.error.ResolutionError =>
ex0.errors.flatMap {
case _: coursier.error.ResolutionError.CantDownloadModule => None
case err => Some(err)
case err => Some(err)
}
case _ =>
Seq(ex)
}
if (otherErrors.isEmpty) {
val r = new ResolveException(
downloadErrors.map(_.getMessage),
downloadErrors.map { err =>
ModuleID(err.module.organization.value, err.module.name.value, err.version)
.withExtraAttributes(err.module.attributes)
}
)
UnresolvedWarning(r, uwconfig)
val r = new ResolveException(
downloadErrors.map(_.getMessage),
downloadErrors.map { err =>
ModuleID(err.module.organization.value, err.module.name.value, err.version)
.withExtraAttributes(err.module.attributes)
}
)
UnresolvedWarning(r, uwconfig)
} else
throw ex
}
@ -359,8 +372,10 @@ object CoursierDependencyResolution {
def apply(configuration: CoursierConfiguration): DependencyResolution =
DependencyResolution(new CoursierDependencyResolution(configuration))
def apply(configuration: CoursierConfiguration,
protocolHandlerConfiguration: Option[CoursierConfiguration]): DependencyResolution =
def apply(
configuration: CoursierConfiguration,
protocolHandlerConfiguration: Option[CoursierConfiguration]
): DependencyResolution =
DependencyResolution(
new CoursierDependencyResolution(
configuration,

View File

@ -1,18 +1,30 @@
package lmcoursier
import coursier.ivy.IvyXml.{mappings => ivyXmlMappings}
import lmcoursier.definitions.{Classifier, Configuration, Dependency, Extension, Info, Module, ModuleName, Organization, Project, Publication, Type}
import coursier.ivy.IvyXml.{ mappings => ivyXmlMappings }
import lmcoursier.definitions.{
Classifier,
Configuration,
Dependency,
Extension,
Info,
Module,
ModuleName,
Organization,
Project,
Publication,
Type
}
import sbt.internal.librarymanagement.mavenint.SbtPomExtraProperties
import sbt.librarymanagement.{Configuration => _, MavenRepository => _, _}
import sbt.librarymanagement.{ Configuration => _, MavenRepository => _, _ }
object FromSbt {
private def sbtModuleIdName(
moduleId: ModuleID,
scalaVersion: => String,
scalaBinaryVersion: => String,
optionalCrossVer: Boolean = false,
projectPlatform: Option[String],
moduleId: ModuleID,
scalaVersion: => String,
scalaBinaryVersion: => String,
optionalCrossVer: Boolean = false,
projectPlatform: Option[String],
): String = {
val name0 = moduleId.name
val name1 =
@ -32,7 +44,11 @@ object FromSbt {
}
}
private def addPlatformSuffix(name: String, platformOpt: Option[String], projectPlatform: Option[String]): String = {
private def addPlatformSuffix(
name: String,
platformOpt: Option[String],
projectPlatform: Option[String]
): String = {
def addSuffix(platformName: String): String =
platformName match {
case "" | "jvm" => name
@ -46,46 +62,60 @@ object FromSbt {
}
private 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)
}
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,
optionalCrossVer: Boolean,
projectPlatform: Option[String],
module: ModuleID,
scalaVersion: String,
scalaBinaryVersion: String,
optionalCrossVer: Boolean,
projectPlatform: Option[String],
): (Module, String) = {
val fullName = sbtModuleIdName(module, scalaVersion, scalaBinaryVersion, optionalCrossVer, projectPlatform)
val fullName =
sbtModuleIdName(module, scalaVersion, scalaBinaryVersion, optionalCrossVer, projectPlatform)
val module0 = Module(Organization(module.organization), ModuleName(fullName), attributes(module.extraDependencyAttributes))
val module0 = Module(
Organization(module.organization),
ModuleName(fullName),
attributes(module.extraDependencyAttributes)
)
val version = module.revision
(module0, version)
}
def moduleVersion(
module: ModuleID,
scalaVersion: String,
scalaBinaryVersion: String
module: ModuleID,
scalaVersion: String,
scalaBinaryVersion: String
): (Module, String) =
moduleVersion(module, scalaVersion, scalaBinaryVersion, optionalCrossVer = false, projectPlatform = None)
moduleVersion(
module,
scalaVersion,
scalaBinaryVersion,
optionalCrossVer = false,
projectPlatform = None
)
def dependencies(
module: ModuleID,
scalaVersion: String,
scalaBinaryVersion: String,
optionalCrossVer: Boolean = false,
projectPlatform: Option[String] = None,
module: ModuleID,
scalaVersion: String,
scalaBinaryVersion: String,
optionalCrossVer: Boolean = false,
projectPlatform: Option[String] = None,
): Seq[(Configuration, Dependency)] = {
// TODO Warn about unsupported properties in `module`
val (module0, version) = moduleVersion(module, scalaVersion, scalaBinaryVersion, optionalCrossVer, projectPlatform)
val (module0, version) =
moduleVersion(module, scalaVersion, scalaBinaryVersion, optionalCrossVer, projectPlatform)
val dep = Dependency(
module0,
@ -101,17 +131,15 @@ object FromSbt {
)
val mapping = module.configurations.getOrElse("compile")
val allMappings = ivyXmlMappings(mapping).map {
case (from, to) =>
(Configuration(from.value), Configuration(to.value))
val allMappings = ivyXmlMappings(mapping).map { case (from, to) =>
(Configuration(from.value), Configuration(to.value))
}
val publications =
if (module.explicitArtifacts.isEmpty)
Seq(Publication("", Type(""), Extension(""), Classifier("")))
else
module
.explicitArtifacts
module.explicitArtifacts
.map { a =>
Publication(
name = a.name,
@ -133,9 +161,9 @@ object FromSbt {
}
def fallbackDependencies(
allDependencies: Seq[ModuleID],
scalaVersion: String,
scalaBinaryVersion: String
allDependencies: Seq[ModuleID],
scalaVersion: String,
scalaBinaryVersion: String
): Seq[FallbackDependency] =
for {
module <- allDependencies
@ -147,19 +175,20 @@ object FromSbt {
}
def project(
projectID: ModuleID,
allDependencies: Seq[ModuleID],
ivyConfigurations: Map[Configuration, Seq[Configuration]],
scalaVersion: String,
scalaBinaryVersion: String,
projectPlatform: Option[String],
projectID: ModuleID,
allDependencies: Seq[ModuleID],
ivyConfigurations: Map[Configuration, Seq[Configuration]],
scalaVersion: String,
scalaBinaryVersion: String,
projectPlatform: Option[String],
): Project = {
val deps = allDependencies.flatMap(dependencies(_, scalaVersion, scalaBinaryVersion, projectPlatform = projectPlatform))
val deps = allDependencies.flatMap(
dependencies(_, scalaVersion, scalaBinaryVersion, projectPlatform = projectPlatform)
)
val prefix = "e:" + SbtPomExtraProperties.POM_INFO_KEY_PREFIX
val properties = projectID
.extraAttributes
val properties = projectID.extraAttributes
.filterKeys(_.startsWith(prefix))
.toSeq
.map { case (k, v) => (k.stripPrefix("e:"), v) }
@ -168,7 +197,14 @@ object FromSbt {
Project(
Module(
Organization(projectID.organization),
ModuleName(sbtModuleIdName(projectID, scalaVersion, scalaBinaryVersion, projectPlatform = projectPlatform)),
ModuleName(
sbtModuleIdName(
projectID,
scalaVersion,
scalaBinaryVersion,
projectPlatform = projectPlatform
)
),
attributes(projectID.extraDependencyAttributes)
),
projectID.revision,

View File

@ -1,8 +1,8 @@
package lmcoursier
import coursier.ivy.IvyXml.{mappings => initialIvyXmlMappings}
import lmcoursier.definitions.{Configuration, Module, ModuleName, Organization, ToCoursier}
import sbt.librarymanagement.{CrossVersion, InclExclRule, ModuleID}
import coursier.ivy.IvyXml.{ mappings => initialIvyXmlMappings }
import lmcoursier.definitions.{ Configuration, Module, ModuleName, Organization, ToCoursier }
import sbt.librarymanagement.{ CrossVersion, InclExclRule, ModuleID }
import sbt.util.Logger
import scala.collection.mutable
@ -10,26 +10,34 @@ import scala.collection.mutable
object Inputs {
def ivyXmlMappings(mapping: String): Seq[(Configuration, Configuration)] =
initialIvyXmlMappings(mapping).map {
case (from, to) =>
Configuration(from.value) -> Configuration(to.value)
initialIvyXmlMappings(mapping).map { case (from, to) =>
Configuration(from.value) -> Configuration(to.value)
}
def configExtendsSeq(configurations: Seq[sbt.librarymanagement.Configuration]): Seq[(Configuration, Seq[Configuration])] =
def configExtendsSeq(
configurations: Seq[sbt.librarymanagement.Configuration]
): Seq[(Configuration, Seq[Configuration])] =
configurations
.map(cfg => Configuration(cfg.name) -> cfg.extendsConfigs.map(c => Configuration(c.name)))
@deprecated("Now unused internally, to be removed in the future", "2.0.0-RC6-5")
def configExtends(configurations: Seq[sbt.librarymanagement.Configuration]): Map[Configuration, Seq[Configuration]] =
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
@deprecated("Use coursierConfigurationsMap instead", "2.0.0-RC6-5")
def coursierConfigurations(configurations: Seq[sbt.librarymanagement.Configuration], shadedConfigOpt: Option[String] = None): Map[Configuration, Set[Configuration]] =
def coursierConfigurations(
configurations: Seq[sbt.librarymanagement.Configuration],
shadedConfigOpt: Option[String] = None
): Map[Configuration, Set[Configuration]] =
coursierConfigurationsMap(configurations)
def coursierConfigurationsMap(configurations: Seq[sbt.librarymanagement.Configuration]): Map[Configuration, Set[Configuration]] = {
def coursierConfigurationsMap(
configurations: Seq[sbt.librarymanagement.Configuration]
): Map[Configuration, Set[Configuration]] = {
val configs0 = configExtendsSeq(configurations).toMap
@ -46,19 +54,21 @@ object Inputs {
helper(Set(c))
}
configs0.map {
case (config, _) =>
config -> allExtends(config)
configs0.map { case (config, _) =>
config -> allExtends(config)
}
}
def orderedConfigurations(
configurations: Seq[(Configuration, Seq[Configuration])]
configurations: Seq[(Configuration, Seq[Configuration])]
): Seq[(Configuration, Seq[Configuration])] = {
val map = configurations.toMap
def helper(done: Set[Configuration], toAdd: List[Configuration]): Stream[(Configuration, Seq[Configuration])] =
def helper(
done: Set[Configuration],
toAdd: List[Configuration]
): Stream[(Configuration, Seq[Configuration])] =
toAdd match {
case Nil => Stream.empty
case config :: rest =>
@ -70,8 +80,7 @@ object Inputs {
helper(done, missingExtends.toList ::: toAdd)
}
helper(Set.empty, configurations.map(_._1).toList)
.toVector
helper(Set.empty, configurations.map(_._1).toList).toVector
}
@deprecated("Now unused internally, to be removed in the future", "2.0.0-RC6-5")
@ -87,12 +96,11 @@ object Inputs {
}
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)
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) {
@ -116,10 +124,10 @@ object Inputs {
}
def exclusionsSeq(
excludeDeps: Seq[InclExclRule],
sv: String,
sbv: String,
log: Logger
excludeDeps: Seq[InclExclRule],
sv: String,
sbv: String,
log: Logger
): Seq[(Organization, ModuleName)] = {
var anyNonSupportedExclusionRule = false
@ -144,10 +152,10 @@ object Inputs {
}
def exclusions(
excludeDeps: Seq[InclExclRule],
sv: String,
sbv: String,
log: Logger
excludeDeps: Seq[InclExclRule],
sv: String,
sbv: String,
log: Logger
): Set[(Organization, ModuleName)] =
exclusionsSeq(excludeDeps, sv, sbv, log).toSet

View File

@ -1,22 +1,22 @@
package lmcoursier
import lmcoursier.definitions.{Configuration, Project}
import lmcoursier.definitions.{ Configuration, Project }
import scala.xml.{Node, PrefixedAttribute}
import scala.xml.{ Node, PrefixedAttribute }
object IvyXml {
@deprecated("Use the override accepting 3 arguments", "2.0.0-RC6-6")
def apply(
currentProject: Project,
exclusions: Seq[(String, String)]
currentProject: Project,
exclusions: Seq[(String, String)]
): String =
apply(currentProject, exclusions, Nil)
def apply(
currentProject: Project,
exclusions: Seq[(String, String)],
overrides: Seq[(String, String, String)]
currentProject: Project,
exclusions: Seq[(String, String)],
overrides: Seq[(String, String, String)]
): String = {
// Important: width = Int.MaxValue, so that no tag gets truncated.
@ -33,24 +33,22 @@ object IvyXml {
// These are required for publish to be fine, later on.
private def content(
project: Project,
exclusions: Seq[(String, String)],
overrides: Seq[(String, String, String)]
project: Project,
exclusions: Seq[(String, String)],
overrides: Seq[(String, String, String)]
): Node = {
val props = project.module.attributes.toSeq ++ project.properties
val infoAttrs = props.foldLeft[xml.MetaData](xml.Null) {
case (acc, (k, v)) =>
new PrefixedAttribute("e", k, v, acc)
val infoAttrs = props.foldLeft[xml.MetaData](xml.Null) { case (acc, (k, v)) =>
new PrefixedAttribute("e", k, v, acc)
}
val licenseElems = project.info.licenses.map {
case (name, urlOpt) =>
val n = <license name={name} />
val licenseElems = project.info.licenses.map { case (name, urlOpt) =>
val n = <license name={name} />
urlOpt.fold(n) { url =>
n % <x url={url} />.attributes
}
urlOpt.fold(n) { url =>
n % <x url={url} />.attributes
}
}
val infoElem = {
@ -64,57 +62,56 @@ object IvyXml {
</info>
} % infoAttrs
val confElems = project.configurations.toVector.collect {
case (name, extends0) =>
val n = <conf name={name.value} visibility="public" description="" />
if (extends0.nonEmpty)
n % <x extends={extends0.map(_.value).mkString(",")} />.attributes
else
n
val confElems = project.configurations.toVector.collect { case (name, extends0) =>
val n = <conf name={name.value} visibility="public" description="" />
if (extends0.nonEmpty)
n % <x extends={extends0.map(_.value).mkString(",")} />.attributes
else
n
}
val publications = project
.publications
val publications = project.publications
.groupBy { case (_, p) => p }
.mapValues { _.map { case (cfg, _) => cfg } }
val publicationElems = publications.map {
case (pub, configs) =>
val n = <artifact name={pub.name} type={pub.`type`.value} ext={pub.ext.value} conf={configs.map(_.value).mkString(",")} />
val publicationElems = publications.map { case (pub, configs) =>
val n = <artifact name={pub.name} type={pub.`type`.value} ext={pub.ext.value} conf={
configs.map(_.value).mkString(",")
} />
if (pub.classifier.value.nonEmpty)
n % <x e:classifier={pub.classifier.value} />.attributes
else
n
if (pub.classifier.value.nonEmpty)
n % <x e:classifier={pub.classifier.value} />.attributes
else
n
}
val dependencyElems = project.dependencies.toVector.map {
case (conf, dep) =>
val excludes = dep.exclusions.toSeq.map {
case (org, name) =>
<exclude org={org.value} module={name.value} name="*" type="*" ext="*" conf="" matcher="exact"/>
}
val dependencyElems = project.dependencies.toVector.map { case (conf, dep) =>
val excludes = dep.exclusions.toSeq.map { case (org, name) =>
<exclude org={org.value} module={
name.value
} name="*" type="*" ext="*" conf="" matcher="exact"/>
}
val n = <dependency org={dep.module.organization.value} name={dep.module.name.value} rev={dep.version} conf={s"${conf.value}->${dep.configuration.value}"}>
val n = <dependency org={dep.module.organization.value} name={dep.module.name.value} rev={
dep.version
} conf={s"${conf.value}->${dep.configuration.value}"}>
{excludes}
</dependency>
val moduleAttrs = dep.module.attributes.foldLeft[xml.MetaData](xml.Null) {
case (acc, (k, v)) =>
new PrefixedAttribute("e", k, v, acc)
}
val moduleAttrs = dep.module.attributes.foldLeft[xml.MetaData](xml.Null) {
case (acc, (k, v)) =>
new PrefixedAttribute("e", k, v, acc)
}
n % moduleAttrs
n % moduleAttrs
}
val excludeElems = exclusions.toVector.map {
case (org, name) =>
<exclude org={org} module={name} artifact="*" type="*" ext="*" matcher="exact"/>
val excludeElems = exclusions.toVector.map { case (org, name) =>
<exclude org={org} module={name} artifact="*" type="*" ext="*" matcher="exact"/>
}
val overrideElems = overrides.toVector.map {
case (org, name, ver) =>
<override org={org} module={name} rev={ver} matcher="exact"/>
val overrideElems = overrides.toVector.map { case (org, name, ver) =>
<override org={org} module={name} rev={ver} matcher="exact"/>
}
<ivy-module version="2.0" xmlns:e="http://ant.apache.org/ivy/extra">

View File

@ -9,9 +9,18 @@ abstract class CacheLogger {
def downloadedArtifact(url: String, success: Boolean): Unit = {}
def checkingUpdates(url: String, currentTimeOpt: Option[Long]): Unit = {}
def checkingUpdatesResult(url: String, currentTimeOpt: Option[Long], remoteTimeOpt: Option[Long]): Unit = {}
def checkingUpdatesResult(
url: String,
currentTimeOpt: Option[Long],
remoteTimeOpt: Option[Long]
): Unit = {}
def downloadLength(url: String, totalLength: Long, alreadyDownloaded: Long, watching: Boolean): Unit = {}
def downloadLength(
url: String,
totalLength: Long,
alreadyDownloaded: Long,
watching: Boolean
): Unit = {}
def gettingLength(url: String): Unit = {}
def gettingLengthResult(url: String, length: Option[Long]): Unit = {}

View File

@ -69,4 +69,4 @@ object CachePolicy {
* Erases files already in cache.
*/
case object ForceDownload extends CachePolicy
}
}

View File

@ -6,13 +6,13 @@ object FromCoursier {
def cachePolicy(r: coursier.cache.CachePolicy): CachePolicy =
r match {
case coursier.cache.CachePolicy.LocalOnly => CachePolicy.LocalOnly
case coursier.cache.CachePolicy.LocalOnlyIfValid => CachePolicy.LocalOnlyIfValid
case coursier.cache.CachePolicy.LocalOnly => CachePolicy.LocalOnly
case coursier.cache.CachePolicy.LocalOnlyIfValid => CachePolicy.LocalOnlyIfValid
case coursier.cache.CachePolicy.LocalUpdateChanging => CachePolicy.LocalUpdateChanging
case coursier.cache.CachePolicy.LocalUpdate => CachePolicy.LocalUpdate
case coursier.cache.CachePolicy.UpdateChanging => CachePolicy.UpdateChanging
case coursier.cache.CachePolicy.Update => CachePolicy.Update
case coursier.cache.CachePolicy.FetchMissing => CachePolicy.FetchMissing
case coursier.cache.CachePolicy.ForceDownload => CachePolicy.ForceDownload
case coursier.cache.CachePolicy.LocalUpdate => CachePolicy.LocalUpdate
case coursier.cache.CachePolicy.UpdateChanging => CachePolicy.UpdateChanging
case coursier.cache.CachePolicy.Update => CachePolicy.Update
case coursier.cache.CachePolicy.FetchMissing => CachePolicy.FetchMissing
case coursier.cache.CachePolicy.ForceDownload => CachePolicy.ForceDownload
}
}
}

View File

@ -12,8 +12,8 @@ object Reconciliation {
input match {
case "default" => Some(Default)
case "relaxed" => Some(Relaxed)
case "strict" => Some(Strict)
case "semver" => Some(SemVer)
case _ => None
case "strict" => Some(Strict)
case "semver" => Some(SemVer)
case _ => None
}
}

View File

@ -1,6 +1,6 @@
package lmcoursier.definitions
import lmcoursier.credentials.{Credentials, DirectCredentials, FileCredentials}
import lmcoursier.credentials.{ Credentials, DirectCredentials, FileCredentials }
import sbt.librarymanagement.InclExclRule
// TODO Make private[lmcoursier]
@ -25,7 +25,8 @@ object ToCoursier {
)
def authentication(authentication: Authentication): coursier.core.Authentication =
coursier.core.Authentication(authentication.user, authentication.password)
coursier.core
.Authentication(authentication.user, authentication.password)
.withOptional(authentication.optional)
.withRealmOpt(authentication.realmOpt)
.withHttpHeaders(authentication.headers)
@ -35,7 +36,11 @@ object ToCoursier {
def module(mod: Module): coursier.core.Module =
module(mod.organization.value, mod.name.value, mod.attributes)
def module(organization: String, name: String, attributes: Map[String, String] = Map.empty): coursier.core.Module =
def module(
organization: String,
name: String,
attributes: Map[String, String] = Map.empty
): coursier.core.Module =
coursier.core.Module(
coursier.core.Organization(organization),
coursier.core.ModuleName(name),
@ -57,18 +62,21 @@ object ToCoursier {
r match {
case Reconciliation.Default => coursier.core.Reconciliation.Default
case Reconciliation.Relaxed => coursier.core.Reconciliation.Relaxed
case Reconciliation.Strict => coursier.core.Reconciliation.Strict
case Reconciliation.SemVer => coursier.core.Reconciliation.SemVer
case Reconciliation.Strict => coursier.core.Reconciliation.Strict
case Reconciliation.SemVer => coursier.core.Reconciliation.SemVer
}
def reconciliation(rs: Vector[(ModuleMatchers, Reconciliation)]):
Vector[(coursier.util.ModuleMatchers, coursier.core.Reconciliation)] =
def reconciliation(
rs: Vector[(ModuleMatchers, Reconciliation)]
): Vector[(coursier.util.ModuleMatchers, coursier.core.Reconciliation)] =
rs map { case (m, r) => (moduleMatchers(m), reconciliation(r)) }
def sameVersions(sv: Seq[Set[InclExclRule]]):
Seq[(coursier.params.rule.SameVersion, coursier.params.rule.RuleResolution)] =
def sameVersions(
sv: Seq[Set[InclExclRule]]
): Seq[(coursier.params.rule.SameVersion, coursier.params.rule.RuleResolution)] =
sv.map { libs =>
val matchers = libs.map(rule => coursier.util.ModuleMatcher(module(rule.organization, rule.name)))
val matchers =
libs.map(rule => coursier.util.ModuleMatcher(module(rule.organization, rule.name)))
coursier.params.rule.SameVersion(matchers) -> coursier.params.rule.RuleResolution.TryResolve
}
@ -77,9 +85,8 @@ object ToCoursier {
module(dependency.module),
dependency.version,
configuration(dependency.configuration),
dependency.exclusions.map {
case (org, name) =>
(coursier.core.Organization(org.value), coursier.core.ModuleName(name.value))
dependency.exclusions.map { case (org, name) =>
(coursier.core.Organization(org.value), coursier.core.ModuleName(name.value))
},
publication(dependency.publication),
dependency.optional,
@ -90,13 +97,11 @@ object ToCoursier {
coursier.core.Project(
module(project.module),
project.version,
project.dependencies.map {
case (conf, dep) =>
configuration(conf) -> dependency(dep)
project.dependencies.map { case (conf, dep) =>
configuration(conf) -> dependency(dep)
},
project.configurations.map {
case (k, l) =>
configuration(k) -> l.map(configuration)
project.configurations.map { case (k, l) =>
configuration(k) -> l.map(configuration)
},
None,
Nil,
@ -107,9 +112,8 @@ object ToCoursier {
project.packagingOpt.map(t => coursier.core.Type(t.value)),
relocated = false,
None,
project.publications.map {
case (conf, pub) =>
configuration(conf) -> publication(pub)
project.publications.map { case (conf, pub) =>
configuration(conf) -> publication(pub)
},
coursier.core.Info(
project.info.description,
@ -139,7 +143,8 @@ object ToCoursier {
def credentials(credentials: Credentials): coursier.credentials.Credentials =
credentials match {
case d: DirectCredentials =>
coursier.credentials.DirectCredentials()
coursier.credentials
.DirectCredentials()
.withHost(d.host)
.withUsername(d.username)
.withPassword(d.password)
@ -148,7 +153,8 @@ object ToCoursier {
.withMatchHost(d.matchHost)
.withHttpsOnly(d.httpsOnly)
case f: FileCredentials =>
coursier.credentials.FileCredentials(f.path)
coursier.credentials
.FileCredentials(f.path)
.withOptional(f.optional)
}
@ -164,9 +170,18 @@ object ToCoursier {
logger.downloadedArtifact(url, success)
override def checkingUpdates(url: String, currentTimeOpt: Option[Long]): Unit =
logger.checkingUpdates(url, currentTimeOpt)
override def checkingUpdatesResult(url: String, currentTimeOpt: Option[Long], remoteTimeOpt: Option[Long]): Unit =
override def checkingUpdatesResult(
url: String,
currentTimeOpt: Option[Long],
remoteTimeOpt: Option[Long]
): Unit =
logger.checkingUpdatesResult(url, currentTimeOpt, remoteTimeOpt)
override def downloadLength(url: String, totalLength: Long, alreadyDownloaded: Long, watching: Boolean): Unit =
override def downloadLength(
url: String,
totalLength: Long,
alreadyDownloaded: Long,
watching: Boolean
): Unit =
logger.downloadLength(url, totalLength, alreadyDownloaded, watching)
override def gettingLength(url: String): Unit =
logger.gettingLength(url)
@ -181,14 +196,17 @@ object ToCoursier {
}
def strict(strict: Strict): coursier.params.rule.Strict =
coursier.params.rule.Strict()
.withInclude(strict.include.map {
case (o, n) =>
coursier.util.ModuleMatcher(coursier.Module(coursier.Organization(o), coursier.ModuleName(n)))
coursier.params.rule
.Strict()
.withInclude(strict.include.map { case (o, n) =>
coursier.util.ModuleMatcher(
coursier.Module(coursier.Organization(o), coursier.ModuleName(n))
)
})
.withExclude(strict.exclude.map {
case (o, n) =>
coursier.util.ModuleMatcher(coursier.Module(coursier.Organization(o), coursier.ModuleName(n)))
.withExclude(strict.exclude.map { case (o, n) =>
coursier.util.ModuleMatcher(
coursier.Module(coursier.Organization(o), coursier.ModuleName(n))
)
})
.withIncludeByDefault(strict.includeByDefault)
.withIgnoreIfForcedVersion(strict.ignoreIfForcedVersion)
@ -196,13 +214,13 @@ object ToCoursier {
def cachePolicy(r: CachePolicy): coursier.cache.CachePolicy =
r match {
case CachePolicy.LocalOnly => coursier.cache.CachePolicy.LocalOnly
case CachePolicy.LocalOnlyIfValid => coursier.cache.CachePolicy.LocalOnlyIfValid
case CachePolicy.LocalOnly => coursier.cache.CachePolicy.LocalOnly
case CachePolicy.LocalOnlyIfValid => coursier.cache.CachePolicy.LocalOnlyIfValid
case CachePolicy.LocalUpdateChanging => coursier.cache.CachePolicy.LocalUpdateChanging
case CachePolicy.LocalUpdate => coursier.cache.CachePolicy.LocalUpdate
case CachePolicy.UpdateChanging => coursier.cache.CachePolicy.UpdateChanging
case CachePolicy.Update => coursier.cache.CachePolicy.Update
case CachePolicy.FetchMissing => coursier.cache.CachePolicy.FetchMissing
case CachePolicy.ForceDownload => coursier.cache.CachePolicy.ForceDownload
case CachePolicy.LocalUpdate => coursier.cache.CachePolicy.LocalUpdate
case CachePolicy.UpdateChanging => coursier.cache.CachePolicy.UpdateChanging
case CachePolicy.Update => coursier.cache.CachePolicy.Update
case CachePolicy.FetchMissing => coursier.cache.CachePolicy.FetchMissing
case CachePolicy.ForceDownload => coursier.cache.CachePolicy.ForceDownload
}
}

View File

@ -1,19 +1,19 @@
package lmcoursier.internal
import coursier.cache.{CacheLogger, FileCache}
import coursier.core.{Classifier, Resolution}
import coursier.cache.{ CacheLogger, FileCache }
import coursier.core.{ Classifier, Resolution }
import coursier.util.Task
// private[coursier]
final case class ArtifactsParams(
classifiers: Option[Seq[Classifier]],
resolutions: Seq[Resolution],
includeSignatures: Boolean,
loggerOpt: Option[CacheLogger],
projectName: String,
sbtClassifiers: Boolean,
cache: FileCache[Task],
parallel: Int,
classpathOrder: Boolean,
missingOk: Boolean
classifiers: Option[Seq[Classifier]],
resolutions: Seq[Resolution],
includeSignatures: Boolean,
loggerOpt: Option[CacheLogger],
projectName: String,
sbtClassifiers: Boolean,
cache: FileCache[Task],
parallel: Int,
classpathOrder: Boolean,
missingOk: Boolean
)

View File

@ -2,7 +2,7 @@ package lmcoursier.internal
import coursier.Artifacts
import coursier.cache.CacheLogger
import coursier.cache.loggers.{FallbackRefreshDisplay, ProgressBarRefreshDisplay, RefreshLogger}
import coursier.cache.loggers.{ FallbackRefreshDisplay, ProgressBarRefreshDisplay, RefreshLogger }
import coursier.core.Type
import sbt.util.Logger
@ -10,9 +10,9 @@ import sbt.util.Logger
object ArtifactsRun {
def apply(
params: ArtifactsParams,
verbosityLevel: Int,
log: Logger
params: ArtifactsParams,
verbosityLevel: Int,
log: Logger
): Either[coursier.error.FetchError, Artifacts.Result] = {
val printOptionalMessage = verbosityLevel >= 0 && verbosityLevel <= 1
@ -40,16 +40,19 @@ object ArtifactsRun {
)
}
Lock.maybeSynchronized(needsLock = params.loggerOpt.nonEmpty || !RefreshLogger.defaultFallbackMode){
Lock.maybeSynchronized(needsLock =
params.loggerOpt.nonEmpty || !RefreshLogger.defaultFallbackMode
) {
result(params, coursierLogger)
}
}
private def result(
params: ArtifactsParams,
coursierLogger: CacheLogger
params: ArtifactsParams,
coursierLogger: CacheLogger
): Either[coursier.error.FetchError, Artifacts.Result] =
coursier.Artifacts()
coursier
.Artifacts()
.withResolutions(params.resolutions)
.withArtifactTypes(Set(Type.all))
.withClassifiers(params.classifiers.getOrElse(Nil).toSet)
@ -62,9 +65,8 @@ object ArtifactsRun {
}
.addTransformArtifacts { artifacts =>
if (params.missingOk)
artifacts.map {
case (dependency, publication, artifact) =>
(dependency, publication, artifact.withOptional(true))
artifacts.map { case (dependency, publication, artifact) =>
(dependency, publication, artifact.withOptional(true))
}
else
artifacts

View File

@ -4,8 +4,8 @@ import lmcoursier.CoursierConfiguration
import sbt.librarymanagement._
private[lmcoursier] final case class CoursierModuleDescriptor(
descriptor: ModuleDescriptorConfiguration,
conf: CoursierConfiguration
descriptor: ModuleDescriptorConfiguration,
conf: CoursierConfiguration
) extends ModuleDescriptor {
def directDependencies: Vector[ModuleID] =

View File

@ -1,7 +1,7 @@
package lmcoursier.internal
import coursier.core._
import coursier.util.{EitherT, Monad}
import coursier.util.{ EitherT, Monad }
// private[coursier]
final case class InterProjectRepository(projects: Seq[Project]) extends Repository {
@ -11,11 +11,11 @@ final case class InterProjectRepository(projects: Seq[Project]) extends Reposito
.toMap
def find[F[_]](
module: Module,
version: String,
fetch: Repository.Fetch[F]
module: Module,
version: String,
fetch: Repository.Fetch[F]
)(implicit
F: Monad[F]
F: Monad[F]
): EitherT[F, String, (ArtifactSource, Project)] = {
val res = map
@ -27,9 +27,9 @@ final case class InterProjectRepository(projects: Seq[Project]) extends Reposito
}
override def artifacts(
dependency: Dependency,
project: Project,
overrideClassifiers: Option[Seq[Classifier]]
dependency: Dependency,
project: Project,
overrideClassifiers: Option[Seq[Classifier]]
) =
Nil
}
}

View File

@ -2,7 +2,7 @@ package lmcoursier.internal
import java.io.File
import coursier.cache.{CacheLogger, FileCache}
import coursier.cache.{ CacheLogger, FileCache }
import coursier.ProjectCache
import coursier.core._
import coursier.params.rule.Strict
@ -11,34 +11,33 @@ import lmcoursier.definitions.ToCoursier
import coursier.util.Task
import scala.collection.mutable
import scala.concurrent.duration.{DurationInt, FiniteDuration}
import scala.concurrent.duration.{ DurationInt, FiniteDuration }
// private[coursier]
final case class ResolutionParams(
dependencies: Seq[(Configuration, Dependency)],
fallbackDependencies: Seq[FallbackDependency],
orderedConfigs: Seq[(Configuration, Seq[Configuration])],
autoScalaLibOpt: Option[(Organization, String)],
mainRepositories: Seq[Repository],
parentProjectCache: ProjectCache,
interProjectDependencies: Seq[Project],
internalRepositories: Seq[Repository],
sbtClassifiers: Boolean,
projectName: String,
loggerOpt: Option[CacheLogger],
cache: coursier.cache.FileCache[Task],
parallel: Int,
params: coursier.params.ResolutionParams,
strictOpt: Option[Strict],
missingOk: Boolean,
retry: (FiniteDuration, Int)
dependencies: Seq[(Configuration, Dependency)],
fallbackDependencies: Seq[FallbackDependency],
orderedConfigs: Seq[(Configuration, Seq[Configuration])],
autoScalaLibOpt: Option[(Organization, String)],
mainRepositories: Seq[Repository],
parentProjectCache: ProjectCache,
interProjectDependencies: Seq[Project],
internalRepositories: Seq[Repository],
sbtClassifiers: Boolean,
projectName: String,
loggerOpt: Option[CacheLogger],
cache: coursier.cache.FileCache[Task],
parallel: Int,
params: coursier.params.ResolutionParams,
strictOpt: Option[Strict],
missingOk: Boolean,
retry: (FiniteDuration, Int)
) {
lazy val allConfigExtends: Map[Configuration, Set[Configuration]] = {
val map = new mutable.HashMap[Configuration, Set[Configuration]]
for ((config, extends0) <- orderedConfigs) {
val allExtends = extends0
.iterator
val allExtends = extends0.iterator
// the else of the getOrElse shouldn't be hit (because of the ordering of the configurations)
.foldLeft(Set(config))((acc, ext) => acc ++ map.getOrElse(ext, Set(ext)))
map += config -> allExtends
@ -50,11 +49,9 @@ final case class ResolutionParams(
if (fallbackDependencies.isEmpty)
Nil
else {
val map = fallbackDependencies
.map { dep =>
(ToCoursier.module(dep.module), dep.version) -> ((dep.url, dep.changing))
}
.toMap
val map = fallbackDependencies.map { dep =>
(ToCoursier.module(dep.module), dep.version) -> ((dep.url, dep.changing))
}.toMap
Seq(
TemporaryInMemoryRepository(map, cache)
@ -84,8 +81,26 @@ final case class ResolutionParams(
override lazy val hashCode =
this match {
case ResolutionParams(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) =>
(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) .##
case ResolutionParams(
a1,
a2,
a3,
a4,
a5,
a6,
a7,
a8,
a9,
a10,
a11,
a12,
a13,
a14,
a15,
a16,
a17
) =>
(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17).##
}
// ResolutionParams.unapply(this).get.##

View File

@ -1,8 +1,8 @@
package lmcoursier.internal
import coursier.{Resolution, Resolve}
import coursier.{ Resolution, Resolve }
import coursier.cache.internal.ThreadUtil
import coursier.cache.loggers.{FallbackRefreshDisplay, ProgressBarRefreshDisplay, RefreshLogger}
import coursier.cache.loggers.{ FallbackRefreshDisplay, ProgressBarRefreshDisplay, RefreshLogger }
import coursier.core._
import coursier.error.ResolutionError
import coursier.error.ResolutionError.CantDownloadModule
@ -19,11 +19,11 @@ import scala.collection.mutable
object ResolutionRun {
private def resolution(
params: ResolutionParams,
verbosityLevel: Int,
log: Logger,
configs: Set[Configuration],
startingResolutionOpt: Option[Resolution]
params: ResolutionParams,
verbosityLevel: Int,
log: Logger,
configs: Set[Configuration],
startingResolutionOpt: Option[Resolution]
): Either[coursier.error.ResolutionError, Resolution] = {
val isScalaToolConfig = configs(Configuration("scala-tool"))
@ -39,25 +39,30 @@ object ResolutionRun {
params.mainRepositories ++
params.fallbackDependenciesRepositories
val rules = params.params.rules ++ params.strictOpt.map(s => Seq((s, RuleResolution.Fail))).getOrElse(Nil)
val rules =
params.params.rules ++ params.strictOpt.map(s => Seq((s, RuleResolution.Fail))).getOrElse(Nil)
val printOptionalMessage = verbosityLevel >= 0 && verbosityLevel <= 1
def depsRepr(deps: Seq[(Configuration, Dependency)]) =
deps.map { case (config, dep) =>
s"${dep.module}:${dep.version}:${config.value}->${dep.configuration.value}"
}.sorted.distinct
deps
.map { case (config, dep) =>
s"${dep.module}:${dep.version}:${config.value}->${dep.configuration.value}"
}
.sorted
.distinct
val initialMessage =
Seq(
if (verbosityLevel >= 0)
Seq(s"Updating ${params.projectName}" + (if (params.sbtClassifiers) " (sbt classifiers)" else ""))
Seq(
s"Updating ${params.projectName}" + (if (params.sbtClassifiers) " (sbt classifiers)"
else "")
)
else
Nil,
if (verbosityLevel >= 2)
depsRepr(params.dependencies).map(depRepr =>
s" $depRepr"
)
depsRepr(params.dependencies).map(depRepr => s" $depRepr")
else
Nil
).flatten.mkString("\n")
@ -96,17 +101,18 @@ object ResolutionRun {
)
.withRepositories(repositories)
.withResolutionParams(
params
.params
.addForceVersion((if (isSandboxConfig) Nil else params.interProjectDependencies.map(_.moduleVersion)): _*)
params.params
.addForceVersion(
(if (isSandboxConfig) Nil
else params.interProjectDependencies.map(_.moduleVersion)): _*
)
.withForceScalaVersion(params.autoScalaLibOpt.nonEmpty)
.withScalaVersionOpt(params.autoScalaLibOpt.map(_._2))
.withTypelevel(params.params.typelevel)
.withRules(rules)
)
.withCache(
params
.cache
params.cache
.withLogger(
params.loggerOpt.getOrElse {
RefreshLogger.create(
@ -127,22 +133,23 @@ object ResolutionRun {
val (period, maxAttempts) = params.retry
val finalResult: Either[ResolutionError, Resolution] = {
def retry(attempt: Int, waitOnError: FiniteDuration): Task[Either[ResolutionError, Resolution]] =
resolveTask
.io
.attempt
def retry(
attempt: Int,
waitOnError: FiniteDuration
): Task[Either[ResolutionError, Resolution]] =
resolveTask.io.attempt
.flatMap {
case Left(e: ResolutionError) =>
val hasConnectionTimeouts = e.errors.exists {
case err: CantDownloadModule => err.perRepositoryErrors.exists(_.contains("Connection timed out"))
case _ => false
case err: CantDownloadModule =>
err.perRepositoryErrors.exists(_.contains("Connection timed out"))
case _ => false
}
if (hasConnectionTimeouts)
if (attempt + 1 >= maxAttempts) {
log.error(s"Failed, maximum iterations ($maxAttempts) reached")
Task.point(Left(e))
}
else {
} else {
log.warn(s"Attempt ${attempt + 1} failed: $e")
Task.completeAfter(retryScheduler, waitOnError).flatMap { _ =>
retry(attempt + 1, waitOnError * 2)
@ -161,14 +168,14 @@ object ResolutionRun {
finalResult match {
case Left(err) if params.missingOk => Right(err.resolution)
case others => others
case others => others
}
}
def resolutions(
params: ResolutionParams,
verbosityLevel: Int,
log: Logger
params: ResolutionParams,
verbosityLevel: Int,
log: Logger
): Either[coursier.error.ResolutionError, Map[Configuration, Resolution]] = {
// TODO Warn about possible duplicated modules from source repositories?
@ -181,23 +188,26 @@ object ResolutionRun {
SbtCoursierCache.default.resolutionOpt(params.resolutionKey).map(Right(_)).getOrElse {
val resOrError =
Lock.maybeSynchronized(needsLock = params.loggerOpt.nonEmpty || !RefreshLogger.defaultFallbackMode) {
Lock.maybeSynchronized(needsLock =
params.loggerOpt.nonEmpty || !RefreshLogger.defaultFallbackMode
) {
val map = new mutable.HashMap[Configuration, Resolution]
val either = params.orderedConfigs.foldLeft[Either[coursier.error.ResolutionError, Unit]](Right(())) {
case (acc, (config, extends0)) =>
for {
_ <- acc
initRes = {
val it = extends0.iterator.flatMap(map.get(_).iterator)
if (it.hasNext) Some(it.next())
else None
}
allExtends = params.allConfigExtends.getOrElse(config, Set.empty)
res <- resolution(params, verbosityLevel, log, allExtends, initRes)
} yield {
map += config -> res
()
val either = params.orderedConfigs.foldLeft[Either[coursier.error.ResolutionError, Unit]](
Right(())
) { case (acc, (config, extends0)) =>
for {
_ <- acc
initRes = {
val it = extends0.iterator.flatMap(map.get(_).iterator)
if (it.hasNext) Some(it.next())
else None
}
allExtends = params.allConfigExtends.getOrElse(config, Set.empty)
res <- resolution(params, verbosityLevel, log, allExtends, initRes)
} yield {
map += config -> res
()
}
}
either.map(_ => map.toMap)
}

View File

@ -4,11 +4,11 @@ import java.net.MalformedURLException
import java.nio.file.Paths
import coursier.cache.CacheUrl
import coursier.core.{Authentication, Repository}
import coursier.core.{ Authentication, Repository }
import coursier.ivy.IvyRepository
import coursier.maven.SbtMavenRepository
import org.apache.ivy.plugins.resolver.IBiblioResolver
import sbt.librarymanagement.{Configuration => _, MavenRepository => _, _}
import sbt.librarymanagement.{ Configuration => _, MavenRepository => _, _ }
import sbt.util.Logger
import scala.collection.JavaConverters._
@ -17,8 +17,8 @@ object Resolvers {
private def mavenCompatibleBaseOpt(patterns: Patterns): Option[String] =
if (patterns.isMavenCompatible) {
//input : /Users/user/custom/repo/[organisation]/[module](_[scalaVersion])(_[sbtVersion])/[revision]/[artifact]-[revision](-[classifier]).[ext]
//output : /Users/user/custom/repo/
// input : /Users/user/custom/repo/[organisation]/[module](_[scalaVersion])(_[sbtVersion])/[revision]/[artifact]-[revision](-[classifier]).[ext]
// output : /Users/user/custom/repo/
def basePattern(pattern: String): String = pattern.takeWhile(c => c != '[' && c != '(')
val baseIvyPattern = basePattern(patterns.ivyPatterns.head)
@ -32,10 +32,10 @@ object Resolvers {
None
private def mavenRepositoryOpt(
root: String,
log: Logger,
authentication: Option[Authentication],
classLoaders: Seq[ClassLoader]
root: String,
log: Logger,
authentication: Option[Authentication],
classLoaders: Seq[ClassLoader]
): Option[SbtMavenRepository] =
try {
CacheUrl.url(root, classLoaders) // ensure root is a URL whose protocol can be handled here
@ -50,9 +50,9 @@ object Resolvers {
case e: MalformedURLException =>
log.warn(
"Error parsing Maven repository base " +
root +
Option(e.getMessage).fold("")(" (" + _ + ")") +
", ignoring it"
root +
Option(e.getMessage).fold("")(" (" + _ + ")") +
", ignoring it"
)
None
@ -71,25 +71,23 @@ object Resolvers {
}
def repository(
resolver: Resolver,
ivyProperties: Map[String, String],
log: Logger,
authentication: Option[Authentication],
classLoaders: Seq[ClassLoader]
resolver: Resolver,
ivyProperties: Map[String, String],
log: Logger,
authentication: Option[Authentication],
classLoaders: Seq[ClassLoader]
): Option[Repository] =
resolver match {
case r: sbt.librarymanagement.MavenRepository =>
mavenRepositoryOpt(r.root, log, authentication, classLoaders)
case r: FileRepository
if r.patterns.ivyPatterns.lengthCompare(1) == 0 &&
r.patterns.artifactPatterns.lengthCompare(1) == 0 =>
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(
pathToUriString(r.patterns.artifactPatterns.head),
metadataPatternOpt = Some(pathToUriString(r.patterns.ivyPatterns.head)),
@ -109,13 +107,19 @@ object Resolvers {
Some(repo)
case Some(mavenCompatibleBase) =>
mavenRepositoryOpt(pathToUriString(mavenCompatibleBase), log, authentication, classLoaders)
mavenRepositoryOpt(
pathToUriString(mavenCompatibleBase),
log,
authentication,
classLoaders
)
}
case r: URLRepository if patternMatchGuard(r.patterns) =>
parseMavenCompatResolver(log, ivyProperties, authentication, r.patterns, classLoaders)
case raw: RawRepository if raw.name == "inter-project" => // sbt.RawRepository.equals just compares names anyway
case raw: RawRepository
if raw.name == "inter-project" => // sbt.RawRepository.equals just compares names anyway
None
// Pattern Match resolver-type-specific RawRepositories
@ -130,8 +134,8 @@ object Resolvers {
private object IBiblioRepository {
private def stringVector(v: java.util.List[_]): Vector[String] =
Option(v).map(_.asScala.toVector).getOrElse(Vector.empty).collect {
case s: String => s
Option(v).map(_.asScala.toVector).getOrElse(Vector.empty).collect { case s: String =>
s
}
private def patterns(resolver: IBiblioResolver): Patterns = Patterns(
@ -162,17 +166,16 @@ object Resolvers {
patterns.artifactPatterns.lengthCompare(1) == 0
private def parseMavenCompatResolver(
log: Logger,
ivyProperties: Map[String, String],
authentication: Option[Authentication],
patterns: Patterns,
classLoaders: Seq[ClassLoader],
log: Logger,
ivyProperties: Map[String, String],
authentication: Option[Authentication],
patterns: Patterns,
classLoaders: Seq[ClassLoader],
): Option[Repository] = {
val mavenCompatibleBaseOpt0 = mavenCompatibleBaseOpt(patterns)
mavenCompatibleBaseOpt0 match {
case None =>
val repo = IvyRepository.parse(
patterns.artifactPatterns.head,
metadataPatternOpt = Some(patterns.ivyPatterns.head),

View File

@ -2,22 +2,20 @@ package lmcoursier.internal
import java.io.File
import coursier.core.{Module, ModuleName, Organization}
import coursier.core.{ Module, ModuleName, Organization }
// private[coursier]
object SbtBootJars {
def apply(
scalaOrg: Organization,
scalaVersion: String,
jars: Seq[File]
scalaOrg: Organization,
scalaVersion: String,
jars: Seq[File]
): Map[(Module, String), File] =
jars
.collect {
case jar if jar.getName.endsWith(".jar") =>
val name = ModuleName(jar.getName.stripSuffix(".jar"))
val mod = Module(scalaOrg, name, Map.empty)
jars.collect {
case jar if jar.getName.endsWith(".jar") =>
val name = ModuleName(jar.getName.stripSuffix(".jar"))
val mod = Module(scalaOrg, name, Map.empty)
(mod, scalaVersion) -> jar
}
.toMap
}
(mod, scalaVersion) -> jar
}.toMap
}

View File

@ -12,12 +12,12 @@ class SbtCoursierCache {
import SbtCoursierCache._
private val resolutionsCache = new ConcurrentHashMap[ResolutionKey, Map[Configuration, Resolution]]
private val resolutionsCache =
new ConcurrentHashMap[ResolutionKey, Map[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[Configuration, Resolution]] =
Option(resolutionsCache.get(key))
def putResolution(key: ResolutionKey, res: Map[Configuration, Resolution]): Unit =
@ -42,24 +42,23 @@ class SbtCoursierCache {
object SbtCoursierCache {
final case class ResolutionKey(
dependencies: Seq[(Configuration, Dependency)],
internalRepositories: Seq[Repository],
mainRepositories: Seq[Repository],
fallbackRepositories: Seq[Repository],
params: ResolutionParams,
cache: FileCache[Task],
sbtClassifiers: Boolean
dependencies: Seq[(Configuration, Dependency)],
internalRepositories: Seq[Repository],
mainRepositories: Seq[Repository],
fallbackRepositories: Seq[Repository],
params: ResolutionParams,
cache: FileCache[Task],
sbtClassifiers: Boolean
)
final case class ReportKey(
dependencies: Seq[(Configuration, Dependency)],
resolution: Map[Configuration, Resolution],
withClassifiers: Boolean,
sbtClassifiers: Boolean,
includeSignatures: Boolean
dependencies: Seq[(Configuration, Dependency)],
resolution: Map[Configuration, Resolution],
withClassifiers: Boolean,
sbtClassifiers: Boolean,
includeSignatures: Boolean
)
// private[coursier]
val default = new SbtCoursierCache

View File

@ -5,11 +5,11 @@ import java.net.URL
import java.util.GregorianCalendar
import java.util.concurrent.ConcurrentHashMap
import coursier.cache.CacheUrl
import coursier.{Attributes, Dependency, Module, Project, Resolution}
import coursier.core.{Classifier, Configuration, Extension, Info, Publication, Type}
import coursier.{ Attributes, Dependency, Module, Project, Resolution }
import coursier.core.{ Classifier, Configuration, Extension, Info, Publication, Type }
import coursier.maven.MavenAttributes
import coursier.util.Artifact
import sbt.librarymanagement.{Artifact => _, Configuration => _, _}
import sbt.librarymanagement.{ Artifact => _, Configuration => _, _ }
import sbt.util.Logger
import scala.annotation.tailrec
@ -47,63 +47,71 @@ private[internal] object SbtUpdateReport {
)
.withExtraAttributes(dependency.module.attributes ++ extraProperties)
.withExclusions(
dependency
.minimizedExclusions
.toVector
.map {
case (org, name) =>
sbt.librarymanagement.InclExclRule()
.withOrganization(org.value)
.withName(name.value)
dependency.minimizedExclusions.toVector
.map { case (org, name) =>
sbt.librarymanagement
.InclExclRule()
.withOrganization(org.value)
.withName(name.value)
}
)
.withIsTransitive(dependency.transitive)
}
private val artifact = caching[(Module, Map[String, String], Publication, Artifact, Seq[ClassLoader]), sbt.librarymanagement.Artifact] {
case (module, extraProperties, pub, artifact, classLoaders) =>
sbt.librarymanagement.Artifact(pub.name)
.withType(pub.`type`.value)
.withExtension(pub.ext.value)
.withClassifier(
Some(pub.classifier)
.filter(_.nonEmpty)
.orElse(MavenAttributes.typeDefaultClassifierOpt(pub.`type`))
.map(_.value)
)
.withUrl(Some(CacheUrl.url(artifact.url, classLoaders).toURI))
.withExtraAttributes(module.attributes ++ extraProperties)
private val artifact = caching[
(Module, Map[String, String], Publication, Artifact, Seq[ClassLoader]),
sbt.librarymanagement.Artifact
] { case (module, extraProperties, pub, artifact, classLoaders) =>
sbt.librarymanagement
.Artifact(pub.name)
.withType(pub.`type`.value)
.withExtension(pub.ext.value)
.withClassifier(
Some(pub.classifier)
.filter(_.nonEmpty)
.orElse(MavenAttributes.typeDefaultClassifierOpt(pub.`type`))
.map(_.value)
)
.withUrl(Some(CacheUrl.url(artifact.url, classLoaders).toURI))
.withExtraAttributes(module.attributes ++ extraProperties)
}
private val moduleReport = caching[(Dependency, Seq[(Dependency, ProjectInfo)], Project, Seq[(Publication, Artifact, Option[File])], Seq[ClassLoader]), ModuleReport] {
case (dependency, dependees, project, artifacts, classLoaders) =>
val sbtArtifacts = artifacts.collect {
case (pub, artifact0, Some(file)) =>
(artifact((dependency.module, infoProperties(project).toMap, pub, artifact0, classLoaders)), file)
private val moduleReport = caching[
(
Dependency,
Seq[(Dependency, ProjectInfo)],
Project,
Seq[(Publication, Artifact, Option[File])],
Seq[ClassLoader]
),
ModuleReport
] { case (dependency, dependees, project, artifacts, classLoaders) =>
val sbtArtifacts = artifacts.collect { case (pub, artifact0, Some(file)) =>
(
artifact((dependency.module, infoProperties(project).toMap, pub, artifact0, classLoaders)),
file
)
}
val sbtMissingArtifacts = artifacts.collect {
case (pub, artifact0, None) =>
artifact((dependency.module, infoProperties(project).toMap, pub, artifact0, classLoaders))
val sbtMissingArtifacts = artifacts.collect { case (pub, artifact0, None) =>
artifact((dependency.module, infoProperties(project).toMap, pub, artifact0, classLoaders))
}
val publicationDate = project.info.publication.map { dt =>
new GregorianCalendar(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
}
val callers = dependees.distinct.map {
case (dependee, dependeeProj) =>
Caller(
moduleId((dependee, dependeeProj.version, Map.empty)),
// FIXME Shouldn't we only keep the configurations pulling dependency?
dependeeProj.configs,
dependee.module.attributes ++ dependeeProj.properties,
// FIXME Set better values here
isForceDependency = false,
isChangingDependency = false,
isTransitiveDependency = dependency.transitive,
isDirectlyForceDependency = false
)
val callers = dependees.distinct.map { case (dependee, dependeeProj) =>
Caller(
moduleId((dependee, dependeeProj.version, Map.empty)),
// FIXME Shouldn't we only keep the configurations pulling dependency?
dependeeProj.configs,
dependee.module.attributes ++ dependeeProj.properties,
// FIXME Set better values here
isForceDependency = false,
isChangingDependency = false,
isTransitiveDependency = dependency.transitive,
isDirectlyForceDependency = false
)
}
val rep = ModuleReport(
@ -132,17 +140,17 @@ private[internal] object SbtUpdateReport {
}
private def moduleReports(
thisModule: (Module, String),
res: Resolution,
interProjectDependencies: Seq[Project],
classifiersOpt: Option[Seq[Classifier]],
artifactFileOpt: (Module, String, Attributes, Artifact) => Option[File],
fullArtifactsOpt: Option[Map[(Dependency, Publication, Artifact), Option[File]]],
log: Logger,
includeSignatures: Boolean,
classpathOrder: Boolean,
missingOk: Boolean,
classLoaders: Seq[ClassLoader]
thisModule: (Module, String),
res: Resolution,
interProjectDependencies: Seq[Project],
classifiersOpt: Option[Seq[Classifier]],
artifactFileOpt: (Module, String, Attributes, Artifact) => Option[File],
fullArtifactsOpt: Option[Map[(Dependency, Publication, Artifact), Option[File]]],
log: Logger,
includeSignatures: Boolean,
classpathOrder: Boolean,
missingOk: Boolean,
classLoaders: Seq[ClassLoader]
): Vector[ModuleReport] = {
val deps = classifiersOpt match {
@ -154,23 +162,20 @@ private[internal] object SbtUpdateReport {
val depArtifacts1 = fullArtifactsOpt match {
case Some(map) =>
deps.map {
case (d, p, a) =>
val d0 = d.withAttributes(d.attributes.withClassifier(p.classifier))
val a0 = if (missingOk) a.withOptional(true) else a
val f = map.get((d0, p, a0)).flatten
(d, p, a0, f) // not d0
deps.map { case (d, p, a) =>
val d0 = d.withAttributes(d.attributes.withClassifier(p.classifier))
val a0 = if (missingOk) a.withOptional(true) else a
val f = map.get((d0, p, a0)).flatten
(d, p, a0, f) // not d0
}
case None =>
deps.map {
case (d, p, a) =>
(d, p, a, None)
deps.map { case (d, p, a) =>
(d, p, a, None)
}
}
val depArtifacts0 = depArtifacts1.filter {
case (_, pub, _, _) =>
pub.attributes != Attributes(Type.pom, Classifier.empty)
val depArtifacts0 = depArtifacts1.filter { case (_, pub, _, _) =>
pub.attributes != Attributes(Type.pom, Classifier.empty)
}
val depArtifacts =
@ -179,14 +184,13 @@ private[internal] object SbtUpdateReport {
val notFound = depArtifacts0.filter(!_._3.extra.contains("sig"))
if (notFound.isEmpty)
depArtifacts0.flatMap {
case (dep, pub, a, f) =>
val sigPub = pub
// not too sure about those
.withExt(Extension(pub.ext.value))
.withType(Type(pub.`type`.value))
Seq((dep, pub, a, f)) ++
a.extra.get("sig").toSeq.map((dep, sigPub, _, None))
depArtifacts0.flatMap { case (dep, pub, a, f) =>
val sigPub = pub
// not too sure about those
.withExt(Extension(pub.ext.value))
.withType(Type(pub.`type`.value))
Seq((dep, pub, a, f)) ++
a.extra.get("sig").toSeq.map((dep, sigPub, _, None))
}
else {
for ((_, _, a, _) <- notFound)
@ -207,10 +211,11 @@ private[internal] object SbtUpdateReport {
fromLib ++ fromInterProj
}
val versions = (Vector(Dependency(thisModule._1, thisModule._2)) ++ res.dependencies.toVector ++ res.rootDependencies.toVector)
.map { dep =>
dep.module -> dep.version
}.toMap
val versions = (Vector(
Dependency(thisModule._1, thisModule._2)
) ++ res.dependencies.toVector ++ res.rootDependencies.toVector).map { dep =>
dep.module -> dep.version
}.toMap
def clean(dep: Dependency): Dependency =
dep
@ -222,9 +227,7 @@ private[internal] object SbtUpdateReport {
res.projectCache.get(mv) match {
case Some((_, p)) => Some(p)
case _ =>
interProjectDependencies.find( p =>
mv == (p.module, p.version)
)
interProjectDependencies.find(p => mv == (p.module, p.version))
}
/**
@ -248,15 +251,14 @@ private[internal] object SbtUpdateReport {
}
val m = Dependency(thisModule._1, "")
val directReverseDependencies = res.rootDependencies.toSet.map(clean).map(_.withVersion(""))
.map(
dep => dep -> Vector(m)
)
val directReverseDependencies = res.rootDependencies.toSet
.map(clean)
.map(_.withVersion(""))
.map(dep => dep -> Vector(m))
.toMap
val reverseDependencies = {
val transitiveReverseDependencies = res.reverseDependencies
.toVector
val transitiveReverseDependencies = res.reverseDependencies.toVector
.map { case (k, v) =>
clean(k) -> v.map(clean)
}
@ -270,137 +272,148 @@ private[internal] object SbtUpdateReport {
.toMap
}
groupedDepArtifacts.toVector.map {
case (dep, artifacts) =>
val proj = lookupProject(dep.moduleVersion).get
val assembledProject = assemble(proj)
groupedDepArtifacts.toVector.map { case (dep, artifacts) =>
val proj = lookupProject(dep.moduleVersion).get
val assembledProject = assemble(proj)
// FIXME Likely flaky...
val dependees = reverseDependencies
.getOrElse(clean(dep.withVersion("")), Vector.empty)
.flatMap { dependee0 =>
val version = versions(dependee0.module)
val dependee = dependee0.withVersion(version)
lookupProject(dependee.moduleVersion) match {
case Some(dependeeProj) =>
Vector((dependee, ProjectInfo(
dependeeProj.version,
dependeeProj.configurations.keys.toVector.map(c => ConfigRef(c.value)),
dependeeProj.properties)))
case _ =>
Vector.empty
}
// FIXME Likely flaky...
val dependees = reverseDependencies
.getOrElse(clean(dep.withVersion("")), Vector.empty)
.flatMap { dependee0 =>
val version = versions(dependee0.module)
val dependee = dependee0.withVersion(version)
lookupProject(dependee.moduleVersion) match {
case Some(dependeeProj) =>
Vector(
(
dependee,
ProjectInfo(
dependeeProj.version,
dependeeProj.configurations.keys.toVector.map(c => ConfigRef(c.value)),
dependeeProj.properties
)
)
)
case _ =>
Vector.empty
}
val filesOpt = artifacts.map {
case (pub, a, fileOpt) =>
val fileOpt0 = fileOpt.orElse {
if (fullArtifactsOpt.isEmpty) artifactFileOpt(proj.module, proj.version, pub.attributes, a)
else None
}
(pub, a, fileOpt0)
}
moduleReport((
val filesOpt = artifacts.map { case (pub, a, fileOpt) =>
val fileOpt0 = fileOpt.orElse {
if (fullArtifactsOpt.isEmpty)
artifactFileOpt(proj.module, proj.version, pub.attributes, a)
else None
}
(pub, a, fileOpt0)
}
moduleReport(
(
dep,
dependees,
assembledProject,
filesOpt,
classLoaders,
))
)
)
}
}
def apply(
thisModule: (Module, String),
configDependencies: Map[Configuration, Seq[Dependency]],
resolutions: Seq[(Configuration, Resolution)],
interProjectDependencies: Vector[Project],
classifiersOpt: Option[Seq[Classifier]],
artifactFileOpt: (Module, String, Attributes, Artifact) => Option[File],
fullArtifactsOpt: Option[Map[(Dependency, Publication, Artifact), Option[File]]],
log: Logger,
includeSignatures: Boolean,
classpathOrder: Boolean,
missingOk: Boolean,
forceVersions: Map[Module, String],
classLoaders: Seq[ClassLoader],
thisModule: (Module, String),
configDependencies: Map[Configuration, Seq[Dependency]],
resolutions: Seq[(Configuration, Resolution)],
interProjectDependencies: Vector[Project],
classifiersOpt: Option[Seq[Classifier]],
artifactFileOpt: (Module, String, Attributes, Artifact) => Option[File],
fullArtifactsOpt: Option[Map[(Dependency, Publication, Artifact), Option[File]]],
log: Logger,
includeSignatures: Boolean,
classpathOrder: Boolean,
missingOk: Boolean,
forceVersions: Map[Module, String],
classLoaders: Seq[ClassLoader],
): UpdateReport = {
val configReports = resolutions.map {
case (config, subRes) =>
val configReports = resolutions.map { case (config, subRes) =>
val reports = moduleReports(
thisModule,
subRes,
interProjectDependencies,
classifiersOpt,
artifactFileOpt,
fullArtifactsOpt,
log,
includeSignatures = includeSignatures,
classpathOrder = classpathOrder,
missingOk = missingOk,
classLoaders = classLoaders,
)
val reports = moduleReports(
thisModule,
subRes,
interProjectDependencies,
classifiersOpt,
artifactFileOpt,
fullArtifactsOpt,
log,
includeSignatures = includeSignatures,
classpathOrder = classpathOrder,
missingOk = missingOk,
classLoaders = classLoaders,
)
val reports0 = subRes.rootDependencies match {
case Seq(dep) if subRes.projectCache.contains(dep.moduleVersion) =>
// 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 (_, proj) = subRes.projectCache(dep.moduleVersion)
val mod = moduleId((dep, proj.version, infoProperties(proj).toMap))
val (main, other) = reports.partition { r =>
r.module.organization == mod.organization &&
r.module.name == mod.name &&
r.module.crossVersion == mod.crossVersion
}
main ++ other
case _ => reports
}
val mainReportDetails = reports0.map { rep =>
OrganizationArtifactReport(rep.module.organization, rep.module.name, Vector(rep))
}
val evicted = for {
c <- coursier.graph.Conflict(subRes)
// ideally, forceVersions should be taken into account by coursier.core.Resolution itself, when
// it computes transitive dependencies. It only handles forced versions at a global level for now,
// rather than handing them for each dependency (where each dependency could have its own forced
// versions, and apply and pass them to its transitive dependencies, just like for exclusions today).
if !forceVersions.contains(c.module)
projOpt = subRes.projectCache.get((c.module, c.wantedVersion))
.orElse(subRes.projectCache.get((c.module, c.version)))
(_, proj) <- projOpt.toSeq
} yield {
val dep = Dependency(c.module, c.wantedVersion)
val dependee = Dependency(c.dependeeModule, c.dependeeVersion)
val dependeeProj = subRes.projectCache.get((c.dependeeModule, c.dependeeVersion)) match {
case Some((_, p)) =>
ProjectInfo(p.version, p.configurations.keys.toVector.map(c => ConfigRef(c.value)), p.properties)
case None =>
// should not happen
ProjectInfo(c.dependeeVersion, Vector.empty, Vector.empty)
}
val rep = moduleReport((dep, Seq((dependee, dependeeProj)), proj.withVersion(c.wantedVersion), Nil, classLoaders))
.withEvicted(true)
.withEvictedData(Some("version selection")) // ??? put latest-revision like sbt/ivy here?
OrganizationArtifactReport(c.module.organization.value, c.module.name.value, Vector(rep))
}
val details = (mainReportDetails ++ evicted)
.groupBy(r => (r.organization, r.name))
.toVector // order?
.map {
case ((org, name), l) =>
val modules = l.flatMap(_.modules)
OrganizationArtifactReport(org, name, modules)
val reports0 = subRes.rootDependencies match {
case Seq(dep) if subRes.projectCache.contains(dep.moduleVersion) =>
// 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 (_, proj) = subRes.projectCache(dep.moduleVersion)
val mod = moduleId((dep, proj.version, infoProperties(proj).toMap))
val (main, other) = reports.partition { r =>
r.module.organization == mod.organization &&
r.module.name == mod.name &&
r.module.crossVersion == mod.crossVersion
}
main ++ other
case _ => reports
}
ConfigurationReport(
ConfigRef(config.value),
reports0,
details
val mainReportDetails = reports0.map { rep =>
OrganizationArtifactReport(rep.module.organization, rep.module.name, Vector(rep))
}
val evicted = for {
c <- coursier.graph.Conflict(subRes)
// ideally, forceVersions should be taken into account by coursier.core.Resolution itself, when
// it computes transitive dependencies. It only handles forced versions at a global level for now,
// rather than handing them for each dependency (where each dependency could have its own forced
// versions, and apply and pass them to its transitive dependencies, just like for exclusions today).
if !forceVersions.contains(c.module)
projOpt = subRes.projectCache
.get((c.module, c.wantedVersion))
.orElse(subRes.projectCache.get((c.module, c.version)))
(_, proj) <- projOpt.toSeq
} yield {
val dep = Dependency(c.module, c.wantedVersion)
val dependee = Dependency(c.dependeeModule, c.dependeeVersion)
val dependeeProj = subRes.projectCache.get((c.dependeeModule, c.dependeeVersion)) match {
case Some((_, p)) =>
ProjectInfo(
p.version,
p.configurations.keys.toVector.map(c => ConfigRef(c.value)),
p.properties
)
case None =>
// should not happen
ProjectInfo(c.dependeeVersion, Vector.empty, Vector.empty)
}
val rep = moduleReport(
(dep, Seq((dependee, dependeeProj)), proj.withVersion(c.wantedVersion), Nil, classLoaders)
)
.withEvicted(true)
.withEvictedData(Some("version selection")) // ??? put latest-revision like sbt/ivy here?
OrganizationArtifactReport(c.module.organization.value, c.module.name.value, Vector(rep))
}
val details = (mainReportDetails ++ evicted)
.groupBy(r => (r.organization, r.name))
.toVector // order?
.map { case ((org, name), l) =>
val modules = l.flatMap(_.modules)
OrganizationArtifactReport(org, name, modules)
}
ConfigurationReport(
ConfigRef(config.value),
reports0,
details
)
}
UpdateReport(
@ -411,5 +424,9 @@ private[internal] object SbtUpdateReport {
)
}
private case class ProjectInfo(version: String, configs: Vector[ConfigRef], properties: Seq[(String, String)])
private case class ProjectInfo(
version: String,
configs: Vector[ConfigRef],
properties: Seq[(String, String)]
)
}

View File

@ -1,11 +1,11 @@
package lmcoursier.internal
import java.io.{File, FileNotFoundException, IOException}
import java.net.{HttpURLConnection, URL, URLConnection}
import java.io.{ File, FileNotFoundException, IOException }
import java.net.{ HttpURLConnection, URL, URLConnection }
import coursier.cache.{ConnectionBuilder, FileCache}
import coursier.cache.{ ConnectionBuilder, FileCache }
import coursier.core._
import coursier.util.{Artifact, EitherT, Monad}
import coursier.util.{ Artifact, EitherT, Monad }
import scala.util.Try
@ -22,15 +22,15 @@ object TemporaryInMemoryRepository {
}
def exists(
url: URL,
localArtifactsShouldBeCached: Boolean
url: URL,
localArtifactsShouldBeCached: Boolean
): Boolean =
exists(url, localArtifactsShouldBeCached, None)
def exists(
url: URL,
localArtifactsShouldBeCached: Boolean,
cacheOpt: Option[FileCache[Nothing]]
url: URL,
localArtifactsShouldBeCached: Boolean,
cacheOpt: Option[FileCache[Nothing]]
): Boolean = {
// Sometimes HEAD attempts fail even though standard GETs are fine.
@ -55,8 +55,12 @@ object TemporaryInMemoryRepository {
var conn: URLConnection = null
try {
conn = ConnectionBuilder(url.toURI.toASCIIString)
.withFollowHttpToHttpsRedirections(cacheOpt.fold(false)(_.followHttpToHttpsRedirections))
.withFollowHttpsToHttpRedirections(cacheOpt.fold(false)(_.followHttpsToHttpRedirections))
.withFollowHttpToHttpsRedirections(
cacheOpt.fold(false)(_.followHttpToHttpsRedirections)
)
.withFollowHttpsToHttpRedirections(
cacheOpt.fold(false)(_.followHttpsToHttpRedirections)
)
.withSslSocketFactoryOpt(cacheOpt.flatMap(_.sslSocketFactoryOpt))
.withHostnameVerifierOpt(cacheOpt.flatMap(_.hostnameVerifierOpt))
.withMethod("HEAD")
@ -66,12 +70,10 @@ object TemporaryInMemoryRepository {
// iff this doesn't throw.
conn.getInputStream.close()
Some(true)
}
catch {
} catch {
case _: FileNotFoundException => Some(false)
case _: IOException => None // error other than not found
}
finally {
} finally {
if (conn != null)
closeConn(conn)
}
@ -91,11 +93,9 @@ object TemporaryInMemoryRepository {
// NOT setting request type to HEAD here.
conn.getInputStream.close()
true
}
catch {
} catch {
case _: IOException => false
}
finally {
} finally {
if (conn != null)
closeConn(conn)
}
@ -106,19 +106,19 @@ object TemporaryInMemoryRepository {
}
def apply(
fallbacks: Map[(Module, String), (URL, Boolean)]
fallbacks: Map[(Module, String), (URL, Boolean)]
): TemporaryInMemoryRepository =
new TemporaryInMemoryRepository(fallbacks, localArtifactsShouldBeCached = false, None)
def apply(
fallbacks: Map[(Module, String), (URL, Boolean)],
localArtifactsShouldBeCached: Boolean
fallbacks: Map[(Module, String), (URL, Boolean)],
localArtifactsShouldBeCached: Boolean
): TemporaryInMemoryRepository =
new TemporaryInMemoryRepository(fallbacks, localArtifactsShouldBeCached, None)
def apply[F[_]](
fallbacks: Map[(Module, String), (URL, Boolean)],
cache: FileCache[F]
fallbacks: Map[(Module, String), (URL, Boolean)],
cache: FileCache[F]
): TemporaryInMemoryRepository =
new TemporaryInMemoryRepository(
fallbacks,
@ -128,25 +128,24 @@ object TemporaryInMemoryRepository {
}
final class TemporaryInMemoryRepository private(
val fallbacks: Map[(Module, String), (URL, Boolean)],
val localArtifactsShouldBeCached: Boolean,
val cacheOpt: Option[FileCache[Nothing]]
final class TemporaryInMemoryRepository private (
val fallbacks: Map[(Module, String), (URL, Boolean)],
val localArtifactsShouldBeCached: Boolean,
val cacheOpt: Option[FileCache[Nothing]]
) extends Repository {
def find[F[_]](
module: Module,
version: String,
fetch: Repository.Fetch[F]
module: Module,
version: String,
fetch: Repository.Fetch[F]
)(implicit
F: Monad[F]
F: Monad[F]
): EitherT[F, String, (ArtifactSource, Project)] = {
def res = fallbacks
.get((module, version))
.fold[Either[String, (ArtifactSource, Project)]](Left("No fallback URL found")) {
case (url, _) =>
val urlStr = url.toExternalForm
val idx = urlStr.lastIndexOf('/')
@ -185,24 +184,23 @@ final class TemporaryInMemoryRepository private(
}
def artifacts(
dependency: Dependency,
project: Project,
overrideClassifiers: Option[Seq[Classifier]]
dependency: Dependency,
project: Project,
overrideClassifiers: Option[Seq[Classifier]]
): Seq[(Publication, Artifact)] = {
fallbacks
.get(dependency.moduleVersion)
.toSeq
.map {
case (url, changing) =>
val url0 = url.toString
val ext = url0.substring(url0.lastIndexOf('.') + 1)
val pub = Publication(
dependency.module.name.value, // ???
Type(ext),
Extension(ext),
Classifier.empty
)
(pub, Artifact(url0, Map.empty, Map.empty, changing, optional = false, None))
.map { case (url, changing) =>
val url0 = url.toString
val ext = url0.substring(url0.lastIndexOf('.') + 1)
val pub = Publication(
dependency.module.name.value, // ???
Type(ext),
Extension(ext),
Classifier.empty
)
(pub, Artifact(url0, Map.empty, Map.empty, changing, optional = false, None))
}
}

View File

@ -7,27 +7,27 @@ import coursier.util.Artifact
// private[coursier]
final case class UpdateParams(
thisModule: (Module, String),
artifacts: Map[Artifact, File],
fullArtifacts: Option[Map[(Dependency, Publication, Artifact), Option[File]]],
classifiers: Option[Seq[Classifier]],
configs: Map[Configuration, Set[Configuration]],
dependencies: Seq[(Configuration, Dependency)],
forceVersions: Map[Module, String],
interProjectDependencies: Seq[Project],
res: Map[Configuration, Resolution],
includeSignatures: Boolean,
sbtBootJarOverrides: Map[(Module, String), File],
classpathOrder: Boolean,
missingOk: Boolean,
classLoaders: Seq[ClassLoader]
thisModule: (Module, String),
artifacts: Map[Artifact, File],
fullArtifacts: Option[Map[(Dependency, Publication, Artifact), Option[File]]],
classifiers: Option[Seq[Classifier]],
configs: Map[Configuration, Set[Configuration]],
dependencies: Seq[(Configuration, Dependency)],
forceVersions: Map[Module, String],
interProjectDependencies: Seq[Project],
res: Map[Configuration, Resolution],
includeSignatures: Boolean,
sbtBootJarOverrides: Map[(Module, String), File],
classpathOrder: Boolean,
missingOk: Boolean,
classLoaders: Seq[ClassLoader]
) {
def artifactFileOpt(
module: Module,
version: String,
attributes: Attributes,
artifact: Artifact
module: Module,
version: String,
attributes: Attributes,
artifact: Artifact
): Option[File] = {
// Under some conditions, SBT puts the scala JARs of its own classpath

View File

@ -12,23 +12,21 @@ object UpdateRun {
// Move back to coursier.util (in core module) after 1.0?
private def allDependenciesByConfig(
res: Map[Configuration, Resolution],
depsByConfig: Map[Configuration, Seq[Dependency]],
configs: Map[Configuration, Set[Configuration]]
res: Map[Configuration, Resolution],
depsByConfig: Map[Configuration, Seq[Dependency]],
configs: Map[Configuration, Set[Configuration]]
): Map[Configuration, Set[Dependency]] = {
val allDepsByConfig = depsByConfig.map {
case (config, deps) =>
config -> res(config).subset(deps).minDependencies
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))
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)
config -> (allDeps -- inherited)
}
filteredAllDepsByConfig
@ -36,26 +34,24 @@ object UpdateRun {
// Move back to coursier.util (in core module) after 1.0?
private def dependenciesWithConfig(
res: Map[Configuration, Resolution],
depsByConfig: Map[Configuration, Seq[Dependency]],
configs: Map[Configuration, Set[Configuration]]
res: Map[Configuration, Resolution],
depsByConfig: Map[Configuration, Seq[Dependency]],
configs: Map[Configuration, Set[Configuration]]
): Set[Dependency] =
allDependenciesByConfig(res, depsByConfig, configs)
.flatMap {
case (config, deps) =>
deps.map(dep => dep.withConfiguration(config --> dep.configuration))
.flatMap { case (config, deps) =>
deps.map(dep => dep.withConfiguration(config --> dep.configuration))
}
.groupBy(_.withConfiguration(Configuration.empty))
.map {
case (dep, l) =>
dep.withConfiguration(Configuration.join(l.map(_.configuration).toSeq: _*))
.map { case (dep, l) =>
dep.withConfiguration(Configuration.join(l.map(_.configuration).toSeq: _*))
}
.toSet
def update(
params: UpdateParams,
verbosityLevel: Int,
log: Logger
params: UpdateParams,
verbosityLevel: Int,
log: Logger
): UpdateReport = Lock.maybeSynchronized(needsLock = !RefreshLogger.defaultFallbackMode) {
val depsByConfig = grouped(params.dependencies)
@ -66,7 +62,9 @@ object UpdateRun {
params.configs
)
val projCache = params.res.values.foldLeft(Map.empty[ModuleVersion, Project])(_ ++ _.projectCache.mapValues(_._2))
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"))
}

View File

@ -3,40 +3,50 @@ package lmcoursier
import coursier.cache.CacheDefaults
import lmcoursier.credentials._
import lmcoursier.definitions._
import sbt.librarymanagement.{Resolver, UpdateConfiguration, ModuleID, CrossVersion, ModuleInfo, ModuleDescriptorConfiguration}
import sbt.librarymanagement.{
Resolver,
UpdateConfiguration,
ModuleID,
CrossVersion,
ModuleInfo,
ModuleDescriptorConfiguration
}
import xsbti.Logger
import scala.concurrent.duration.{Duration, FiniteDuration}
import scala.concurrent.duration.{ Duration, FiniteDuration }
import java.io.File
import java.net.URL
import java.net.URLClassLoader
package object syntax {
implicit class CoursierConfigurationModule(value: CoursierConfiguration.type) {
@deprecated("Legacy cache location support was dropped, this method does nothing.", "2.0.0-RC6-10")
@deprecated(
"Legacy cache location support was dropped, this method does nothing.",
"2.0.0-RC6-10"
)
def checkLegacyCache(): Unit = ()
def apply(
log: Logger,
resolvers: Vector[Resolver],
parallelDownloads: Int,
maxIterations: Int,
sbtScalaOrganization: String,
sbtScalaVersion: String,
sbtScalaJars: Vector[File],
interProjectDependencies: Vector[Project],
excludeDependencies: Vector[(String, String)],
fallbackDependencies: Vector[FallbackDependency],
autoScalaLibrary: Boolean,
hasClassifiers: Boolean,
classifiers: Vector[String],
mavenProfiles: Vector[String],
scalaOrganization: String,
scalaVersion: String,
authenticationByRepositoryId: Vector[(String, Authentication)],
credentials: Seq[Credentials],
logger: CacheLogger,
cache: File
log: Logger,
resolvers: Vector[Resolver],
parallelDownloads: Int,
maxIterations: Int,
sbtScalaOrganization: String,
sbtScalaVersion: String,
sbtScalaJars: Vector[File],
interProjectDependencies: Vector[Project],
excludeDependencies: Vector[(String, String)],
fallbackDependencies: Vector[FallbackDependency],
autoScalaLibrary: Boolean,
hasClassifiers: Boolean,
classifiers: Vector[String],
mavenProfiles: Vector[String],
scalaOrganization: String,
scalaVersion: String,
authenticationByRepositoryId: Vector[(String, Authentication)],
credentials: Seq[Credentials],
logger: CacheLogger,
cache: File
): CoursierConfiguration =
CoursierConfiguration(
Option(log),
@ -96,7 +106,9 @@ package object syntax {
value.withCache(Option(cache))
def withIvyHome(ivyHome: File): CoursierConfiguration =
value.withIvyHome(Option(ivyHome))
def withFollowHttpToHttpsRedirections(followHttpToHttpsRedirections: Boolean): CoursierConfiguration =
def withFollowHttpToHttpsRedirections(
followHttpToHttpsRedirections: Boolean
): CoursierConfiguration =
value.withFollowHttpToHttpsRedirections(Some(followHttpToHttpsRedirections))
def withFollowHttpToHttpsRedirections(): CoursierConfiguration =
value.withFollowHttpToHttpsRedirections(Some(true))
@ -104,8 +116,13 @@ package object syntax {
value.withStrict(Some(strict))
def withTtl(ttl: Duration): CoursierConfiguration =
value.withTtl(Some(ttl))
def addRepositoryAuthentication(repositoryId: String, authentication: Authentication): CoursierConfiguration =
value.withAuthenticationByRepositoryId(value.authenticationByRepositoryId :+ (repositoryId, authentication))
def addRepositoryAuthentication(
repositoryId: String,
authentication: Authentication
): CoursierConfiguration =
value.withAuthenticationByRepositoryId(
value.authenticationByRepositoryId :+ (repositoryId, authentication)
)
def withUpdateConfiguration(conf: UpdateConfiguration): CoursierConfiguration =
value.withMissingOk(conf.missingOk)
@ -119,19 +136,20 @@ package object syntax {
Attributes(value.`type`, value.classifier)
def withAttributes(attributes: Attributes): Publication =
value.withType(attributes.`type`)
value
.withType(attributes.`type`)
.withClassifier(attributes.classifier)
}
implicit class DependencyModule(value: Dependency.type) {
def apply(
module: Module,
version: String,
configuration: Configuration,
exclusions: Set[(Organization, ModuleName)],
attributes: Attributes,
optional: Boolean,
transitive: Boolean
module: Module,
version: String,
configuration: Configuration,
exclusions: Set[(Organization, ModuleName)],
attributes: Attributes,
optional: Boolean,
transitive: Boolean
): Dependency =
Dependency(
module,
@ -159,7 +177,11 @@ package object syntax {
def all: ModuleMatchers =
ModuleMatchers(Set.empty, Set.empty)
def only(organization: String, moduleName: String): ModuleMatchers =
ModuleMatchers(Set.empty, Set(Module(Organization(organization), ModuleName(moduleName), Map())), includeByDefault = false)
ModuleMatchers(
Set.empty,
Set(Module(Organization(organization), ModuleName(moduleName), Map())),
includeByDefault = false
)
def only(mod: Module): ModuleMatchers =
ModuleMatchers(Set.empty, Set(mod), includeByDefault = false)
}
@ -179,7 +201,13 @@ package object syntax {
implicit class DirectCredentialsModule(value: DirectCredentials.type) {
def apply(host: String, username: String, password: String, realm: String): DirectCredentials =
DirectCredentials(host, username, password, Option(realm))
def apply(host: String, username: String, password: String, realm: String, optional: Boolean): DirectCredentials =
def apply(
host: String,
username: String,
password: String,
realm: String,
optional: Boolean
): DirectCredentials =
DirectCredentials(host, username, password, Option(realm))
}
@ -192,14 +220,47 @@ package object syntax {
def apply(): DirectCredentials = DirectCredentials()
def apply(host: String, username: String, password: String): DirectCredentials =
DirectCredentials(host, username, password)
def apply(host: String, username: String, password: String, realm: Option[String]): DirectCredentials =
def apply(
host: String,
username: String,
password: String,
realm: Option[String]
): DirectCredentials =
DirectCredentials(host, username, password, realm)
def apply(host: String, username: String, password: String, realm: String): DirectCredentials =
DirectCredentials(host, username, password, Option(realm))
def apply(host: String, username: String, password: String, realm: Option[String], optional: Boolean): DirectCredentials =
DirectCredentials(host, username, password, realm, optional, matchHost = false, httpsOnly = true)
def apply(host: String, username: String, password: String, realm: String, optional: Boolean): DirectCredentials =
DirectCredentials(host, username, password, Option(realm), optional, matchHost = false, httpsOnly = true)
def apply(
host: String,
username: String,
password: String,
realm: Option[String],
optional: Boolean
): DirectCredentials =
DirectCredentials(
host,
username,
password,
realm,
optional,
matchHost = false,
httpsOnly = true
)
def apply(
host: String,
username: String,
password: String,
realm: String,
optional: Boolean
): DirectCredentials =
DirectCredentials(
host,
username,
password,
Option(realm),
optional,
matchHost = false,
httpsOnly = true
)
def apply(f: File): FileCredentials =
FileCredentials(f.getAbsolutePath)

View File

@ -17,10 +17,13 @@ class CoursierDependencyResolutionTests extends AnyPropSpec with Matchers {
val depRes = CoursierDependencyResolution(CoursierConfiguration().withAutoScalaLibrary(false))
val desc = ModuleDescriptorConfiguration(ModuleID("test", "foo", "1.0"), ModuleInfo("foo"))
.withDependencies(Vector(
ModuleID("io.get-coursier", "coursier_2.13", "0.1.53").withConfigurations(Some("compile")),
ModuleID("org.scala-lang", "scala-library", "2.12.11").withConfigurations(Some("compile"))
))
.withDependencies(
Vector(
ModuleID("io.get-coursier", "coursier_2.13", "0.1.53")
.withConfigurations(Some("compile")),
ModuleID("org.scala-lang", "scala-library", "2.12.11").withConfigurations(Some("compile"))
)
)
.withConfigurations(Vector(Configuration.of("Compile", "compile")))
val module = depRes.moduleDescriptor(desc)
@ -33,10 +36,17 @@ class CoursierDependencyResolutionTests extends AnyPropSpec with Matchers {
System.err.println(s"trace $t")
}
depRes.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), logger)
depRes
.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), logger)
.fold(w => (), rep => sys.error(s"Expected resolution to fail, got report $rep"))
val report = depRes.update(module, UpdateConfiguration().withMissingOk(true), UnresolvedWarningConfiguration(), logger)
val report = depRes
.update(
module,
UpdateConfiguration().withMissingOk(true),
UnresolvedWarningConfiguration(),
logger
)
.fold(w => throw w.resolveException, identity)
}

View File

@ -1,6 +1,6 @@
package lmcoursier
import lmcoursier.definitions.{Configuration, Info, Module, ModuleName, Organization, Project}
import lmcoursier.definitions.{ Configuration, Info, Module, ModuleName, Organization, Project }
import org.scalatest.matchers.should.Matchers
import org.scalatest.propspec.AnyPropSpec
@ -12,7 +12,9 @@ class IvyXmlTests extends AnyPropSpec with Matchers {
"ver",
Nil,
Map(
Configuration("foo") -> (1 to 80).map(n => Configuration("bar" + n)) // long list of configurations -> no truncation any way
Configuration("foo") -> (1 to 80).map(n =>
Configuration("bar" + n)
) // long list of configurations -> no truncation any way
),
Nil,
None,

View File

@ -6,8 +6,12 @@ import sbt.internal.librarymanagement.cross.CrossVersionUtil
import sbt.internal.util.ConsoleLogger
import sbt.librarymanagement._
import sbt.librarymanagement.Configurations.Component
import sbt.librarymanagement.Resolver.{DefaultMavenRepository, JCenterRepository, JavaNet2Repository}
import sbt.librarymanagement.{Resolver, UnresolvedWarningConfiguration, UpdateConfiguration}
import sbt.librarymanagement.Resolver.{
DefaultMavenRepository,
JCenterRepository,
JavaNet2Repository
}
import sbt.librarymanagement.{ Resolver, UnresolvedWarningConfiguration, UpdateConfiguration }
import sbt.librarymanagement.syntax._
final class ResolutionSpec extends AnyPropSpec with Matchers {
@ -16,11 +20,11 @@ final class ResolutionSpec extends AnyPropSpec with Matchers {
def configurations = Vector(Compile, Test, Runtime, Provided, Optional, Component)
def module(
lmEngine: DependencyResolution,
moduleId: ModuleID,
deps: Vector[ModuleID],
scalaFullVersion: Option[String],
overrideScalaVersion: Boolean = true
lmEngine: DependencyResolution,
moduleId: ModuleID,
deps: Vector[ModuleID],
scalaFullVersion: Option[String],
overrideScalaVersion: Boolean = true
): ModuleDescriptor = {
val scalaModuleInfo = scalaFullVersion map { fv =>
ScalaModuleInfo(
@ -117,7 +121,7 @@ final class ResolutionSpec extends AnyPropSpec with Matchers {
assert(resolution.isRight)
}
/*
/*
property("resolve with resolvers using a custom protocols") {
val sbtModule = "org.scala-sbt" % "sbt" % "1.1.0"
val dependencies = Vector(sbtModule)
@ -199,7 +203,7 @@ final class ResolutionSpec extends AnyPropSpec with Matchers {
(sbtModule.organization, sbtModule.name, sbtModule.revision)
)
}
*/
*/
property("resolve plugin") {
val pluginAttributes = Map("scalaVersion" -> "2.12", "sbtVersion" -> "1.0")
@ -244,7 +248,8 @@ final class ResolutionSpec extends AnyPropSpec with Matchers {
lmEngine.update(coursierModule, UpdateConfiguration(), UnresolvedWarningConfiguration(), log)
assert(resolution.isRight)
val componentConfig = resolution.right.get.configurations.find(_.configuration == Compile.toConfigRef).get
val componentConfig =
resolution.right.get.configurations.find(_.configuration == Compile.toConfigRef).get
val compress = componentConfig.modules.find(_.module.name == "commons-compress").get
compress.licenses should have size 1
}