From 730f4a525d113192cf14391c220a8eb37fc8af1d Mon Sep 17 00:00:00 2001 From: carlos4s <71615127+carlos4s@users.noreply.github.com> Date: Mon, 13 Apr 2026 04:40:31 +0200 Subject: [PATCH] fix: Fix clean task to delete managed sources located outside the target directory --- main/src/main/scala/sbt/Defaults.scala | 13 ++++++++++++- main/src/main/scala/sbt/internal/Clean.scala | 5 ++++- .../actions/clean-managed-outside-target/test | 8 ++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 1b567de2c..618d0f311 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -1965,7 +1965,18 @@ object Defaults extends BuildCommon with DefExtra { } /** Implements `cleanFiles` task. */ - private[sbt] def cleanFilesTask: Initialize[Task[Vector[File]]] = Def.task { Vector.empty[File] } + private[sbt] def cleanFilesTask: Initialize[Task[Vector[File]]] = { + import ScopeFilter.Make.* + val allConfigs = ScopeFilter(configurations = inAnyConfiguration) + Def.task { + val targetDir = target.value.toPath + val managedSrcDirs = managedSourceDirectories.?.all(allConfigs).value.flatten.flatten + val managedRscDirs = managedResourceDirectories.?.all(allConfigs).value.flatten.flatten + (managedSrcDirs ++ managedRscDirs) + .filter(d => !d.toPath.startsWith(targetDir)) + .toVector + } + } def runMainTask( classpath: Initialize[Task[Classpath]], diff --git a/main/src/main/scala/sbt/internal/Clean.scala b/main/src/main/scala/sbt/internal/Clean.scala index 0d1d9eb96..e154a6835 100644 --- a/main/src/main/scala/sbt/internal/Clean.scala +++ b/main/src/main/scala/sbt/internal/Clean.scala @@ -107,6 +107,7 @@ private[sbt] object Clean { val excludeFilter = cleanFilter(scope).value val delete = cleanDelete(scope).value val targetDir = (scope / target).?.value.map(_.toPath) + val baseDir = (scope / baseDirectory).?.value.map(_.toPath) targetDir.withFilter(_ => full).foreach(deleteContents(_, excludeFilter, view, delete)) (scope / cleanFiles).?.value.getOrElse(Nil).foreach { x => @@ -123,8 +124,10 @@ private[sbt] object Clean { val streamsGlobs = (streamsKey.toSeq ++ stampsKey) .map(k => manager(k).cacheDirectory.toPath.toGlob / **) + // Use baseDirectory instead of target so that file outputs outside the + // target directory but within the project root are also cleaned. ((scope / fileOutputs).value.filter { g => - targetDir.fold(true)(g.base.startsWith) + baseDir.fold(true)(g.base.startsWith) } ++ streamsGlobs) .foreach { g => val filter: Path => Boolean = { path => 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 index b65fdc4db..51b6bcbe5 100644 --- a/sbt-app/src/sbt-test/actions/clean-managed-outside-target/test +++ b/sbt-app/src/sbt-test/actions/clean-managed-outside-target/test @@ -1,6 +1,14 @@ +# Test that managedSourcePaths / clean removes managed sources outside target > compile $ exists src_managed/demo/Test.scala > Compile / managedSourcePaths > Compile / managedSourcePaths / clean $ absent src_managed/demo/Test.scala + +# Test that clean also removes managed sources outside target +> compile +$ exists src_managed/demo/Test.scala + +> clean +$ absent src_managed/demo/Test.scala