mirror of https://github.com/sbt/sbt.git
Add resolve command, plus some tidying
This commit is contained in:
parent
5a24701cb0
commit
68768cc871
|
|
@ -0,0 +1,54 @@
|
|||
package coursier
|
||||
|
||||
import coursier.ivy.IvyRepository
|
||||
import coursier.util.Parse
|
||||
|
||||
import scalaz._, Scalaz._
|
||||
|
||||
object CacheParse {
|
||||
|
||||
def repository(s: String): Validation[String, Repository] =
|
||||
if (s == "ivy2local" || s == "ivy2Local")
|
||||
Cache.ivy2Local.success
|
||||
else {
|
||||
val repo = Parse.repository(s)
|
||||
|
||||
val url = repo match {
|
||||
case m: MavenRepository =>
|
||||
m.root
|
||||
case i: IvyRepository =>
|
||||
i.pattern
|
||||
case r =>
|
||||
sys.error(s"Unrecognized repository: $r")
|
||||
}
|
||||
|
||||
if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("file:/"))
|
||||
repo.success
|
||||
else
|
||||
s"Unrecognized protocol in $url".failure
|
||||
}
|
||||
|
||||
def repositories(l: Seq[String]): ValidationNel[String, Seq[Repository]] =
|
||||
l.toVector.traverseU { s =>
|
||||
repository(s).leftMap(_.wrapNel)
|
||||
}
|
||||
|
||||
def cachePolicies(s: String): ValidationNel[String, Seq[CachePolicy]] =
|
||||
s.split(',').toVector.traverseU {
|
||||
case "offline" =>
|
||||
Seq(CachePolicy.LocalOnly).successNel
|
||||
case "update-changing" =>
|
||||
Seq(CachePolicy.UpdateChanging).successNel
|
||||
case "update" =>
|
||||
Seq(CachePolicy.Update).successNel
|
||||
case "missing" =>
|
||||
Seq(CachePolicy.FetchMissing).successNel
|
||||
case "force" =>
|
||||
Seq(CachePolicy.ForceDownload).successNel
|
||||
case "default" =>
|
||||
Seq(CachePolicy.LocalOnly, CachePolicy.FetchMissing).successNel
|
||||
case other =>
|
||||
s"Unrecognized mode: $other".failureNel
|
||||
}.map(_.flatten)
|
||||
|
||||
}
|
||||
|
|
@ -1,45 +1,45 @@
|
|||
package coursier
|
||||
package cli
|
||||
|
||||
import java.io.{ByteArrayOutputStream, FileOutputStream, File, IOException}
|
||||
import java.io.{ ByteArrayOutputStream, File, IOException }
|
||||
import java.net.URLClassLoader
|
||||
import java.nio.file.{ Files => NIOFiles }
|
||||
import java.nio.file.attribute.{FileTime, PosixFilePermission}
|
||||
import java.nio.file.attribute.PosixFilePermission
|
||||
import java.util.Properties
|
||||
import java.util.zip.{ZipEntry, ZipOutputStream, ZipInputStream, ZipFile}
|
||||
import java.util.zip.{ ZipEntry, ZipOutputStream, ZipInputStream }
|
||||
|
||||
import caseapp._
|
||||
import caseapp.{ HelpMessage => Help, ValueDescription => Value, ExtraName => Short, _ }
|
||||
import coursier.util.ClasspathFilter
|
||||
|
||||
case class CommonOptions(
|
||||
@HelpMessage("Keep optional dependencies (Maven)")
|
||||
@Help("Keep optional dependencies (Maven)")
|
||||
keepOptional: Boolean,
|
||||
@HelpMessage("Download mode (default: missing, that is fetch things missing from cache)")
|
||||
@ValueDescription("offline|update-changing|update|missing|force")
|
||||
@ExtraName("m")
|
||||
mode: String = "missing",
|
||||
@HelpMessage("Quiet output")
|
||||
@ExtraName("q")
|
||||
@Help("Download mode (default: missing, that is fetch things missing from cache)")
|
||||
@Value("offline|update-changing|update|missing|force")
|
||||
@Short("m")
|
||||
mode: String = "default",
|
||||
@Help("Quiet output")
|
||||
@Short("q")
|
||||
quiet: Boolean,
|
||||
@HelpMessage("Increase verbosity (specify several times to increase more)")
|
||||
@ExtraName("v")
|
||||
@Help("Increase verbosity (specify several times to increase more)")
|
||||
@Short("v")
|
||||
verbose: List[Unit],
|
||||
@HelpMessage("Maximum number of resolution iterations (specify a negative value for unlimited, default: 100)")
|
||||
@ExtraName("N")
|
||||
@Help("Maximum number of resolution iterations (specify a negative value for unlimited, default: 100)")
|
||||
@Short("N")
|
||||
maxIterations: Int = 100,
|
||||
@HelpMessage("Repositories - for multiple repositories, separate with comma and/or repeat this option (e.g. -r central,ivy2local -r sonatype-snapshots, or equivalently -r central,ivy2local,sonatype-snapshots)")
|
||||
@ExtraName("r")
|
||||
@Help("Repositories - for multiple repositories, separate with comma and/or repeat this option (e.g. -r central,ivy2local -r sonatype-snapshots, or equivalently -r central,ivy2local,sonatype-snapshots)")
|
||||
@Short("r")
|
||||
repository: List[String],
|
||||
@HelpMessage("Do not add default repositories (~/.ivy2/local, and Central)")
|
||||
@Help("Do not add default repositories (~/.ivy2/local, and Central)")
|
||||
noDefault: Boolean = false,
|
||||
@HelpMessage("Modify names in Maven repository paths for SBT plugins")
|
||||
@Help("Modify names in Maven repository paths for SBT plugins")
|
||||
sbtPluginHack: Boolean = false,
|
||||
@HelpMessage("Force module version")
|
||||
@ValueDescription("organization:name:forcedVersion")
|
||||
@ExtraName("V")
|
||||
@Help("Force module version")
|
||||
@Value("organization:name:forcedVersion")
|
||||
@Short("V")
|
||||
forceVersion: List[String],
|
||||
@HelpMessage("Maximum number of parallel downloads (default: 6)")
|
||||
@ExtraName("n")
|
||||
@Help("Maximum number of parallel downloads (default: 6)")
|
||||
@Short("n")
|
||||
parallel: Int = 6,
|
||||
@Recurse
|
||||
cacheOptions: CacheOptions
|
||||
|
|
@ -48,22 +48,32 @@ case class CommonOptions(
|
|||
}
|
||||
|
||||
case class CacheOptions(
|
||||
@HelpMessage("Cache directory (defaults to environment variable COURSIER_CACHE or ~/.coursier/cache/v1)")
|
||||
@ExtraName("C")
|
||||
@Help("Cache directory (defaults to environment variable COURSIER_CACHE or ~/.coursier/cache/v1)")
|
||||
@Short("C")
|
||||
cache: String = Cache.defaultBase.toString
|
||||
)
|
||||
|
||||
sealed trait CoursierCommand extends Command
|
||||
|
||||
case class Resolve(
|
||||
@Recurse
|
||||
common: CommonOptions
|
||||
) extends CoursierCommand {
|
||||
|
||||
// the `val helper = ` part is needed because of DelayedInit it seems
|
||||
val helper = new Helper(common, remainingArgs)
|
||||
|
||||
}
|
||||
|
||||
case class Fetch(
|
||||
@HelpMessage("Fetch source artifacts")
|
||||
@ExtraName("S")
|
||||
@Help("Fetch source artifacts")
|
||||
@Short("S")
|
||||
sources: Boolean,
|
||||
@HelpMessage("Fetch javadoc artifacts")
|
||||
@ExtraName("D")
|
||||
@Help("Fetch javadoc artifacts")
|
||||
@Short("D")
|
||||
javadoc: Boolean,
|
||||
@HelpMessage("Print java -cp compatible output")
|
||||
@ExtraName("p")
|
||||
@Help("Print java -cp compatible output")
|
||||
@Short("p")
|
||||
classpath: Boolean,
|
||||
@Recurse
|
||||
common: CommonOptions
|
||||
|
|
@ -88,11 +98,11 @@ case class Fetch(
|
|||
}
|
||||
|
||||
case class Launch(
|
||||
@ExtraName("M")
|
||||
@ExtraName("main")
|
||||
@Short("M")
|
||||
@Short("main")
|
||||
mainClass: String,
|
||||
@ExtraName("c")
|
||||
@HelpMessage("Assume coursier is a dependency of the launched app, and share the coursier dependency of the launcher with it - allows the launched app to get the resolution that launched it via ResolutionClassLoader")
|
||||
@Short("c")
|
||||
@Help("Assume coursier is a dependency of the launched app, and share the coursier dependency of the launcher with it - allows the launched app to get the resolution that launched it via ResolutionClassLoader")
|
||||
addCoursier: Boolean,
|
||||
@Recurse
|
||||
common: CommonOptions
|
||||
|
|
@ -200,21 +210,21 @@ case class Launch(
|
|||
}
|
||||
|
||||
case class Bootstrap(
|
||||
@ExtraName("M")
|
||||
@ExtraName("main")
|
||||
@Short("M")
|
||||
@Short("main")
|
||||
mainClass: String,
|
||||
@ExtraName("o")
|
||||
@Short("o")
|
||||
output: String = "bootstrap",
|
||||
@ExtraName("D")
|
||||
@Short("D")
|
||||
downloadDir: String,
|
||||
@ExtraName("f")
|
||||
@Short("f")
|
||||
force: Boolean,
|
||||
@HelpMessage(s"Internal use - prepend base classpath options to arguments (like -B jar1 -B jar2 etc.)")
|
||||
@ExtraName("b")
|
||||
@Help(s"Internal use - prepend base classpath options to arguments (like -B jar1 -B jar2 etc.)")
|
||||
@Short("b")
|
||||
prependClasspath: Boolean,
|
||||
@HelpMessage("Set environment variables in the generated launcher. No escaping is done. Value is simply put between quotes in the launcher preamble.")
|
||||
@ValueDescription("key=value")
|
||||
@ExtraName("P")
|
||||
@Help("Set environment variables in the generated launcher. No escaping is done. Value is simply put between quotes in the launcher preamble.")
|
||||
@Value("key=value")
|
||||
@Short("P")
|
||||
property: List[String],
|
||||
@Recurse
|
||||
common: CommonOptions
|
||||
|
|
@ -365,7 +375,7 @@ case class Bootstrap(
|
|||
|
||||
case class BaseCommand(
|
||||
@Hidden
|
||||
@ExtraName("B")
|
||||
@Short("B")
|
||||
baseCp: List[String]
|
||||
) extends Command {
|
||||
Coursier.baseCp = baseCp
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ import java.io.{ OutputStreamWriter, File }
|
|||
import java.util.concurrent.Executors
|
||||
|
||||
import coursier.ivy.IvyRepository
|
||||
import coursier.util.{Print, Parse}
|
||||
|
||||
import scalaz.{ \/-, -\/ }
|
||||
import scalaz.{Failure, Success, \/-, -\/}
|
||||
import scalaz.concurrent.{ Task, Strategy }
|
||||
|
||||
object Helper {
|
||||
|
|
@ -33,6 +34,28 @@ object Helper {
|
|||
}
|
||||
}
|
||||
|
||||
object Util {
|
||||
|
||||
def prematureExit(msg: String): Nothing = {
|
||||
Console.err.println(msg)
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
def prematureExitIf(cond: Boolean)(msg: => String): Unit =
|
||||
if (cond)
|
||||
prematureExit(msg)
|
||||
|
||||
def exit(msg: String): Nothing = {
|
||||
Console.err.println(msg)
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
def exitIf(cond: Boolean)(msg: => String): Unit =
|
||||
if (cond)
|
||||
exit(msg)
|
||||
|
||||
}
|
||||
|
||||
class Helper(
|
||||
common: CommonOptions,
|
||||
remainingArgs: Seq[String]
|
||||
|
|
@ -40,22 +63,16 @@ class Helper(
|
|||
import common._
|
||||
import Helper.errPrintln
|
||||
|
||||
val cachePolicies = mode match {
|
||||
case "offline" =>
|
||||
Seq(CachePolicy.LocalOnly)
|
||||
case "update-changing" =>
|
||||
Seq(CachePolicy.UpdateChanging)
|
||||
case "update" =>
|
||||
Seq(CachePolicy.Update)
|
||||
case "missing" =>
|
||||
Seq(CachePolicy.FetchMissing)
|
||||
case "force" =>
|
||||
Seq(CachePolicy.ForceDownload)
|
||||
case "default" =>
|
||||
Seq(CachePolicy.LocalOnly, CachePolicy.FetchMissing)
|
||||
case other =>
|
||||
errPrintln(s"Unrecognized mode: $other")
|
||||
sys.exit(255)
|
||||
import Util._
|
||||
|
||||
val cachePoliciesValidation = CacheParse.cachePolicies(common.mode)
|
||||
|
||||
val cachePolicies = cachePoliciesValidation match {
|
||||
case Success(cp) => cp
|
||||
case Failure(errors) =>
|
||||
prematureExit(
|
||||
s"Error parsing modes:\n${errors.list.map(" "+_).mkString("\n")}"
|
||||
)
|
||||
}
|
||||
|
||||
val caches =
|
||||
|
|
@ -66,58 +83,30 @@ class Helper(
|
|||
|
||||
val pool = Executors.newFixedThreadPool(parallel, Strategy.DefaultDaemonThreadFactory)
|
||||
|
||||
val central = MavenRepository("https://repo1.maven.org/maven2/")
|
||||
|
||||
val defaultRepositories = Seq(
|
||||
Cache.ivy2Local,
|
||||
central
|
||||
MavenRepository("https://repo1.maven.org/maven2")
|
||||
)
|
||||
|
||||
val repositories0 = common.repository.map { repo =>
|
||||
val repo0 = repo.toLowerCase
|
||||
if (repo0 == "central")
|
||||
Right(central)
|
||||
else if (repo0 == "ivy2local")
|
||||
Right(Cache.ivy2Local)
|
||||
else if (repo0.startsWith("sonatype:"))
|
||||
Right(
|
||||
MavenRepository(s"https://oss.sonatype.org/content/repositories/${repo.drop("sonatype:".length)}")
|
||||
)
|
||||
else {
|
||||
val (url, r) =
|
||||
if (repo.startsWith("ivy:")) {
|
||||
val url = repo.drop("ivy:".length)
|
||||
(url, IvyRepository(url))
|
||||
} else
|
||||
(repo, MavenRepository(repo))
|
||||
|
||||
if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("file:/"))
|
||||
Right(r)
|
||||
else
|
||||
Left(repo -> s"Unrecognized protocol or repository: $url")
|
||||
}
|
||||
}
|
||||
|
||||
val unrecognizedRepos = repositories0.collect { case Left(e) => e }
|
||||
if (unrecognizedRepos.nonEmpty) {
|
||||
errPrintln(s"${unrecognizedRepos.length} error(s) parsing repositories:")
|
||||
for ((repo, err) <- unrecognizedRepos)
|
||||
errPrintln(s"$repo: $err")
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
val repositories1 =
|
||||
(if (common.noDefault) Nil else defaultRepositories) ++
|
||||
repositories0.collect { case Right(r) => r }
|
||||
|
||||
val repositories =
|
||||
val repositoriesValidation = CacheParse.repositories(common.repository).map { repos0 =>
|
||||
val repos = (if (common.noDefault) Nil else defaultRepositories) ++ repos0
|
||||
if (common.sbtPluginHack)
|
||||
repositories1.map {
|
||||
repos.map {
|
||||
case m: MavenRepository => m.copy(sbtAttrStub = true)
|
||||
case other => other
|
||||
}
|
||||
else
|
||||
repositories1
|
||||
repos
|
||||
}
|
||||
|
||||
val repositories = repositoriesValidation match {
|
||||
case Success(repos) => repos
|
||||
case Failure(errors) =>
|
||||
prematureExit(
|
||||
s"Error parsing repositories:\n${errors.list.map(" "+_).mkString("\n")}"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
val (rawDependencies, extraArgs) = {
|
||||
val idxOpt = Some(remainingArgs.indexOf("--")).filter(_ >= 0)
|
||||
|
|
@ -128,63 +117,25 @@ class Helper(
|
|||
}
|
||||
}
|
||||
|
||||
val (splitDependencies, malformed) = rawDependencies.toList
|
||||
.map(_.split(":", 3).toSeq)
|
||||
.partition(_.length == 3)
|
||||
val (modVerErrors, moduleVersions) = Parse.moduleVersions(remainingArgs)
|
||||
|
||||
val (splitForceVersions, malformedForceVersions) = forceVersion
|
||||
.map(_.split(":", 3).toSeq)
|
||||
.partition(_.length == 3)
|
||||
|
||||
if (splitDependencies.isEmpty) {
|
||||
Console.err.println(s"Error: no dependencies specified.")
|
||||
// CaseApp.printUsage[Coursier]()
|
||||
sys exit 1
|
||||
prematureExitIf(modVerErrors.nonEmpty) {
|
||||
s"Cannot parse dependencies:\n" + modVerErrors.map(" "+_).mkString("\n")
|
||||
}
|
||||
|
||||
if (malformed.nonEmpty || malformedForceVersions.nonEmpty) {
|
||||
if (malformed.nonEmpty) {
|
||||
errPrintln("Malformed dependency(ies), should be like org:name:version")
|
||||
for (s <- malformed)
|
||||
errPrintln(s" ${s.mkString(":")}")
|
||||
}
|
||||
|
||||
if (malformedForceVersions.nonEmpty) {
|
||||
errPrintln("Malformed force version(s), should be like org:name:forcedVersion")
|
||||
for (s <- malformedForceVersions)
|
||||
errPrintln(s" ${s.mkString(":")}")
|
||||
}
|
||||
|
||||
sys.exit(1)
|
||||
val dependencies = moduleVersions.map {
|
||||
case (module, version) =>
|
||||
Dependency(module, version, configuration = "default(compile)")
|
||||
}
|
||||
|
||||
val moduleVersions = splitDependencies.map{
|
||||
case Seq(org, namePart, version) =>
|
||||
val p = namePart.split(';')
|
||||
val name = p.head
|
||||
val splitAttributes = p.tail.map(_.split("=", 2).toSeq).toSeq
|
||||
val malformedAttributes = splitAttributes.filter(_.length != 2)
|
||||
if (malformedAttributes.nonEmpty) {
|
||||
// FIXME Get these for all dependencies at once
|
||||
Console.err.println(s"Malformed attributes in ${splitDependencies.mkString(":")}")
|
||||
// :(
|
||||
sys.exit(255)
|
||||
}
|
||||
val attributes = splitAttributes.collect {
|
||||
case Seq(k, v) => k -> v
|
||||
}
|
||||
(Module(org, name, attributes.toMap), version)
|
||||
}
|
||||
|
||||
val deps = moduleVersions.map{case (mod, ver) =>
|
||||
Dependency(mod, ver, configuration = "runtime")
|
||||
val (forceVersionErrors, forceVersions0) = Parse.moduleVersions(forceVersion)
|
||||
|
||||
prematureExitIf(forceVersionErrors.nonEmpty) {
|
||||
s"Cannot parse forced versions:\n" + forceVersionErrors.map(" "+_).mkString("\n")
|
||||
}
|
||||
|
||||
val forceVersions = {
|
||||
val forceVersions0 = splitForceVersions.map {
|
||||
case Seq(org, name, version) => (Module(org, name), version)
|
||||
}
|
||||
|
||||
val grouped = forceVersions0
|
||||
.groupBy { case (mod, _) => mod }
|
||||
.map { case (mod, l) => mod -> l.map { case (_, version) => version } }
|
||||
|
|
@ -196,7 +147,7 @@ class Helper(
|
|||
}
|
||||
|
||||
val startRes = Resolution(
|
||||
deps.toSet,
|
||||
dependencies.toSet,
|
||||
forceVersions = forceVersions,
|
||||
filter = Some(dep => keepOptional || !dep.optional)
|
||||
)
|
||||
|
|
@ -219,17 +170,21 @@ class Helper(
|
|||
if (verbose0 <= 0) fetchQuiet
|
||||
else {
|
||||
modVers: Seq[(Module, String)] =>
|
||||
val print = Task{
|
||||
val print = Task {
|
||||
errPrintln(s"Getting ${modVers.length} project definition(s)")
|
||||
}
|
||||
|
||||
print.flatMap(_ => fetchQuiet(modVers))
|
||||
}
|
||||
|
||||
def indent(s: String): String =
|
||||
if (s.isEmpty)
|
||||
s
|
||||
else
|
||||
s.split('\n').map(" "+_).mkString("\n")
|
||||
|
||||
if (verbose0 >= 0) {
|
||||
errPrintln("Dependencies:")
|
||||
for ((mod, ver) <- moduleVersions)
|
||||
errPrintln(s" $mod:$ver")
|
||||
errPrintln(s"Dependencies:\n${indent(Print.dependenciesUnknownConfigs(dependencies))}")
|
||||
|
||||
if (forceVersions.nonEmpty) {
|
||||
errPrintln("Force versions:")
|
||||
|
|
@ -247,64 +202,29 @@ class Helper(
|
|||
|
||||
logger.foreach(_.stop())
|
||||
|
||||
if (!res.isDone) {
|
||||
// FIXME Better to print all the messages related to the exit conditions below, then exit
|
||||
// rather than exit at the first one
|
||||
|
||||
exitIf(!res.isDone) {
|
||||
errPrintln(s"Maximum number of iteration reached!")
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
def repr(dep: Dependency) = {
|
||||
// dep.version can be an interval, whereas the one from project can't
|
||||
val version = res
|
||||
.projectCache
|
||||
.get(dep.moduleVersion)
|
||||
.map(_._2.version)
|
||||
.getOrElse(dep.version)
|
||||
val extra =
|
||||
if (version == dep.version) ""
|
||||
else s" ($version for ${dep.version})"
|
||||
|
||||
(
|
||||
Seq(
|
||||
dep.module.organization,
|
||||
dep.module.name,
|
||||
dep.attributes.`type`
|
||||
) ++
|
||||
Some(dep.attributes.classifier)
|
||||
.filter(_.nonEmpty)
|
||||
.toSeq ++
|
||||
Seq(
|
||||
version
|
||||
)
|
||||
).mkString(":") + extra
|
||||
exitIf(res.errors.nonEmpty) {
|
||||
s"\n${res.errors.size} error(s):\n" +
|
||||
res.errors.map { case (dep, errs) =>
|
||||
s" ${dep.module}:${dep.version}:\n${errs.map(" " + _.replace("\n", " \n")).mkString("\n")}"
|
||||
}.mkString("\n")
|
||||
}
|
||||
|
||||
val trDeps = res
|
||||
.minDependencies
|
||||
.toList
|
||||
.sortBy(repr)
|
||||
|
||||
if (verbose0 >= 1) {
|
||||
println("")
|
||||
println(
|
||||
trDeps
|
||||
.map(repr)
|
||||
.distinct
|
||||
.mkString("\n")
|
||||
)
|
||||
exitIf(res.conflicts.nonEmpty) {
|
||||
s"${res.conflicts.size} conflict(s):\n${Print.dependenciesUnknownConfigs(res.conflicts.toVector)}"
|
||||
}
|
||||
|
||||
if (res.conflicts.nonEmpty) {
|
||||
// Needs test
|
||||
println(s"${res.conflicts.size} conflict(s):\n ${res.conflicts.toList.map(repr).sorted.mkString(" \n")}")
|
||||
}
|
||||
val trDeps = res.minDependencies.toVector
|
||||
|
||||
val errors = res.errors
|
||||
if (errors.nonEmpty) {
|
||||
println(s"\n${errors.size} error(s):")
|
||||
for ((dep, errs) <- errors) {
|
||||
println(s" ${dep.module}:${dep.version}:\n${errs.map(" " + _.replace("\n", " \n")).mkString("\n")}")
|
||||
}
|
||||
}
|
||||
if (verbose0 >= 0)
|
||||
errPrintln(s"Result:\n${indent(Print.dependenciesUnknownConfigs(trDeps))}")
|
||||
|
||||
def fetch(sources: Boolean, javadoc: Boolean): Seq[File] = {
|
||||
if (verbose0 >= 0) {
|
||||
|
|
@ -354,11 +274,11 @@ class Helper(
|
|||
|
||||
logger.foreach(_.stop())
|
||||
|
||||
if (errors.nonEmpty) {
|
||||
println(s"${errors.size} error(s):")
|
||||
for ((artifact, error) <- errors) {
|
||||
println(s" ${artifact.url}: $error")
|
||||
}
|
||||
exitIf(errors.nonEmpty) {
|
||||
s"${errors.size} error(s):\n" +
|
||||
errors.map { case (artifact, error) =>
|
||||
s" ${artifact.url}: $error"
|
||||
}.mkString("\n")
|
||||
}
|
||||
|
||||
files0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package coursier.util
|
||||
|
||||
import coursier.core.{ Dependency, Resolution }
|
||||
|
||||
object Config {
|
||||
|
||||
// loose attempt at minimizing a set of dependencies from various configs
|
||||
// `configs` is assumed to be fully unfold
|
||||
def allDependenciesByConfig(
|
||||
res: Resolution,
|
||||
depsByConfig: Map[String, Set[Dependency]],
|
||||
configs: Map[String, Set[String]]
|
||||
): Map[String, Set[Dependency]] = {
|
||||
|
||||
val allDepsByConfig = depsByConfig.map {
|
||||
case (config, deps) =>
|
||||
config -> res.subset(deps).minDependencies
|
||||
}
|
||||
|
||||
val filteredAllDepsByConfig = depsByConfig.map {
|
||||
case (config, allDeps) =>
|
||||
val inherited = configs
|
||||
.getOrElse(config, Set.empty)
|
||||
.flatMap(allDepsByConfig.getOrElse(_, Set.empty))
|
||||
|
||||
config -> (allDeps -- inherited)
|
||||
}
|
||||
|
||||
filteredAllDepsByConfig
|
||||
}
|
||||
|
||||
def dependenciesWithConfig(
|
||||
res: Resolution,
|
||||
depsByConfig: Map[String, Set[Dependency]],
|
||||
configs: Map[String, Set[String]]
|
||||
): Set[Dependency] =
|
||||
allDependenciesByConfig(res, depsByConfig, configs)
|
||||
.flatMap {
|
||||
case (config, deps) =>
|
||||
deps.map(dep => dep.copy(configuration = s"$config->${dep.configuration}"))
|
||||
}
|
||||
.groupBy(_.copy(configuration = ""))
|
||||
.map {
|
||||
case (dep, l) =>
|
||||
dep.copy(configuration = l.map(_.configuration).mkString(","))
|
||||
}
|
||||
.toSet
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package coursier.util
|
||||
|
||||
import coursier.core.{Repository, Module}
|
||||
import coursier.ivy.IvyRepository
|
||||
import coursier.maven.MavenRepository
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
object Parse {
|
||||
|
||||
/**
|
||||
* Parses coordinates like
|
||||
* org:name:version
|
||||
* possibly with attributes, like
|
||||
* org:name;attr1=val1;attr2=val2:version
|
||||
*/
|
||||
def moduleVersion(s: String): Either[String, (Module, String)] = {
|
||||
|
||||
val parts = s.split(":", 3)
|
||||
|
||||
parts match {
|
||||
case Array(org, rawName, version) =>
|
||||
val splitName = rawName.split(';')
|
||||
|
||||
if (splitName.tail.exists(!_.contains("=")))
|
||||
Left(s"Malformed attribute in $s")
|
||||
else {
|
||||
val name = splitName.head
|
||||
val attributes = splitName.tail.map(_.split("=", 2)).map {
|
||||
case Array(key, value) => key -> value
|
||||
}.toMap
|
||||
|
||||
Right((Module(org, name, attributes), version))
|
||||
}
|
||||
|
||||
case _ =>
|
||||
Left(s"Malformed coordinates: $s")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a sequence of coordinates.
|
||||
*
|
||||
* @return Sequence of errors, and sequence of modules/versions
|
||||
*/
|
||||
def moduleVersions(l: Seq[String]): (Seq[String], Seq[(Module, String)]) = {
|
||||
|
||||
val errors = new ArrayBuffer[String]
|
||||
val moduleVersions = new ArrayBuffer[(Module, String)]
|
||||
|
||||
for (elem <- l)
|
||||
moduleVersion(elem) match {
|
||||
case Left(err) => errors += err
|
||||
case Right(modVer) => moduleVersions += modVer
|
||||
}
|
||||
|
||||
(errors.toSeq, moduleVersions.toSeq)
|
||||
}
|
||||
|
||||
def repository(s: String): Repository =
|
||||
if (s == "central")
|
||||
MavenRepository("https://repo1.maven.org/maven2")
|
||||
else if (s.startsWith("sonatype:"))
|
||||
MavenRepository(s"https://oss.sonatype.org/content/repositories/${s.stripPrefix("sonatype:")}")
|
||||
else if (s.startsWith("ivy:"))
|
||||
IvyRepository(s.stripPrefix("ivy:"))
|
||||
else
|
||||
MavenRepository(s)
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package coursier.util
|
||||
|
||||
import coursier.core.{ Orders, Dependency }
|
||||
|
||||
object Print {
|
||||
|
||||
def dependency(dep: Dependency): String =
|
||||
s"${dep.module}:${dep.version}:${dep.configuration}"
|
||||
|
||||
def dependenciesUnknownConfigs(deps: Seq[Dependency]): String = {
|
||||
|
||||
val minDeps = Orders.minDependencies(
|
||||
deps.toSet,
|
||||
_ => Map.empty
|
||||
)
|
||||
|
||||
val deps0 = minDeps
|
||||
.groupBy(_.copy(configuration = ""))
|
||||
.toVector
|
||||
.map { case (k, l) =>
|
||||
k.copy(configuration = l.toVector.map(_.configuration).sorted.mkString(","))
|
||||
}
|
||||
.sortBy { dep =>
|
||||
(dep.module.organization, dep.module.name, dep.module.toString, dep.version)
|
||||
}
|
||||
|
||||
deps0.map(dependency).mkString("\n")
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue