diff --git a/sbt-dotty/sbt-test/source-dependencies/inline/changes/B2.scala b/sbt-dotty/sbt-test/source-dependencies/inline/changes/B2.scala index ca7395adf16d..f2383d73748a 100644 --- a/sbt-dotty/sbt-test/source-dependencies/inline/changes/B2.scala +++ b/sbt-dotty/sbt-test/source-dependencies/inline/changes/B2.scala @@ -1,4 +1,4 @@ object B { - transparent def getInline: Double = - A.get + transparent def getInline: String = + A.get.toString } diff --git a/sbt-dotty/sbt-test/source-dependencies/inline/test b/sbt-dotty/sbt-test/source-dependencies/inline/test index 56fdb048641f..4742773e1038 100644 --- a/sbt-dotty/sbt-test/source-dependencies/inline/test +++ b/sbt-dotty/sbt-test/source-dependencies/inline/test @@ -2,7 +2,7 @@ $ copy-file changes/B1.scala B.scala > compile $ copy-file changes/B2.scala B.scala -# Compilation of C.scala should fail because B.getInline now has type Double instead of Int +# Compilation of C.scala should fail because B.getInline now has type String instead of Int -> compile $ copy-file changes/B1.scala B.scala diff --git a/sbt-dotty/sbt-test/source-dependencies/restore-classes/test b/sbt-dotty/sbt-test/source-dependencies/restore-classes/pending similarity index 100% rename from sbt-dotty/sbt-test/source-dependencies/restore-classes/test rename to sbt-dotty/sbt-test/source-dependencies/restore-classes/pending diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala index efd342e656ad..50cc0ddbba52 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala @@ -112,33 +112,20 @@ object DottyPlugin extends AutoPlugin { * corresponding .tasty or .hasTasty file is also deleted. */ def dottyPatchIncOptions(incOptions: IncOptions): IncOptions = { - val inheritedNewClassFileManager = ClassFileManagerUtil.getDefaultClassFileManager(incOptions) - val tastyFileManager = new ClassFileManager { - private[this] val inherited = inheritedNewClassFileManager - - def delete(classes: Array[File]): Unit = { - val tastySuffixes = List(".tasty", ".hasTasty") - inherited.delete(classes flatMap { classFile => - if (classFile.getPath endsWith ".class") { - val prefix = classFile.getAbsolutePath.stripSuffix(".class") - tastySuffixes.map(suffix => new File(prefix + suffix)).filter(_.exists) - } else Nil - }) - } + val tastyFileManager = new TastyFileManager - def generated(classes: Array[File]): Unit = {} - def complete(success: Boolean): Unit = {} - } + // Once sbt/zinc#562 is fixed, can be: + // val newExternalHooks = + // incOptions.externalHooks.withExternalClassFileManager(tastyFileManager) val inheritedHooks = incOptions.externalHooks - val externalClassFileManager: Optional[ClassFileManager] = Option(inheritedHooks.getExternalClassFileManager.orElse(null)) match { - case Some(prevManager) => - Optional.of(WrappedClassFileManager.of(prevManager, Optional.of(tastyFileManager))) - case None => - Optional.of(tastyFileManager) - } - - val hooks = new DefaultExternalHooks(inheritedHooks.getExternalLookup, externalClassFileManager) - incOptions.withExternalHooks(hooks) + val external = Optional.of(tastyFileManager: ClassFileManager) + val prevManager = inheritedHooks.getExternalClassFileManager + val fileManager: Optional[ClassFileManager] = + if (prevManager.isPresent) Optional.of(WrappedClassFileManager.of(prevManager.get, external)) + else external + val newExternalHooks = new DefaultExternalHooks(inheritedHooks.getExternalLookup, fileManager) + + incOptions.withExternalHooks(newExternalHooks) } override val globalSettings: Seq[Def.Setting[_]] = Seq( diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/TastyFileManager.scala b/sbt-dotty/src/dotty/tools/sbtplugin/TastyFileManager.scala new file mode 100644 index 000000000000..e6688e95cd05 --- /dev/null +++ b/sbt-dotty/src/dotty/tools/sbtplugin/TastyFileManager.scala @@ -0,0 +1,62 @@ +package dotty.tools.sbtplugin + +import java.io.File +import java.nio.file.Files + +import sbt.io.IO +import xsbti.compile.ClassFileManager + +import scala.collection.mutable + + +/** A class file manger that prunes .tasty and .hasTasty as needed. + * + * This makes sure that, when a .class file must be deleted, the + * corresponding .tasty or .hasTasty file is also deleted. + * + * This code is adapted from Zinc `TransactionalClassFileManager`. + * We need to duplicate the logic since forwarding to the default class + * file manager doesn't work: we need to backup tasty files in a different + * temporary directory as class files. + */ +final class TastyFileManager extends ClassFileManager { + private[this] val tempDir = Files.createTempDirectory("backup").toFile + + private[this] val generatedTastyFiles = new mutable.HashSet[File] + private[this] val movedTastyFiles = new mutable.HashMap[File, File] + + override def delete(classes: Array[File]): Unit = { + val toBeBackedUp = tastyFiles(classes) + .filter(t => t.exists && !movedTastyFiles.contains(t) && !generatedTastyFiles(t)) + for (c <- toBeBackedUp) + movedTastyFiles.put(c, move(c)) + IO.deleteFilesEmptyDirs(classes) + } + + override def generated(classes: Array[File]): Unit = + generatedTastyFiles ++= tastyFiles(classes) + + override def complete(success: Boolean): Unit = { + if (!success) { + IO.deleteFilesEmptyDirs(generatedTastyFiles) + for ((orig, tmp) <- movedTastyFiles) IO.move(tmp, orig) + } + IO.delete(tempDir) + } + + private def tastyFiles(classes: Array[File]): Array[File] = { + val tastySuffixes = List(".tasty", ".hasTasty") + classes.flatMap { classFile => + if (classFile.getPath.endsWith(".class")) { + val prefix = classFile.getAbsolutePath.stripSuffix(".class") + tastySuffixes.map(suffix => new File(prefix + suffix)).filter(_.exists) + } else Nil + } + } + + private def move(c: File): File = { + val target = File.createTempFile("sbt", ".tasty", tempDir) + IO.move(c, target) + target + } +}