Skip to content

Commit

Permalink
Merge pull request #298 from etorreborre/master
Browse files Browse the repository at this point in the history
Added the possibility to specify resolvers from the repl
  • Loading branch information
lihaoyi committed Dec 12, 2015
2 parents f02e034 + 059f6cd commit ea73548
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 81 deletions.
9 changes: 6 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ val sharedSettings = Seq(
organization := "com.lihaoyi",
version := _root_.ammonite.Constants.version,
libraryDependencies += "com.lihaoyi" %% "utest" % "0.3.0" % "test",
testFrameworks += new TestFramework("utest.runner.Framework"),
testFrameworks := Seq(new TestFramework("utest.runner.Framework")),
scalacOptions += "-target:jvm-1.7",
autoCompilerPlugins := true,
addCompilerPlugin("com.lihaoyi" %% "acyclic" % "0.1.3"),
Expand Down Expand Up @@ -66,7 +66,7 @@ val sharedSettings = Seq(
<url>https://github.com/lihaoyi</url>
</developer>
</developers>
)
) ++ prompt

/**
* Concise, type-safe operating-system operations in Scala: filesystem,
Expand All @@ -78,6 +78,10 @@ lazy val ops = project
name := "ammonite-ops"
)

lazy val prompt = shellPrompt in ThisBuild := { state =>
val name = Project.extract(state).currentRef.project
(if (name == "ammonite") "" else name) + "> "
}

/**
* A standalone re-implementation of a composable readline-style REPL,
Expand Down Expand Up @@ -251,7 +255,6 @@ lazy val readme = ScalatexReadme(
(unmanagedSources in Compile) += baseDirectory.value/".."/"project"/"Constants.scala"
)


lazy val tested = project
.in(file("target/tested"))
.aggregate(published, integration)
Expand Down
5 changes: 5 additions & 0 deletions readme/Repl.scalatex
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@

@hl.ref(ammoniteTests/"ProjectTests.scala", Seq("akkahttp", "@"), "\"\"\"")

@p
Ammonite-REPL is configured with a set of default resolvers but you can add your own

@hl.ref(ammoniteTests/"ProjectTests.scala", Seq("resolvers", "@"), "\"\"\"")

@sect{Pretty-printed output}

@hl.ref(advancedTests, Seq("'pprint", "@"), "\"\"\"")
Expand Down
194 changes: 119 additions & 75 deletions repl/src/main/scala/ammonite/repl/IvyThing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ package ammonite.repl
import org.apache.ivy.Ivy
import org.apache.ivy.core.module.descriptor.{DefaultDependencyDescriptor, DefaultModuleDescriptor}
import org.apache.ivy.core.module.id.ModuleRevisionId
import org.apache.ivy.core.report.DownloadStatus
import org.apache.ivy.core.resolve.{IvyNode, ResolveOptions}
import org.apache.ivy.core.resolve.{ResolveOptions}
import org.apache.ivy.core.settings.IvySettings
import org.apache.ivy.plugins.repository.file.FileRepository
import org.apache.ivy.util._

import org.apache.ivy.plugins.resolver.{ChainResolver, FileSystemResolver, IBiblioResolver}
import org.apache.ivy.plugins.resolver._
import acyclic.file
import IvyThing._
import Resolvers._

object IvyConstructor extends IvyConstructor
trait IvyConstructor{
implicit class GroupIdExt(groupId: String){
Expand All @@ -29,17 +31,11 @@ trait IvyConstructor{
*
* And transliterated into Scala. I have no idea how or why it works.
*/
object IvyThing {
case class IvyThing(resolvers: List[RepositoryResolver]) {

case class IvyResolutionException(failed: Seq[String]) extends Exception(
"failed to resolve ivy dependencies " + failed.mkString(", ")
)
val scalaBinaryVersion =
scala.util.Properties
.versionString
.stripPrefix("version ")
.split('.')
.take(2)
.mkString(".")

var maxLevel = 2
Message.setDefaultLogger(new AbstractMessageLogger {
Expand All @@ -54,71 +50,8 @@ object IvyThing {
version: String,
verbosity: Int = 2) = synchronized {
maxLevel = verbosity
val ivy = Ivy.newInstance {

def resolver(name: String) = {
val res = new IBiblioResolver()
res.setUsepoms(true)
res.setM2compatible(true)
res.setName(name)
res.setRoot("https://repo1.maven.org/maven2/")
res
}
def fileResolver(name: String, root: String, pattern: String, m2: Boolean = false) = {
val testRepoDir = sys.props("user.home") + root
val repo = new FileRepository(new java.io.File(testRepoDir))

val res = new FileSystemResolver()
res.addIvyPattern(testRepoDir + pattern)
res.addArtifactPattern(testRepoDir + pattern)
res.setRepository(repo)
res.setM2compatible(m2)
res.setName(name)

res
}

//add duplicate resolvers with different name to make Ivy shut up
//and stop giving `unknown resolver null` or `unknown resolver sbt-chain`
//errors


val resolvers = Seq(
fileResolver(
"cache",
"/.ivy2/cache",
"/[organisation]/[module]/jars/[artifact]-[revision].[ext]"
),
fileResolver(
"local",
"/.ivy2/local",
"/[organisation]/[module]/[revision]/jars/[artifact].[ext]"
),
fileResolver(
"m2",
"/.m2/repository",
"/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]",
m2 = true
),
resolver("central")
)

//creates clear ivy settings
val ivySettings = new IvySettings()

//adding maven repo resolver
val chainResolver = new ChainResolver
chainResolver.setName("chain-resolver")
chainResolver.setReturnFirst(true)
resolvers.foreach(chainResolver.add)
ivySettings.addResolver(chainResolver)

//set to the default resolver
ivySettings.setDefaultResolver(chainResolver.getName)

//creates an Ivy instance with settings
ivySettings
}
val ivy = ivyInstance(resolvers)

val md = DefaultModuleDescriptor.newDefaultInstance(
ModuleRevisionId.newInstance(
Expand Down Expand Up @@ -167,4 +100,115 @@ object IvyThing {
if (unresolved.size == 0) report.getAllArtifactsReports.map(_.getLocalFile)
else throw IvyResolutionException(unresolved.toSeq.map(_.toString))
}

}

object IvyThing {

def ivyInstance(resolvers: List[RepositoryResolver]) = Ivy.newInstance {

//creates clear ivy settings
val ivySettings = new IvySettings()

//adding maven repo resolver
val chainResolver = new ChainResolver
chainResolver.setName("chain-resolver")
chainResolver.setReturnFirst(true)
resolvers.foreach(chainResolver.add)
ivySettings.addResolver(chainResolver)

//set to the default resolver
ivySettings.setDefaultResolver(chainResolver.getName)

//creates an Ivy instance with settings
ivySettings
}

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

}

trait Resolvers {
def resolvers: List[RepositoryResolver]
}

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]"

/** create a Maven resolver located at a given http address */
def httpResolver(name: String, root: String,
m2Compatible: Boolean = true,
pattern: String = MavenPattern) = {
val res = new IBiblioResolver()
res.setUsepoms(true)
res.setM2compatible(m2Compatible)
res.setName(name)
res.setRoot(root)
res.setPattern(pattern)
res
}

/** create a local resolver */
def fileResolver(name: String, root: String,
pattern: String, m2: Boolean = false): RepositoryResolver = {
val testRepoDir = sys.props("user.home") + root
val repo = new FileRepository(new java.io.File(testRepoDir))

val res = new FileSystemResolver()
res.addIvyPattern(testRepoDir + pattern)
res.addArtifactPattern(testRepoDir + pattern)
res.setRepository(repo)
res.setM2compatible(m2)
res.setName(name)

res
}

//add duplicate resolvers with different name to make Ivy shut up
//and stop giving `unknown resolver null` or `unknown resolver sbt-chain`
//errors
lazy val defaultResolvers: List[RepositoryResolver] = List(
fileResolver(
"ivy-cache",
"/.ivy2/cache",
"/[organisation]/[module]/jars/[artifact]-[revision].[ext]"
),
fileResolver(
"cache",
"/.ivy2/cache",
"/[organisation]/[module]/jars/[artifact]-[revision].[ext]"
),
fileResolver(
"local",
"/.ivy2/local",
"/[organisation]/[module]/[revision]/jars/[artifact].[ext]"
),
fileResolver(
"m2",
"/.m2/repository",
"/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]",
m2 = true
),
httpResolver(name = "central", root = "http://repo1.maven.org/maven2/")
)

}
7 changes: 7 additions & 0 deletions repl/src/main/scala/ammonite/repl/frontend/ReplAPI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import java.io.File
import ammonite.ops._
import ammonite.repl._
import ammonite.repl.interp.Frame
import org.apache.ivy.plugins.resolver.RepositoryResolver
import pprint.{PPrinter, PPrint, Config}

import scala.collection.mutable
Expand Down Expand Up @@ -80,6 +81,11 @@ trait ReplAPI {
*/
def load: Load

/**
* resolvers to use when loading jars
*/
def resolvers: Ref[List[RepositoryResolver]]

/**
* The colors that will be used to render the Ammonite REPL in the terminal
*/
Expand Down Expand Up @@ -210,6 +216,7 @@ object SessionChanged{
}
// End of OpsAPI
trait LoadJar {

/**
* Load a `.jar` file
*/
Expand Down
19 changes: 16 additions & 3 deletions repl/src/main/scala/ammonite/repl/interp/Interpreter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package ammonite.repl.interp

import java.io.File
import java.nio.file.NotDirectoryException
import org.apache.ivy.plugins.resolver.RepositoryResolver

import scala.collection.mutable
import acyclic.file
import ammonite.ops._
Expand Down Expand Up @@ -182,7 +184,9 @@ class Interpreter(prompt0: Ref[String],
}
}

abstract class DefaultLoadJar extends LoadJar{
abstract class DefaultLoadJar extends LoadJar with Resolvers {

lazy val ivyThing = IvyThing(resolvers)

def handleJar(jar: File): Unit

Expand All @@ -201,7 +205,7 @@ class Interpreter(prompt0: Ref[String],
psOpt match{
case Some(ps) => ps.map(handleJar)
case None =>
val resolved = IvyThing.resolveArtifact(
val resolved = ivyThing.resolveArtifact(
groupId,
artifactId,
version,
Expand All @@ -220,14 +224,20 @@ class Interpreter(prompt0: Ref[String],
}
}

lazy val replApi: ReplAPI = new DefaultReplAPI {
lazy val replApi: ReplAPI = new DefaultReplAPI { outer =>

def imports = interp.eval.previousImportBlock
val colors = colors0
val prompt = prompt0
val frontEnd = frontEnd0

lazy val resolvers =
Ref(Resolvers.defaultResolvers)

object load extends DefaultLoadJar with Load {

def resolvers: List[RepositoryResolver] =
outer.resolvers()

def handleJar(jar: File) = {
eval.sess.frames.head.extraJars = eval.sess.frames.head.extraJars ++ Seq(jar)
Expand All @@ -244,6 +254,9 @@ class Interpreter(prompt0: Ref[String],
}

object plugin extends DefaultLoadJar {
def resolvers: List[RepositoryResolver] =
outer.resolvers()

def handleJar(jar: File) =
sess.frames.head.pluginClassloader.add(jar.toURI.toURL)
}
Expand Down
16 changes: 16 additions & 0 deletions repl/src/test/scala/ammonite/repl/session/ProjectTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,22 @@ object ProjectTests extends TestSuite{
@ system.shutdown()
""")
}
'resolvers{
check.session("""
@ import ammonite.repl._, Resolvers._
@ val oss = httpResolver("ambiata-oss",
@ "https://ambiata-oss.s3-ap-southeast-2.amazonaws.com",
@ m2Compatible = false,
@ pattern = IvyPattern)
@ resolvers() = resolvers() :+ oss
@ load.ivy("com.ambiata" %% "mundane" % "1.2.1-20141230225616-50fc792")
@ import com.ambiata.mundane._
""")
}
}
'code{
check.session("""
Expand Down

0 comments on commit ea73548

Please sign in to comment.