From b096d1b17560871d228dfb7b6df9200c909ec8b7 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sat, 9 Jul 2011 16:54:41 -0400 Subject: [PATCH] global settings preparation: separate compilation/loading stages of Eval --- compile/Eval.scala | 12 ++++++------ main/Build.scala | 17 ++++++++++------- main/Defaults.scala | 2 +- main/Load.scala | 20 ++++++++++++-------- main/Main.scala | 4 ++-- main/Project.scala | 1 + main/Script.scala | 2 +- main/Structure.scala | 2 +- tasks/standard/TaskExtra.scala | 4 ++-- util/collection/TypeFunctions.scala | 1 + 10 files changed, 37 insertions(+), 28 deletions(-) diff --git a/compile/Eval.scala b/compile/Eval.scala index bb3615e76..d9cf4fa07 100644 --- a/compile/Eval.scala +++ b/compile/Eval.scala @@ -16,12 +16,12 @@ import java.net.URLClassLoader // TODO: provide a way to cleanup backing directory 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 EvalResult(val tpe: String, val getValue: ClassLoader => 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(optionsNoncp: Seq[String], classpath: Seq[File], mkReporter: Settings => Reporter, parent: ClassLoader, backing: Option[File]) +final class Eval(optionsNoncp: Seq[String], classpath: Seq[File], mkReporter: Settings => Reporter, backing: Option[File]) { - def this(mkReporter: Settings => Reporter, backing: Option[File]) = this(Nil, IO.classLocationFile[ScalaObject] :: Nil, mkReporter, getClass.getClassLoader, backing) + def this(mkReporter: Settings => Reporter, backing: Option[File]) = this(Nil, IO.classLocationFile[ScalaObject] :: Nil, mkReporter, backing) def this() = this(s => new ConsoleReporter(s), None) backing.foreach(IO.createDirectory) @@ -64,7 +64,7 @@ final class Eval(optionsNoncp: Seq[String], classpath: Seq[File], mkReporter: Se val classFiles = getClassFiles(backing, moduleName) new EvalResult(tpe, value, classFiles, moduleName) } - def eval0(expression: String, imports: EvalImports, tpeName: Option[String], run: Run, unit: CompilationUnit, backing: Option[File], moduleName: String): (String, Any) = + def eval0(expression: String, imports: EvalImports, tpeName: Option[String], run: Run, unit: CompilationUnit, backing: Option[File], moduleName: String): (String, ClassLoader => Any) = { val dir = backing match { case None => new VirtualDirectory("", None); case Some(dir) => new PlainFile(dir) } settings.outputDirs setSingleOutput dir @@ -99,8 +99,8 @@ final class Eval(optionsNoncp: Seq[String], classpath: Seq[File], mkReporter: Se (tpe, load(dir, moduleName)) } - def load(dir: AbstractFile, moduleName: String): Any = getValue(moduleName, new AbstractFileClassLoader(dir, parent)) - def loadPlain(dir: File, moduleName: String): Any = getValue(moduleName, new URLClassLoader(Array(dir.toURI.toURL), parent)) + def load(dir: AbstractFile, moduleName: String): ClassLoader => Any = parent => getValue[Any](moduleName, new AbstractFileClassLoader(dir, parent)) + def loadPlain(dir: File, moduleName: String): ClassLoader => Any = parent => getValue[Any](moduleName, new URLClassLoader(Array(dir.toURI.toURL), parent)) val WrapValName = "$sbtdef" //wrap tree in object objectName { def WrapValName = } diff --git a/main/Build.scala b/main/Build.scala index 78aad1e0f..16cf9a3e0 100644 --- a/main/Build.scala +++ b/main/Build.scala @@ -81,28 +81,31 @@ object RetrieveUnit } object EvaluateConfigurations { - def apply(eval: Eval, srcs: Seq[File], imports: Seq[String]): Seq[Setting[_]] = - srcs.sortBy(_.getName) flatMap { src => evaluateConfiguration(eval, src, imports) } - def evaluateConfiguration(eval: Eval, src: File, imports: Seq[String]): Seq[Setting[_]] = + def apply(eval: Eval, srcs: Seq[File], imports: Seq[String]): ClassLoader => Seq[Setting[_]] = + flatten(srcs.sortBy(_.getName) map { src => evaluateConfiguration(eval, src, imports) }) + def evaluateConfiguration(eval: Eval, src: File, imports: Seq[String]): ClassLoader => Seq[Setting[_]] = evaluateConfiguration(eval, src.getPath, IO.readLines(src), imports, 0) - def evaluateConfiguration(eval: Eval, name: String, lines: Seq[String], imports: Seq[String], offset: Int): Seq[Setting[_]] = + def evaluateConfiguration(eval: Eval, name: String, lines: Seq[String], imports: Seq[String], offset: Int): ClassLoader => Seq[Setting[_]] = { val (importExpressions, settingExpressions) = splitExpressions(lines) - addOffset(offset, settingExpressions) flatMap { case (settingExpression,line) => + val settings = addOffset(offset, settingExpressions) map { case (settingExpression,line) => evaluateSetting(eval, name, (imports.map(s => (s, -1)) ++ addOffset(offset, importExpressions)), settingExpression, line) } + flatten(settings) } + def flatten(mksettings: Seq[ClassLoader => Seq[Setting[_]]]): ClassLoader => Seq[Setting[_]] = + loader => mksettings.flatMap(_ apply loader) def addOffset(offset: Int, lines: Seq[(String,Int)]): Seq[(String,Int)] = lines.map { case (s, i) => (s, i + offset) } - def evaluateSetting(eval: Eval, name: String, imports: Seq[(String,Int)], expression: String, line: Int): Seq[Setting[_]] = + def evaluateSetting(eval: Eval, name: String, imports: Seq[(String,Int)], expression: String, line: Int): ClassLoader => Seq[Setting[_]] = { val result = try { eval.eval(expression, imports = new EvalImports(imports, name), srcName = name, tpeName = Some("sbt.Project.SettingsDefinition"), line = line) } catch { case e: sbt.compiler.EvalException => throw new MessageOnlyException(e.getMessage) } - result.value.asInstanceOf[Project.SettingsDefinition].settings + loader => result.getValue(loader).asInstanceOf[Project.SettingsDefinition].settings } private[this] def isSpace = (c: Char) => Character isSpace c private[this] def fstS(f: String => Boolean): ((String,Int)) => Boolean = { case (s,i) => f(s) } diff --git a/main/Defaults.scala b/main/Defaults.scala index d530d6478..8818836fb 100644 --- a/main/Defaults.scala +++ b/main/Defaults.scala @@ -860,7 +860,7 @@ object Classpaths def unmanagedLibs(dep: ResolvedReference, conf: String, data: Settings[Scope]): Task[Classpath] = getClasspath(unmanagedJars, dep, conf, data) def getClasspath(key: TaskKey[Classpath], dep: ResolvedReference, conf: String, data: Settings[Scope]): Task[Classpath] = - ( key in (dep, ConfigKey(conf)) ) get data getOrElse const(Nil) + ( key in (dep, ConfigKey(conf)) ) get data getOrElse constant(Nil) def defaultConfigurationTask(p: ResolvedReference, data: Settings[Scope]): Configuration = flatten(defaultConfiguration in p get data) getOrElse Configurations.Default def flatten[T](o: Option[Option[T]]): Option[T] = o flatMap idFun diff --git a/main/Load.scala b/main/Load.scala index 3f1369489..5271175d7 100644 --- a/main/Load.scala +++ b/main/Load.scala @@ -17,6 +17,7 @@ package sbt import tools.nsc.reporters.ConsoleReporter import Build.{analyzed, data} import Scope.{GlobalScope, ThisScope} + import Types.const object Load { @@ -37,7 +38,7 @@ object Load val evalPluginDef = EvaluateTask.evalPluginDef(log) _ val delegates = defaultDelegates val injectGlobal: Seq[Project.Setting[_]] = ((appConfiguration in GlobalScope) :== state.configuration) +: EvaluateTask.injectSettings - val inject = InjectSettings(injectGlobal, Nil) + val inject = InjectSettings(injectGlobal, Nil, const(Nil)) val definesClass = FileValueCache(Locate.definesClass _) val rawConfig = new LoadBuildConfiguration(stagingDirectory, classpath, loader, compilers, evalPluginDef, definesClass.get, delegates, EvaluateTask.injectStreams, inject, None, log) val config = loadGlobal(state, baseDirectory, defaultGlobalPlugins, rawConfig) @@ -91,7 +92,7 @@ object Load val loaded = resolveProjects(load(rootBase, s, config)) val projects = loaded.units lazy val rootEval = lazyEval(loaded.units(loaded.root).unit) - val settings = finalTransforms(config.injectSettings.global ++ buildConfigurations(loaded, getRootProject(projects), rootEval, config.injectSettings.project)) + val settings = finalTransforms(buildConfigurations(loaded, getRootProject(projects), rootEval, config.injectSettings)) val delegates = config.delegates(loaded) val data = Project.makeSettings(settings, delegates, config.scopeLocal) val index = structureIndex(data) @@ -137,8 +138,9 @@ object Load } def isProjectThis(s: Setting[_]) = s.key.scope.project match { case This | Select(ThisProject) => true; case _ => false } - def buildConfigurations(loaded: LoadedBuild, rootProject: URI => String, rootEval: () => Eval, injectProjectSettings: Seq[Setting[_]]): Seq[Setting[_]] = - loaded.units.toSeq flatMap { case (uri, build) => + def buildConfigurations(loaded: LoadedBuild, rootProject: URI => String, rootEval: () => Eval, injectSettings: InjectSettings): Seq[Setting[_]] = + injectSettings.global ++ + loaded.units.toSeq.flatMap { case (uri, build) => val eval = if(uri == loaded.root) rootEval else lazyEval(build.unit) val pluginSettings = build.unit.plugins.plugins val (pluginThisProject, pluginNotThis) = pluginSettings partition isProjectThis @@ -146,10 +148,12 @@ object Load val srcs = configurationSources(project.base) val ref = ProjectRef(uri, id) val defineConfig = for(c <- project.configurations) yield ( (configuration in (ref, ConfigKey(c.name))) :== c) + val loader = build.unit.definitions.loader + val injected = injectSettings.project ++ injectSettings.projectLoaded(loader) val settings = (thisProject :== project) +: (thisProjectRef :== ref) +: - (defineConfig ++ project.settings ++ pluginThisProject ++ configurations(srcs, eval, build.imports) ++ injectProjectSettings ) + (defineConfig ++ project.settings ++ pluginThisProject ++ configurations(srcs, eval, build.imports)(loader) ++ injected) // map This to thisScope, Select(p) to mapRef(uri, rootProject, p) transformSettings(projectScope(ref), uri, rootProject, settings) @@ -170,10 +174,10 @@ object Load } def mkEval(unit: BuildUnit): Eval = mkEval(unit.definitions, unit.plugins, Nil) def mkEval(defs: LoadedDefinitions, plugs: LoadedPlugins, options: Seq[String]): Eval = - new Eval(options, defs.target +: plugs.classpath, s => new ConsoleReporter(s), defs.loader, Some(evalOutputDirectory(defs.base))) + new Eval(options, defs.target +: plugs.classpath, s => new ConsoleReporter(s), Some(evalOutputDirectory(defs.base))) - def configurations(srcs: Seq[File], eval: () => Eval, imports: Seq[String]): Seq[Setting[_]] = - if(srcs.isEmpty) Nil else EvaluateConfigurations(eval(), srcs, imports) + def configurations(srcs: Seq[File], eval: () => Eval, imports: Seq[String]): ClassLoader => Seq[Setting[_]] = + if(srcs.isEmpty) const(Nil) else EvaluateConfigurations(eval(), srcs, imports) def load(file: File, s: State, config: LoadBuildConfiguration): PartBuild = { diff --git a/main/Main.scala b/main/Main.scala index 5b146b69c..b6a746538 100644 --- a/main/Main.scala +++ b/main/Main.scala @@ -264,7 +264,7 @@ object BuiltinCommands val extracted = Project extract s import extracted._ val result = session.currentEval().eval(arg, srcName = "", imports = autoImports(extracted)) - log.info("ans: " + result.tpe + " = " + result.value) + log.info("ans: " + result.tpe + " = " + result.getValue(currentLoader)) s } def sessionCommand = Command.make(SessionCommand, sessionBrief, SessionSettings.Help)(SessionSettings.command) @@ -277,7 +277,7 @@ object BuiltinCommands def set = Command.single(SetCommand, setBrief, setDetailed) { (s, arg) => val extracted = Project extract s import extracted._ - val settings = EvaluateConfigurations.evaluateSetting(session.currentEval(), "", imports(extracted), arg, 0) + val settings = EvaluateConfigurations.evaluateSetting(session.currentEval(), "", imports(extracted), arg, 0)(currentLoader) val append = Load.transformSettings(Load.projectScope(currentRef), currentRef.build, rootProject, settings) val newSession = session.appendSettings( append map (a => (a, arg))) reapply(newSession, structure, s) diff --git a/main/Project.scala b/main/Project.scala index a1098fc5a..b3d0f69a6 100644 --- a/main/Project.scala +++ b/main/Project.scala @@ -65,6 +65,7 @@ final case class Extracted(structure: BuildStructure, session: SessionSettings, def rootProject = structure.rootProject lazy val currentUnit = structure units currentRef.build lazy val currentProject = currentUnit defined currentRef.project + lazy val currentLoader: ClassLoader = currentUnit.loader def get[T](key: ScopedTask[T]): Task[T] = get(key.task) def get[T](key: ScopedSetting[T]): T = { diff --git a/main/Script.scala b/main/Script.scala index dc03fd1de..623db937f 100644 --- a/main/Script.scala +++ b/main/Script.scala @@ -25,7 +25,7 @@ object Script import extracted._ val embeddedSettings = blocks(script).flatMap { block => - evaluate(eval(), script.getPath, block.lines, currentUnit.imports, block.offset+1) + evaluate(eval(), script.getPath, block.lines, currentUnit.imports, block.offset+1)(currentLoader) } val scriptAsSource = sources in Compile := script :: Nil val asScript = scalacOptions ++= Seq("-Xscript", script.getName.stripSuffix(".scala")) diff --git a/main/Structure.scala b/main/Structure.scala index 424c667e1..bfe93d58c 100644 --- a/main/Structure.scala +++ b/main/Structure.scala @@ -175,7 +175,7 @@ object Scoped def :==(value: S): ScS = :=(value) def ::=(value: Task[S]): ScS = Project.setting(scoped, Project.value( value )) def := (value: => S): ScS = ::=(mktask(value)) - def :== (v: ScopedSetting[S]): ScS = <<=( v(const)) + def :== (v: ScopedSetting[S]): ScS = <<=( v(constant)) def ~= (f: S => S): ScS = Project.update(scoped)( _ map f ) def <<= (app: App[S]): ScS = Project.setting(scoped, app) diff --git a/tasks/standard/TaskExtra.scala b/tasks/standard/TaskExtra.scala index 22b7ae8b7..5810a02f4 100644 --- a/tasks/standard/TaskExtra.scala +++ b/tasks/standard/TaskExtra.scala @@ -74,8 +74,8 @@ sealed trait ProcessPipe trait TaskExtra { - final def nop: Task[Unit] = const( () ) - final def const[T](t: T): Task[T] = task(t) + final def nop: Task[Unit] = constant( () ) + final def constant[T](t: T): Task[T] = task(t) final implicit def t2ToMulti[A,B](t: (Task[A],Task[B])) = multInputTask(t._1 :^: t._2 :^: KNil) final implicit def f2ToHfun[A,B,R](f: (A,B) => R): (A :+: B :+: HNil => R) = { case a :+: b :+: HNil => f(a,b) } diff --git a/util/collection/TypeFunctions.scala b/util/collection/TypeFunctions.scala index 8f542fb99..185c72226 100644 --- a/util/collection/TypeFunctions.scala +++ b/util/collection/TypeFunctions.scala @@ -15,6 +15,7 @@ trait TypeFunctions final val right = new (Id ~> P1of2[Right, Nothing]#Apply) { def apply[T](t: T) = Right(t) } final val some = new (Id ~> Some) { def apply[T](t: T) = Some(t) } final def idFun[T] = (t: T) => t + final def const[A,B](b: B): A=> B = _ => b def nestCon[M[_], N[_], G[_]](f: M ~> N): (M ∙ G)#l ~> (N ∙ G)#l = f.asInstanceOf[(M ∙ G)#l ~> (N ∙ G)#l] // implemented with a cast to avoid extra object+method call. castless version: