From a946cbf702d2d7fa2a0496932768963c9c2be686 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Tue, 15 Sep 2020 08:27:12 -0700 Subject: [PATCH] Skip contents of symlinked directories in clean The clean task was previously deleting the contents of directories that were symlinked into the target directory. This was an oversight because it never occurred to me that users might symlink a directory whose contents they did not want deleted into the target directory. --- main/src/main/scala/sbt/internal/Clean.scala | 14 +++++--------- sbt/src/sbt-test/nio/clean-symlinks/build.sbt | 5 +++++ sbt/src/sbt-test/nio/clean-symlinks/foo/bar | 1 + sbt/src/sbt-test/nio/clean-symlinks/test | 9 +++++++++ 4 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 sbt/src/sbt-test/nio/clean-symlinks/build.sbt create mode 100644 sbt/src/sbt-test/nio/clean-symlinks/foo/bar create mode 100644 sbt/src/sbt-test/nio/clean-symlinks/test diff --git a/main/src/main/scala/sbt/internal/Clean.scala b/main/src/main/scala/sbt/internal/Clean.scala index a5b19e465..0f314fd60 100644 --- a/main/src/main/scala/sbt/internal/Clean.scala +++ b/main/src/main/scala/sbt/internal/Clean.scala @@ -41,7 +41,7 @@ private[sbt] object Clean { .list(Glob(path, AnyPath)) .filterNot { case (p, _) => exclude(p) } .foreach { - case (dir, attrs) if attrs.isDirectory => + case (dir, attrs) if attrs.isDirectory && !attrs.isSymbolicLink => deleteRecursive(dir) delete(dir) case (file, _) => delete(file) @@ -89,15 +89,11 @@ private[sbt] object Clean { val excludeFilter = cleanFilter(scope).value val delete = cleanDelete(scope).value val targetDir = (target in scope).?.value.map(_.toPath) - def recursiveFiles(dir: Path): Seq[Path] = - view.list(dir.toGlob / **).collect { case (p, _) if !excludeFilter(p) => p } - val targetFiles = (if (full) targetDir else None).fold(Nil: Seq[Path])(recursiveFiles) - val cleanPaths = (cleanFiles in scope).?.value.getOrElse(Nil).flatMap { f => - val path = f.toPath - if (Files.isDirectory(path)) path +: recursiveFiles(path) else path :: Nil + + targetDir.filter(_ => full).foreach(deleteContents(_, excludeFilter, view, delete)) + (cleanFiles in scope).?.value.getOrElse(Nil).foreach { f => + deleteContents(f.toPath, excludeFilter, view, delete) } - val allFiles = cleanPaths.view ++ targetFiles - allFiles.sorted.reverseIterator.foreach(delete) // This is the special portion of the task where we clear out the relevant streams // and file outputs of a task. diff --git a/sbt/src/sbt-test/nio/clean-symlinks/build.sbt b/sbt/src/sbt-test/nio/clean-symlinks/build.sbt new file mode 100644 index 000000000..b7ad34a81 --- /dev/null +++ b/sbt/src/sbt-test/nio/clean-symlinks/build.sbt @@ -0,0 +1,5 @@ +import java.nio.file.Files + +TaskKey[Unit]("createSymlinkedDirectory") := { + Files.createSymbolicLink(target.value.toPath / "foo", baseDirectory.value.toPath / "foo") +} diff --git a/sbt/src/sbt-test/nio/clean-symlinks/foo/bar b/sbt/src/sbt-test/nio/clean-symlinks/foo/bar new file mode 100644 index 000000000..5716ca598 --- /dev/null +++ b/sbt/src/sbt-test/nio/clean-symlinks/foo/bar @@ -0,0 +1 @@ +bar diff --git a/sbt/src/sbt-test/nio/clean-symlinks/test b/sbt/src/sbt-test/nio/clean-symlinks/test new file mode 100644 index 000000000..48dfcb255 --- /dev/null +++ b/sbt/src/sbt-test/nio/clean-symlinks/test @@ -0,0 +1,9 @@ +> createSymlinkedDirectory + +$ exists target/foo/bar +$ exists foo/bar + +> clean + +$ absent target/foo +$ exists foo/bar