Skip to content

Commit e7161a0

Browse files
committed
[2.x] fix: Auto-skip caching for tasks with java.io.File output type
When a task's return type contains java.io.File, automatically skip ActionCache.cache() wrapping. This prevents stale cached File paths The fix is in callActionCache: when containsFileType detects java.io.File in the return type, it still processes Def.declareOutput via letOutput but returns the body value directly instead of caching it. Generated-by: GitHub Copilot (Claude Opus 4.6)
1 parent c045c72 commit e7161a0

3 files changed

Lines changed: 65 additions & 32 deletions

File tree

  • core-macros/src/main/scala/sbt/internal/util/appmacro
  • sbt-app/src/sbt-test/cache/skip-file-cache

core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,16 @@ trait Cont:
178178
case Left(l) => (l, TypeRepr.of[Effect[A]])
179179
case Right(r) => (r, faTpe)
180180

181+
def containsFileType[A1: Type]: Boolean =
182+
val fileRepr = TypeRepr.of[java.io.File]
183+
def containsFile(tpe: TypeRepr): Boolean =
184+
if tpe =:= fileRepr then true
185+
else
186+
tpe.dealias match
187+
case AppliedType(_, args) => args.exists(containsFile)
188+
case _ => false
189+
containsFile(TypeRepr.of[A1])
190+
181191
val inputBuf = ListBuffer[Input]()
182192
val outputBuf = ListBuffer[Output]()
183193

@@ -342,38 +352,42 @@ trait Cont:
342352
cacheConfigExpr: Expr[BuildWideCacheConfiguration],
343353
tags: List[CacheLevelTag],
344354
)(body: Expr[A1], input: Expr[A2]): Expr[A1] =
345-
val codeContentHash =
346-
try Expr[Long](body.show.##)
347-
catch
348-
case e: Throwable =>
349-
Expr[Long](Printer.TreeStructure.show(body.asTerm).##)
350-
val extraHash = Expr[Long](0L)
351-
val aJsonFormat = summonJsonFormat[A1]
352-
val aClassTag = summonClassTag[A1]
353-
val inputHashWriter =
354-
if TypeRepr.of[A2] =:= TypeRepr.of[Unit] then
355-
'{
356-
import BasicJsonProtocol.*
357-
summon[HashWriter[Unit]]
358-
}.asExprOf[HashWriter[A2]]
359-
else summonHashWriter[A2]
360-
val tagsExpr = '{ List(${ Varargs(tags.map(Expr[CacheLevelTag](_))) }*) }
361-
val block = letOutput(outputs, cacheConfigExpr)(body)
362-
'{
363-
given HashWriter[A2] = $inputHashWriter
364-
given JsonFormat[A1] = $aJsonFormat
365-
given ClassTag[A1] = $aClassTag
366-
ActionCache
367-
.cache(
368-
$input,
369-
codeContentHash = Digest.dummy($codeContentHash),
370-
extraHash = Digest.dummy($extraHash),
371-
tags = $tagsExpr,
372-
config = $cacheConfigExpr,
373-
)({ _ =>
374-
$block
375-
})
376-
}
355+
if containsFileType[A1] then
356+
val block = letOutput(outputs, cacheConfigExpr)(body)
357+
'{ $block.value }
358+
else
359+
val codeContentHash =
360+
try Expr[Long](body.show.##)
361+
catch
362+
case e: Throwable =>
363+
Expr[Long](Printer.TreeStructure.show(body.asTerm).##)
364+
val extraHash = Expr[Long](0L)
365+
val aJsonFormat = summonJsonFormat[A1]
366+
val aClassTag = summonClassTag[A1]
367+
val inputHashWriter =
368+
if TypeRepr.of[A2] =:= TypeRepr.of[Unit] then
369+
'{
370+
import BasicJsonProtocol.*
371+
summon[HashWriter[Unit]]
372+
}.asExprOf[HashWriter[A2]]
373+
else summonHashWriter[A2]
374+
val tagsExpr = '{ List(${ Varargs(tags.map(Expr[CacheLevelTag](_))) }*) }
375+
val block = letOutput(outputs, cacheConfigExpr)(body)
376+
'{
377+
given HashWriter[A2] = $inputHashWriter
378+
given JsonFormat[A1] = $aJsonFormat
379+
given ClassTag[A1] = $aClassTag
380+
ActionCache
381+
.cache(
382+
$input,
383+
codeContentHash = Digest.dummy($codeContentHash),
384+
extraHash = Digest.dummy($extraHash),
385+
tags = $tagsExpr,
386+
config = $cacheConfigExpr,
387+
)({ _ =>
388+
$block
389+
})
390+
}
377391

378392
// This will generate following code for Def.declareOutput(...):
379393
// var $o1: VirtualFile = null
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import java.io.File
2+
3+
val myFileTask = taskKey[File]("task that returns File")
4+
val checkFileTask = taskKey[Unit]("verifies file task returns correct value")
5+
6+
myFileTask := {
7+
new File(scalaVersion.value)
8+
}
9+
10+
checkFileTask := Def.uncached {
11+
val f = myFileTask.value
12+
val expected = new File(scalaVersion.value)
13+
assert(f == expected, s"Expected $expected but got $f")
14+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# File-returning tasks should not be cached, so
2+
# changing scalaVersion should immediately be reflected.
3+
> checkFileTask
4+
> set scalaVersion := "2.13.18"
5+
> checkFileTask

0 commit comments

Comments
 (0)