From 0b4d57b893cbd44e571f174f5409ec7b95c93f5a Mon Sep 17 00:00:00 2001 From: carlos4s Date: Fri, 10 Apr 2026 00:27:29 +0200 Subject: [PATCH] [2.x] fix: Allow cleanFileOutputTask to delete files outside target directory **Problem** When sourceManaged or resourceManaged is set to a path outside the target directory, neither clean nor managedSourcePaths/clean removes the generated files. The cleanFileOutputTask has a hard guard that skips any file not inside the target directory, even though these files are tracked via the previous mechanism and were produced by the task itself. **Solution** Replace the targetDir guard in cleanFileOutputTask with a baseDirectory guard. Files tracked by previous are task-produced outputs that are safe to delete as long as they are within the project root. The cleanKeepFiles/cleanKeepGlobs filter still applies for user-specified exclusions. Fixes #6895 --- main/src/main/scala/sbt/internal/Clean.scala | 5 ++--- .../actions/clean-managed-outside-target/build.sbt | 8 ++++++++ .../sbt-test/actions/clean-managed-outside-target/test | 6 ++++++ 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 sbt-app/src/sbt-test/actions/clean-managed-outside-target/build.sbt create mode 100644 sbt-app/src/sbt-test/actions/clean-managed-outside-target/test diff --git a/main/src/main/scala/sbt/internal/Clean.scala b/main/src/main/scala/sbt/internal/Clean.scala index 9d0094ce3..0d1d9eb96 100644 --- a/main/src/main/scala/sbt/internal/Clean.scala +++ b/main/src/main/scala/sbt/internal/Clean.scala @@ -167,10 +167,9 @@ private[sbt] object Clean { }) .flatMapTask { case scope => Def.task { - val targetDir = (scope / target).value.toPath + val baseDir = (scope / baseDirectory).value.toPath val filter = cleanFilter(scope).value - // We do not want to inadvertently delete files that are not in the target directory. - val excludeFilter: Path => Boolean = path => !path.startsWith(targetDir) || filter(path) + val excludeFilter: Path => Boolean = path => !path.startsWith(baseDir) || filter(path) val delete = cleanDelete(scope).value val st = (scope / streams).value taskKey.previous.foreach(_.toSeqPath.foreach(p => if (!excludeFilter(p)) delete(p))) diff --git a/sbt-app/src/sbt-test/actions/clean-managed-outside-target/build.sbt b/sbt-app/src/sbt-test/actions/clean-managed-outside-target/build.sbt new file mode 100644 index 000000000..77a169f59 --- /dev/null +++ b/sbt-app/src/sbt-test/actions/clean-managed-outside-target/build.sbt @@ -0,0 +1,8 @@ +name := "clean-managed-outside-target" +scalaVersion := "3.3.1" +Compile / sourceManaged := baseDirectory.value / "src_managed" +Compile / sourceGenerators += Def.task { + val file = (Compile / sourceManaged).value / "demo" / "Test.scala" + IO.write(file, """object Test extends App { println("Hi") }""") + Seq(file) +}.taskValue diff --git a/sbt-app/src/sbt-test/actions/clean-managed-outside-target/test b/sbt-app/src/sbt-test/actions/clean-managed-outside-target/test new file mode 100644 index 000000000..b65fdc4db --- /dev/null +++ b/sbt-app/src/sbt-test/actions/clean-managed-outside-target/test @@ -0,0 +1,6 @@ +> compile +$ exists src_managed/demo/Test.scala + +> Compile / managedSourcePaths +> Compile / managedSourcePaths / clean +$ absent src_managed/demo/Test.scala