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.
This commit is contained in:
James Roper 2014-05-27 14:50:39 +10:00 committed by Josh Suereth
parent e2942f167b
commit 0f784ab101
5 changed files with 73 additions and 31 deletions

View File

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

View File

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

View File

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

View File

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

View File

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