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:
Mark Harrah 2011-01-26 19:49:54 -05:00
parent f182b3a896
commit 6b91ad59fd
5 changed files with 222 additions and 74 deletions

View File

@ -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
}
}

View File

@ -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 + ")"
}

View File

@ -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
}

View File

@ -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) =
{

View File

@ -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
{