From 9bab2d9178bc9ddbdc212c952b1519f268514292 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Wed, 30 Dec 2015 01:34:39 +0100 Subject: [PATCH] Move thing around, add support for updateSbtClassifiers --- .../main/scala/coursier/CoursierPlugin.scala | 165 +++++++++--------- plugin/src/main/scala/coursier/Keys.scala | 4 +- plugin/src/main/scala/coursier/ToSbt.scala | 93 ++++++++++ 3 files changed, 177 insertions(+), 85 deletions(-) diff --git a/plugin/src/main/scala/coursier/CoursierPlugin.scala b/plugin/src/main/scala/coursier/CoursierPlugin.scala index a082e3505..3d85bbfc6 100644 --- a/plugin/src/main/scala/coursier/CoursierPlugin.scala +++ b/plugin/src/main/scala/coursier/CoursierPlugin.scala @@ -12,6 +12,12 @@ import scalaz.concurrent.Task object CoursierPlugin extends AutoPlugin { + private def grouped[K, V](map: Seq[(K, V)]): Map[K, Seq[V]] = + map.groupBy { case (k, _) => k }.map { + case (k, l) => + k -> l.map { case (_, v) => v } + } + override def trigger = allRequirements override def requires = sbt.plugins.IvyPlugin @@ -28,6 +34,7 @@ object CoursierPlugin extends AutoPlugin { val coursierCache = Keys.coursierCache val coursierProject = Keys.coursierProject val coursierProjects = Keys.coursierProjects + val coursierSbtClassifiersModule = Keys.coursierSbtClassifiersModule } import autoImport._ @@ -45,13 +52,28 @@ object CoursierPlugin extends AutoPlugin { } - private def updateTask(withClassifiers: Boolean) = Def.task { + private def updateTask(withClassifiers: Boolean, sbtClassifiers: Boolean = false) = Def.task { // let's update only one module at once, for a better output // Downloads are already parallel, no need to parallelize further anyway synchronized { - val (currentProject, _) = coursierProject.value + lazy val cm = coursierSbtClassifiersModule.value + + val currentProject = + if (sbtClassifiers) { + FromSbt.project( + cm.id, + cm.modules, + cm.configurations.map(cfg => cfg.name -> cfg.extendsConfigs.map(_.name)).toMap, + scalaVersion.value, + scalaBinaryVersion.value + ) + } else { + val (p, _) = coursierProject.value + p + } + val projects = coursierProjects.value val parallelDownloads = coursierParallelDownloads.value @@ -157,6 +179,7 @@ object CoursierPlugin extends AutoPlugin { } val errors = res.errors + if (errors.nonEmpty) { println(s"\n${errors.size} error(s):") for ((dep, errs) <- errors) { @@ -166,7 +189,12 @@ object CoursierPlugin extends AutoPlugin { val classifiers = if (withClassifiers) - Some(transitiveClassifiers.value) + Some { + if (sbtClassifiers) + cm.classifiers + else + transitiveClassifiers.value + } else None @@ -176,102 +204,69 @@ object CoursierPlugin extends AutoPlugin { case Some(cl) => res.classifiersArtifacts(cl) } - val trDepsWithArtifactsTasks = allArtifacts - .toVector - .map { a => - files.file(a, checksums = checksums, logger = logger)(cachePolicy = cachePolicy).run.map((a, _)) - } + val artifactFileOrErrorTasks = allArtifacts.toVector.map { a => + files.file(a, checksums = checksums, logger = logger)(cachePolicy = cachePolicy).run.map((a, _)) + } if (verbosity >= 0) errPrintln(s"Fetching artifacts") - // rename - val trDepsWithArtifacts = Task.gatherUnordered(trDepsWithArtifactsTasks).attemptRun match { + + val artifactFilesOrErrors = Task.gatherUnordered(artifactFileOrErrorTasks).attemptRun match { case -\/(ex) => throw new Exception(s"Error while downloading / verifying artifacts", ex) - case \/-(l) => l.toMap + case \/-(l) => + l.toMap } + if (verbosity >= 0) errPrintln(s"Fetching artifacts: done") - val configs = ivyConfigurations.value.map(c => c.name -> c.extendsConfigs.map(_.name)).toMap - def allExtends(c: String) = { - // possibly bad complexity - def helper(current: Set[String]): Set[String] = { - val newSet = current ++ current.flatMap(configs.getOrElse(_, Nil)) - if ((newSet -- current).nonEmpty) - helper(newSet) - else - newSet + val configs = { + val configs0 = ivyConfigurations.value.map { config => + config.name -> config.extendsConfigs.map(_.name) + }.toMap + + def allExtends(c: String) = { + // possibly bad complexity + def helper(current: Set[String]): Set[String] = { + val newSet = current ++ current.flatMap(configs0.getOrElse(_, Nil)) + if ((newSet -- current).nonEmpty) + helper(newSet) + else + newSet + } + + helper(Set(c)) } - helper(Set(c)) + configs0.map { + case (config, _) => + config -> allExtends(config) + } } - val depsByConfig = currentProject - .dependencies - .groupBy { case (c, _) => c } - .map { case (c, l) => - c -> l.map { case (_, d) => d } + def artifactFileOpt(artifact: Artifact) = { + val fileOrError = artifactFilesOrErrors.getOrElse(artifact, -\/("Not downloaded")) + + fileOrError match { + case \/-(file) => + if (file.toString.contains("file:/")) + throw new Exception(s"Wrong path: $file") + Some(file) + case -\/(err) => + errPrintln(s"${artifact.url}: $err") + None } + } - val sbtModuleReportsPerScope = configs.map { case (c, _) => c -> { - val a = allExtends(c).flatMap(depsByConfig.getOrElse(_, Nil)) - val partialRes = res.part(a) - val depArtifacts = - classifiers match { - case None => partialRes.dependencyArtifacts - case Some(cl) => partialRes.dependencyClassifiersArtifacts(cl) - } + val depsByConfig = grouped(currentProject.dependencies) - depArtifacts - .groupBy { case (dep, _) => dep } - .map { case (dep, l) => dep -> l.map { case (_, a) => a } } - .map { case (dep, artifacts) => - val fe = artifacts.map { a => - a -> trDepsWithArtifacts.getOrElse(a, -\/("Not downloaded")) - } - new ModuleReport( - ModuleID(dep.module.organization, dep.module.name, dep.version, configurations = Some(dep.configuration)), - fe.collect { case (artifact, \/-(file)) => - if (file.toString.contains("file:/")) - throw new Exception(s"Wrong path: $file") - ToSbt.artifact(dep.module, artifact) -> file - }, - fe.collect { case (artifact, -\/(e)) => - errPrintln(s"${artifact.url}: $e") - ToSbt.artifact(dep.module, artifact) - }, - None, - None, - None, - None, - false, - None, - None, - None, - None, - Map.empty, - None, - None, - Nil, - Nil, - Nil - ) - } - }} - - new UpdateReport( - null, - sbtModuleReportsPerScope.toVector.map { case (c, r) => - new ConfigurationReport( - c, - r.toVector, - Nil, - Nil - ) - }, - new UpdateStats(-1L, -1L, -1L, cached = false), - Map.empty + ToSbt.updateReport( + depsByConfig, + res, + configs, + classifiers, + artifactFileOpt ) } } @@ -286,8 +281,10 @@ object CoursierPlugin extends AutoPlugin { coursierCache := new File(sys.props("user.home") + "/.coursier/sbt"), update <<= updateTask(withClassifiers = false), updateClassifiers <<= updateTask(withClassifiers = true), + updateSbtClassifiers in Defaults.TaskGlobal <<= updateTask(withClassifiers = true, sbtClassifiers = true), coursierProject <<= Tasks.coursierProjectTask, - coursierProjects <<= Tasks.coursierProjectsTask + coursierProjects <<= Tasks.coursierProjectsTask, + coursierSbtClassifiersModule <<= classifiersModule in updateSbtClassifiers ) } diff --git a/plugin/src/main/scala/coursier/Keys.scala b/plugin/src/main/scala/coursier/Keys.scala index ba66e8178..506684174 100644 --- a/plugin/src/main/scala/coursier/Keys.scala +++ b/plugin/src/main/scala/coursier/Keys.scala @@ -1,7 +1,7 @@ package coursier import java.io.File -import sbt.{ Resolver, SettingKey, TaskKey } +import sbt.{ GetClassifiersModule, Resolver, SettingKey, TaskKey } object Keys { val coursierParallelDownloads = SettingKey[Int]("coursier-parallel-downloads", "") // 6 @@ -17,4 +17,6 @@ object Keys { val coursierProject = TaskKey[(Project, Seq[(String, Seq[Artifact])])]("coursier-project", "") val coursierProjects = TaskKey[Seq[(Project, Seq[(String, Seq[Artifact])])]]("coursier-projects", "") + + val coursierSbtClassifiersModule = TaskKey[GetClassifiersModule]("coursier-sbt-classifiers-module", "") } diff --git a/plugin/src/main/scala/coursier/ToSbt.scala b/plugin/src/main/scala/coursier/ToSbt.scala index 4f003d4f4..552327ca8 100644 --- a/plugin/src/main/scala/coursier/ToSbt.scala +++ b/plugin/src/main/scala/coursier/ToSbt.scala @@ -4,6 +4,15 @@ import sbt._ object ToSbt { + def moduleId(dependency: Dependency): sbt.ModuleID = + sbt.ModuleID( + dependency.module.organization, + dependency.module.name, + dependency.version, + configurations = Some(dependency.configuration), + extraAttributes = dependency.module.attributes + ) + def artifact(module: Module, artifact: Artifact): sbt.Artifact = sbt.Artifact( module.name, @@ -15,4 +24,88 @@ object ToSbt { Map.empty ) + def moduleReport(dependency: Dependency, artifacts: Seq[(Artifact, Option[File])]): sbt.ModuleReport = + new sbt.ModuleReport( + ToSbt.moduleId(dependency), + artifacts.collect { + case (artifact, Some(file)) => + (ToSbt.artifact(dependency.module, artifact), file) + }, + artifacts.collect { + case (artifact, None) => + ToSbt.artifact(dependency.module, artifact) + }, + None, + None, + None, + None, + false, + None, + None, + None, + None, + Map.empty, + None, + None, + Nil, + Nil, + Nil + ) + + private def grouped[K, V](map: Seq[(K, V)]): Map[K, Seq[V]] = + map.groupBy { case (k, _) => k }.map { + case (k, l) => + k -> l.map { case (_, v) => v } + } + + def moduleReports( + res: Resolution, + classifiersOpt: Option[Seq[String]], + artifactFileOpt: Artifact => Option[File] + ) = { + val depArtifacts = + classifiersOpt match { + case None => res.dependencyArtifacts + case Some(cl) => res.dependencyClassifiersArtifacts(cl) + } + + val groupedDepArtifacts = grouped(depArtifacts) + + groupedDepArtifacts.map { + case (dep, artifacts) => + ToSbt.moduleReport(dep, artifacts.map(a => a -> artifactFileOpt(a))) + } + } + + def updateReport( + configDependencies: Map[String, Seq[Dependency]], + resolution: Resolution, + configs: Map[String, Set[String]], + classifiersOpt: Option[Seq[String]], + artifactFileOpt: Artifact => Option[File] + ): sbt.UpdateReport = { + + val configReports = configs.map { + case (config, extends0) => + val configDeps = extends0.flatMap(configDependencies.getOrElse(_, Nil)) + val partialRes = resolution.part(configDeps) + + val reports = ToSbt.moduleReports(partialRes, classifiersOpt, artifactFileOpt) + + new ConfigurationReport( + config, + reports.toVector, + Nil, + Nil + ) + } + + new UpdateReport( + null, + configReports.toVector, + new UpdateStats(-1L, -1L, -1L, cached = false), + Map.empty + ) + } + }