mirror of https://github.com/sbt/sbt.git
Handle compilation cancellation properly.
Incremental compiler didn't have any explicit logic to handle cancelled compilation so it would go into inconsistent state. Specifically, what would happen is that it would treat cancelled compilation as a compilation that finished normally and try to produce a new Analysis object out of partial information collected in AnalysisCallback. The most obvious outcome would be that the new Analysis would contain latest hashes for source files. The next time incremental compiler was asked to recompile the same files that it didn't recompile due to cancelled compilation it would think they were already successfully compiled and would do nothing. We fix that problem by following the same logic that handles compilation errors, cleans up partial results (produced class files) and makes sure that no Analysis is created out of broken state. We do that by introducing a new exception `CompileCancelled` and throwing it at the same spot as an exception signalizing compilation errors is being thrown. We also modify `IncrementalCompile` to catch that exception and gracefully return as there was no compilation invoked. NOTE: In case there were compilation errors reported _before_ compilation cancellations was requested we'll still report them using an old mechanism so partial errors are not lost in case of cancelled compilation.
This commit is contained in:
parent
94e6bb406e
commit
d77930394f
|
|
@ -22,7 +22,15 @@ object IncrementalCompile
|
|||
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, output, options), log, options)
|
||||
try {
|
||||
Incremental.compile(sources, entry, previous, current, forEntry, doCompile(compile, internalMap, externalAPI, current, output, options), log, options)
|
||||
} catch {
|
||||
case e: xsbti.CompileCancelled =>
|
||||
log.info("Compilation has been cancelled")
|
||||
// in case compilation got cancelled potential partial compilation results (e.g. produced classs files) got rolled back
|
||||
// and we can report back as there was no change (false) and return a previous Analysis which is still up-to-date
|
||||
(false, previous)
|
||||
}
|
||||
}
|
||||
def doCompile(compile: (Set[File], DependencyChanges, xsbti.AnalysisCallback) => Unit, internalMap: File => Option[File], externalAPI: (File, String) => Option[Source], current: ReadStamps, output: Output, options: IncOptions) =
|
||||
(srcs: Set[File], changes: DependencyChanges) => {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ sealed abstract class CallbackGlobal(settings: Settings, reporter: reporters.Rep
|
|||
}
|
||||
class InterfaceCompileFailed(val arguments: Array[String], val problems: Array[Problem], override val toString: String) extends xsbti.CompileFailed
|
||||
|
||||
class InterfaceCompileCancelled(val arguments: Array[String], override val toString: String) extends xsbti.CompileCancelled
|
||||
|
||||
private final class WeakLog(private[this] var log: Logger, private[this] var delegate: Reporter)
|
||||
{
|
||||
def apply(message: String) {
|
||||
|
|
@ -124,12 +126,21 @@ private final class CachedCompiler0(args: Array[String], output: Output, initial
|
|||
}
|
||||
dreporter.printSummary()
|
||||
if(!noErrors(dreporter)) handleErrors(dreporter, log)
|
||||
// the case where we cancelled compilation _after_ some compilation errors got reported
|
||||
// will be handled by line above so errors still will be reported properly just potentially not
|
||||
// all of them (because we cancelled the compilation)
|
||||
if (dreporter.cancelled) handleCompilationCancellation(dreporter, log)
|
||||
}
|
||||
def handleErrors(dreporter: DelegatingReporter, log: Logger): Nothing =
|
||||
{
|
||||
debug(log, "Compilation failed (CompilerInterface)")
|
||||
throw new InterfaceCompileFailed(args, dreporter.problems, "Compilation failed")
|
||||
}
|
||||
def handleCompilationCancellation(dreporter: DelegatingReporter, log: Logger): Nothing = {
|
||||
assert(dreporter.cancelled, "We should get here only if when compilation got cancelled")
|
||||
debug(log, "Compilation cancelled (CompilerInterface)")
|
||||
throw new InterfaceCompileCancelled(args, "Compilation has been cancelled")
|
||||
}
|
||||
def processUnreportedWarnings(run: compiler.Run)
|
||||
{
|
||||
// allConditionalWarnings and the ConditionalWarning class are only in 2.10+
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package xsbti;
|
||||
|
||||
/**
|
||||
* An exception thrown when compilation cancellation has been requested during
|
||||
* Scala compiler run.
|
||||
*/
|
||||
public abstract class CompileCancelled extends RuntimeException {
|
||||
public abstract String[] arguments();
|
||||
}
|
||||
Loading…
Reference in New Issue