mirror of https://github.com/sbt/sbt.git
Changes required to use sbt as-is from Scala-IDE.
This commit is contained in:
parent
e23df839b7
commit
b5a29987e6
|
|
@ -5,7 +5,7 @@ package sbt
|
|||
package compiler
|
||||
|
||||
import xsbti.{AnalysisCallback, Logger => xLogger, Reporter}
|
||||
import xsbti.compile.{CachedCompiler, CachedCompilerProvider, DependencyChanges, GlobalsCache}
|
||||
import xsbti.compile.{CachedCompiler, CachedCompilerProvider, DependencyChanges, GlobalsCache, CompileProgress, Output}
|
||||
import java.io.File
|
||||
import java.net.{URL, URLClassLoader}
|
||||
|
||||
|
|
@ -16,33 +16,35 @@ package compiler
|
|||
final class AnalyzingCompiler(val scalaInstance: xsbti.compile.ScalaInstance, val provider: CompilerInterfaceProvider, val cp: xsbti.compile.ClasspathOptions, log: Logger) extends CachedCompilerProvider
|
||||
{
|
||||
def this(scalaInstance: ScalaInstance, provider: CompilerInterfaceProvider, log: Logger) = this(scalaInstance, provider, ClasspathOptions.auto, log)
|
||||
def apply(sources: Seq[File], changes: DependencyChanges, classpath: Seq[File], outputDirectory: File, options: Seq[String], callback: AnalysisCallback, maximumErrors: Int, cache: GlobalsCache, log: Logger)
|
||||
def apply(sources: Seq[File], changes: DependencyChanges, classpath: Seq[File], singleOutput: File, options: Seq[String], callback: AnalysisCallback, maximumErrors: Int, cache: GlobalsCache, log: Logger)
|
||||
{
|
||||
val arguments = (new CompilerArguments(scalaInstance, cp))(Nil, classpath, outputDirectory, options)
|
||||
compile(sources, changes, arguments, callback, maximumErrors, cache, log)
|
||||
val arguments = (new CompilerArguments(scalaInstance, cp))(Nil, classpath, None, options)
|
||||
val output = CompileOutput(singleOutput)
|
||||
compile(sources, changes, arguments, output, callback, maximumErrors, cache, log, None)
|
||||
}
|
||||
|
||||
def compile(sources: Seq[File], changes: DependencyChanges, options: Seq[String], callback: AnalysisCallback, maximumErrors: Int, cache: GlobalsCache, log: Logger): Unit =
|
||||
def compile(sources: Seq[File], changes: DependencyChanges, options: Seq[String], output: Output, callback: AnalysisCallback, maximumErrors: Int, cache: GlobalsCache, log: Logger, progressOpt: Option[CompileProgress]): Unit =
|
||||
{
|
||||
val reporter = new LoggerReporter(maximumErrors, log)
|
||||
val cached = cache(options.toArray, !changes.isEmpty, this, log, reporter)
|
||||
compile(sources, changes, callback, log, reporter, cached)
|
||||
val cached = cache(options.toArray, output, !changes.isEmpty, this, log, reporter)
|
||||
val progress = progressOpt getOrElse IgnoreProgress
|
||||
compile(sources, changes, callback, log, reporter, progress, cached)
|
||||
}
|
||||
|
||||
def compile(sources: Seq[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, reporter: Reporter, compiler: CachedCompiler)
|
||||
def compile(sources: Seq[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, reporter: Reporter, progress: CompileProgress, compiler: CachedCompiler)
|
||||
{
|
||||
call("xsbt.CompilerInterface", "run", log)(
|
||||
classOf[Array[File]], classOf[DependencyChanges], classOf[AnalysisCallback], classOf[xLogger], classOf[Reporter], classOf[CachedCompiler]) (
|
||||
sources.toArray, changes, callback, log, reporter, compiler )
|
||||
classOf[Array[File]], classOf[DependencyChanges], classOf[AnalysisCallback], classOf[xLogger], classOf[Reporter], classOf[CompileProgress], classOf[CachedCompiler]) (
|
||||
sources.toArray, changes, callback, log, reporter, progress, compiler )
|
||||
}
|
||||
def newCachedCompiler(arguments: Array[String], log: xLogger, reporter: Reporter, resident: Boolean): CachedCompiler =
|
||||
newCachedCompiler(arguments: Seq[String], log, reporter, resident)
|
||||
def newCachedCompiler(arguments: Array[String], output: Output, log: xLogger, reporter: Reporter, resident: Boolean): CachedCompiler =
|
||||
newCachedCompiler(arguments: Seq[String], output, log, reporter, resident)
|
||||
|
||||
def newCachedCompiler(arguments: Seq[String], log: xLogger, reporter: Reporter, resident: Boolean): CachedCompiler =
|
||||
def newCachedCompiler(arguments: Seq[String], output: Output, log: xLogger, reporter: Reporter, resident: Boolean): CachedCompiler =
|
||||
{
|
||||
call("xsbt.CompilerInterface", "newCompiler", log)(
|
||||
classOf[Array[String]], classOf[xLogger], classOf[Reporter], classOf[Boolean] ) (
|
||||
arguments.toArray[String] : Array[String], log, reporter, resident: java.lang.Boolean ).
|
||||
classOf[Array[String]], classOf[Output], classOf[xLogger], classOf[Reporter], classOf[Boolean] ) (
|
||||
arguments.toArray[String] : Array[String], output, log, reporter, resident: java.lang.Boolean ).
|
||||
asInstanceOf[CachedCompiler]
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +52,7 @@ final class AnalyzingCompiler(val scalaInstance: xsbti.compile.ScalaInstance, va
|
|||
doc(sources, classpath, outputDirectory, options, log, new LoggerReporter(maximumErrors, log))
|
||||
def doc(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], log: Logger, reporter: Reporter): Unit =
|
||||
{
|
||||
val arguments = (new CompilerArguments(scalaInstance, cp))(sources, classpath, outputDirectory, options)
|
||||
val arguments = (new CompilerArguments(scalaInstance, cp))(sources, classpath, Some(outputDirectory), options)
|
||||
call("xsbt.ScaladocInterface", "run", log) (classOf[Array[String]], classOf[xLogger], classOf[Reporter]) (
|
||||
arguments.toArray[String] : Array[String], log, reporter)
|
||||
}
|
||||
|
|
@ -124,4 +126,9 @@ object AnalyzingCompiler
|
|||
}
|
||||
}
|
||||
private def isSourceName(name: String): Boolean = name.endsWith(".scala") || name.endsWith(".java")
|
||||
}
|
||||
}
|
||||
|
||||
private[this] object IgnoreProgress extends CompileProgress {
|
||||
def startUnit(phase: String, unitPath: String) {}
|
||||
def advance(current: Int, total: Int) = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ package compiler
|
|||
* this would lead to compiling against the wrong library jar.*/
|
||||
final class CompilerArguments(scalaInstance: xsbti.compile.ScalaInstance, cp: xsbti.compile.ClasspathOptions)
|
||||
{
|
||||
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String]): Seq[String] =
|
||||
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: Option[File], options: Seq[String]): Seq[String] =
|
||||
{
|
||||
checkScalaHomeUnset()
|
||||
val cpWithCompiler = finishClasspath(classpath)
|
||||
|
|
@ -24,7 +24,7 @@ final class CompilerArguments(scalaInstance: xsbti.compile.ScalaInstance, cp: xs
|
|||
// We append a random dummy element as workaround.
|
||||
val dummy = "dummy_" + Integer.toHexString(util.Random.nextInt)
|
||||
val classpathOption = Seq("-classpath", if(cpWithCompiler.isEmpty) dummy else absString(cpWithCompiler))
|
||||
val outputOption = Seq("-d", outputDirectory.getAbsolutePath)
|
||||
val outputOption = outputDirectory map {out => Seq("-d", out.getAbsolutePath)} getOrElse Seq()
|
||||
options ++ outputOption ++ bootClasspathOption(hasLibrary(classpath)) ++ classpathOption ++ abs(sources)
|
||||
}
|
||||
def finishClasspath(classpath: Seq[File]): Seq[File] =
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package sbt
|
|||
package compiler
|
||||
|
||||
import xsbti.{Logger => xLogger, Reporter}
|
||||
import xsbti.compile.{CachedCompiler, CachedCompilerProvider, GlobalsCache}
|
||||
import xsbti.compile.{CachedCompiler, CachedCompilerProvider, GlobalsCache, Output}
|
||||
import Logger.f0
|
||||
import java.io.File
|
||||
import java.util.{LinkedHashMap,Map}
|
||||
|
|
@ -13,14 +13,14 @@ private final class CompilerCache(val maxInstances: Int) extends GlobalsCache
|
|||
private[this] def lru[A,B](max: Int) = new LinkedHashMap[A,B](8, 0.75f, true) {
|
||||
override def removeEldestEntry(eldest: Map.Entry[A,B]): Boolean = size > max
|
||||
}
|
||||
def apply(args: Array[String], forceNew: Boolean, c: CachedCompilerProvider, log: xLogger, reporter: Reporter): CachedCompiler = synchronized
|
||||
def apply(args: Array[String], output: Output, forceNew: Boolean, c: CachedCompilerProvider, log: xLogger, reporter: Reporter): CachedCompiler = synchronized
|
||||
{
|
||||
val key = CompilerKey(dropSources(args.toList), c.scalaInstance.actualVersion)
|
||||
if(forceNew) cache.remove(key)
|
||||
cache.get(key) match {
|
||||
case null =>
|
||||
log.debug(f0("Compiler cache miss. " + key.toString))
|
||||
put(key, c.newCachedCompiler(args, log, reporter, /* resident = */ !forceNew))
|
||||
put(key, c.newCachedCompiler(args, output, log, reporter, /* resident = */ !forceNew))
|
||||
case cc =>
|
||||
log.debug(f0("Compiler cache hit (" + cc.hashCode.toHexString + "). " + key.toString))
|
||||
cc
|
||||
|
|
@ -46,7 +46,7 @@ object CompilerCache
|
|||
|
||||
val fresh: GlobalsCache = new GlobalsCache {
|
||||
def clear() {}
|
||||
def apply(args: Array[String], forceNew: Boolean, c: CachedCompilerProvider, log: xLogger, reporter: Reporter): CachedCompiler =
|
||||
c.newCachedCompiler(args, log, reporter, /*resident = */ false)
|
||||
def apply(args: Array[String], output: Output, forceNew: Boolean, c: CachedCompilerProvider, log: xLogger, reporter: Reporter): CachedCompiler =
|
||||
c.newCachedCompiler(args, output, log, reporter, /*resident = */ false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2012 Eugene Vigdorchik
|
||||
*/
|
||||
|
||||
package sbt
|
||||
package compiler
|
||||
|
||||
import xsbti.compile.{Output, SingleOutput, MultipleOutput}
|
||||
import java.io.File
|
||||
|
||||
object CompileOutput {
|
||||
def apply(dir: File): Output = new SingleOutput {
|
||||
def outputDirectory = dir
|
||||
}
|
||||
|
||||
def apply(groups: (File, File)*): Output = new MultipleOutput {
|
||||
def outputGroups = groups.toArray map {
|
||||
case (src, out) => new MultipleOutput.OutputGroup {
|
||||
def sourceDirectory = src
|
||||
def outputDirectory = out
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,8 +13,13 @@ trait JavaCompiler extends xsbti.compile.JavaCompiler
|
|||
{
|
||||
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String])(implicit log: Logger)
|
||||
|
||||
def compile(sources: Array[File], classpath: Array[File], outputDirectory: File, options: Array[String], maxErrors: Int, log: xsbti.Logger): Unit =
|
||||
def compile(sources: Array[File], classpath: Array[File], output: xsbti.compile.Output, options: Array[String], maxErrors: Int, log: xsbti.Logger): Unit = {
|
||||
val outputDirectory = output match {
|
||||
case single: xsbti.compile.SingleOutput => single.outputDirectory
|
||||
case _ => throw new RuntimeException("Javac doesn't support multiple output directories")
|
||||
}
|
||||
apply(sources, classpath, outputDirectory, options)(log)
|
||||
}
|
||||
}
|
||||
trait Javadoc
|
||||
{
|
||||
|
|
@ -52,7 +57,7 @@ object JavaCompiler
|
|||
def compile(contract: JavacContract, sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String])(implicit log: Logger) {
|
||||
val augmentedClasspath = if(cp.autoBoot) classpath ++ Seq(scalaInstance.libraryJar) else classpath
|
||||
val javaCp = ClasspathOptions.javac(cp.compiler)
|
||||
val arguments = (new CompilerArguments(scalaInstance, javaCp))(sources, augmentedClasspath, outputDirectory, options)
|
||||
val arguments = (new CompilerArguments(scalaInstance, javaCp))(sources, augmentedClasspath, Some(outputDirectory), options)
|
||||
log.debug("Calling " + contract.name.capitalize + " with arguments:\n\t" + arguments.mkString("\n\t"))
|
||||
val code: Int = f(contract, arguments, log)
|
||||
log.debug(contract.name + " returned exit code: " + code)
|
||||
|
|
@ -106,4 +111,4 @@ object JavaCompiler
|
|||
// javac's argument file seems to allow naive space escaping with quotes. escaping a quote with a backslash does not work
|
||||
def escapeSpaces(s: String): String = '\"' + normalizeSlash(s) + '\"'
|
||||
def normalizeSlash(s: String) = s.replace(File.separatorChar, '/')
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class RawCompiler(val scalaInstance: xsbti.compile.ScalaInstance, cp: ClasspathO
|
|||
// but should not be otherwise directly referenced
|
||||
import scala.tools.nsc.Main.{process => _}
|
||||
|
||||
val arguments = compilerArguments(sources, classpath, outputDirectory, options)
|
||||
val arguments = compilerArguments(sources, classpath, Some(outputDirectory), options)
|
||||
log.debug("Plain interface to Scala compiler " + scalaInstance.actualVersion + " with arguments: " + arguments.mkString("\n\t", "\n\t", ""))
|
||||
val mainClass = Class.forName("scala.tools.nsc.Main", true, scalaInstance.loader)
|
||||
val process = mainClass.getMethod("process", classOf[Array[String]])
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ object APIs
|
|||
def empty: APIs = apply(Map.empty, Map.empty)
|
||||
|
||||
val emptyAPI = new xsbti.api.SourceAPI(Array(), Array())
|
||||
val emptyCompilation = new xsbti.api.Compilation(-1, "")
|
||||
val emptyCompilation = new xsbti.api.Compilation(-1, Array())
|
||||
val emptySource = new xsbti.api.Source(emptyCompilation, Array(), emptyAPI, 0, false)
|
||||
def getAPI[T](map: Map[T, Source], src: T): Source = map.getOrElse(src, emptySource)
|
||||
}
|
||||
|
|
@ -61,4 +61,4 @@ private class MAPIs(val internal: Map[File, Source], val external: Map[String, S
|
|||
def internalAPI(src: File) = getAPI(internal, src)
|
||||
def externalAPI(ext: String) = getAPI(external, ext)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,23 +4,23 @@
|
|||
package sbt
|
||||
package inc
|
||||
|
||||
import xsbti.api.{Source, SourceAPI}
|
||||
import xsbti.compile.DependencyChanges
|
||||
import xsbti.api.{Source, SourceAPI, Compilation, OutputSetting}
|
||||
import xsbti.compile.{DependencyChanges, Output, SingleOutput, MultipleOutput}
|
||||
import xsbti.{Position,Problem,Severity}
|
||||
import Logger.{m2o, problem}
|
||||
import java.io.File
|
||||
|
||||
object IncrementalCompile
|
||||
{
|
||||
def apply(sources: Set[File], entry: String => Option[File], compile: (Set[File], DependencyChanges, xsbti.AnalysisCallback) => Unit, previous: Analysis, forEntry: File => Option[Analysis], outputPath: File, log: Logger): (Boolean, Analysis) =
|
||||
def apply(sources: Set[File], entry: String => Option[File], compile: (Set[File], DependencyChanges, xsbti.AnalysisCallback) => Unit, previous: Analysis, forEntry: File => Option[Analysis], output: Output, log: Logger): (Boolean, Analysis) =
|
||||
{
|
||||
val current = Stamps.initial(Stamp.exists, Stamp.hash, Stamp.lastModified)
|
||||
val internalMap = (f: File) => previous.relations.produced(f).headOption
|
||||
val externalAPI = getExternalAPI(entry, forEntry)
|
||||
Incremental.compile(sources, entry, previous, current, forEntry, doCompile(compile, internalMap, externalAPI, current, outputPath), log)
|
||||
Incremental.compile(sources, entry, previous, current, forEntry, doCompile(compile, internalMap, externalAPI, current, output), log)
|
||||
}
|
||||
def doCompile(compile: (Set[File], DependencyChanges, xsbti.AnalysisCallback) => Unit, internalMap: File => Option[File], externalAPI: (File, String) => Option[Source], current: ReadStamps, outputPath: File) = (srcs: Set[File], changes: DependencyChanges) => {
|
||||
val callback = new AnalysisCallback(internalMap, externalAPI, current, outputPath)
|
||||
def doCompile(compile: (Set[File], DependencyChanges, xsbti.AnalysisCallback) => Unit, internalMap: File => Option[File], externalAPI: (File, String) => Option[Source], current: ReadStamps, output: Output) = (srcs: Set[File], changes: DependencyChanges) => {
|
||||
val callback = new AnalysisCallback(internalMap, externalAPI, current, output)
|
||||
compile(srcs, changes, callback)
|
||||
callback.get
|
||||
}
|
||||
|
|
@ -37,10 +37,16 @@ object IncrementalCompile
|
|||
}
|
||||
}
|
||||
}
|
||||
private final class AnalysisCallback(internalMap: File => Option[File], externalAPI: (File, String) => Option[Source], current: ReadStamps, outputPath: File) extends xsbti.AnalysisCallback
|
||||
private final class AnalysisCallback(internalMap: File => Option[File], externalAPI: (File, String) => Option[Source], current: ReadStamps, output: Output) extends xsbti.AnalysisCallback
|
||||
{
|
||||
val time = System.currentTimeMillis
|
||||
val compilation = new xsbti.api.Compilation(time, outputPath.getAbsolutePath)
|
||||
val compilation = {
|
||||
val outputSettings = output match {
|
||||
case single: SingleOutput => Array(new OutputSetting("/", single.outputDirectory.getAbsolutePath))
|
||||
case multi: MultipleOutput =>
|
||||
multi.outputGroups.map(out => new OutputSetting(out.sourceDirectory.getAbsolutePath, out.outputDirectory.getAbsolutePath)).toArray
|
||||
}
|
||||
new Compilation(System.currentTimeMillis, outputSettings)
|
||||
}
|
||||
|
||||
override def toString = ( List("APIs", "Binary deps", "Products", "Source deps") zip List(apis, binaryDeps, classes, sourceDeps)).map { case (label, map) => label + "\n\t" + map.mkString("\n\t") }.mkString("\n")
|
||||
|
||||
|
|
@ -146,4 +152,4 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
|
|||
} }
|
||||
|
||||
def beginSource(source: File) {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
package sbt
|
||||
|
||||
import xsbti.compile.CompileOrder
|
||||
import xsbti.compile.{ CompileOrder, Output, SingleOutput, MultipleOutput }
|
||||
import java.io.File
|
||||
|
||||
// this class exists because of Scala's restriction on implicit parameter search.
|
||||
|
|
@ -11,21 +11,32 @@ package sbt
|
|||
// because complexity(Equiv[Seq[String]]) > complexity(Equiv[CompileSetup])
|
||||
// (6 > 4)
|
||||
final class CompileOptions(val options: Seq[String], val javacOptions: Seq[String])
|
||||
final class CompileSetup(val outputDirectory: File, val options: CompileOptions, val compilerVersion: String, val order: CompileOrder)
|
||||
final class CompileSetup(val output: Output, val options: CompileOptions, val compilerVersion: String, val order: CompileOrder)
|
||||
|
||||
object CompileSetup
|
||||
{
|
||||
// Equiv[CompileOrder.Value] dominates Equiv[CompileSetup]
|
||||
implicit def equivCompileSetup(implicit equivFile: Equiv[File], equivOpts: Equiv[CompileOptions], equivComp: Equiv[String]/*, equivOrder: Equiv[CompileOrder]*/): Equiv[CompileSetup] = new Equiv[CompileSetup] {
|
||||
implicit def equivCompileSetup(implicit equivOutput: Equiv[Output], equivOpts: Equiv[CompileOptions], equivComp: Equiv[String]/*, equivOrder: Equiv[CompileOrder]*/): Equiv[CompileSetup] = new Equiv[CompileSetup] {
|
||||
def equiv(a: CompileSetup, b: CompileSetup) =
|
||||
equivFile.equiv(a.outputDirectory, b.outputDirectory) &&
|
||||
equivOutput.equiv(a.output, b.output) &&
|
||||
equivOpts.equiv(a.options, b.options) &&
|
||||
equivComp.equiv(a.compilerVersion, b.compilerVersion) &&
|
||||
a.order == b.order // equivOrder.equiv(a.order, b.order)
|
||||
}
|
||||
implicit val equivOutputDirectory: Equiv[File] = new Equiv[File] {
|
||||
implicit val equivFile: Equiv[File] = new Equiv[File] {
|
||||
def equiv(a: File, b: File) = a.getAbsoluteFile == b.getAbsoluteFile
|
||||
}
|
||||
implicit val equivOutput: Equiv[Output] = new Equiv[Output] {
|
||||
def equiv(out1: Output, out2: Output) = (out1, out2) match {
|
||||
case (m1: MultipleOutput, m2: MultipleOutput) =>
|
||||
m1.outputGroups zip (m2.outputGroups) forall {
|
||||
case (a,b) =>
|
||||
equivFile.equiv(a.sourceDirectory, b.sourceDirectory) && equivFile.equiv(a.outputDirectory, b.outputDirectory)
|
||||
}
|
||||
case (s1: SingleOutput, s2: SingleOutput) => equivFile.equiv(s1.outputDirectory, s2.outputDirectory)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
implicit val equivOpts: Equiv[CompileOptions] = new Equiv[CompileOptions] {
|
||||
def equiv(a: CompileOptions, b: CompileOptions) =
|
||||
(a.options sameElements b.options) &&
|
||||
|
|
@ -38,4 +49,4 @@ object CompileSetup
|
|||
implicit val equivOrder: Equiv[CompileOrder] = new Equiv[CompileOrder] {
|
||||
def equiv(a: CompileOrder, b: CompileOrder) = a == b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,9 @@ object Incremental
|
|||
}
|
||||
|
||||
def shortcutSameSource(a: Source, b: Source): Boolean = !a.hash.isEmpty && !b.hash.isEmpty && sameCompilation(a.compilation, b.compilation) && (a.hash.deep equals b.hash.deep)
|
||||
def sameCompilation(a: Compilation, b: Compilation): Boolean = a.startTime == b.startTime && a.target == b.target
|
||||
def sameCompilation(a: Compilation, b: Compilation): Boolean = a.startTime == b.startTime && a.outputs.corresponds(b.outputs){
|
||||
case (co1, co2) => co1.sourceDirectory == co2.sourceDirectory && co1.outputDirectory == co2.outputDirectory
|
||||
}
|
||||
|
||||
def changedInitial(entry: String => Option[File], sources: Set[File], previousAnalysis: Analysis, current: ReadStamps, forEntry: File => Option[Analysis])(implicit equivS: Equiv[Stamp]): InitialChanges =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package compiler
|
|||
|
||||
import inc._
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import java.io.File
|
||||
import classpath.ClasspathUtilities
|
||||
import classfile.Analyze
|
||||
|
|
@ -15,31 +16,31 @@ import inc._
|
|||
|
||||
import xsbti.AnalysisCallback
|
||||
import xsbti.api.Source
|
||||
import xsbti.compile.{CompileOrder, DependencyChanges, GlobalsCache}
|
||||
import xsbti.compile.{CompileOrder, DependencyChanges, GlobalsCache, Output, SingleOutput, MultipleOutput, CompileProgress}
|
||||
import CompileOrder.{JavaThenScala, Mixed, ScalaThenJava}
|
||||
|
||||
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 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 maxErrors: Int, val compiler: AnalyzingCompiler, val javac: xsbti.compile.JavaCompiler, val cache: GlobalsCache)
|
||||
|
||||
class AggressiveCompile(cacheFile: File)
|
||||
{
|
||||
def apply(compiler: AnalyzingCompiler, javac: xsbti.compile.JavaCompiler, sources: Seq[File], classpath: Seq[File], outputDirectory: File, cache: GlobalsCache, options: Seq[String] = Nil, javacOptions: Seq[String] = Nil, analysisMap: File => Option[Analysis] = { _ => None }, definesClass: DefinesClass = Locate.definesClass _, maxErrors: Int = 100, compileOrder: CompileOrder = Mixed, skip: Boolean = false)(implicit log: Logger): Analysis =
|
||||
def apply(compiler: AnalyzingCompiler, javac: xsbti.compile.JavaCompiler, sources: Seq[File], classpath: Seq[File], output: Output, cache: GlobalsCache, progress: Option[CompileProgress] = None, options: Seq[String] = Nil, javacOptions: Seq[String] = Nil, analysisMap: File => Option[Analysis] = { _ => None }, definesClass: DefinesClass = Locate.definesClass _, maxErrors: Int = 100, compileOrder: CompileOrder = Mixed, skip: Boolean = false)(implicit log: Logger): Analysis =
|
||||
{
|
||||
val setup = new CompileSetup(outputDirectory, new CompileOptions(options, javacOptions), compiler.scalaInstance.actualVersion, compileOrder)
|
||||
compile1(sources, classpath, setup, store, analysisMap, definesClass, compiler, javac, maxErrors, skip, cache)
|
||||
val setup = new CompileSetup(output, new CompileOptions(options, javacOptions), compiler.scalaInstance.actualVersion, compileOrder)
|
||||
compile1(sources, classpath, setup, progress, store, analysisMap, definesClass, compiler, javac, maxErrors, skip, cache)
|
||||
}
|
||||
|
||||
def withBootclasspath(args: CompilerArguments, classpath: Seq[File]): Seq[File] =
|
||||
args.bootClasspathFor(classpath) ++ args.finishClasspath(classpath)
|
||||
|
||||
def compile1(sources: Seq[File], classpath: Seq[File], setup: CompileSetup, store: AnalysisStore, analysis: File => Option[Analysis], definesClass: DefinesClass, compiler: AnalyzingCompiler, javac: xsbti.compile.JavaCompiler, maxErrors: Int, skip: Boolean, cache: GlobalsCache)(implicit log: Logger): Analysis =
|
||||
def compile1(sources: Seq[File], classpath: Seq[File], setup: CompileSetup, progress: Option[CompileProgress], store: AnalysisStore, analysis: File => Option[Analysis], definesClass: DefinesClass, compiler: AnalyzingCompiler, javac: xsbti.compile.JavaCompiler, maxErrors: Int, skip: Boolean, cache: GlobalsCache)(implicit log: Logger): Analysis =
|
||||
{
|
||||
val (previousAnalysis, previousSetup) = extract(store.get())
|
||||
if(skip)
|
||||
previousAnalysis
|
||||
else {
|
||||
val config = new CompileConfiguration(sources, classpath, previousAnalysis, previousSetup, setup, analysis, definesClass, maxErrors, compiler, javac, cache)
|
||||
val config = new CompileConfiguration(sources, classpath, previousAnalysis, previousSetup, setup, progress, analysis, definesClass, maxErrors, compiler, javac, cache)
|
||||
val (modified, result) = compile2(config)
|
||||
if(modified)
|
||||
store.set(result, setup)
|
||||
|
|
@ -51,36 +52,60 @@ class AggressiveCompile(cacheFile: File)
|
|||
import config._
|
||||
import currentSetup._
|
||||
val absClasspath = classpath.map(_.getCanonicalFile)
|
||||
val apiOption= (api: Either[Boolean, Source]) => api.right.toOption
|
||||
val apiOption = (api: Either[Boolean, Source]) => api.right.toOption
|
||||
val cArgs = new CompilerArguments(compiler.scalaInstance, compiler.cp)
|
||||
val searchClasspath = explicitBootClasspath(options.options) ++ withBootclasspath(cArgs, absClasspath)
|
||||
val entry = Locate.entry(searchClasspath, definesClass)
|
||||
|
||||
val compile0 = (include: Set[File], changes: DependencyChanges, callback: AnalysisCallback) => {
|
||||
IO.createDirectory(outputDirectory)
|
||||
val outputDirs = outputDirectories(output)
|
||||
outputDirs foreach (IO.createDirectory)
|
||||
val incSrc = sources.filter(include)
|
||||
val (javaSrcs, scalaSrcs) = incSrc partition javaOnly
|
||||
logInputs(log, javaSrcs.size, scalaSrcs.size, outputDirectory)
|
||||
logInputs(log, javaSrcs.size, scalaSrcs.size, outputDirs)
|
||||
def compileScala() =
|
||||
if(!scalaSrcs.isEmpty)
|
||||
{
|
||||
val sources = if(order == Mixed) incSrc else scalaSrcs
|
||||
val arguments = cArgs(Nil, absClasspath, outputDirectory, options.options)
|
||||
val arguments = cArgs(Nil, absClasspath, None, options.options)
|
||||
timed("Scala compilation", log) {
|
||||
compiler.compile(sources, changes, arguments, callback, maxErrors, cache, log)
|
||||
compiler.compile(sources, changes, arguments, output, callback, maxErrors, cache, log, progress)
|
||||
}
|
||||
}
|
||||
def compileJava() =
|
||||
if(!javaSrcs.isEmpty)
|
||||
{
|
||||
import Path._
|
||||
val loader = ClasspathUtilities.toLoader(searchClasspath)
|
||||
def readAPI(source: File, classes: Seq[Class[_]]) { callback.api(source, ClassToAPI(classes)) }
|
||||
timed("Java compilation and analysis", log) {
|
||||
Analyze(outputDirectory, javaSrcs, log)(callback, loader, readAPI) {
|
||||
timed("Java compilation", log) {
|
||||
javac.compile(javaSrcs.toArray, absClasspath.toArray, outputDirectory, options.javacOptions.toArray, maxErrors, log)
|
||||
@tailrec def ancestor(f1: File, f2: File): Boolean =
|
||||
if (f2 eq null) false else
|
||||
if (f1 == f2) true else ancestor(f1, f2.getParentFile)
|
||||
|
||||
val chunks: Map[Option[File], Seq[File]] = output match {
|
||||
case single: SingleOutput => Map(Some(single.outputDirectory) -> javaSrcs)
|
||||
case multi: MultipleOutput =>
|
||||
javaSrcs groupBy { src =>
|
||||
multi.outputGroups find {out => ancestor(out.sourceDirectory, src)} map (_.outputDirectory)
|
||||
}
|
||||
}
|
||||
chunks.get(None) foreach { srcs =>
|
||||
log.error("No output directory mapped for: " + srcs.map(_.getAbsolutePath).mkString(","))
|
||||
}
|
||||
val memo = for ((Some(outputDirectory), srcs) <- chunks) yield {
|
||||
val classesFinder = PathFinder(outputDirectory) ** "*.class"
|
||||
(classesFinder, classesFinder.get, srcs)
|
||||
}
|
||||
|
||||
val loader = ClasspathUtilities.toLoader(searchClasspath)
|
||||
timed("Java compilation", log) {
|
||||
javac.compile(javaSrcs.toArray, absClasspath.toArray, output, options.javacOptions.toArray, maxErrors, log)
|
||||
}
|
||||
|
||||
def readAPI(source: File, classes: Seq[Class[_]]) { callback.api(source, ClassToAPI(classes)) }
|
||||
|
||||
timed("Java analysis", log) {
|
||||
for ((classesFinder, oldClasses, srcs) <- memo) {
|
||||
val newClasses = Set(classesFinder.get: _*) -- oldClasses
|
||||
Analyze(newClasses.toSeq, srcs, log)(callback, loader, readAPI)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -92,7 +117,11 @@ class AggressiveCompile(cacheFile: File)
|
|||
case Some(previous) if equiv.equiv(previous, currentSetup) => previousAnalysis
|
||||
case _ => Incremental.prune(sourcesSet, previousAnalysis)
|
||||
}
|
||||
IncrementalCompile(sourcesSet, entry, compile0, analysis, getAnalysis, outputDirectory, log)
|
||||
IncrementalCompile(sourcesSet, entry, compile0, analysis, getAnalysis, output, log)
|
||||
}
|
||||
private[this] def outputDirectories(output: Output): Seq[File] = output match {
|
||||
case single: SingleOutput => List(single.outputDirectory)
|
||||
case mult: MultipleOutput => mult.outputGroups map (_.outputDirectory)
|
||||
}
|
||||
private[this] def timed[T](label: String, log: Logger)(t: => T): T =
|
||||
{
|
||||
|
|
@ -102,13 +131,13 @@ class AggressiveCompile(cacheFile: File)
|
|||
log.debug(label + " took " + (elapsed/1e9) + " s")
|
||||
result
|
||||
}
|
||||
private[this] def logInputs(log: Logger, javaCount: Int, scalaCount: Int, out: File)
|
||||
private[this] def logInputs(log: Logger, javaCount: Int, scalaCount: Int, outputDirs: Seq[File])
|
||||
{
|
||||
val scalaMsg = Analysis.counted("Scala source", "", "s", scalaCount)
|
||||
val javaMsg = Analysis.counted("Java source", "", "s", javaCount)
|
||||
val combined = scalaMsg ++ javaMsg
|
||||
if(!combined.isEmpty)
|
||||
log.info(combined.mkString("Compiling ", " and ", " to " + out.getAbsolutePath + "..."))
|
||||
log.info(combined.mkString("Compiling ", " and ", " to " + outputDirs.map(_.getAbsolutePath).mkString(",") + "..."))
|
||||
}
|
||||
private def extract(previous: Option[(Analysis, CompileSetup)]): (Analysis, Option[CompileSetup]) =
|
||||
previous match
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ object IC extends IncrementalCompiler[Analysis, AnalyzingCompiler]
|
|||
val agg = new AggressiveCompile(setup.cacheFile)
|
||||
val aMap = (f: File) => m2o(analysisMap(f))
|
||||
val defClass = (f: File) => { val dc = definesClass(f); (name: String) => dc.apply(name) }
|
||||
agg(scalac, javac, sources, classpath, classesDirectory, cache, scalacOptions, javacOptions, aMap, defClass, maxErrors, order, skip)(log)
|
||||
agg(scalac, javac, sources, classpath, output, cache, m2o(progress), scalacOptions, javacOptions, aMap, defClass, maxErrors, order, skip)(log)
|
||||
}
|
||||
|
||||
private[this] def m2o[S](opt: Maybe[S]): Option[S] = if(opt.isEmpty) None else Some(opt.get)
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ final class Analyzer(val global: CallbackGlobal) extends Compat
|
|||
def name = Analyzer.name
|
||||
def run
|
||||
{
|
||||
val outputDirectory = new File(global.settings.outdir.value)
|
||||
|
||||
for(unit <- currentRun.units if !unit.isJava)
|
||||
{
|
||||
// build dependencies structure
|
||||
|
|
@ -64,8 +62,7 @@ final class Analyzer(val global: CallbackGlobal) extends Compat
|
|||
val sym = iclass.symbol
|
||||
def addGenerated(separatorRequired: Boolean)
|
||||
{
|
||||
val classFile = fileForClass(outputDirectory, sym, separatorRequired)
|
||||
if(classFile.exists)
|
||||
for(classFile <- outputDirs map (fileForClass(_, sym, separatorRequired)) find (_.exists))
|
||||
callback.generatedClass(sourceFile, classFile, className(sym, '.', separatorRequired))
|
||||
}
|
||||
if(sym.isModuleClass && !sym.isImplClass)
|
||||
|
|
@ -152,4 +149,4 @@ abstract class Compat
|
|||
private[this] def sourceCompatibilityOnly: Nothing = throw new RuntimeException("For source compatibility only: should not get here.")
|
||||
|
||||
private[this] final implicit def miscCompat(n: AnyRef): MiscCompat = new MiscCompat
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
package xsbt
|
||||
|
||||
import xsbti.{AnalysisCallback,Logger,Problem,Reporter,Severity}
|
||||
import xsbti.compile.{CachedCompiler, DependencyChanges}
|
||||
import xsbti.compile._
|
||||
import scala.tools.nsc.{backend, io, reporters, symtab, util, Phase, Global, Settings, SubComponent}
|
||||
import backend.JavaPlatform
|
||||
import scala.tools.util.PathResolver
|
||||
|
|
@ -19,19 +19,25 @@ import java.io.File
|
|||
|
||||
final class CompilerInterface
|
||||
{
|
||||
def newCompiler(options: Array[String], initialLog: Logger, initialDelegate: Reporter, resident: Boolean): CachedCompiler =
|
||||
new CachedCompiler0(options, new WeakLog(initialLog, initialDelegate), resident)
|
||||
def newCompiler(options: Array[String], output: Output, initialLog: Logger, initialDelegate: Reporter, resident: Boolean): CachedCompiler =
|
||||
new CachedCompiler0(options, output, new WeakLog(initialLog, initialDelegate), resident)
|
||||
|
||||
def run(sources: Array[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, delegate: Reporter, cached: CachedCompiler): Unit =
|
||||
cached.run(sources, changes, callback, log, delegate)
|
||||
def run(sources: Array[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, delegate: Reporter, progress: CompileProgress, cached: CachedCompiler): Unit =
|
||||
cached.run(sources, changes, callback, log, delegate, progress)
|
||||
}
|
||||
// for compatibility with Scala versions without Global.registerTopLevelSym (2.8.1 and earlier)
|
||||
sealed trait GlobalCompat { self: Global =>
|
||||
def registerTopLevelSym(sym: Symbol): Unit
|
||||
}
|
||||
sealed abstract class CallbackGlobal(settings: Settings, reporter: reporters.Reporter) extends Global(settings, reporter) with GlobalCompat {
|
||||
sealed abstract class CallbackGlobal(settings: Settings, reporter: reporters.Reporter, output: Output) extends Global(settings, reporter) with GlobalCompat {
|
||||
def callback: AnalysisCallback
|
||||
def findClass(name: String): Option[(AbstractFile,Boolean)]
|
||||
lazy val outputDirs: Iterable[File] = {
|
||||
output match {
|
||||
case single: SingleOutput => List(single.outputDirectory)
|
||||
case multi: MultipleOutput => multi.outputGroups.toStream map (_.outputDirectory)
|
||||
}
|
||||
}
|
||||
}
|
||||
class InterfaceCompileFailed(val arguments: Array[String], val problems: Array[Problem], override val toString: String) extends xsbti.CompileFailed
|
||||
|
||||
|
|
@ -49,9 +55,17 @@ private final class WeakLog(private[this] var log: Logger, private[this] var del
|
|||
}
|
||||
}
|
||||
|
||||
private final class CachedCompiler0(args: Array[String], initialLog: WeakLog, resident: Boolean) extends CachedCompiler
|
||||
private final class CachedCompiler0(args: Array[String], output: Output, initialLog: WeakLog, resident: Boolean) extends CachedCompiler
|
||||
{
|
||||
val settings = new Settings(s => initialLog(s))
|
||||
output match {
|
||||
case multi: MultipleOutput =>
|
||||
for (out <- multi.outputGroups)
|
||||
settings.outputDirs.add(out.sourceDirectory.getAbsolutePath, out.outputDirectory.getAbsolutePath)
|
||||
case single: SingleOutput =>
|
||||
settings.outputDirs.setSingleOutput(single.outputDirectory.getAbsolutePath)
|
||||
}
|
||||
|
||||
val command = Command(args.toList, settings)
|
||||
private[this] val dreporter = DelegatingReporter(settings, initialLog.reporter)
|
||||
try {
|
||||
|
|
@ -65,14 +79,14 @@ private final class CachedCompiler0(args: Array[String], initialLog: WeakLog, re
|
|||
|
||||
def noErrors(dreporter: DelegatingReporter) = !dreporter.hasErrors && command.ok
|
||||
|
||||
def run(sources: Array[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, delegate: Reporter): Unit = synchronized
|
||||
def run(sources: Array[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, delegate: Reporter, progress: CompileProgress): Unit = synchronized
|
||||
{
|
||||
debug(log, "Running cached compiler " + hashCode.toHexString + ", interfacing (CompilerInterface) with Scala compiler " + scala.tools.nsc.Properties.versionString)
|
||||
val dreporter = DelegatingReporter(settings, delegate)
|
||||
try { run(sources.toList, changes, callback, log, dreporter) }
|
||||
try { run(sources.toList, changes, callback, log, dreporter, progress) }
|
||||
finally { dreporter.dropDelegate() }
|
||||
}
|
||||
private[this] def run(sources: List[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, dreporter: DelegatingReporter)
|
||||
private[this] def run(sources: List[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, dreporter: DelegatingReporter, compileProgress: CompileProgress)
|
||||
{
|
||||
if(command.shouldStopWithInfo)
|
||||
{
|
||||
|
|
@ -84,8 +98,16 @@ private final class CachedCompiler0(args: Array[String], initialLog: WeakLog, re
|
|||
debug(log, args.mkString("Calling Scala compiler with arguments (CompilerInterface):\n\t", "\n\t", ""))
|
||||
compiler.set(callback, dreporter)
|
||||
try {
|
||||
val run = new compiler.Run
|
||||
if(resident) compiler.reload(changes)
|
||||
val run = new compiler.Run {
|
||||
override def informUnitStarting(phase: Phase, unit: compiler.CompilationUnit) {
|
||||
compileProgress.startUnit(phase.name, unit.source.path)
|
||||
}
|
||||
override def progress(current: Int, total: Int) {
|
||||
if (!compileProgress.advance(current, total))
|
||||
cancel
|
||||
}
|
||||
}
|
||||
if (resident) compiler.reload(changes)
|
||||
val sortedSourceFiles = sources.map(_.getAbsolutePath).sortWith(_ < _)
|
||||
run compile sortedSourceFiles
|
||||
processUnreportedWarnings(run)
|
||||
|
|
@ -113,7 +135,7 @@ private final class CachedCompiler0(args: Array[String], initialLog: WeakLog, re
|
|||
if(!warnings.isEmpty)
|
||||
compiler.logUnreportedWarnings(warnings.map(cw => ("" /*cw.what*/, cw.warnings.toList)))
|
||||
}
|
||||
object compiler extends CallbackGlobal(command.settings, dreporter)
|
||||
object compiler extends CallbackGlobal(command.settings, dreporter, output)
|
||||
{
|
||||
object dummy // temporary fix for #4426
|
||||
object sbtAnalyzer extends
|
||||
|
|
@ -145,7 +167,6 @@ private final class CachedCompiler0(args: Array[String], initialLog: WeakLog, re
|
|||
def name = phaseName
|
||||
}
|
||||
|
||||
val out = new File(settings.outdir.value)
|
||||
override lazy val phaseDescriptors =
|
||||
{
|
||||
phasesSet += sbtAnalyzer
|
||||
|
|
@ -187,8 +208,9 @@ private final class CachedCompiler0(args: Array[String], initialLog: WeakLog, re
|
|||
|
||||
def getOutputClass(name: String): Option[AbstractFile] =
|
||||
{
|
||||
val f = new File(out, name.replace('.', '/') + ".class")
|
||||
if(f.exists) Some(AbstractFile.getFile(f)) else None
|
||||
// This could be improved if a hint where to look is given.
|
||||
val className = name.replace('.', '/') + ".class"
|
||||
outputDirs map (new File(_, className)) find (_.exists) map (AbstractFile.getFile(_))
|
||||
}
|
||||
|
||||
def findOnClassPath(name: String): Option[AbstractFile] =
|
||||
|
|
@ -213,7 +235,7 @@ private final class CachedCompiler0(args: Array[String], initialLog: WeakLog, re
|
|||
// must drop whole CachedCompiler when !changes.isEmpty
|
||||
def reload(changes: DependencyChanges)
|
||||
{
|
||||
inv(settings.outdir.value)
|
||||
for ((_,out) <- settings.outputDirs.outputs) inv(out.path)
|
||||
}
|
||||
|
||||
private[this] var toForget = mutable.Set[Symbol]()
|
||||
|
|
@ -358,4 +380,4 @@ private final class CachedCompiler0(args: Array[String], initialLog: WeakLog, re
|
|||
def sourcepaths = delegate.sourcepaths
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ package inc
|
|||
|
||||
import xsbti.api.Source
|
||||
import xsbti.{Position,Problem,Severity}
|
||||
import xsbti.compile.CompileOrder
|
||||
import xsbti.compile.{CompileOrder, Output => APIOutput, SingleOutput, MultipleOutput}
|
||||
import MultipleOutput.OutputGroup
|
||||
import java.io.File
|
||||
import sbinary._
|
||||
import DefaultProtocol._
|
||||
import DefaultProtocol.tuple2Format
|
||||
import Logger.{m2o, position, problem}
|
||||
|
||||
object AnalysisFormats
|
||||
|
|
@ -63,8 +65,22 @@ object AnalysisFormats
|
|||
wrap[Severity, Byte]( _.ordinal.toByte, b => Severity.values.apply(b.toInt) )
|
||||
|
||||
|
||||
implicit def setupFormat(implicit outDirF: Format[File], optionF: Format[CompileOptions], compilerVersion: Format[String], orderF: Format[CompileOrder]): Format[CompileSetup] =
|
||||
asProduct4[CompileSetup, File, CompileOptions, String, CompileOrder]( (a,b,c,d) => new CompileSetup(a,b,c,d) )(s => (s.outputDirectory, s.options, s.compilerVersion, s.order))(outDirF, optionF, compilerVersion, orderF)
|
||||
implicit def setupFormat(implicit outputF: Format[APIOutput], optionF: Format[CompileOptions], compilerVersion: Format[String], orderF: Format[CompileOrder]): Format[CompileSetup] =
|
||||
asProduct4[CompileSetup, APIOutput, CompileOptions, String, CompileOrder]( (a,b,c,d) => new CompileSetup(a,b,c,d) )(s => (s.output, s.options, s.compilerVersion, s.order))(outputF, optionF, compilerVersion, orderF)
|
||||
|
||||
implicit val outputGroupFormat: Format[OutputGroup] =
|
||||
asProduct2((a: File,b: File) => new OutputGroup{def sourceDirectory = a; def outputDirectory = b}) { out => (out.sourceDirectory, out.outputDirectory) }(fileFormat, fileFormat)
|
||||
implicit val multipleOutputFormat: Format[MultipleOutput] =
|
||||
wrap[MultipleOutput, Array[OutputGroup]](
|
||||
(_.outputGroups),
|
||||
{ groups => new MultipleOutput { def outputGroups = groups } }
|
||||
)
|
||||
implicit val singleOutputFormat: Format[SingleOutput] =
|
||||
wrap[SingleOutput, File](
|
||||
(_.outputDirectory),
|
||||
{out => new SingleOutput{def outputDirectory = out}}
|
||||
)(fileFormat)
|
||||
implicit val outputFormat: Format[APIOutput] = asUnion(singleOutputFormat, multipleOutputFormat)
|
||||
|
||||
implicit def stampsFormat(implicit prodF: Format[Map[File, Stamp]], srcF: Format[Map[File, Stamp]], binF: Format[Map[File, Stamp]], nameF: Format[Map[File, String]]): Format[Stamps] =
|
||||
asProduct4( Stamps.apply _ )( s => (s.products, s.sources, s.binaries, s.classNames) )(prodF, srcF, binF, nameF)
|
||||
|
|
|
|||
|
|
@ -9,9 +9,13 @@ SourceAPI
|
|||
packages : Package*
|
||||
definitions: Definition*
|
||||
|
||||
OutputSetting
|
||||
sourceDirectory: String
|
||||
outputDirectory: String
|
||||
|
||||
Compilation
|
||||
startTime: Long
|
||||
target: String
|
||||
outputs: OutputSetting*
|
||||
|
||||
Package
|
||||
name: String
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ import java.io.File;
|
|||
|
||||
public interface CachedCompiler
|
||||
{
|
||||
public void run(File[] sources, DependencyChanges cpChanges, AnalysisCallback callback, Logger log, Reporter delegate);
|
||||
public void run(File[] sources, DependencyChanges cpChanges, AnalysisCallback callback, Logger log, Reporter delegate, CompileProgress progress);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@ import xsbti.Reporter;
|
|||
public interface CachedCompilerProvider
|
||||
{
|
||||
ScalaInstance scalaInstance();
|
||||
CachedCompiler newCachedCompiler(String[] arguments, Logger log, Reporter reporter, boolean resident);
|
||||
CachedCompiler newCachedCompiler(String[] arguments, Output output, Logger log, Reporter reporter, boolean resident);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package xsbti.compile;
|
||||
|
||||
public interface CompileProgress {
|
||||
void startUnit(String phase, String unitPath);
|
||||
|
||||
boolean advance(int current, int total);
|
||||
}
|
||||
|
|
@ -5,6 +5,6 @@ import xsbti.Reporter;
|
|||
|
||||
public interface GlobalsCache
|
||||
{
|
||||
public CachedCompiler apply(String[] args, boolean forceNew, CachedCompilerProvider provider, Logger log, Reporter reporter);
|
||||
public CachedCompiler apply(String[] args, Output output, boolean forceNew, CachedCompilerProvider provider, Logger log, Reporter reporter);
|
||||
public void clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,5 +11,5 @@ public interface JavaCompiler
|
|||
/** Compiles Java sources using the provided classpath, output directory, and additional options.
|
||||
* If supported, the number of reported errors should be limited to `maximumErrors`.
|
||||
* Output should be sent to the provided logger.*/
|
||||
void compile(File[] sources, File[] classpath, File outputDirectory, String[] options, int maximumErrors, Logger log);
|
||||
void compile(File[] sources, File[] classpath, Output output, String[] options, int maximumErrors, Logger log);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package xsbti.compile;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface MultipleOutput extends Output {
|
||||
|
||||
interface OutputGroup {
|
||||
/** The directory where source files are stored for this group.
|
||||
* Source directories should uniquely identify the group for a source file. */
|
||||
File sourceDirectory();
|
||||
|
||||
/** The directory where class files should be generated.
|
||||
* Incremental compilation will manage the class files in this directory.
|
||||
* In particular, outdated class files will be deleted before compilation.
|
||||
* It is important that this directory is exclusively used for one set of sources. */
|
||||
File outputDirectory();
|
||||
}
|
||||
|
||||
OutputGroup[] outputGroups();
|
||||
}
|
||||
|
|
@ -13,11 +13,8 @@ public interface Options
|
|||
* This should include Scala and Java sources, which are identified by their extension. */
|
||||
File[] sources();
|
||||
|
||||
/** The directory where class files should be generated.
|
||||
* Incremental compilation will manage the class files in this directory.
|
||||
* In particular, outdated class files will be deleted before compilation.
|
||||
* It is important that this directory is exclusively used for one set of sources. */
|
||||
File classesDirectory();
|
||||
/** Output for the compilation. */
|
||||
Output output();
|
||||
|
||||
/** The options to pass to the Scala compiler other than the sources and classpath to use. */
|
||||
String[] options();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package xsbti.compile;
|
||||
|
||||
import java.io.File;
|
||||
/** Abstract interface denoting the output of the compilation. Inheritors are SingleOutput with a global output directory and
|
||||
* MultipleOutput that specifies the output directory per source file.
|
||||
*/
|
||||
public interface Output
|
||||
{
|
||||
}
|
||||
|
|
@ -23,4 +23,7 @@ public interface Setup<Analysis>
|
|||
File cacheFile();
|
||||
|
||||
GlobalsCache cache();
|
||||
|
||||
/** If returned, the progress that should be used to report scala compilation to. */
|
||||
Maybe<CompileProgress> progress();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package xsbti.compile;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface SingleOutput extends Output {
|
||||
|
||||
/** The directory where class files should be generated.
|
||||
* Incremental compilation will manage the class files in this directory.
|
||||
* In particular, outdated class files will be deleted before compilation.
|
||||
* It is important that this directory is exclusively used for one set of sources. */
|
||||
File outputDirectory();
|
||||
}
|
||||
|
|
@ -76,6 +76,6 @@ object Compiler
|
|||
import in.incSetup._
|
||||
|
||||
val agg = new AggressiveCompile(cacheFile)
|
||||
agg(scalac, javac, sources, classpath, classesDirectory, cache, options, javacOptions, analysisMap, definesClass, maxErrors, order, skip)(log)
|
||||
agg(scalac, javac, sources, classpath, CompileOutput(classesDirectory), cache, None, options, javacOptions, analysisMap, definesClass, maxErrors, order, skip)(log)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,76 +14,64 @@ import java.lang.reflect.Modifier.{STATIC, PUBLIC, ABSTRACT}
|
|||
|
||||
private[sbt] object Analyze
|
||||
{
|
||||
def apply[T](outputDirectory: File, sources: Seq[File], log: Logger)(analysis: xsbti.AnalysisCallback, loader: ClassLoader, readAPI: (File,Seq[Class[_]]) => Unit)(compile: => Unit)
|
||||
def apply[T](newClasses: Seq[File], sources: Seq[File], log: Logger)(analysis: xsbti.AnalysisCallback, loader: ClassLoader, readAPI: (File,Seq[Class[_]]) => Unit)
|
||||
{
|
||||
val sourceMap = sources.toSet[File].groupBy(_.getName)
|
||||
val classesFinder = PathFinder(outputDirectory) ** "*.class"
|
||||
val existingClasses = classesFinder.get
|
||||
|
||||
def load(tpe: String, errMsg: => Option[String]): Option[Class[_]] =
|
||||
try { Some(Class.forName(tpe, false, loader)) }
|
||||
catch { case e => errMsg.foreach(msg => log.warn(msg + " : " +e.toString)); None }
|
||||
|
||||
// runs after compilation
|
||||
def analyze()
|
||||
{
|
||||
val allClasses = Set(classesFinder.get: _*)
|
||||
val newClasses = allClasses -- existingClasses
|
||||
|
||||
val productToSource = new mutable.HashMap[File, File]
|
||||
val sourceToClassFiles = new mutable.HashMap[File, Buffer[ClassFile]]
|
||||
val productToSource = new mutable.HashMap[File, File]
|
||||
val sourceToClassFiles = new mutable.HashMap[File, Buffer[ClassFile]]
|
||||
|
||||
// parse class files and assign classes to sources. This must be done before dependencies, since the information comes
|
||||
// as class->class dependencies that must be mapped back to source->class dependencies using the source+class assignment
|
||||
for(newClass <- newClasses;
|
||||
classFile = Parser(newClass);
|
||||
sourceFile <- classFile.sourceFile orElse guessSourceName(newClass.getName);
|
||||
source <- guessSourcePath(sourceMap, classFile, log))
|
||||
{
|
||||
analysis.beginSource(source)
|
||||
analysis.generatedClass(source, newClass, classFile.className)
|
||||
productToSource(newClass) = source
|
||||
sourceToClassFiles.getOrElseUpdate(source, new ArrayBuffer[ClassFile]) += classFile
|
||||
}
|
||||
// parse class files and assign classes to sources. This must be done before dependencies, since the information comes
|
||||
// as class->class dependencies that must be mapped back to source->class dependencies using the source+class assignment
|
||||
for(newClass <- newClasses;
|
||||
classFile = Parser(newClass);
|
||||
sourceFile <- classFile.sourceFile orElse guessSourceName(newClass.getName);
|
||||
source <- guessSourcePath(sourceMap, classFile, log))
|
||||
{
|
||||
analysis.beginSource(source)
|
||||
analysis.generatedClass(source, newClass, classFile.className)
|
||||
productToSource(newClass) = source
|
||||
sourceToClassFiles.getOrElseUpdate(source, new ArrayBuffer[ClassFile]) += classFile
|
||||
}
|
||||
|
||||
// get class to class dependencies and map back to source to class dependencies
|
||||
for( (source, classFiles) <- sourceToClassFiles )
|
||||
// get class to class dependencies and map back to source to class dependencies
|
||||
for( (source, classFiles) <- sourceToClassFiles )
|
||||
{
|
||||
def processDependency(tpe: String)
|
||||
{
|
||||
def processDependency(tpe: String)
|
||||
trapAndLog(log)
|
||||
{
|
||||
trapAndLog(log)
|
||||
for (url <- Option(loader.getResource(tpe.replace('.', '/') + ClassExt)); file <- IO.urlAsFile(url))
|
||||
{
|
||||
for (url <- Option(loader.getResource(tpe.replace('.', '/') + ClassExt)); file <- IO.urlAsFile(url))
|
||||
if(url.getProtocol == "jar")
|
||||
analysis.binaryDependency(file, tpe, source)
|
||||
else
|
||||
{
|
||||
if(url.getProtocol == "jar")
|
||||
analysis.binaryDependency(file, tpe, source)
|
||||
else
|
||||
assume(url.getProtocol == "file")
|
||||
productToSource.get(file) match
|
||||
{
|
||||
assume(url.getProtocol == "file")
|
||||
productToSource.get(file) match
|
||||
{
|
||||
case Some(dependsOn) => analysis.sourceDependency(dependsOn, source)
|
||||
case None => analysis.binaryDependency(file, tpe, source)
|
||||
}
|
||||
case Some(dependsOn) => analysis.sourceDependency(dependsOn, source)
|
||||
case None => analysis.binaryDependency(file, tpe, source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
classFiles.flatMap(_.types).toSet.foreach(processDependency)
|
||||
readAPI(source, classFiles.toSeq.flatMap(c => load(c.className, Some("Error reading API from class file") )))
|
||||
analysis.endSource(source)
|
||||
}
|
||||
|
||||
for( source <- sources filterNot sourceToClassFiles.keySet ) {
|
||||
analysis.beginSource(source)
|
||||
analysis.api(source, new xsbti.api.SourceAPI(Array(), Array()))
|
||||
analysis.endSource(source)
|
||||
}
|
||||
classFiles.flatMap(_.types).toSet.foreach(processDependency)
|
||||
readAPI(source, classFiles.toSeq.flatMap(c => load(c.className, Some("Error reading API from class file") )))
|
||||
analysis.endSource(source)
|
||||
}
|
||||
|
||||
for( source <- sources filterNot sourceToClassFiles.keySet ) {
|
||||
analysis.beginSource(source)
|
||||
analysis.api(source, new xsbti.api.SourceAPI(Array(), Array()))
|
||||
analysis.endSource(source)
|
||||
}
|
||||
|
||||
compile
|
||||
analyze()
|
||||
}
|
||||
private def trapAndLog(log: Logger)(execute: => Unit)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue