mirror of https://github.com/sbt/sbt.git
Eval-related improvements
- support import clauses - error display: source name, line numbers for each expression - for 'eval', 'get', 'set', and .sbt files, use default imports and import from Plugins, Builds
This commit is contained in:
parent
f182b3a896
commit
6b91ad59fd
|
|
@ -4,17 +4,23 @@ package compile
|
|||
import scala.reflect.Manifest
|
||||
import scala.tools.nsc.{ast, interpreter, io, reporters, util, CompilerCommand, Global, Phase, Settings}
|
||||
import interpreter.AbstractFileClassLoader
|
||||
import io.VirtualDirectory
|
||||
import io.{PlainFile, VirtualDirectory}
|
||||
import ast.parser.Tokens
|
||||
import reporters.{ConsoleReporter, Reporter}
|
||||
import util.BatchSourceFile
|
||||
import Tokens.EOF
|
||||
import java.io.File
|
||||
|
||||
// TODO: if backing is Some, only recompile if out of date
|
||||
|
||||
final class EvalImports(val strings: Seq[(String,Int)], val srcName: String)
|
||||
final class EvalResult(val tpe: String, val value: Any, val generated: Seq[File], val enclosingModule: String)
|
||||
final class EvalException(msg: String) extends RuntimeException(msg)
|
||||
// not thread safe, since it reuses a Global instance
|
||||
final class Eval(options: Seq[String], mkReporter: Settings => Reporter, parent: ClassLoader)
|
||||
{
|
||||
def this() = this("-cp" :: IO.classLocationFile[ScalaObject].getAbsolutePath :: Nil, s => new ConsoleReporter(s), getClass.getClassLoader)
|
||||
def this(mkReporter: Settings => Reporter) = this("-cp" :: IO.classLocationFile[ScalaObject].getAbsolutePath :: Nil, mkReporter, getClass.getClassLoader)
|
||||
def this() = this(s => new ConsoleReporter(s))
|
||||
|
||||
val settings = new Settings(Console.println)
|
||||
val command = new CompilerCommand(options.toList, settings)
|
||||
|
|
@ -23,40 +29,39 @@ final class Eval(options: Seq[String], mkReporter: Settings => Reporter, parent:
|
|||
import global._
|
||||
import definitions._
|
||||
|
||||
def eval[T](expression: String)(implicit mf: Manifest[T]): T = eval(expression, Some(mf.toString))._2.asInstanceOf[T]
|
||||
def eval(expression: String, tpeName: Option[String]): (String,Any) =
|
||||
def eval(expression: String, imports: EvalImports = noImports, tpeName: Option[String] = None, backing: Option[File] = None, srcName: String = "<setting>", line: Int = DefaultStartLine): EvalResult =
|
||||
{
|
||||
val moduleName = makeModuleName(srcName, line)
|
||||
reporter.reset
|
||||
val unit = mkUnit(expression)
|
||||
val unit = mkUnit(srcName, line, expression)
|
||||
val run = new Run {
|
||||
override def units = (unit :: Nil).iterator
|
||||
}
|
||||
def unlinkAll(): Unit = for( (sym, _) <- run.symSource ) unlink(sym)
|
||||
def unlink(sym: Symbol) = sym.owner.info.decls.unlink(sym)
|
||||
|
||||
try { eval0(expression, tpeName, run, unit) } finally { unlinkAll() }
|
||||
try
|
||||
{
|
||||
val (tpe, value) = eval0(expression, imports, tpeName, run, unit, backing, moduleName)
|
||||
val classFiles = getClassFiles(backing, moduleName)
|
||||
new EvalResult(tpe, value, classFiles, moduleName)
|
||||
}
|
||||
finally { unlinkAll() }
|
||||
}
|
||||
def eval0(expression: String, tpeName: Option[String], run: Run, unit: CompilationUnit): (String, Any) =
|
||||
def eval0(expression: String, imports: EvalImports, tpeName: Option[String], run: Run, unit: CompilationUnit, backing: Option[File], moduleName: String): (String, Any) =
|
||||
{
|
||||
val virtualDirectory = new VirtualDirectory("<virtual>", None)
|
||||
settings.outputDirs setSingleOutput virtualDirectory
|
||||
val dir = backing match { case None => new VirtualDirectory("<virtual>", None); case Some(dir) => new PlainFile(dir) }
|
||||
settings.outputDirs setSingleOutput dir
|
||||
|
||||
val parser = new syntaxAnalyzer.UnitParser(unit)
|
||||
val tree: Tree = parser.expr()
|
||||
parser.accept(EOF)
|
||||
checkError("Error parsing expression.")
|
||||
val importTrees = parseImports(imports)
|
||||
val (parser, tree) = parseExpr(unit)
|
||||
|
||||
val tpt: Tree = tpeName match {
|
||||
case Some(tpe) =>
|
||||
val tpeParser = new syntaxAnalyzer.UnitParser(mkUnit(tpe))
|
||||
val tpt0: Tree = tpeParser.typ()
|
||||
tpeParser.accept(EOF)
|
||||
checkError("Error parsing type.")
|
||||
tpt0
|
||||
case Some(tpe) => parseType(tpe)
|
||||
case None => TypeTree(NoType)
|
||||
}
|
||||
|
||||
unit.body = augment(parser, tree, tpt)
|
||||
unit.body = augment(parser, importTrees, tree, tpt, moduleName)
|
||||
|
||||
def compile(phase: Phase): Unit =
|
||||
{
|
||||
|
|
@ -76,13 +81,13 @@ final class Eval(options: Seq[String], mkReporter: Settings => Reporter, parent:
|
|||
checkError("Type error.")
|
||||
val tpe = atPhase(run.typerPhase.next) { (new TypeExtractor).getType(unit.body) }
|
||||
|
||||
val loader = new AbstractFileClassLoader(virtualDirectory, parent)
|
||||
(tpe, getValue(loader))
|
||||
val loader = new AbstractFileClassLoader(dir, parent)
|
||||
(tpe, getValue(moduleName, loader))
|
||||
}
|
||||
val WrapObjectName = "$sbtobj"
|
||||
|
||||
val WrapValName = "$sbtdef"
|
||||
//wrap tree in object WrapObjectName { def WrapValName = <tree> }
|
||||
def augment(parser: global.syntaxAnalyzer.UnitParser, tree: Tree, tpt: Tree): Tree =
|
||||
//wrap tree in object objectName { def WrapValName = <tree> }
|
||||
def augment(parser: global.syntaxAnalyzer.UnitParser, imports: Seq[Tree], tree: Tree, tpt: Tree, objectName: String): Tree =
|
||||
{
|
||||
def emptyPkg = parser.atPos(0, 0, 0) { Ident(nme.EMPTY_PACKAGE_NAME) }
|
||||
def emptyInit = DefDef(
|
||||
|
|
@ -96,13 +101,13 @@ final class Eval(options: Seq[String], mkReporter: Settings => Reporter, parent:
|
|||
|
||||
def method = DefDef(NoMods, WrapValName, Nil, Nil, tpt, tree)
|
||||
def moduleBody = Template(List(gen.scalaScalaObjectConstr), emptyValDef, List(emptyInit, method))
|
||||
def moduleDef = ModuleDef(NoMods, WrapObjectName, moduleBody)
|
||||
parser.makePackaging(0, emptyPkg, List(moduleDef))
|
||||
def moduleDef = ModuleDef(NoMods, objectName, moduleBody)
|
||||
parser.makePackaging(0, emptyPkg, (imports :+ moduleDef).toList)
|
||||
}
|
||||
|
||||
def getValue[T](loader: ClassLoader): T =
|
||||
def getValue[T](objectName: String, loader: ClassLoader): T =
|
||||
{
|
||||
val clazz = Class.forName(WrapObjectName + "$", true, loader)
|
||||
val clazz = Class.forName(objectName + "$", true, loader)
|
||||
val module = clazz.getField("MODULE$").get(null)
|
||||
val accessor = module.getClass.getMethod(WrapValName)
|
||||
val value = accessor.invoke(module)
|
||||
|
|
@ -117,7 +122,53 @@ final class Eval(options: Seq[String], mkReporter: Settings => Reporter, parent:
|
|||
case _ => super.traverse(tree)
|
||||
}
|
||||
}
|
||||
// TODO: use the code from Analyzer
|
||||
private[this] def getClassFiles(backing: Option[File], moduleName: String): Seq[File] =
|
||||
backing match {
|
||||
case None => Nil
|
||||
case Some(dir) => dir listFiles moduleClassFilter(moduleName)
|
||||
}
|
||||
private[this] def moduleClassFilter(moduleName: String) = new java.io.FilenameFilter { def accept(dir: File, s: String) =
|
||||
(s contains moduleName) && (s endsWith ".class")
|
||||
}
|
||||
private[this] def parseExpr(unit: CompilationUnit) =
|
||||
{
|
||||
val parser = new syntaxAnalyzer.UnitParser(unit)
|
||||
val tree: Tree = parser.expr()
|
||||
parser.accept(EOF)
|
||||
checkError("Error parsing expression.")
|
||||
(parser, tree)
|
||||
}
|
||||
private[this] def parseType(tpe: String): Tree =
|
||||
{
|
||||
val tpeParser = new syntaxAnalyzer.UnitParser(mkUnit("<expected-type>", DefaultStartLine, tpe))
|
||||
val tpt0: Tree = tpeParser.typ()
|
||||
tpeParser.accept(EOF)
|
||||
checkError("Error parsing type.")
|
||||
tpt0
|
||||
}
|
||||
private[this] def parseImports(imports: EvalImports): Seq[Tree] =
|
||||
imports.strings flatMap { case (s, line) => parseImport(mkUnit(imports.srcName, line, s)) }
|
||||
private[this] def parseImport(importUnit: CompilationUnit): Seq[Tree] =
|
||||
{
|
||||
val parser = new syntaxAnalyzer.UnitParser(importUnit)
|
||||
val trees: Seq[Tree] = parser.importClause()
|
||||
parser.accept(EOF)
|
||||
checkError("Error parsing imports.")
|
||||
trees
|
||||
}
|
||||
|
||||
def mkUnit(s: String) = new CompilationUnit(new BatchSourceFile("<setting>", s))
|
||||
def checkError(label: String) = if(reporter.hasErrors) throw new EvalException(label)
|
||||
|
||||
val DefaultStartLine = 0
|
||||
private[this] def makeModuleName(src: String, line: Int): String = "$" + halve(Hash.toHex(Hash(src + ":" + line)))
|
||||
private[this] def halve(s: String) = if(s.length > 2) s.substring(0, s.length / 2)
|
||||
private[this] def noImports = new EvalImports(Nil, "")
|
||||
private[this] def mkUnit(srcName: String, firstLine: Int, s: String) = new CompilationUnit(new EvalSourceFile(srcName, firstLine, s))
|
||||
private[this] def checkError(label: String) = if(reporter.hasErrors) throw new EvalException(label)
|
||||
|
||||
private[this] final class EvalSourceFile(name: String, startLine: Int, contents: String) extends BatchSourceFile(name, contents)
|
||||
{
|
||||
override def lineToOffset(line: Int): Int = super.lineToOffset((line - startLine) max 0)
|
||||
override def offsetToLine(offset: Int): Int = super.offsetToLine(offset) + startLine
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
package sbt
|
||||
package compile
|
||||
|
||||
import org.scalacheck._
|
||||
import Prop._
|
||||
import scala.tools.nsc.reporters.StoreReporter
|
||||
|
||||
object EvalTest extends Properties("eval")
|
||||
{
|
||||
private[this] val reporter = new StoreReporter
|
||||
import reporter.{ERROR,Info,Severity}
|
||||
private[this] val eval = new Eval(_ => reporter)
|
||||
|
||||
property("inferred integer") = forAll{ (i: Int) =>
|
||||
val result = eval.eval(i.toString)
|
||||
(label("Value", result.value) |: (result.value == i)) &&
|
||||
(label("Type", result.tpe) |: (result.tpe == IntType)) &&
|
||||
(label("Files", result.generated) |: (result.generated.isEmpty))
|
||||
}
|
||||
|
||||
property("explicit integer") = forAll{ (i: Int) =>
|
||||
val result = eval.eval(i.toString, tpeName = Some(IntType))
|
||||
(label("Value", result.value) |: (result.value == i)) &&
|
||||
(label("Type", result.tpe) |: (result.tpe == IntType)) &&
|
||||
(label("Files", result.generated) |: (result.generated.isEmpty))
|
||||
}
|
||||
|
||||
property("type mismatch") = forAll{ (i: Int, l: Int) =>
|
||||
val line = math.abs(l)
|
||||
val src = "mismatch"
|
||||
throws(eval.eval(i.toString, tpeName =Some(BooleanType), line = line, srcName = src), classOf[RuntimeException]) &&
|
||||
hasErrors(line+1, src)
|
||||
}
|
||||
|
||||
property("backed local class") = forAll{ (i: Int) =>
|
||||
IO.withTemporaryDirectory { dir =>
|
||||
val result = eval.eval(local(i), backing = Some(dir))
|
||||
val value = result.value.asInstanceOf[{def i: Int}].i
|
||||
(label("Value", value) |: (value == i)) &&
|
||||
(label("Type", result.tpe) |: (result.tpe == LocalType)) &&
|
||||
(label("Module name", result.enclosingModule) |: (result.enclosingModule == ModuleName)) &&
|
||||
(label("Files", result.generated) |: (!result.generated.isEmpty))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
property("explicit import") = forAll(testImport("import math.abs" :: Nil))
|
||||
property("wildcard import") = forAll(testImport("import math._" :: Nil))
|
||||
property("comma-separated imports") = forAll(testImport("import util._, math._, xml._" :: Nil))
|
||||
property("multiple imports") = forAll(testImport("import util._" :: "import math._" :: "import xml._" :: Nil))
|
||||
|
||||
private[this] def testImport(imports: Seq[String]): Int => Prop = i =>
|
||||
eval.eval("abs("+i+")", new EvalImports(imports.zipWithIndex, "imp")).value == math.abs(i)
|
||||
|
||||
private[this] def local(i: Int) = "{ class ETest(val i: Int); new ETest(" + i + ") }"
|
||||
val LocalType = "java.lang.Object with ScalaObject{val i: Int}"
|
||||
|
||||
private[this] def hasErrors(line: Int, src: String) =
|
||||
{
|
||||
val is = reporter.infos
|
||||
("Has errors" |: (!is.isEmpty)) &&
|
||||
all(is.toSeq.map(validPosition(line,src)) :_*)
|
||||
}
|
||||
private[this] def validPosition(line: Int, src: String)(i: Info) =
|
||||
{
|
||||
val nme = i.pos.source.file.name
|
||||
(label("Severity", i.severity) |: (i.severity == ERROR)) &&
|
||||
(label("Line", i.pos.line) |: (i.pos.line == line)) &&
|
||||
(label("Name", nme) |: (nme == src))
|
||||
}
|
||||
val IntType = "Int"
|
||||
val BooleanType = "Boolean"
|
||||
val ModuleName = "$c1f67bf3eebc642f70dd"
|
||||
|
||||
def label(s: String, value: Any) = s + " (" + value + ")"
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ package sbt
|
|||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import compile.{Discovered,Discovery,Eval}
|
||||
import compile.{Discovered,Discovery,Eval,EvalImports}
|
||||
import classpath.ClasspathUtilities
|
||||
import inc.Analysis
|
||||
import scala.annotation.tailrec
|
||||
|
|
@ -53,44 +53,45 @@ object RetrieveUnit
|
|||
}
|
||||
object EvaluateConfigurations
|
||||
{
|
||||
def apply(eval: Eval, srcs: Seq[File]): Seq[Setting[_]] =
|
||||
srcs flatMap { src => evaluateConfiguration(eval, src) }
|
||||
def evaluateConfiguration(eval: Eval, src: File): Seq[Setting[_]] =
|
||||
evaluateConfiguration(eval, src.getPath, IO.readLines(src))
|
||||
def evaluateConfiguration(eval: Eval, name: String, lines: Seq[String]): Seq[Setting[_]] =
|
||||
def apply(eval: Eval, srcs: Seq[File], imports: Seq[String]): Seq[Setting[_]] =
|
||||
srcs flatMap { src => evaluateConfiguration(eval, src, imports) }
|
||||
def evaluateConfiguration(eval: Eval, src: File, imports: Seq[String]): Seq[Setting[_]] =
|
||||
evaluateConfiguration(eval, src.getPath, IO.readLines(src), imports)
|
||||
def evaluateConfiguration(eval: Eval, name: String, lines: Seq[String], imports: Seq[String]): Seq[Setting[_]] =
|
||||
{
|
||||
val (importExpressions, settingExpressions) = splitExpressions(name, lines)
|
||||
for(settingExpression <- settingExpressions) yield evaluateSetting(eval, name, importExpressions, settingExpression)
|
||||
for((settingExpression,line) <- settingExpressions) yield
|
||||
evaluateSetting(eval, name, (imports.map(s => (s, -1)) ++ importExpressions), settingExpression, line = line)
|
||||
}
|
||||
|
||||
def evaluateSetting(eval: Eval, name: String, imports: Seq[String], expression: String): Setting[_] =
|
||||
def evaluateSetting(eval: Eval, name: String, imports: Seq[(String,Int)], expression: String, line: Int): Setting[_] =
|
||||
{
|
||||
// TODO: Eval needs to be expanded to be able to:
|
||||
// handle multiple expressions at once (for efficiency and better error handling)
|
||||
// accept the source name for error display
|
||||
// accept imports to use
|
||||
// persist the results (critical for start up time)
|
||||
eval.eval(expression, Some("sbt.Project.Setting[_]"))._2.asInstanceOf[Setting[_]]
|
||||
// TODO: need to persist the results, which is critical for start up time
|
||||
val result = eval.eval(expression, imports = new EvalImports(imports, name), srcName = name, tpeName = Some("sbt.Project.Setting[_]"), line = line)
|
||||
result.value.asInstanceOf[Setting[_]]
|
||||
}
|
||||
def splitExpressions(name: String, lines: Seq[String]): (Seq[String], Seq[String]) =
|
||||
private[this] def fstS(f: String => Boolean): ((String,Int)) => Boolean = { case (s,i) => f(s) }
|
||||
def splitExpressions(name: String, lines: Seq[String]): (Seq[(String,Int)], Seq[(String,Int)]) =
|
||||
{
|
||||
val blank = (_: String).trim.isEmpty
|
||||
val importOrBlank = (t: String) => blank(t) || (t.trim startsWith "import ")
|
||||
val importOrBlank = fstS(t => blank(t) || (t.trim startsWith "import "))
|
||||
|
||||
val (imports, settings) = lines span importOrBlank
|
||||
(imports dropWhile blank, groupedLines(settings, blank))
|
||||
val (imports, settings) = lines.zipWithIndex span importOrBlank
|
||||
(imports filterNot fstS(blank), groupedLines(settings, blank))
|
||||
}
|
||||
def groupedLines(lines: Seq[String], delimiter: String => Boolean): Seq[String] =
|
||||
def groupedLines(lines: Seq[(String,Int)], delimiter: String => Boolean): Seq[(String,Int)] =
|
||||
{
|
||||
@tailrec def group0(lines: Seq[String], delimiter: String => Boolean, accum: Seq[String]): Seq[String] =
|
||||
val fdelim = fstS(delimiter)
|
||||
@tailrec def group0(lines: Seq[(String,Int)], accum: Seq[(String,Int)]): Seq[(String,Int)] =
|
||||
if(lines.isEmpty) accum.reverse
|
||||
else
|
||||
{
|
||||
val start = lines dropWhile delimiter
|
||||
val (next, tail) = start.span (s => !delimiter(s))
|
||||
group0(tail, delimiter, next.mkString("\n") +: accum)
|
||||
val start = lines dropWhile fstS(delimiter)
|
||||
val (next, tail) = start.span { case (s,_) => !delimiter(s) }
|
||||
val grouped = if(next.isEmpty) accum else (next.map(_._1).mkString("\n"), next.head._2) +: accum
|
||||
group0(tail, grouped)
|
||||
}
|
||||
group0(lines, delimiter, Nil)
|
||||
group0(lines, Nil)
|
||||
}
|
||||
}
|
||||
object EvaluateTask
|
||||
|
|
@ -172,7 +173,7 @@ object Index
|
|||
def stringToKeyMap(settings: Settings[Scope]): Map[String, AttributeKey[_]] =
|
||||
{
|
||||
val multiMap = settings.data.values.flatMap(_.keys).toList.removeDuplicates.groupBy(_.label)
|
||||
val duplicates = multiMap collect { case (k, x1 :: x2 :: _) => println(k + ": " + x1 + ", " + x2); k }
|
||||
val duplicates = multiMap collect { case (k, x1 :: x2 :: _) => k }
|
||||
if(duplicates.isEmpty)
|
||||
multiMap.mapValues(_.head)
|
||||
else
|
||||
|
|
@ -256,7 +257,7 @@ object Load
|
|||
val (pluginThisProject, pluginGlobal) = pluginSettings partition isProjectThis
|
||||
val projectSettings = build.defined flatMap { case (id, project) =>
|
||||
val srcs = configurationSources(project.base)
|
||||
val settings = injectSettings ++ project.settings ++ pluginThisProject ++ configurations(srcs, eval)
|
||||
val settings = injectSettings ++ project.settings ++ pluginThisProject ++ configurations(srcs, eval, build.imports)
|
||||
|
||||
// map This to thisScope, Select(p) to mapRef(uri, rootProject, p)
|
||||
transformSettings(projectScope(uri, id), uri, rootProject, settings)
|
||||
|
|
@ -281,8 +282,8 @@ object Load
|
|||
val optionsCp = "-cp" +: classpathString +: options // TODO: probably need to properly set up options with CompilerArguments
|
||||
new Eval(optionsCp, s => new ConsoleReporter(s), defs.loader)
|
||||
}
|
||||
def configurations(srcs: Seq[File], eval: () => Eval): Seq[Setting[_]] =
|
||||
if(srcs.isEmpty) Nil else EvaluateConfigurations(eval(), srcs)
|
||||
def configurations(srcs: Seq[File], eval: () => Eval, imports: Seq[String]): Seq[Setting[_]] =
|
||||
if(srcs.isEmpty) Nil else EvaluateConfigurations(eval(), srcs, imports)
|
||||
|
||||
def load(file: File, config: LoadBuildConfiguration): LoadedBuild = load(file, uri => loadUnit(uri, RetrieveUnit(config.stagingDirectory, uri), config) )
|
||||
def load(file: File, loader: URI => BuildUnit): LoadedBuild = loadURI(IO.directoryURI(file), loader)
|
||||
|
|
@ -408,7 +409,7 @@ object Load
|
|||
val defs = definitionSources(defDir)
|
||||
val loadedDefs =
|
||||
if(defs.isEmpty)
|
||||
new LoadedDefinitions(defDir, outputDirectory(defDir), plugs.loader, Build.default(normBase) :: Nil)
|
||||
new LoadedDefinitions(defDir, outputDirectory(defDir), plugs.loader, Build.default(normBase) :: Nil, Nil)
|
||||
else
|
||||
definitions(defDir, defs, plugs, config.compilers, config.log)
|
||||
|
||||
|
|
@ -416,7 +417,7 @@ object Load
|
|||
}
|
||||
|
||||
def plugins(dir: File, config: LoadBuildConfiguration): LoadedPlugins = if(dir.exists) buildPlugins(dir, config) else noPlugins(config)
|
||||
def noPlugins(config: LoadBuildConfiguration): LoadedPlugins = new LoadedPlugins(config.classpath, config.loader, Nil)
|
||||
def noPlugins(config: LoadBuildConfiguration): LoadedPlugins = new LoadedPlugins(config.classpath, config.loader, Nil, Nil)
|
||||
def buildPlugins(dir: File, config: LoadBuildConfiguration): LoadedPlugins =
|
||||
{
|
||||
val (_,pluginDef) = apply(dir, config)
|
||||
|
|
@ -430,8 +431,9 @@ object Load
|
|||
val (inputs, defAnalysis) = build(plugins.classpath, srcs, outputDirectory(base), compilers, log)
|
||||
val target = inputs.config.classesDirectory
|
||||
val definitionLoader = ClasspathUtilities.toLoader(target :: Nil, plugins.loader)
|
||||
val defs = loadDefinitions(definitionLoader, findDefinitions(defAnalysis))
|
||||
new LoadedDefinitions(base, target, definitionLoader, defs)
|
||||
val defNames = findDefinitions(defAnalysis)
|
||||
val defs = loadDefinitions(definitionLoader, defNames)
|
||||
new LoadedDefinitions(base, target, definitionLoader, defs, defNames)
|
||||
}
|
||||
|
||||
def loadDefinitions(loader: ClassLoader, defs: Seq[String]): Seq[Build] =
|
||||
|
|
@ -448,7 +450,13 @@ object Load
|
|||
}
|
||||
|
||||
def loadPlugins(classpath: Seq[File], loader: ClassLoader, analysis: Analysis): LoadedPlugins =
|
||||
new LoadedPlugins(classpath, loader, if(classpath.isEmpty) Nil else loadPlugins(loader, findPlugins(analysis) ) )
|
||||
{
|
||||
val (pluginNames, plugins) = if(classpath.isEmpty) (Nil, Nil) else {
|
||||
val names = findPlugins(analysis)
|
||||
(names, loadPlugins(loader, names) )
|
||||
}
|
||||
new LoadedPlugins(classpath, loader, plugins, pluginNames)
|
||||
}
|
||||
|
||||
def loadPlugins(loader: ClassLoader, pluginNames: Seq[String]): Seq[Setting[_]] =
|
||||
pluginNames.flatMap(pluginName => loadPlugin(pluginName, loader))
|
||||
|
|
@ -456,6 +464,8 @@ object Load
|
|||
def loadPlugin(pluginName: String, loader: ClassLoader): Seq[Setting[_]] =
|
||||
ModuleUtilities.getObject(pluginName, loader).asInstanceOf[Plugin].settings
|
||||
|
||||
def importAll(values: Seq[String]) = if(values.isEmpty) Nil else values.map( _ + "._" ).mkString("import ", ", ", "") :: Nil
|
||||
|
||||
def findPlugins(analysis: Analysis): Seq[String] = discover(analysis, "sbt.Plugin")
|
||||
def findDefinitions(analysis: Analysis): Seq[String] = discover(analysis, "sbt.Build")
|
||||
def discover(analysis: Analysis, subclasses: String*): Seq[String] =
|
||||
|
|
@ -466,18 +476,20 @@ object Load
|
|||
|
||||
def initialSession(structure: BuildStructure, rootEval: () => Eval): SessionSettings =
|
||||
new SessionSettings(structure.root, rootProjectMap(structure.units), structure.settings, Map.empty, Map.empty, rootEval)
|
||||
|
||||
|
||||
def rootProjectMap(units: Map[URI, LoadedBuildUnit]): Map[URI, String] =
|
||||
{
|
||||
val getRoot = getRootProject(units)
|
||||
units.keys.map(uri => (uri, getRoot(uri))).toMap
|
||||
}
|
||||
|
||||
def baseImports = "import sbt._, Process._, java.io.File, java.net.URI" :: Nil
|
||||
|
||||
final class EvaluatedConfigurations(val eval: Eval, val settings: Seq[Setting[_]])
|
||||
final class LoadedDefinitions(val base: File, val target: File, val loader: ClassLoader, val builds: Seq[Build])
|
||||
final class LoadedPlugins(val classpath: Seq[File], val loader: ClassLoader, val plugins: Seq[Setting[_]])
|
||||
final class LoadedDefinitions(val base: File, val target: File, val loader: ClassLoader, val builds: Seq[Build], val buildNames: Seq[String])
|
||||
final class LoadedPlugins(val classpath: Seq[File], val loader: ClassLoader, val plugins: Seq[Setting[_]], val pluginNames: Seq[String])
|
||||
object LoadedPlugins {
|
||||
def empty(loader: ClassLoader) = new LoadedPlugins(Nil, loader, Nil)
|
||||
def empty(loader: ClassLoader) = new LoadedPlugins(Nil, loader, Nil, Nil)
|
||||
}
|
||||
final class BuildUnit(val uri: URI, val localBase: File, val definitions: LoadedDefinitions, val plugins: LoadedPlugins)
|
||||
{
|
||||
|
|
@ -490,7 +502,8 @@ object Load
|
|||
assert(!rootProjects.isEmpty, "No root projects defined for build unit " + unit)
|
||||
def localBase = unit.localBase
|
||||
def classpath = unit.definitions.target +: unit.plugins.classpath
|
||||
def loade = unit.definitions.loader
|
||||
def loader = unit.definitions.loader
|
||||
def imports = baseImports ++ importAll(unit.plugins.pluginNames ++ unit.definitions.buildNames)
|
||||
override def toString = unit.toString
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package sbt
|
|||
import HistoryCommands.{Start => HistoryPrefix}
|
||||
import Project.{SessionKey, StructureKey}
|
||||
import sbt.build.{AggressiveCompile, Auto, BuildException, LoadCommand, Parse, ParseException, ProjectLoad, SourceLoad}
|
||||
import compile.EvalImports
|
||||
import sbt.complete.{DefaultParsers, Parser}
|
||||
|
||||
import Command.{applyEffect,Analysis,HistoryPath,Logged,Watch}
|
||||
|
|
@ -216,14 +217,14 @@ object Commands
|
|||
val log = logger(s)
|
||||
val extracted = Project extract s
|
||||
import extracted._
|
||||
val (tpe, value) = session.currentEval().eval(arg, None)
|
||||
log.info("ans: " + tpe + " = " + value.toString)
|
||||
val result = session.currentEval().eval(arg, srcName = "<eval>", imports = autoImports(extracted))
|
||||
log.info("ans: " + result.tpe + " = " + result.value.toString)
|
||||
s
|
||||
}
|
||||
def set = Command.single(SetCommand, setBrief, setDetailed) { (s, arg) =>
|
||||
val extracted = Project extract s
|
||||
import extracted._
|
||||
val setting = EvaluateConfigurations.evaluateSetting(session.currentEval(), "", Nil, arg)
|
||||
val setting = EvaluateConfigurations.evaluateSetting(session.currentEval(), "<set>", imports(extracted), arg, 0)
|
||||
val append = Load.transformSettings(Load.projectScope(curi, cid), curi, rootProject, setting :: Nil)
|
||||
val newSession = session.appendSettings( append map (a => (a, arg)))
|
||||
logger(s).info("Reapplying settings...")
|
||||
|
|
@ -233,13 +234,20 @@ object Commands
|
|||
def get = Command.single(GetCommand, getBrief, getDetailed) { (s, arg) =>
|
||||
val extracted = Project extract s
|
||||
import extracted._
|
||||
val scoped = session.currentEval().eval(arg, Some("sbt.ScopedSetting[_]"))._2.asInstanceOf[ScopedSetting[_]]
|
||||
val result = session.currentEval().eval(arg, srcName = "get", imports = autoImports(extracted), tpeName = Some("sbt.ScopedSetting[_]"))
|
||||
val scoped = result.value.asInstanceOf[ScopedSetting[_]]
|
||||
val resolve = Scope.resolveScope(Load.projectScope(curi, cid), curi, rootProject)
|
||||
(structure.data.get(resolve(scoped.scope), scoped.key)) match {
|
||||
case None => logger(s).error("No entry for key."); s.fail
|
||||
case Some(v) => logger(s).info(v.toString); s
|
||||
}
|
||||
}
|
||||
def autoImports(extracted: Extracted): EvalImports = new EvalImports(imports(extracted), "<auto-imports>")
|
||||
def imports(extracted: Extracted): Seq[(String,Int)] =
|
||||
{
|
||||
import extracted._
|
||||
structure.units(curi).imports.map(s => (s, -1))
|
||||
}
|
||||
|
||||
def listBuild(uri: URI, build: Load.LoadedBuildUnit, current: Boolean, currentID: String, log: Logger) =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
|||
class CompileProject(info: ProjectInfo) extends Base(info) with TestWithLog with TestWithLaunch with TestWithAPI
|
||||
{
|
||||
override def testCompileAction = super.testCompileAction dependsOn(compileInterfaceSub.`package`, interfaceSub.`package`)
|
||||
override def testClasspath = super.testClasspath +++ compileInterfaceSub.packageSrcJar --- compilerInterfaceClasspath --- interfaceSub.mainCompilePath +++ interfaceSub.jarPath
|
||||
override def testClasspath = super.testClasspath +++ compileInterfaceSub.packageSrcJar --- compilerInterfaceClasspath --- interfaceSub.mainCompilePath +++ interfaceSub.jarPath +++ buildCompilerJar
|
||||
}
|
||||
class IvyProject(info: ProjectInfo) extends Base(info) with TestWithIO with TestWithLog with TestWithLaunch
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue