diff --git a/README.md b/README.md index 8dca0ff7b..ccf3e89d9 100644 --- a/README.md +++ b/README.md @@ -781,5 +781,13 @@ add dependencies on-the-fly ([#4](https://github.com/apache/incubator-toree/pull - [Quill](https://github.com/getquill/quill) is using coursier for faster dependency resolution ([#591](https://github.com/getquill/quill/pull/591)), - Your project here :-) +## Acknowledgements + + + + + +Thanks to [JProfiler](https://www.ej-technologies.com/products/jprofiler/overview.html) for having kindly granted an [Open Source license](https://www.ej-technologies.com/buy/jprofiler/openSource) to help for the development of coursier. + Released under the Apache license, v2. diff --git a/core/shared/src/main/scala/coursier/package.scala b/core/shared/src/main/scala/coursier/package.scala index c8d5ee792..20a477f03 100644 --- a/core/shared/src/main/scala/coursier/package.scala +++ b/core/shared/src/main/scala/coursier/package.scala @@ -54,6 +54,7 @@ package object coursier { type ModuleVersion = (core.Module, String) + type ProjectCache = Map[ModuleVersion, (Artifact.Source, Project)] type Repository = core.Repository val Repository = core.Repository @@ -69,7 +70,7 @@ package object coursier { dependencies: Set[Dependency] = Set.empty, forceVersions: Map[Module, String] = Map.empty, conflicts: Set[Dependency] = Set.empty, - projectCache: Map[ModuleVersion, (Artifact.Source, Project)] = Map.empty, + projectCache: ProjectCache = Map.empty, errorCache: Map[ModuleVersion, Seq[String]] = Map.empty, finalDependencies: Map[Dependency, Seq[Dependency]] = Map.empty, filter: Option[Dependency => Boolean] = None, diff --git a/doc/README.md b/doc/README.md index c85b43913..48dc98691 100644 --- a/doc/README.md +++ b/doc/README.md @@ -815,5 +815,13 @@ add dependencies on-the-fly ([#4](https://github.com/apache/incubator-toree/pull - [Quill](https://github.com/getquill/quill) is using coursier for faster dependency resolution ([#591](https://github.com/getquill/quill/pull/591)), - Your project here :-) +## Acknowledgements + + + + + +Thanks to [JProfiler](https://www.ej-technologies.com/products/jprofiler/overview.html) for having kindly granted an [Open Source license](https://www.ej-technologies.com/buy/jprofiler/openSource) to help for the development of coursier. + Released under the Apache license, v2. diff --git a/sbt-coursier/src/main/scala-2.10/coursier/CoursierPlugin.scala b/sbt-coursier/src/main/scala-2.10/coursier/CoursierPlugin.scala index fe47affd9..6a53a3428 100644 --- a/sbt-coursier/src/main/scala-2.10/coursier/CoursierPlugin.scala +++ b/sbt-coursier/src/main/scala-2.10/coursier/CoursierPlugin.scala @@ -32,6 +32,7 @@ object CoursierPlugin extends AutoPlugin { val coursierConfigurations = Keys.coursierConfigurations + val coursierParentProjectCache = Keys.coursierParentProjectCache val coursierResolution = Keys.coursierResolution val coursierSbtClassifiersResolution = Keys.coursierSbtClassifiersResolution @@ -118,6 +119,7 @@ object CoursierPlugin extends AutoPlugin { coursierPublications <<= Tasks.coursierPublicationsTask(packageConfigs: _*), coursierSbtClassifiersModule <<= classifiersModule in updateSbtClassifiers, coursierConfigurations <<= Tasks.coursierConfigurationsTask(None), + coursierParentProjectCache <<= Tasks.parentProjectCacheTask, coursierResolution <<= Tasks.resolutionTask(), coursierSbtClassifiersResolution <<= Tasks.resolutionTask( sbtClassifiers = true diff --git a/sbt-coursier/src/main/scala-2.10/coursier/Keys.scala b/sbt-coursier/src/main/scala-2.10/coursier/Keys.scala index 155ff3b1f..27a4ecb1d 100644 --- a/sbt-coursier/src/main/scala-2.10/coursier/Keys.scala +++ b/sbt-coursier/src/main/scala-2.10/coursier/Keys.scala @@ -40,6 +40,8 @@ object Keys { val coursierConfigurations = TaskKey[Map[String, Set[String]]]("coursier-configurations") + + val coursierParentProjectCache = TaskKey[Map[Seq[Resolver], Seq[ProjectCache]]]("coursier-parent-project-cache") val coursierResolution = TaskKey[Resolution]("coursier-resolution") val coursierSbtClassifiersResolution = TaskKey[Resolution]("coursier-sbt-classifiers-resolution") diff --git a/sbt-coursier/src/main/scala-2.10/coursier/Tasks.scala b/sbt-coursier/src/main/scala-2.10/coursier/Tasks.scala index 23883a39c..a7624c3bb 100644 --- a/sbt-coursier/src/main/scala-2.10/coursier/Tasks.scala +++ b/sbt-coursier/src/main/scala-2.10/coursier/Tasks.scala @@ -1,27 +1,25 @@ package coursier -import java.io.{ File, InputStream, OutputStreamWriter } +import java.io.{File, InputStream, OutputStreamWriter} import java.net.URL -import java.util.concurrent.{ ExecutorService, Executors } +import java.util.concurrent.{ExecutorService, Executors} -import coursier.core.{ Authentication, Publication } -import coursier.ivy.{ IvyRepository, PropertiesPattern } +import coursier.core.{Authentication, Publication} +import coursier.ivy.{IvyRepository, PropertiesPattern} import coursier.Keys._ import coursier.Structure._ import coursier.internal.FileUtil -import coursier.util.{ Config, Print } +import coursier.util.{Config, Print} import org.apache.ivy.core.module.id.ModuleRevisionId - -import sbt.{ UpdateReport, Classpaths, Resolver, Def } +import sbt.{ClasspathDep, Classpaths, Def, ProjectRef, Resolver, UpdateReport} import sbt.Keys._ import scala.collection.mutable import scala.collection.JavaConverters._ import scala.collection.mutable.ArrayBuffer import scala.util.Try - -import scalaz.{ \/-, -\/ } -import scalaz.concurrent.{ Task, Strategy } +import scalaz.{-\/, \/-} +import scalaz.concurrent.{Strategy, Task} object Tasks { @@ -364,6 +362,28 @@ object Tasks { } } + def parentProjectCacheTask: Def.Initialize[sbt.Task[Map[Seq[sbt.Resolver],Seq[coursier.ProjectCache]]]] = + (sbt.Keys.state, + sbt.Keys.thisProjectRef).flatMap{ (state, projectRef) => + + val projectDeps = structure(state).allProjects + .find(_.id == projectRef.project) + .map(_.dependencies.map(_.project.project).toSet) + .getOrElse(Set.empty) + + val projects = structure(state).allProjectRefs.filter(p => projectDeps(p.project)) + + coursierRecursiveResolvers.forAllProjects(state, projects).flatMap{ m => + coursierResolution.forAllProjects(state, m.keys.toSeq).map{ n => + n.foldLeft(Map.empty[Seq[Resolver], Seq[ProjectCache]]){ case (caches, (ref, resolution)) => + m.get(ref).fold(caches)(resolvers => + caches.updated(resolvers, resolution.projectCache +: caches.getOrElse(resolvers, Seq.empty))) + } + } + } + } + + def resolutionTask( sbtClassifiers: Boolean = false ) = Def.task { @@ -447,6 +467,11 @@ object Tasks { else coursierRecursiveResolvers.value.distinct + val parentProjectCache: ProjectCache = coursierParentProjectCache.value + .get(resolvers) + .map(_.foldLeft[ProjectCache](Map.empty)(_ ++ _)) + .getOrElse(Map.empty) + // TODO Warn about possible duplicated modules from source repositories? val verbosityLevel = coursierVerbosity.value @@ -468,7 +493,8 @@ object Tasks { // order matters here userForceVersions ++ forcedScalaModules(so, sv) ++ - interProjectDependencies.map(_.moduleVersion) + interProjectDependencies.map(_.moduleVersion), + projectCache = parentProjectCache ) if (verbosityLevel >= 2) { diff --git a/sbt-coursier/src/main/scala-2.10/coursier/ToSbt.scala b/sbt-coursier/src/main/scala-2.10/coursier/ToSbt.scala index cd7aa7bcc..9da9a6d23 100644 --- a/sbt-coursier/src/main/scala-2.10/coursier/ToSbt.scala +++ b/sbt-coursier/src/main/scala-2.10/coursier/ToSbt.scala @@ -1,6 +1,7 @@ package coursier import java.util.GregorianCalendar +import java.util.concurrent.ConcurrentHashMap import coursier.maven.MavenSource @@ -8,43 +9,57 @@ import sbt._ object ToSbt { - def moduleId(dependency: Dependency, extraProperties: Seq[(String, String)]): sbt.ModuleID = - sbt.ModuleID( - dependency.module.organization, - dependency.module.name, - dependency.version, - configurations = Some(dependency.configuration), - extraAttributes = dependency.module.attributes ++ extraProperties - ) + private def caching[K, V](f: K => V): K => V = { - def artifact(module: Module, artifact: Artifact, extraProperties: Seq[(String, String)]): sbt.Artifact = - sbt.Artifact( - module.name, - // FIXME Get these two from publications - artifact.attributes.`type`, - MavenSource.typeExtension(artifact.attributes.`type`), - Some(artifact.attributes.classifier) - .filter(_.nonEmpty) - .orElse(MavenSource.typeDefaultClassifierOpt(artifact.attributes.`type`)), - Nil, - Some(url(artifact.url)), - module.attributes ++ extraProperties - ) + val cache = new ConcurrentHashMap[K, V] - def moduleReport( - dependency: Dependency, - dependees: Seq[(Dependency, Project)], - project: Project, - artifacts: Seq[(Artifact, Option[File])] - ): sbt.ModuleReport = { + key => + val previousValueOpt = Option(cache.get(key)) + + previousValueOpt.getOrElse { + val value = f(key) + val concurrentValueOpt = Option(cache.putIfAbsent(key, value)) + concurrentValueOpt.getOrElse(value) + } + } + + val moduleId = caching[(Dependency, Map[String, String]), sbt.ModuleID] { + case (dependency, extraProperties) => + sbt.ModuleID( + dependency.module.organization, + dependency.module.name, + dependency.version, + configurations = Some(dependency.configuration), + extraAttributes = dependency.module.attributes ++ extraProperties + ) + } + + val artifact = caching[(Module, Map[String, String], Artifact), sbt.Artifact] { + case (module, extraProperties, artifact) => + sbt.Artifact( + module.name, + // FIXME Get these two from publications + artifact.attributes.`type`, + MavenSource.typeExtension(artifact.attributes.`type`), + Some(artifact.attributes.classifier) + .filter(_.nonEmpty) + .orElse(MavenSource.typeDefaultClassifierOpt(artifact.attributes.`type`)), + Nil, + Some(url(artifact.url)), + module.attributes ++ extraProperties + ) + } + + val moduleReport = caching[(Dependency, Seq[(Dependency, Project)], Project, Seq[(Artifact, Option[File])]), sbt.ModuleReport] { + case (dependency, dependees, project, artifacts) => val sbtArtifacts = artifacts.collect { case (artifact, Some(file)) => - (ToSbt.artifact(dependency.module, artifact, project.properties), file) + (ToSbt.artifact(dependency.module, project.properties.toMap, artifact), file) } val sbtMissingArtifacts = artifacts.collect { case (artifact, None) => - ToSbt.artifact(dependency.module, artifact, project.properties) + ToSbt.artifact(dependency.module, project.properties.toMap, artifact) } val publicationDate = project.info.publication.map { dt => @@ -54,7 +69,7 @@ object ToSbt { val callers = dependees.map { case (dependee, dependeeProj) => new Caller( - ToSbt.moduleId(dependee, dependeeProj.properties), + ToSbt.moduleId(dependee, dependeeProj.properties.toMap), dependeeProj.configurations.keys.toVector, dependee.module.attributes ++ dependeeProj.properties, // FIXME Set better values here @@ -66,7 +81,7 @@ object ToSbt { } new sbt.ModuleReport( - module = ToSbt.moduleId(dependency, project.properties), + module = ToSbt.moduleId(dependency, project.properties.toMap), artifacts = sbtArtifacts, missingArtifacts = sbtMissingArtifacts, status = None,