From 0f784ab101eca6250cc840f6580753839c85e2ce Mon Sep 17 00:00:00 2001 From: James Roper Date: Tue, 27 May 2014 14:50:39 +1000 Subject: [PATCH] Allow bytecode enhancement to update analysis This breaks the loading/saving of the incremental compiler analysis out into separate task, thereby providing the necessary hooks for byte code enhancement tasks to enhance bytecode and update the analysis before the analysis gets stored to disk. --- .../sbt/compiler/AggressiveCompile.scala | 30 +++++++-------- .../sbt/compiler/IncrementalCompiler.scala | 13 +++++-- .../actions/src/main/scala/sbt/Compiler.scala | 21 ++++++++--- main/src/main/scala/sbt/Defaults.scala | 37 ++++++++++++++++--- main/src/main/scala/sbt/Keys.scala | 3 ++ 5 files changed, 73 insertions(+), 31 deletions(-) diff --git a/compile/integration/src/main/scala/sbt/compiler/AggressiveCompile.scala b/compile/integration/src/main/scala/sbt/compiler/AggressiveCompile.scala index c4ce04804..8241bd3ed 100644 --- a/compile/integration/src/main/scala/sbt/compiler/AggressiveCompile.scala +++ b/compile/integration/src/main/scala/sbt/compiler/AggressiveCompile.scala @@ -24,7 +24,7 @@ final class CompileConfiguration(val sources: Seq[File], val classpath: Seq[File val previousAnalysis: Analysis, val previousSetup: Option[CompileSetup], val currentSetup: CompileSetup, val progress: Option[CompileProgress], val getAnalysis: File => Option[Analysis], val definesClass: DefinesClass, val reporter: Reporter, val compiler: AnalyzingCompiler, val javac: xsbti.compile.JavaCompiler, val cache: GlobalsCache, val incOptions: IncOptions) -class AggressiveCompile(cacheFile: File) { +class AggressiveCompile { def apply(compiler: AnalyzingCompiler, javac: xsbti.compile.JavaCompiler, sources: Seq[File], classpath: Seq[File], @@ -33,17 +33,20 @@ class AggressiveCompile(cacheFile: File) { progress: Option[CompileProgress] = None, options: Seq[String] = Nil, javacOptions: Seq[String] = Nil, + previousAnalysis: Analysis, + previousSetup: Option[CompileSetup], analysisMap: File => Option[Analysis] = { _ => None }, definesClass: DefinesClass = Locate.definesClass _, reporter: Reporter, compileOrder: CompileOrder = Mixed, skip: Boolean = false, - incrementalCompilerOptions: IncOptions)(implicit log: Logger): Analysis = + incrementalCompilerOptions: IncOptions)(implicit log: Logger): (Analysis, CompileSetup, Boolean) = { val setup = new CompileSetup(output, new CompileOptions(options, javacOptions), compiler.scalaInstance.actualVersion, compileOrder, incrementalCompilerOptions.nameHashing) - compile1(sources, classpath, setup, progress, store, analysisMap, definesClass, + val (analysis, modified) = compile1(sources, classpath, setup, progress, previousAnalysis, previousSetup, analysisMap, definesClass, compiler, javac, reporter, skip, cache, incrementalCompilerOptions) + (analysis, setup, modified) } def withBootclasspath(args: CompilerArguments, classpath: Seq[File]): Seq[File] = @@ -52,25 +55,23 @@ class AggressiveCompile(cacheFile: File) { def compile1(sources: Seq[File], classpath: Seq[File], setup: CompileSetup, progress: Option[CompileProgress], - store: AnalysisStore, + previousAnalysis: Analysis, + previousSetup: Option[CompileSetup], analysis: File => Option[Analysis], definesClass: DefinesClass, compiler: AnalyzingCompiler, javac: xsbti.compile.JavaCompiler, reporter: Reporter, skip: Boolean, cache: GlobalsCache, - incrementalCompilerOptions: IncOptions)(implicit log: Logger): Analysis = + incrementalCompilerOptions: IncOptions)(implicit log: Logger): (Analysis, Boolean) = { - val (previousAnalysis, previousSetup) = extract(store.get(), incrementalCompilerOptions) if (skip) - previousAnalysis + (previousAnalysis, false) else { val config = new CompileConfiguration(sources, classpath, previousAnalysis, previousSetup, setup, progress, analysis, definesClass, reporter, compiler, javac, cache, incrementalCompilerOptions) val (modified, result) = compile2(config) - if (modified) - store.set(result, setup) - result + (result, modified) } } def compile2(config: CompileConfiguration)(implicit log: Logger, equiv: Equiv[CompileSetup]): (Boolean, Analysis) = @@ -176,17 +177,11 @@ class AggressiveCompile(cacheFile: File) { if (!combined.isEmpty) log.info(combined.mkString("Compiling ", " and ", " to " + outputDirs.map(_.getAbsolutePath).mkString(",") + "...")) } - private def extract(previous: Option[(Analysis, CompileSetup)], incOptions: IncOptions): (Analysis, Option[CompileSetup]) = - previous match { - case Some((an, setup)) => (an, Some(setup)) - case None => (Analysis.empty(nameHashing = incOptions.nameHashing), None) - } def javaOnly(f: File) = f.getName.endsWith(".java") private[this] def explicitBootClasspath(options: Seq[String]): Seq[File] = options.dropWhile(_ != CompilerArguments.BootClasspathOption).drop(1).take(1).headOption.toList.flatMap(IO.parseClasspath) - val store = AggressiveCompile.staticCache(cacheFile, AnalysisStore.sync(AnalysisStore.cached(FileBasedStore(cacheFile)))) } object AggressiveCompile { import collection.mutable @@ -200,6 +195,9 @@ object AggressiveCompile { b } } + + def staticCachedStore(cacheFile: File) = staticCache(cacheFile, AnalysisStore.sync(AnalysisStore.cached(FileBasedStore(cacheFile)))) + @deprecated("0.13.8", "Deprecated in favor of new sbt.compiler.javac package.") def directOrFork(instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File]): JavaTool = if (javaHome.isDefined) diff --git a/compile/integration/src/main/scala/sbt/compiler/IncrementalCompiler.scala b/compile/integration/src/main/scala/sbt/compiler/IncrementalCompiler.scala index b7a4b6c57..e2f7cd1d1 100644 --- a/compile/integration/src/main/scala/sbt/compiler/IncrementalCompiler.scala +++ b/compile/integration/src/main/scala/sbt/compiler/IncrementalCompiler.scala @@ -12,12 +12,19 @@ object IC extends IncrementalCompiler[Analysis, AnalyzingCompiler] { val setup = in.setup; import setup._ val options = in.options; import options.{ options => scalacOptions, _ } val compilers = in.compilers; import compilers._ - val agg = new AggressiveCompile(setup.cacheFile) + val agg = new AggressiveCompile val aMap = (f: File) => m2o(analysisMap(f)) val defClass = (f: File) => { val dc = definesClass(f); (name: String) => dc.apply(name) } val incOptions = IncOptions.fromStringMap(incrementalCompilerOptions) - agg(scalac, javac, sources, classpath, output, cache, m2o(progress), scalacOptions, javacOptions, aMap, - defClass, reporter, order, skip, incOptions)(log) + val (previousAnalysis, previousSetup) = { + AggressiveCompile.staticCachedStore(setup.cacheFile()).get().map { + case (a, s) => (a, Some(s)) + } getOrElse { + (Analysis.empty(nameHashing = incOptions.nameHashing), None) + } + } + agg(scalac, javac, sources, classpath, output, cache, m2o(progress), scalacOptions, javacOptions, previousAnalysis, + previousSetup, aMap, defClass, reporter, order, skip, incOptions)(log)._1 } private[this] def m2o[S](opt: Maybe[S]): Option[S] = if (opt.isEmpty) None else Some(opt.get) diff --git a/main/actions/src/main/scala/sbt/Compiler.scala b/main/actions/src/main/scala/sbt/Compiler.scala index 672e4105e..0d2ed5ad7 100644 --- a/main/actions/src/main/scala/sbt/Compiler.scala +++ b/main/actions/src/main/scala/sbt/Compiler.scala @@ -15,9 +15,10 @@ import java.io.File object Compiler { val DefaultMaxErrors = 100 - final case class Inputs(compilers: Compilers, config: Options, incSetup: IncSetup) + final case class Inputs(compilers: Compilers, config: Options, incSetup: IncSetup, previousAnalysis: PreviousAnalysis) final case class Options(classpath: Seq[File], sources: Seq[File], classesDirectory: File, options: Seq[String], javacOptions: Seq[String], maxErrors: Int, sourcePositionMapper: Position => Position, order: CompileOrder) final case class IncSetup(analysisMap: File => Option[Analysis], definesClass: DefinesClass, skip: Boolean, cacheFile: File, cache: GlobalsCache, incOptions: IncOptions) +<<<<<<< HEAD private[sbt] trait JavaToolWithNewInterface extends JavaTool { def newJavac: IncrementalCompilerJavaTools } @@ -29,12 +30,20 @@ object Compiler { } } final case class NewCompilers(scalac: AnalyzingCompiler, javac: JavaTools) +======= + final case class Compilers(scalac: AnalyzingCompiler, javac: JavaTool) + final case class PreviousAnalysis(analysis: Analysis, setup: Option[CompileSetup]) + final case class AnalysisResult(analysis: Analysis, setup: CompileSetup, modified: Boolean) +>>>>>>> Allow bytecode enhancement to update analysis - def inputs(classpath: Seq[File], sources: Seq[File], classesDirectory: File, options: Seq[String], javacOptions: Seq[String], maxErrors: Int, sourcePositionMappers: Seq[Position => Option[Position]], order: CompileOrder)(implicit compilers: Compilers, incSetup: IncSetup, log: Logger): Inputs = + def inputs(classpath: Seq[File], sources: Seq[File], classesDirectory: File, options: Seq[String], + javacOptions: Seq[String], maxErrors: Int, sourcePositionMappers: Seq[Position => Option[Position]], + order: CompileOrder, previousAnalysis: PreviousAnalysis)(implicit compilers: Compilers, incSetup: IncSetup, log: Logger): Inputs = new Inputs( compilers, new Options(classpath, sources, classesDirectory, options, javacOptions, maxErrors, foldMappers(sourcePositionMappers), order), - incSetup + incSetup, + previousAnalysis ) def compilers(cpOptions: ClasspathOptions)(implicit app: AppConfiguration, log: Logger): Compilers = @@ -79,14 +88,14 @@ object Compiler { val provider = ComponentCompiler.interfaceProvider(componentManager) new AnalyzingCompiler(instance, provider, cpOptions, log) } - def apply(in: Inputs, log: Logger): Analysis = + def apply(in: Inputs, log: Logger): AnalysisResult = { import in.compilers._ import in.config._ import in.incSetup._ apply(in, log, new LoggerReporter(maxErrors, log, sourcePositionMapper)) } - def apply(in: Inputs, log: Logger, reporter: xsbti.Reporter): Analysis = + def apply(in: Inputs, log: Logger, reporter: xsbti.Reporter): AnalysisResult = { import in.compilers._ import in.config._ @@ -98,7 +107,7 @@ object Compiler { val javacChosen: xsbti.compile.JavaCompiler = in.compilers.newJavac.map(_.xsbtiCompiler).getOrElse(in.compilers.javac) agg(scalac, javacChosen, sources, classpath, CompileOutput(classesDirectory), cache, None, options, javacOptions, - analysisMap, definesClass, reporter, order, skip, incOptions)(log) + in.previousAnalysis.analysis, in.previousAnalysis.setup, analysisMap, definesClass, reporter, order, skip, incOptions)(log) } private[sbt] def foldMappers[A](mappers: Seq[A => Option[A]]) = diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 40645e4fe..210db6b3a 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -14,7 +14,8 @@ import Configurations.{ Compile, CompilerPlugin, IntegrationTest, names, Provide import CrossVersion.{ binarySbtVersion, binaryScalaVersion, partialVersion } import complete._ import std.TaskExtra._ -import inc.{ FileValueCache, IncOptions, Locate } +import sbt.inc.{ Analysis, FileValueCache, IncOptions, Locate } +import sbt.compiler.AggressiveCompile import testing.{ Framework, Runner, AnnotatedFingerprint, SubclassFingerprint } import sys.error @@ -246,8 +247,9 @@ object Defaults extends BuildCommon { def compilersSetting = compilers := Compiler.compilers(scalaInstance.value, classpathOptions.value, javaHome.value)(appConfiguration.value, streams.value.log) - lazy val configTasks = docTaskSettings(doc) ++ inTask(compile)(compileInputsSettings) ++ configGlobal ++ Seq( - compile <<= compileTask tag (Tags.Compile, Tags.CPU), + lazy val configTasks = docTaskSettings(doc) ++ inTask(compile)(compileInputsSettings) ++ configGlobal ++ compileAnalysisSettings ++ Seq( + compile <<= compileTask, + compileIncremental <<= compileIncrementalTask tag (Tags.Compile, Tags.CPU), printWarnings <<= printWarningsTask, compileAnalysisFilename := { // Here, if the user wants cross-scala-versioning, we also append it @@ -778,8 +780,11 @@ object Defaults extends BuildCommon { @deprecated("Use inTask(compile)(compileInputsSettings)", "0.13.0") def compileTaskSettings: Seq[Setting[_]] = inTask(compile)(compileInputsSettings) - def compileTask: Initialize[Task[inc.Analysis]] = Def.task { compileTaskImpl(streams.value, (compileInputs in compile).value, (compilerReporter in compile).value) } - private[this] def compileTaskImpl(s: TaskStreams, ci: Compiler.Inputs, reporter: Option[xsbti.Reporter]): inc.Analysis = + def compileTask: Initialize[Task[inc.Analysis]] = Def.task { saveAnalysis.value } + def compileIncrementalTask = Def.task { + compileIncrementalTaskImpl(streams.value, (compileInputs in compile).value, (compilerReporter in compile).value) + } + private[this] def compileIncrementalTaskImpl(s: TaskStreams, ci: Compiler.Inputs, reporter: Option[xsbti.Reporter]): Compiler.AnalysisResult = { lazy val x = s.text(ExportStream) def onArgs(cs: Compiler.Compilers) = cs.copy(scalac = cs.scalac.onArgs(exported(x, "scalac")), javac = cs.javac.onArgs(exported(x, "javac"))) @@ -803,9 +808,29 @@ object Defaults extends BuildCommon { def compileInputsSettings: Seq[Setting[_]] = Seq(compileInputs := { val cp = classDirectory.value +: data(dependencyClasspath.value) - Compiler.inputs(cp, sources.value, classDirectory.value, scalacOptions.value, javacOptions.value, maxErrors.value, sourcePositionMappers.value, compileOrder.value)(compilers.value, compileIncSetup.value, streams.value.log) + Compiler.inputs(cp, sources.value, classDirectory.value, scalacOptions.value, javacOptions.value, + maxErrors.value, sourcePositionMappers.value, compileOrder.value, readAnalysis.value)(compilers.value, compileIncSetup.value, streams.value.log) }, compilerReporter := None) + def compileAnalysisSettings: Seq[Setting[_]] = Seq( + readAnalysis := { + val setup: Compiler.IncSetup = compileIncSetup.value + val store = AggressiveCompile.staticCachedStore(setup.cacheFile) + store.get() match { + case Some((an, setup)) => Compiler.PreviousAnalysis(an, Some(setup)) + case None => Compiler.PreviousAnalysis(Analysis.empty(nameHashing = setup.incOptions.nameHashing), None) + } + }, + saveAnalysis := { + val setup: Compiler.IncSetup = compileIncSetup.value + val analysisResult: Compiler.AnalysisResult = compileIncremental.value + if (analysisResult.modified) { + val store = AggressiveCompile.staticCachedStore(setup.cacheFile) + store.set(analysisResult.analysis, analysisResult.setup) + } + analysisResult.analysis + } + ) def printWarningsTask: Initialize[Task[Unit]] = (streams, compile, maxErrors, sourcePositionMappers) map { (s, analysis, max, spms) => diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 8795d0795..cbc27853f 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -141,6 +141,9 @@ object Keys { val consoleQuick = TaskKey[Unit]("console-quick", "Starts the Scala interpreter with the project dependencies on the classpath.", ATask, console) val consoleProject = TaskKey[Unit]("console-project", "Starts the Scala interpreter with the sbt and the build definition on the classpath and useful imports.", AMinusTask) val compile = TaskKey[Analysis]("compile", "Compiles sources.", APlusTask) + val compileIncremental = TaskKey[Compiler.AnalysisResult]("compileIncremental", "Actually runs the incremental compliation", DTask) + val readAnalysis = TaskKey[Compiler.PreviousAnalysis]("readAnalysis", "Read the incremental compiler analysis from disk", DTask) + val saveAnalysis = TaskKey[Analysis]("saveAnalysis", "Save the incremental compiler analysis to disk", DTask) val compilers = TaskKey[Compiler.Compilers]("compilers", "Defines the Scala and Java compilers to use for compilation.", DTask) val compileAnalysisFilename = TaskKey[String]("compileAnalysisFilename", "Defines the filename used to store the incremental compiler analysis file (inside the streams cacheDirectory).", DTask) val compileIncSetup = TaskKey[Compiler.IncSetup]("inc-compile-setup", "Configures aspects of incremental compilation.", DTask)