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 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) 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, def apply(compiler: AnalyzingCompiler,
javac: xsbti.compile.JavaCompiler, javac: xsbti.compile.JavaCompiler,
sources: Seq[File], classpath: Seq[File], sources: Seq[File], classpath: Seq[File],
@ -33,17 +33,20 @@ class AggressiveCompile(cacheFile: File) {
progress: Option[CompileProgress] = None, progress: Option[CompileProgress] = None,
options: Seq[String] = Nil, options: Seq[String] = Nil,
javacOptions: Seq[String] = Nil, javacOptions: Seq[String] = Nil,
previousAnalysis: Analysis,
previousSetup: Option[CompileSetup],
analysisMap: File => Option[Analysis] = { _ => None }, analysisMap: File => Option[Analysis] = { _ => None },
definesClass: DefinesClass = Locate.definesClass _, definesClass: DefinesClass = Locate.definesClass _,
reporter: Reporter, reporter: Reporter,
compileOrder: CompileOrder = Mixed, compileOrder: CompileOrder = Mixed,
skip: Boolean = false, 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), val setup = new CompileSetup(output, new CompileOptions(options, javacOptions),
compiler.scalaInstance.actualVersion, compileOrder, incrementalCompilerOptions.nameHashing) 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) compiler, javac, reporter, skip, cache, incrementalCompilerOptions)
(analysis, setup, modified)
} }
def withBootclasspath(args: CompilerArguments, classpath: Seq[File]): Seq[File] = def withBootclasspath(args: CompilerArguments, classpath: Seq[File]): Seq[File] =
@ -52,25 +55,23 @@ class AggressiveCompile(cacheFile: File) {
def compile1(sources: Seq[File], def compile1(sources: Seq[File],
classpath: Seq[File], classpath: Seq[File],
setup: CompileSetup, progress: Option[CompileProgress], setup: CompileSetup, progress: Option[CompileProgress],
store: AnalysisStore, previousAnalysis: Analysis,
previousSetup: Option[CompileSetup],
analysis: File => Option[Analysis], analysis: File => Option[Analysis],
definesClass: DefinesClass, definesClass: DefinesClass,
compiler: AnalyzingCompiler, compiler: AnalyzingCompiler,
javac: xsbti.compile.JavaCompiler, javac: xsbti.compile.JavaCompiler,
reporter: Reporter, skip: Boolean, reporter: Reporter, skip: Boolean,
cache: GlobalsCache, cache: GlobalsCache,
incrementalCompilerOptions: IncOptions)(implicit log: Logger): Analysis = incrementalCompilerOptions: IncOptions)(implicit log: Logger): (Analysis, Boolean) =
{ {
val (previousAnalysis, previousSetup) = extract(store.get(), incrementalCompilerOptions)
if (skip) if (skip)
previousAnalysis (previousAnalysis, false)
else { else {
val config = new CompileConfiguration(sources, classpath, previousAnalysis, previousSetup, setup, val config = new CompileConfiguration(sources, classpath, previousAnalysis, previousSetup, setup,
progress, analysis, definesClass, reporter, compiler, javac, cache, incrementalCompilerOptions) progress, analysis, definesClass, reporter, compiler, javac, cache, incrementalCompilerOptions)
val (modified, result) = compile2(config) val (modified, result) = compile2(config)
if (modified) (result, modified)
store.set(result, setup)
result
} }
} }
def compile2(config: CompileConfiguration)(implicit log: Logger, equiv: Equiv[CompileSetup]): (Boolean, Analysis) = def compile2(config: CompileConfiguration)(implicit log: Logger, equiv: Equiv[CompileSetup]): (Boolean, Analysis) =
@ -176,17 +177,11 @@ class AggressiveCompile(cacheFile: File) {
if (!combined.isEmpty) if (!combined.isEmpty)
log.info(combined.mkString("Compiling ", " and ", " to " + outputDirs.map(_.getAbsolutePath).mkString(",") + "...")) 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") def javaOnly(f: File) = f.getName.endsWith(".java")
private[this] def explicitBootClasspath(options: Seq[String]): Seq[File] = private[this] def explicitBootClasspath(options: Seq[String]): Seq[File] =
options.dropWhile(_ != CompilerArguments.BootClasspathOption).drop(1).take(1).headOption.toList.flatMap(IO.parseClasspath) 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 { object AggressiveCompile {
import collection.mutable import collection.mutable
@ -200,6 +195,9 @@ object AggressiveCompile {
b 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.") @deprecated("0.13.8", "Deprecated in favor of new sbt.compiler.javac package.")
def directOrFork(instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File]): JavaTool = def directOrFork(instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File]): JavaTool =
if (javaHome.isDefined) if (javaHome.isDefined)

View File

@ -12,12 +12,19 @@ object IC extends IncrementalCompiler[Analysis, AnalyzingCompiler] {
val setup = in.setup; import setup._ val setup = in.setup; import setup._
val options = in.options; import options.{ options => scalacOptions, _ } val options = in.options; import options.{ options => scalacOptions, _ }
val compilers = in.compilers; import compilers._ val compilers = in.compilers; import compilers._
val agg = new AggressiveCompile(setup.cacheFile) val agg = new AggressiveCompile
val aMap = (f: File) => m2o(analysisMap(f)) val aMap = (f: File) => m2o(analysisMap(f))
val defClass = (f: File) => { val dc = definesClass(f); (name: String) => dc.apply(name) } val defClass = (f: File) => { val dc = definesClass(f); (name: String) => dc.apply(name) }
val incOptions = IncOptions.fromStringMap(incrementalCompilerOptions) val incOptions = IncOptions.fromStringMap(incrementalCompilerOptions)
agg(scalac, javac, sources, classpath, output, cache, m2o(progress), scalacOptions, javacOptions, aMap, val (previousAnalysis, previousSetup) = {
defClass, reporter, order, skip, incOptions)(log) 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) 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 { object Compiler {
val DefaultMaxErrors = 100 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 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) 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 { private[sbt] trait JavaToolWithNewInterface extends JavaTool {
def newJavac: IncrementalCompilerJavaTools def newJavac: IncrementalCompilerJavaTools
} }
@ -29,12 +30,20 @@ object Compiler {
} }
} }
final case class NewCompilers(scalac: AnalyzingCompiler, javac: JavaTools) 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( new Inputs(
compilers, compilers,
new Options(classpath, sources, classesDirectory, options, javacOptions, maxErrors, foldMappers(sourcePositionMappers), order), new Options(classpath, sources, classesDirectory, options, javacOptions, maxErrors, foldMappers(sourcePositionMappers), order),
incSetup incSetup,
previousAnalysis
) )
def compilers(cpOptions: ClasspathOptions)(implicit app: AppConfiguration, log: Logger): Compilers = def compilers(cpOptions: ClasspathOptions)(implicit app: AppConfiguration, log: Logger): Compilers =
@ -79,14 +88,14 @@ object Compiler {
val provider = ComponentCompiler.interfaceProvider(componentManager) val provider = ComponentCompiler.interfaceProvider(componentManager)
new AnalyzingCompiler(instance, provider, cpOptions, log) 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.compilers._
import in.config._ import in.config._
import in.incSetup._ import in.incSetup._
apply(in, log, new LoggerReporter(maxErrors, log, sourcePositionMapper)) 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.compilers._
import in.config._ import in.config._
@ -98,7 +107,7 @@ object Compiler {
val javacChosen: xsbti.compile.JavaCompiler = val javacChosen: xsbti.compile.JavaCompiler =
in.compilers.newJavac.map(_.xsbtiCompiler).getOrElse(in.compilers.javac) in.compilers.newJavac.map(_.xsbtiCompiler).getOrElse(in.compilers.javac)
agg(scalac, javacChosen, sources, classpath, CompileOutput(classesDirectory), cache, None, options, javacOptions, 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]]) = 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 CrossVersion.{ binarySbtVersion, binaryScalaVersion, partialVersion }
import complete._ import complete._
import std.TaskExtra._ 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 testing.{ Framework, Runner, AnnotatedFingerprint, SubclassFingerprint }
import sys.error 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) 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( lazy val configTasks = docTaskSettings(doc) ++ inTask(compile)(compileInputsSettings) ++ configGlobal ++ compileAnalysisSettings ++ Seq(
compile <<= compileTask tag (Tags.Compile, Tags.CPU), compile <<= compileTask,
compileIncremental <<= compileIncrementalTask tag (Tags.Compile, Tags.CPU),
printWarnings <<= printWarningsTask, printWarnings <<= printWarningsTask,
compileAnalysisFilename := { compileAnalysisFilename := {
// Here, if the user wants cross-scala-versioning, we also append it // 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") @deprecated("Use inTask(compile)(compileInputsSettings)", "0.13.0")
def compileTaskSettings: Seq[Setting[_]] = inTask(compile)(compileInputsSettings) 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) } def compileTask: Initialize[Task[inc.Analysis]] = Def.task { saveAnalysis.value }
private[this] def compileTaskImpl(s: TaskStreams, ci: Compiler.Inputs, reporter: Option[xsbti.Reporter]): inc.Analysis = 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) 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"))) 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[_]] = def compileInputsSettings: Seq[Setting[_]] =
Seq(compileInputs := { Seq(compileInputs := {
val cp = classDirectory.value +: data(dependencyClasspath.value) 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) 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]] = def printWarningsTask: Initialize[Task[Unit]] =
(streams, compile, maxErrors, sourcePositionMappers) map { (s, analysis, max, spms) => (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 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 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 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 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 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) val compileIncSetup = TaskKey[Compiler.IncSetup]("inc-compile-setup", "Configures aspects of incremental compilation.", DTask)