From c919a9c3fd2352513c8e7ff34a8ff26950a70665 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Wed, 22 Jun 2011 19:17:10 -0400 Subject: [PATCH] cache calls to definesClass within a run. closes #67 --- compile/inc/Locate.scala | 6 ++++-- main/Defaults.scala | 15 ++++++++++----- main/Keys.scala | 3 +++ main/Load.scala | 21 +++++++++++++-------- main/actions/AggressiveCompile.scala | 13 +++++++------ main/actions/Compiler.scala | 14 ++++++++------ 6 files changed, 45 insertions(+), 27 deletions(-) diff --git a/compile/inc/Locate.scala b/compile/inc/Locate.scala index 5a2396086..08eaeed82 100644 --- a/compile/inc/Locate.scala +++ b/compile/inc/Locate.scala @@ -10,6 +10,8 @@ import Function.const object Locate { + type DefinesClass = File => String => Boolean + /** Right(src) provides the value for the found class * Left(true) means that the class was found, but it had no associated value * Left(false) means that the class was not found */ @@ -31,9 +33,9 @@ object Locate /** Returns a function that searches the provided class path for * a class name and returns the entry that defines that class.*/ - def entry(classpath: Seq[File]): String => Option[File] = + def entry(classpath: Seq[File], f: DefinesClass): String => Option[File] = { - val entries = classpath.toStream.map { entry => (entry, definesClass(entry)) } + val entries = classpath.toStream.map { entry => (entry, f(entry)) } className => entries collect { case (entry, defines) if defines(className) => entry } headOption; } def resolve(f: File, className: String): File = if(f.isDirectory) classFile(f, className) else f diff --git a/main/Defaults.scala b/main/Defaults.scala index cf84612cd..e6ff31f80 100644 --- a/main/Defaults.scala +++ b/main/Defaults.scala @@ -11,6 +11,7 @@ package sbt import Configurations.{Compile, CompilerPlugin, IntegrationTest, names, Runtime, Test} import complete._ import std.TaskExtra._ + import inc.{FileValueCache, Locate} import org.scalatools.testing.{AnnotatedFingerprint, SubclassFingerprint} import scala.xml.{Node => XNode,NodeSeq} @@ -50,6 +51,7 @@ object Defaults extends BuildCommon pollInterval :== 500, logBuffered :== false, autoScalaLibrary :== true, + definesClass := FileValueCache(Locate.definesClass _ ).get, trapExit :== false, trapExit in run :== true, logBuffered in testOnly :== true, @@ -162,6 +164,7 @@ object Defaults extends BuildCommon initialCommands in GlobalScope :== "", compile <<= compileTask, compileInputs <<= compileInputsTask, + compileIncSetup <<= compileIncSetupTask, console <<= consoleTask, consoleQuick <<= consoleQuickTask, discoveredMainClasses <<= TaskData.write(compile map discoverMainClasses) triggeredBy compile, @@ -412,13 +415,15 @@ object Defaults extends BuildCommon } def compileTask = (compileInputs, streams) map { (i,s) => Compiler(i,s.log) } + def compileIncSetupTask = + (dependencyClasspath, cacheDirectory, definesClass) map { (cp, cacheDir, definesC) => + Compiler.IncSetup(analysisMap(cp), definesC, cacheDir / "compile") + } def compileInputsTask = - (dependencyClasspath, sources, compilers, javacOptions, scalacOptions, cacheDirectory, classDirectory, compileOrder, streams) map { - (cp, srcs, cs, javacOpts, scalacOpts, cacheDir, classes, order, s) => + (dependencyClasspath, sources, compilers, javacOptions, scalacOptions, classDirectory, compileOrder, compileIncSetup, streams) map { + (cp, srcs, cs, javacOpts, scalacOpts, classes, order, incSetup, s) => val classpath = classes +: data(cp) - val analysis = analysisMap(cp) - val cache = cacheDir / "compile" - Compiler.inputs(classpath, srcs, classes, scalacOpts, javacOpts, analysis, cache, 100, order)(cs, s.log) + Compiler.inputs(classpath, srcs, classes, scalacOpts, javacOpts, 100, order)(cs, incSetup, s.log) } def writePluginsDescriptor(plugins: Set[String], dir: File): List[File] = diff --git a/main/Keys.scala b/main/Keys.scala index 393ae43f6..e5612bbf6 100644 --- a/main/Keys.scala +++ b/main/Keys.scala @@ -7,6 +7,7 @@ package sbt import Project.ScopedKey import complete._ import inc.Analysis + import inc.Locate.DefinesClass import std.TaskExtra._ import scala.xml.{Node => XNode, NodeSeq} import org.apache.ivy.core.module.{descriptor, id} @@ -120,6 +121,8 @@ object Keys val consoleProject = TaskKey[Unit]("console-project", "Starts the Scala interpreter with the sbt and the build definition on the classpath and useful imports.") val compile = TaskKey[Analysis]("compile", "Compiles sources.") val compilers = TaskKey[Compiler.Compilers]("compilers", "Defines the Scala and Java compilers to use for compilation.") + val compileIncSetup = TaskKey[Compiler.IncSetup]("inc-compile-setup", "Configurations aspects of incremental compilation.") + val definesClass = TaskKey[DefinesClass]("defines-class", "Internal use: provides a function that determines whether the provided file contains a given class.") val doc = TaskKey[File]("doc", "Generates API documentation.") val copyResources = TaskKey[Seq[(File,File)]]("copy-resources", "Copies resources to the output directory.") val aggregate = SettingKey[Aggregation]("aggregate", "Configures task aggregation.") diff --git a/main/Load.scala b/main/Load.scala index 7b9a20254..cc666a7d3 100644 --- a/main/Load.scala +++ b/main/Load.scala @@ -10,6 +10,7 @@ package sbt import scala.annotation.tailrec import collection.mutable import Compiler.{Compilers,Inputs} + import inc.{FileValueCache, Locate} import Project.{inScope, ScopedKey, ScopeLocal, Setting} import Keys.{appConfiguration, baseDirectory, configuration, streams, Streams, thisProject, thisProjectRef} import Keys.{isDummy, parseResult, resolvedScoped, taskDefinitionKey} @@ -21,6 +22,7 @@ object Load { import BuildPaths._ import BuildStreams._ + import Locate.DefinesClass // note that there is State passed in but not pulled out def defaultLoad(state: State, baseDirectory: File, log: Logger): (() => Eval, BuildStructure) = @@ -35,10 +37,13 @@ object Load val evalPluginDef = EvaluateTask.evalPluginDef(log) _ val delegates = defaultDelegates val inject: Seq[Project.Setting[_]] = ((appConfiguration in GlobalScope) :== state.configuration) +: EvaluateTask.injectSettings - val rawConfig = new LoadBuildConfiguration(stagingDirectory, Nil, classpath, loader, compilers, evalPluginDef, delegates, EvaluateTask.injectStreams, inject, log) + val definesClass = FileValueCache(Locate.definesClass _) + val rawConfig = new LoadBuildConfiguration(stagingDirectory, Nil, classpath, loader, compilers, evalPluginDef, definesClass.get, delegates, EvaluateTask.injectStreams, inject, log) val commonPlugins = if(baseDirectory == defaultGlobalPlugins) Nil else buildGlobalPlugins(defaultGlobalPlugins, state, rawConfig) val config = rawConfig.copy(commonPluginClasspath = commonPlugins) - apply(base, state, config) + val result = apply(base, state, config) + definesClass.clear() + result } def buildGlobalPlugins(baseDirectory: File, state: State, config: LoadBuildConfiguration): Seq[Attributed[File]] = if(baseDirectory.isDirectory) buildPluginDefinition(baseDirectory, state, config) else Nil @@ -324,7 +329,7 @@ object Load if(defs.isEmpty) new LoadedDefinitions(defDir, target, plugs.loader, Build.default :: Nil, Nil) else - definitions(defDir, target, defs, plugs, config.compilers, config.log, normBase) + definitions(defDir, target, defs, plugs, config.definesClass, config.compilers, config.log, normBase) new BuildUnit(uri, normBase, loadedDefs, plugs) } @@ -348,9 +353,9 @@ object Load (thisPluginClasspath ++ config.commonPluginClasspath).distinct } - def definitions(base: File, targetBase: File, srcs: Seq[File], plugins: LoadedPlugins, compilers: Compilers, log: Logger, buildBase: File): LoadedDefinitions = + def definitions(base: File, targetBase: File, srcs: Seq[File], plugins: LoadedPlugins, definesClass: DefinesClass, compilers: Compilers, log: Logger, buildBase: File): LoadedDefinitions = { - val (inputs, defAnalysis) = build(plugins.classpath, srcs, targetBase, compilers, log) + val (inputs, defAnalysis) = build(plugins.classpath, srcs, targetBase, compilers, definesClass, log) val target = inputs.config.classesDirectory val definitionLoader = ClasspathUtilities.toLoader(target :: Nil, plugins.loader) val defNames = findDefinitions(defAnalysis) @@ -363,9 +368,9 @@ object Load def loadDefinition(loader: ClassLoader, definition: String): Build = ModuleUtilities.getObject(definition, loader).asInstanceOf[Build] - def build(classpath: Seq[File], sources: Seq[File], target: File, compilers: Compilers, log: Logger): (Inputs, inc.Analysis) = + def build(classpath: Seq[File], sources: Seq[File], target: File, compilers: Compilers, definesClass: DefinesClass, log: Logger): (Inputs, inc.Analysis) = { - val inputs = Compiler.inputs(classpath, sources, target, Nil, Nil, Compiler.DefaultMaxErrors, CompileOrder.Mixed)(compilers, log) + val inputs = Compiler.inputs(classpath, sources, target, Nil, Nil, definesClass, Compiler.DefaultMaxErrors, CompileOrder.Mixed)(compilers, log) val analysis = try { Compiler(inputs, log) } catch { case _: xsbti.CompileFailed => throw new NoMessageException } // compiler already logged errors @@ -470,7 +475,7 @@ object Load def allProjectRefs(build: URI): Seq[ProjectRef] = refs(build, allProjects(build)) private[this] def refs(build: URI, projects: Seq[ResolvedProject]): Seq[ProjectRef] = projects.map { p => ProjectRef(build, p.id) } } - final case class LoadBuildConfiguration(stagingDirectory: File, commonPluginClasspath: Seq[Attributed[File]], classpath: Seq[File], loader: ClassLoader, compilers: Compilers, evalPluginDef: (BuildStructure, State) => Seq[Attributed[File]], delegates: LoadedBuild => Scope => Seq[Scope], scopeLocal: ScopeLocal, injectSettings: Seq[Setting[_]], log: Logger) + final case class LoadBuildConfiguration(stagingDirectory: File, commonPluginClasspath: Seq[Attributed[File]], classpath: Seq[File], loader: ClassLoader, compilers: Compilers, evalPluginDef: (BuildStructure, State) => Seq[Attributed[File]], definesClass: DefinesClass, delegates: LoadedBuild => Scope => Seq[Scope], scopeLocal: ScopeLocal, injectSettings: Seq[Setting[_]], log: Logger) // information that is not original, but can be reconstructed from the rest of BuildStructure final class StructureIndex(val keyMap: Map[String, AttributeKey[_]], val taskToKey: Map[Task[_], ScopedKey[Task[_]]], val triggers: Triggers[Task], val keyIndex: KeyIndex) } diff --git a/main/actions/AggressiveCompile.scala b/main/actions/AggressiveCompile.scala index 54235d421..484ac503d 100644 --- a/main/actions/AggressiveCompile.scala +++ b/main/actions/AggressiveCompile.scala @@ -11,29 +11,30 @@ import inc._ import classfile.Analyze import xsbti.api.Source import xsbti.AnalysisCallback + import inc.Locate.DefinesClass import CompileSetup._ import CompileOrder.{JavaThenScala, Mixed, ScalaThenJava} import sbinary.DefaultProtocol.{ immutableMapFormat, immutableSetFormat, StringFormat } final class CompileConfiguration(val sources: Seq[File], val classpath: Seq[File], - val previousAnalysis: Analysis, val previousSetup: Option[CompileSetup], val currentSetup: CompileSetup, val getAnalysis: File => Option[Analysis], + val previousAnalysis: Analysis, val previousSetup: Option[CompileSetup], val currentSetup: CompileSetup, val getAnalysis: File => Option[Analysis], val definesClass: DefinesClass, val maxErrors: Int, val compiler: AnalyzingCompiler, val javac: JavaCompiler) class AggressiveCompile(cacheDirectory: File) { - def apply(compiler: AnalyzingCompiler, javac: JavaCompiler, sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String] = Nil, javacOptions: Seq[String] = Nil, analysisMap: Map[File, Analysis] = Map.empty, maxErrors: Int = 100, compileOrder: CompileOrder.Value = Mixed)(implicit log: Logger): Analysis = + def apply(compiler: AnalyzingCompiler, javac: JavaCompiler, sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String] = Nil, javacOptions: Seq[String] = Nil, analysisMap: Map[File, Analysis] = Map.empty, definesClass: DefinesClass = Locate.definesClass _, maxErrors: Int = 100, compileOrder: CompileOrder.Value = Mixed)(implicit log: Logger): Analysis = { val setup = new CompileSetup(outputDirectory, new CompileOptions(options, javacOptions), compiler.scalaInstance.actualVersion, compileOrder) - compile1(sources, classpath, setup, store, analysisMap, compiler, javac, maxErrors) + compile1(sources, classpath, setup, store, analysisMap, definesClass, compiler, javac, maxErrors) } def withBootclasspath(args: CompilerArguments, classpath: Seq[File]): Seq[File] = args.bootClasspath ++ args.finishClasspath(classpath) - def compile1(sources: Seq[File], classpath: Seq[File], setup: CompileSetup, store: AnalysisStore, analysis: Map[File, Analysis], compiler: AnalyzingCompiler, javac: JavaCompiler, maxErrors: Int)(implicit log: Logger): Analysis = + def compile1(sources: Seq[File], classpath: Seq[File], setup: CompileSetup, store: AnalysisStore, analysis: Map[File, Analysis], definesClass: DefinesClass, compiler: AnalyzingCompiler, javac: JavaCompiler, maxErrors: Int)(implicit log: Logger): Analysis = { val (previousAnalysis, previousSetup) = extract(store.get()) - val config = new CompileConfiguration(sources, classpath, previousAnalysis, previousSetup, setup, analysis.get _, maxErrors, compiler, javac) + val config = new CompileConfiguration(sources, classpath, previousAnalysis, previousSetup, setup, analysis.get _, definesClass, maxErrors, compiler, javac) val (modified, result) = compile2(config) if(modified) store.set(result, setup) @@ -51,7 +52,7 @@ class AggressiveCompile(cacheDirectory: File) val apiOption= (api: Either[Boolean, Source]) => api.right.toOption val cArgs = new CompilerArguments(compiler.scalaInstance, compiler.cp) val searchClasspath = withBootclasspath(cArgs, absClasspath) - val entry = Locate.entry(searchClasspath) + val entry = Locate.entry(searchClasspath, definesClass) val compile0 = (include: Set[File], callback: AnalysisCallback) => { IO.createDirectory(outputDirectory) diff --git a/main/actions/Compiler.scala b/main/actions/Compiler.scala index d1c9e5c7a..c81ea5e3c 100644 --- a/main/actions/Compiler.scala +++ b/main/actions/Compiler.scala @@ -6,6 +6,7 @@ package sbt import xsbti.{Logger => _,_} import compiler._ import inc._ + import Locate.DefinesClass import java.io.File object Compiler @@ -24,22 +25,23 @@ object Compiler final case class Inputs(compilers: Compilers, config: Options, incSetup: IncSetup) final case class Options(classpath: Seq[File], sources: Seq[File], classesDirectory: File, options: Seq[String], javacOptions: Seq[String], maxErrors: Int, order: CompileOrder.Value) - final case class IncSetup(analysisMap: Map[File, Analysis], cacheDirectory: File) + final case class IncSetup(analysisMap: Map[File, Analysis], definesClass: DefinesClass, cacheDirectory: File) final case class Compilers(scalac: AnalyzingCompiler, javac: JavaCompiler) - def inputs(classpath: Seq[File], sources: Seq[File], outputDirectory: File, options: Seq[String], javacOptions: Seq[String], maxErrors: Int, order: CompileOrder.Value)(implicit compilers: Compilers, log: Logger): Inputs = + def inputs(classpath: Seq[File], sources: Seq[File], outputDirectory: File, options: Seq[String], javacOptions: Seq[String], definesClass: DefinesClass, maxErrors: Int, order: CompileOrder.Value)(implicit compilers: Compilers, log: Logger): Inputs = { import Path._ val classesDirectory = outputDirectory / "classes" val cacheDirectory = outputDirectory / "cache" val augClasspath = classesDirectory.asFile +: classpath - inputs(augClasspath, sources, classesDirectory, options, javacOptions, Map.empty, cacheDirectory, maxErrors, order) + val incSetup = IncSetup(Map.empty, definesClass, cacheDirectory) + inputs(augClasspath, sources, classesDirectory, options, javacOptions, maxErrors, order)(compilers, incSetup, log) } - def inputs(classpath: Seq[File], sources: Seq[File], classesDirectory: File, options: Seq[String], javacOptions: Seq[String], analysisMap: Map[File, Analysis], cacheDirectory: File, maxErrors: Int, order: CompileOrder.Value)(implicit compilers: Compilers, log: Logger): Inputs = + def inputs(classpath: Seq[File], sources: Seq[File], classesDirectory: File, options: Seq[String], javacOptions: Seq[String], maxErrors: Int, order: CompileOrder.Value)(implicit compilers: Compilers, incSetup: IncSetup, log: Logger): Inputs = new Inputs( compilers, new Options(classpath, sources, classesDirectory, options, javacOptions, maxErrors, order), - new IncSetup(analysisMap, cacheDirectory) + incSetup ) def compilers(cpOptions: ClasspathOptions)(implicit app: AppConfiguration, log: Logger): Compilers = @@ -95,6 +97,6 @@ object Compiler import in.incSetup._ val agg = new AggressiveCompile(cacheDirectory) - agg(scalac, javac, sources, classpath, classesDirectory, options, javacOptions, analysisMap, maxErrors, order)(log) + agg(scalac, javac, sources, classpath, classesDirectory, options, javacOptions, analysisMap, definesClass, maxErrors, order)(log) } } \ No newline at end of file