All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ammonite.runtime.tools.DependencyThing.scala Maven / Gradle / Ivy

There is a newer version: 0.8.3-3
Show newest version
package ammonite.runtime.tools

import java.io.PrintWriter

import ammonite.util.Printer
import coursier._
import coursier.ivy.IvyRepository
import coursier.maven.MavenRepository

import scalaz.{-\/, \/-}
import scalaz.concurrent.Task


object DependencyConstructor extends DependencyConstructor
trait DependencyConstructor{
  implicit class GroupIdExt(groupId: String){
    def %(artifactId: String) = (groupId, artifactId)
    def %%(artifactId: String) = (groupId, artifactId + "_" + DependencyThing.scalaBinaryVersion)
  }
  implicit class ArtifactIdExt(t: (String, String)){
    def %(version: String) = (t._1, t._2, version)
  }
}

/**
 * Resolve artifacts from Ivy. Originally taken from
 *
 * http://makandracards.com/evgeny-goldin/5817-calling-ivy-from-groovy-or-java
 *
 * And transliterated into Scala. I have no idea how or why it works.
 */
class DependencyThing(resolvers: () => List[Resolver], printer: Printer, verboseOutput: Boolean) {

  def exceptionMessage(conflicts: Seq[String], failed: Seq[String], converged: Boolean) =
    Seq(
      if (conflicts.isEmpty)
        Nil
      else
        Seq(s"Conflicts:\n" + conflicts.map("  " + _).mkString("\n")),
      if (failed.isEmpty)
        Nil
      else
        Seq(s"failed to resolve dependencies:\n" + failed.map("  " + _).mkString("\n")),
      if (converged)
        Nil
      else
        Seq("Did not converge")
    ).flatten.mkString("\n")

  case class IvyResolutionException(conflicts: Seq[String], failed: Seq[String], converged: Boolean) extends Exception(
    exceptionMessage(conflicts, failed, converged)
  )

  def resolveArtifact(groupId: String,
                      artifactId: String,
                      version: String,
                      previousCoordinates: Seq[(String, String, String)],
                      exclusions: Seq[(String, String)],
                      profiles: Set[String],
                      verbosity: Int = 2) = synchronized {

    val dep = Dependency(Module(groupId, artifactId), version)
    val previousDeps = previousCoordinates.map { case (org, name, ver) => Dependency(Module(org, name), ver) }

    val start = Resolution(
      (previousDeps :+ dep).map { dep0 =>
        dep0.copy(
          exclusions = dep0.exclusions ++ exclusions
        )
      }.toSet,
      userActivations = if (profiles.isEmpty) None else Some(profiles.map(_ -> true).toMap)
    )

    val metadataLogger = new TermDisplay(new PrintWriter(System.out))

    val fetch = Fetch.from(
      resolvers().map(_()),
      Cache.fetch(cachePolicy = CachePolicy.default.head, logger = Some(metadataLogger)),
      CachePolicy.default.tail.map(p =>
        Cache.fetch(cachePolicy = p, logger = Some(metadataLogger))
      ): _*
    )

    metadataLogger.init()
    val res =
      try start.process.run(fetch, maxIterations = 200).unsafePerformSync
      finally metadataLogger.stop()

    if (!res.isDone || res.errors.nonEmpty || res.conflicts.nonEmpty)
      throw IvyResolutionException(
        res.conflicts.toVector.map(_.toString).sorted,
        res.errors.map { case (dep, errors) => s"$dep: ${errors.mkString(", ")}" },
        res.isDone
      )

    val artifactLogger = new TermDisplay(new PrintWriter(System.out))

    artifactLogger.init()

    val a =
      try {
        Task.gatherUnordered(res.dependencyArtifacts.map(_._2).filter(_.`type` == "jar").map { artifact =>
          def fetch(p: CachePolicy) =
            Cache.file(artifact, logger = Some(artifactLogger), cachePolicy = p)

          (fetch(CachePolicy.default.head) /: CachePolicy.default.tail)(_ orElse fetch(_))
            .run
            .map(artifact -> _)
        }).unsafePerformSync
      }
      finally
        artifactLogger.stop()

    val downloadErrors = a.collect { case (artifact, -\/(err)) => (artifact, err) }

    if (downloadErrors.nonEmpty)
      throw IvyResolutionException(
        Nil,
        downloadErrors.map { case (artifact, err) => s"${artifact.url}: ${err.describe}" },
        converged = true
      )

    a.collect { case (_, \/-(f)) => f }
  }

}

object DependencyThing {

  val scalaBinaryVersion =
    scala.util.Properties
              .versionString
              .stripPrefix("version ")
              .split('.')
              .take(2)
              .mkString(".")
  
}


object Resolvers {

  // this pattern comes from sbt.Resolver  
  val IvyPattern: String = 
    "[organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)"+
    "[revision]/[type]s/[artifact](-[classifier]).[ext]"
    
  // this pattern comes from IBiblioResolver  
  val MavenPattern: String =
    "[organisation]/[module]/" + 
    "[revision]/[artifact]-[revision](-[classifier]).[ext]"
  
  // this pattern comes from IBiblioResolver  
  val DefaultPattern: String =
    "[module]/[type]s/[artifact]-[revision].[ext]"

  

 lazy val defaultResolvers: List[Resolver] = List(
   Resolver.File(
     "local",
     "/.ivy2/local",
     "/[organisation]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext]",
     m2 = false
   ),
   Resolver.Http(
     "central",
     "https://repo1.maven.org/maven2/",
     MavenPattern,
     m2 = true
   ),
   Resolver.Http(
     "sonatype-releases",
     "https://oss.sonatype.org/content/repositories/releases/",
     MavenPattern,
     m2 = true
   ),
   Resolver.Http(
     "sonatype-snapshots",
     "https://oss.sonatype.org/content/repositories/snapshots/",
     MavenPattern,
     m2 = true
   )
 )
}

/**
  * A thin wrapper around [[Repository]], which wraps them and provides
  * hashability in order to set the cache tags. This lets us invalidate the ivy
  * resolution cache if the set of resolvers changes
  */
sealed trait Resolver{
  def apply(): Repository
}
object Resolver{
  case class File(name: String, root: String, pattern: String, m2: Boolean) extends Resolver{
    def apply() = {
      val testRepoDir = new java.io.File(sys.props("user.home") + root).toURI.toString

      if (m2)
        MavenRepository(testRepoDir, changing = None)
      else
        IvyRepository.parse(testRepoDir + pattern).getOrElse {
          throw new Exception(s"Error parsing Ivy pattern $testRepoDir$pattern")
        }
    }
  }
  case class Http(name: String, root: String, pattern: String, m2: Boolean) extends Resolver{
    def apply() =
      if (m2)
        MavenRepository(root, changing = None)
      else
        IvyRepository.parse((root + pattern).replace("[ivyPattern]", Resolvers.IvyPattern)).getOrElse {
          throw new Exception(s"Error parsing Ivy pattern $root$pattern")
        }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy