From 5a767e344230ad7e32901fae022d44cde0809652 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Tue, 17 Mar 2026 01:54:45 -0400 Subject: [PATCH] [2.x] Add VF keys **Problem** java.io.File cannot participate in cached task. **Solution** This adds a few VF-equivalent keys so they can participate in cached task. --- main/src/main/scala/sbt/Defaults.scala | 50 ++++++++++++++++++++--- main/src/main/scala/sbt/Keys.scala | 22 ++++++---- sbt-app/src/sbt-test/actions/compile/test | 2 +- 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 9edc4a3bb..0a9e072bd 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -601,6 +601,11 @@ object Defaults extends BuildCommon with DefExtra { .map(d => Globs(d.toPath, recursive = true, filter)) ++ baseSources }, unmanagedSources := Def.uncached((unmanagedSources / inputFileStamps).value.map(_._1.toFile)), + unmanagedSourcesVF := Def.uncached { + val conv = fileConverter.value + unmanagedSources.value.toVector.map: x => + (conv.toVirtualFile(x.toPath()): HashedVirtualFileRef) + }, managedSourceDirectories := Seq(sourceManaged.value), managedSources := { val stamper = inputFileStamper.value @@ -611,13 +616,23 @@ object Defaults extends BuildCommon with DefExtra { } Def.uncached(res) }, + managedSourcesVF := Def.uncached { + val conv = fileConverter.value + managedSources.value.toVector.map: x => + (conv.toVirtualFile(x.toPath()): HashedVirtualFileRef) + }, managedSourcePaths / outputFileStamper := sbt.nio.FileStamper.Hash, managedSourcePaths := managedSources.value.map(_.toPath), sourceGenerators :== Nil, sourceDirectories := Classpaths .concatSettings(unmanagedSourceDirectories, managedSourceDirectories) .value, - sources := Classpaths.concatDistinct(unmanagedSources, managedSources).value + sources := Classpaths.concatDistinct(unmanagedSources, managedSources).value, + sourcesVF := Def.uncached { + val conv = fileConverter.value + sources.value.toVector.map: x => + (conv.toVirtualFile(x.toPath()): HashedVirtualFileRef) + }, ) lazy val resourceConfigPaths = Seq( resourceDirectory := sourceDirectory.value / "resources", @@ -639,12 +654,25 @@ object Defaults extends BuildCommon with DefExtra { unmanagedResources := Def.uncached( (unmanagedResources / inputFileStamps).value.map(_._1.toFile) ), + unmanagedResourcesVF := Def.uncached { + val conv = fileConverter.value + unmanagedResources.value.toVector.map: x => + (conv.toVirtualFile(x.toPath()): HashedVirtualFileRef) + }, resourceGenerators :== Nil, resourceGenerators += (Def.task { PluginDiscovery.writeDescriptors(discoveredSbtPlugins.value, resourceManaged.value) }).taskValue, managedResources := generate(resourceGenerators).value, + managedResourcesVF := Def.uncached { + val conv = fileConverter.value + managedResources.value.toVector.map: x => + (conv.toVirtualFile(x.toPath()): HashedVirtualFileRef) + }, resources := Classpaths.concat(managedResources, unmanagedResources).value, + resourcesVF := Def.uncached( + managedResourcesVF.value ++ unmanagedResourcesVF.value + ), resourceDigests := Def.uncached { val uifs = (unmanagedResources / inputFileStamps).value val mifs = (managedResources / inputFileStamps).value @@ -953,6 +981,11 @@ object Defaults extends BuildCommon with DefExtra { tastyFiles.map(_.getAbsoluteFile) } else Nil }.value, + tastyFilesVF := Def.uncached { + val conv = fileConverter.value + tastyFiles.value.map: x => + (conv.toVirtualFile(x.toPath()): HashedVirtualFileRef) + }, clean := { (compileOutputs / clean).value (products / clean).value @@ -2042,7 +2075,12 @@ object Defaults extends BuildCommon with DefExtra { Seq("-project", project) } else Seq.empty }, - (TaskZero / key) := Def.uncached(Compiler.docTask(key).value) + (TaskZero / key) := Def.uncached(Compiler.docTask(key).value), + (TaskZero / docVF) := Def.uncached { + val conv = fileConverter.value + val orig = (TaskZero / key).value + (conv.toVirtualFile(orig.toPath()): HashedVirtualFileRef) + }, ) ++ compilersSetting ) @@ -2274,9 +2312,9 @@ object Defaults extends BuildCommon with DefExtra { val cp0 = classpathTask.value val cp1 = backendOutput.value +: data(cp0) val cp = cp1.map(c.toPath).map(c.toVirtualFile) - val vs = sources.value.toVector map { x => - c.toVirtualFile(x.toPath) - } + val vs0 = sourcesVF.value + val vs = vs0.toVector.map: x => + c.toVirtualFile(c.toPath(x)) val eo = CompileOutput(c.toPath(earlyOutput.value)) val eoOpt = if (exportPipelining.value) Some(eo) @@ -2323,7 +2361,7 @@ object Defaults extends BuildCommon with DefExtra { val c = fileConverter.value CompileInputs2( data(cp0).toVector, - inputs.options.sources.toVector, + sourcesVF.value, scalacOptions.value.toVector, javacOptions.value.toVector, c.toVirtualFile(inputs.options.classesDirectory), diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 7abda798e..6700fa2a0 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -155,12 +155,15 @@ object Keys { val sourceDirectories = settingKey[Seq[File]]("List of all source directories, both managed and unmanaged.").withRank(AMinusSetting) val unmanagedSourceDirectories = settingKey[Seq[File]]("Unmanaged source directories, which contain manually created sources.").withRank(ASetting) @transient - val unmanagedSources = taskKey[Seq[File]]("Unmanaged sources, which are manually created.").withRank(BPlusTask) + val unmanagedSources = taskKey[Seq[File]]("Unmanaged sources, which are manually created.").withRank(DTask) + val unmanagedSourcesVF = taskKey[Vector[HashedVirtualFileRef]]("Unmanaged sources, which are manually created.").withRank(BPlusTask) val managedSourceDirectories = settingKey[Seq[File]]("Managed source directories, which contain sources generated by the build.").withRank(BSetting) @transient - val managedSources = taskKey[Seq[File]]("Sources generated by the build.").withRank(BTask) + val managedSources = taskKey[Seq[File]]("Sources generated by the build.").withRank(DTask) + val managedSourcesVF = taskKey[Vector[HashedVirtualFileRef]]("Sources generated by the build.").withRank(BTask) @transient - val sources = taskKey[Seq[File]]("All sources, both managed and unmanaged.").withRank(BTask) + val sources = taskKey[Seq[File]]("All sources, both managed and unmanaged.").withRank(DTask) + val sourcesVF = taskKey[Vector[HashedVirtualFileRef]]("All sources, both managed and unmanaged.").withRank(BTask) val sourcesInBase = settingKey[Boolean]("If true, sources from the project's base directory are included as main sources.") // Filters @@ -172,13 +175,16 @@ object Keys { val resourceManaged = settingKey[File]("Default managed resource directory, used when generating resources.").withRank(BSetting) val unmanagedResourceDirectories = settingKey[Seq[File]]("Unmanaged resource directories, containing resources manually created by the user.").withRank(AMinusSetting) @transient - val unmanagedResources = taskKey[Seq[File]]("Unmanaged resources, which are manually created.").withRank(BPlusTask) + val unmanagedResources = taskKey[Seq[File]]("Unmanaged resources, which are manually created.").withRank(DTask) + val unmanagedResourcesVF = taskKey[Vector[HashedVirtualFileRef]]("Unmanaged resources, which are manually created.").withRank(BPlusTask) val managedResourceDirectories = settingKey[Seq[File]]("List of managed resource directories.").withRank(AMinusSetting) @transient - val managedResources = taskKey[Seq[File]]("Resources generated by the build.").withRank(BTask) + val managedResources = taskKey[Seq[File]]("Resources generated by the build.").withRank(DTask) + val managedResourcesVF = taskKey[Vector[HashedVirtualFileRef]]("Resources generated by the build.").withRank(BTask) val resourceDirectories = settingKey[Seq[File]]("List of all resource directories, both managed and unmanaged.").withRank(BPlusSetting) @transient - val resources = taskKey[Seq[File]]("All resource files, both managed and unmanaged.").withRank(BTask) + val resources = taskKey[Seq[File]]("All resource files, both managed and unmanaged.").withRank(DTask) + val resourcesVF = taskKey[Vector[HashedVirtualFileRef]]("All resource files, both managed and unmanaged.").withRank(BTask) private[sbt] val resourceDigests = taskKey[Seq[Digest]]("All resource files, both managed and unmanaged.").withRank(BTask) // Output paths @@ -262,6 +268,7 @@ object Keys { val previousCompile = taskKey[PreviousResult]("Read the incremental compiler analysis from disk").withRank(DTask) @transient val tastyFiles = taskKey[Seq[File]]("Returns the TASTy files produced by compilation").withRank(DTask) + val tastyFilesVF = taskKey[Seq[HashedVirtualFileRef]]("Returns the TASTy files produced by compilation").withRank(DTask) private[sbt] val compileScalaBackend = taskKey[CompileResult]("Compiles only Scala sources if pipelining is enabled. Compiles both Scala and Java sources otherwise").withRank(Invisible) private[sbt] val compileEarly = taskKey[CompileAnalysis]("Compiles only Scala sources if pipelining is enabled, and produce an early output (pickle JAR)").withRank(Invisible) @@ -291,6 +298,7 @@ object Keys { val classpathEntryDefinesClassVF = taskKey[VirtualFile => DefinesClass]("Internal use: provides a function that determines whether the provided file contains a given class.").withRank(Invisible) @transient val doc = taskKey[File]("Generates API documentation.").withRank(AMinusTask) + val docVF = taskKey[HashedVirtualFileRef]("Generates API documentation.").withRank(AMinusTask) @transient val copyResources = taskKey[Seq[(File, File)]]("Copies resources to the output directory.").withRank(AMinusTask) val aggregate = settingKey[Boolean]("Configures task aggregation.").withRank(BMinusSetting) @@ -416,7 +424,7 @@ object Keys { val defaultConfiguration = settingKey[Option[Configuration]]("Defines the configuration used when none is specified for a dependency in ivyXML.").withRank(CSetting) @transient - val products = taskKey[Seq[File]]("Build products that get packaged.").withRank(BMinusTask) + val products = taskKey[Seq[File]]("Build products that get packaged.").withRank(DTask) @transient val productDirectories = taskKey[Seq[File]]("Base directories of build products.").withRank(CTask) val exportJars = settingKey[Boolean]("Determines whether the exported classpath for this project contains classes (false) or a packaged jar (true).").withRank(BSetting) diff --git a/sbt-app/src/sbt-test/actions/compile/test b/sbt-app/src/sbt-test/actions/compile/test index ce9f0cb28..1c66bca20 100644 --- a/sbt-app/src/sbt-test/actions/compile/test +++ b/sbt-app/src/sbt-test/actions/compile/test @@ -1,5 +1,5 @@ -> compile -> 'set Compile / compile / sources := Def.uncached { val src = (Compile / compile / sources).value; src.filterNot(_.getName contains "C") }' +> 'set Compile / compile / sourcesVF := { val src = (Compile / compile / sourcesVF).value; src.filterNot(_.name contains "C") }' > compile