From a7fb54e4dfd26c80da5dc402a88f6315c8d0e68e Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Fri, 11 Apr 2014 13:42:21 +0200 Subject: [PATCH] Add debug logging in ClassfileManager Add logging of various operations the transactional class file manager is doing. You can pass logger to be used by the transactional class file manager by using overloaded definition of `ClassfileManager.transactional` method. The old overload has been deprecated. The factory methods for class file manager in IncOptions companion object has been deprecated in favor of using ClassfileManager companion object directly. The code in Defaults.scala has been updated to use non-deprecated methods. The logging is turned off by default. The canonical way of enabling transactional class file manager in sbt project is: ``` incOptions := incOptions.value.withNewClassfileManager( sbt.inc.ClassfileManager.transactional( crossTarget.value / "classes.bak", (streams in (compile, Compile)).value.log ) ) ``` It's a bit verbose which shows that the api for this is not the best. However, I don't expect sbt users to need this code very often. This patch should help debug the problem described in #1184 --- .../main/scala/sbt/inc/ClassfileManager.scala | 26 +++++++++++++++---- .../src/main/scala/sbt/inc/IncOptions.scala | 7 +++-- main/src/main/scala/sbt/Defaults.scala | 3 ++- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/compile/inc/src/main/scala/sbt/inc/ClassfileManager.scala b/compile/inc/src/main/scala/sbt/inc/ClassfileManager.scala index 636c0aec3..e4a449a4e 100644 --- a/compile/inc/src/main/scala/sbt/inc/ClassfileManager.scala +++ b/compile/inc/src/main/scala/sbt/inc/ClassfileManager.scala @@ -13,7 +13,7 @@ trait ClassfileManager * Any empty ancestor directories of deleted files must not exist either.*/ def delete(classes: Iterable[File]): Unit - /** Called once per compilation step with the class files generated during that step.*/ + /** Called once per compilation step with the class files generated during that step.*/ def generated(classes: Iterable[File]): Unit /** Called once at the end of the whole compilation run, with `success` indicating whether compilation succeeded (true) or not (false).*/ @@ -29,29 +29,45 @@ object ClassfileManager def generated(classes: Iterable[File]) {} def complete(success: Boolean) {} } + @deprecated("Use overloaded variant that takes additional logger argument, instead.", "0.13.5") + def transactional(tempDir0: File): () => ClassfileManager = + transactional(tempDir0, sbt.Logger.Null) /** When compilation fails, this ClassfileManager restores class files to the way they were before compilation.*/ - def transactional(tempDir0: File): () => ClassfileManager = () => new ClassfileManager + def transactional(tempDir0: File, logger: sbt.Logger): () => ClassfileManager = () => new ClassfileManager { val tempDir = tempDir0.getCanonicalFile IO.delete(tempDir) IO.createDirectory(tempDir) + logger.debug(s"Created transactional ClassfileManager with tempDir = $tempDir") private[this] val generatedClasses = new mutable.HashSet[File] private[this] val movedClasses = new mutable.HashMap[File, File] - + + private def showFiles(files: Iterable[File]): String = files.map(f => s"\t$f").mkString("\n") def delete(classes: Iterable[File]) { - for(c <- classes) if(c.exists && !movedClasses.contains(c) && !generatedClasses(c)) + logger.debug(s"About to delete class files:\n${showFiles(classes)}") + val toBeBackedUp = classes.filter(c => c.exists && !movedClasses.contains(c) && !generatedClasses(c)) + logger.debug(s"We backup classs files:\n${showFiles(toBeBackedUp)}") + for(c <- toBeBackedUp) { movedClasses.put(c, move(c)) + } IO.deleteFilesEmptyDirs(classes) } - def generated(classes: Iterable[File]): Unit = generatedClasses ++= classes + def generated(classes: Iterable[File]): Unit = { + logger.debug(s"Registering generated classes:\n${showFiles(classes)}") + generatedClasses ++= classes + } def complete(success: Boolean) { if(!success) { + logger.debug("Rolling back changes to class files.") + logger.debug(s"Removing generated classes:\n${showFiles(generatedClasses)}") IO.deleteFilesEmptyDirs(generatedClasses) + logger.debug(s"Restoring class files: \n${showFiles(movedClasses.map(_._1))}") for( (orig, tmp) <- movedClasses ) IO.move(tmp, orig) } + logger.debug(s"Removing the temporary directory used for backing up class files: $tempDir") IO.delete(tempDir) } diff --git a/compile/inc/src/main/scala/sbt/inc/IncOptions.scala b/compile/inc/src/main/scala/sbt/inc/IncOptions.scala index 0e634aa4f..70add5183 100644 --- a/compile/inc/src/main/scala/sbt/inc/IncOptions.scala +++ b/compile/inc/src/main/scala/sbt/inc/IncOptions.scala @@ -234,9 +234,12 @@ object IncOptions extends Serializable { private def readResolve(): Object = IncOptions //- EXPANDED CASE CLASS METHOD END -// - def defaultTransactional(tempDir: File): IncOptions = setTransactional(Default, tempDir) + @deprecated("Use IncOptions.Default.withNewClassfileManager(ClassfileManager.transactional(tempDir)), instead.", "0.13.5") + def defaultTransactional(tempDir: File): IncOptions = + setTransactional(Default, tempDir) + @deprecated("Use opts.withNewClassfileManager(ClassfileManager.transactional(tempDir)), instead.", "0.13.5") def setTransactional(opts: IncOptions, tempDir: File): IncOptions = - opts.copy(newClassfileManager = ClassfileManager.transactional(tempDir)) + opts.withNewClassfileManager(ClassfileManager.transactional(tempDir, sbt.Logger.Null)) private val transitiveStepKey = "transitiveStep" private val recompileAllFractionKey = "recompileAllFraction" diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 96390d869..d24010467 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -215,7 +215,8 @@ object Defaults extends BuildCommon ) def compileBase = inTask(console)(compilersSetting :: Nil) ++ compileBaseGlobal ++ Seq( - incOptions := IncOptions.setTransactional(incOptions.value, crossTarget.value / "classes.bak"), + incOptions := incOptions.value.withNewClassfileManager( + sbt.inc.ClassfileManager.transactional(crossTarget.value / "classes.bak", sbt.Logger.Null)), scalaInstance <<= scalaInstanceTask, crossVersion := (if(crossPaths.value) CrossVersion.binary else CrossVersion.Disabled), crossTarget := makeCrossTarget(target.value, scalaBinaryVersion.value, sbtBinaryVersion.value, sbtPlugin.value, crossPaths.value)