diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index cac5e7338..f2646f50a 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -256,9 +256,9 @@ object Defaults extends BuildCommon { skip :== false, taskTemporaryDirectory := { val dir = IO.createTemporaryDirectory; dir.deleteOnExit(); dir }, onComplete := { - val dir = taskTemporaryDirectory.value; + val tempDirectory = taskTemporaryDirectory.value () => - { IO.delete(dir); IO.createDirectory(dir) } + Clean.deleteContents(tempDirectory, _ => false) }, useSuperShell :== sbt.internal.TaskProgress.isEnabled, progressReports := { (s: State) => diff --git a/main/src/main/scala/sbt/internal/Clean.scala b/main/src/main/scala/sbt/internal/Clean.scala index f66323e74..83629d505 100644 --- a/main/src/main/scala/sbt/internal/Clean.scala +++ b/main/src/main/scala/sbt/internal/Clean.scala @@ -14,12 +14,31 @@ import java.nio.file.{ DirectoryNotEmptyException, Files } import sbt.Def._ import sbt.Keys._ import sbt.Project.richInitializeTask -import sbt.internal.GlobLister._ -import sbt.io.AllPassFilter import sbt.io.syntax._ +import sbt.io.{ AllPassFilter, FileTreeView, TypedPath } +import sbt.util.Level object Clean { + def deleteContents(file: File, exclude: TypedPath => Boolean): Unit = + deleteContents(file, exclude, FileTreeView.DEFAULT, tryDelete((_: String) => {})) + def deleteContents( + file: File, + exclude: TypedPath => Boolean, + view: FileTreeView, + delete: File => Unit + ): Unit = { + def deleteRecursive(file: File): Unit = { + view.list(file * AllPassFilter).filterNot(exclude).foreach { + case dir if dir.isDirectory => + deleteRecursive(dir.toPath.toFile) + delete(dir.toPath.toFile) + case f => delete(f.toPath.toFile) + } + } + deleteRecursive(file) + } + /** * Provides an implementation for the clean task. It delegates to [[taskIn]] using the * resolvedScoped key to set the scope. @@ -41,22 +60,37 @@ object Clean { case f if f.isDirectory => f * AllPassFilter case f => f.toGlob } ++ cleanKeepGlobs.value - val excludeFilter: File => Boolean = excludes.toFileFilter.accept - val globDeletions = (outputs in scope).value.unique.filterNot(excludeFilter) - val toDelete = cleanFiles.value.filterNot(excludeFilter) match { - case f @ Seq(_, _*) => (globDeletions ++ f).distinct - case _ => globDeletions + val excludeFilter: TypedPath => Boolean = excludes.toTypedPathFilter + val debug = (logLevel in scope).?.value.orElse(state.value.get(logLevel.key)) match { + case Some(Level.Debug) => + (string: String) => + println(s"[debug] $string") + case _ => + (_: String) => + {} } - val logger = streams.value.log - toDelete.sorted.reverseIterator.foreach { f => - logger.debug(s"clean -- deleting file $f") - try Files.deleteIfExists(f.toPath) - catch { - case _: DirectoryNotEmptyException => - logger.debug(s"clean -- unable to delete non-empty directory $f") - case e: IOException => - logger.debug(s"Caught unexpected exception $e deleting $f") + val delete = tryDelete(debug) + cleanFiles.value.sorted.reverseIterator.foreach(delete) + (outputs in scope).value.foreach { g => + val filter: TypedPath => Boolean = { + val globFilter = g.toTypedPathFilter + tp => + !globFilter(tp) || excludeFilter(tp) } + deleteContents(g.base.toFile, filter, FileTreeView.DEFAULT, delete) + delete(g.base.toFile) } } tag Tags.Clean + private def tryDelete(debug: String => Unit): File => Unit = file => { + try { + debug(s"clean -- deleting file $file") + Files.deleteIfExists(file.toPath) + () + } catch { + case _: DirectoryNotEmptyException => + debug(s"clean -- unable to delete non-empty directory $file") + case e: IOException => + debug(s"Caught unexpected exception $e deleting $file") + } + } }