diff --git a/compile/Eval.scala b/compile/Eval.scala index 15cf115b9..67c61509a 100644 --- a/compile/Eval.scala +++ b/compile/Eval.scala @@ -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 = "", 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("", None) - settings.outputDirs setSingleOutput virtualDirectory + val dir = backing match { case None => new VirtualDirectory("", 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 = } - def augment(parser: global.syntaxAnalyzer.UnitParser, tree: Tree, tpt: Tree): Tree = + //wrap tree in object objectName { def WrapValName = } + 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("", 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("", 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 + } } diff --git a/compile/src/test/scala/EvalTest.scala b/compile/src/test/scala/EvalTest.scala new file mode 100644 index 000000000..85c8a7bd3 --- /dev/null +++ b/compile/src/test/scala/EvalTest.scala @@ -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 + ")" +} diff --git a/main/Build.scala b/main/Build.scala index ea464d4b9..c471940f6 100644 --- a/main/Build.scala +++ b/main/Build.scala @@ -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 } diff --git a/main/Main.scala b/main/Main.scala index cdbe4b863..68064356a 100644 --- a/main/Main.scala +++ b/main/Main.scala @@ -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 = "", 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(), "", 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), "") + 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) = { diff --git a/project/build/XSbt.scala b/project/build/XSbt.scala index 30fade826..9c8b45338 100644 --- a/project/build/XSbt.scala +++ b/project/build/XSbt.scala @@ -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 {