diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7e8ae501..e65246569 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,10 @@ jobs: java: 17 distribution: temurin jobtype: 10 + - os: windows-latest + java: 17 + distribution: zulu + jobtype: 12 runs-on: ${{ matrix.os }} timeout-minutes: 25 env: @@ -192,3 +196,8 @@ jobs: find **/src/main/contraband-scala -name "*.scala" -delete || true sbt generateContrabands git diff --exit-code + - name: Build and test (12) + if: ${{ matrix.jobtype == 12 }} + shell: bash + run: | + ./sbt -v "scripted cache/*" \ No newline at end of file diff --git a/sbt-app/src/sbt-test/cache/basic/build.sbt b/sbt-app/src/sbt-test/cache/basic/build.sbt index 30f1f6298..34c378d69 100644 --- a/sbt-app/src/sbt-test/cache/basic/build.sbt +++ b/sbt-app/src/sbt-test/cache/basic/build.sbt @@ -9,15 +9,15 @@ val checkMapN1 = taskKey[Unit]("") Global / localCacheDirectory := baseDirectory.value / "diskcache" pure1 := { - val output = StringVirtualFile1("target/out/a.txt", "foo") + val output = StringVirtualFile1("${OUT}/a.txt", "foo") Def.declareOutput(output) () } map1 := { pure1.value - val output1 = StringVirtualFile1("target/out/b1.txt", "foo") - val output2 = StringVirtualFile1("target/out/b2.txt", "foo") + val output1 = StringVirtualFile1("${OUT}/b1.txt", "foo") + val output2 = StringVirtualFile1("${OUT}/b2.txt", "foo") Def.declareOutput(output1) Def.declareOutput(output2) "something" @@ -26,7 +26,7 @@ map1 := { mapN1 := { pure1.value map1.value - val output = StringVirtualFile1("target/out/c.txt", "foo") + val output = StringVirtualFile1("${OUT}/c.txt", "foo") Def.declareOutput(output) () } diff --git a/sbt-app/src/sbt-test/cache/optout/build.sbt b/sbt-app/src/sbt-test/cache/optout/build.sbt index f300a13a9..d71451ada 100644 --- a/sbt-app/src/sbt-test/cache/optout/build.sbt +++ b/sbt-app/src/sbt-test/cache/optout/build.sbt @@ -9,8 +9,8 @@ aa := Def.uncached(A()) // This tests that aa is opted out from caching map1 := { aa.value - val output1 = StringVirtualFile1("target/out/b1.txt", "foo") - val output2 = StringVirtualFile1("target/out/b2.txt", "foo") + val output1 = StringVirtualFile1("${OUT}/b1.txt", "foo") + val output2 = StringVirtualFile1("${OUT}/b2.txt", "foo") Def.declareOutput(output1) Def.declareOutput(output2) "something" @@ -19,7 +19,7 @@ map1 := { mapN1 := { aa.value map1.value - val output = StringVirtualFile1("target/out/c.txt", "foo") + val output = StringVirtualFile1("${OUT}/c.txt", "foo") Def.declareOutput(output) () } diff --git a/sbt-app/src/sbt-test/cache/transitive/build.sbt b/sbt-app/src/sbt-test/cache/transitive/build.sbt index f94a6d203..1d65f7e00 100644 --- a/sbt-app/src/sbt-test/cache/transitive/build.sbt +++ b/sbt-app/src/sbt-test/cache/transitive/build.sbt @@ -7,14 +7,14 @@ val checkTask = taskKey[Unit]("Check that files exist") Global / localCacheDirectory := baseDirectory.value / "diskcache" task2 := { - val output = StringVirtualFile1("target/out/t2.txt", "task2 output") + val output = StringVirtualFile1("${OUT}/t2.txt", "task2 output") Def.declareOutput(output) () } task1 := { task2.value - val output = StringVirtualFile1("target/out/t1.txt", "task1 output") + val output = StringVirtualFile1("${OUT}/t1.txt", "task1 output") Def.declareOutput(output) () } diff --git a/util-cache/src/main/scala/sbt/util/ActionCache.scala b/util-cache/src/main/scala/sbt/util/ActionCache.scala index 506f6ee73..f7331a469 100644 --- a/util-cache/src/main/scala/sbt/util/ActionCache.scala +++ b/util-cache/src/main/scala/sbt/util/ActionCache.scala @@ -82,8 +82,16 @@ object ActionCache: cacheEventLog.append(ActionCacheEvent.Error) throw e val json = Converter.toJsonUnsafe(result) + val normalizedOutputDir = outputDirectory.toAbsolutePath.normalize() val uncacheableOutputs = - outputs.filter(f => !fileConverter.toPath(f).toAbsolutePath.startsWith(outputDirectory)) + outputs.filter(f => + f match + case vf if vf.id.endsWith(ActionCache.dirZipExt) => + false + case _ => + val outputPath = fileConverter.toPath(f).toAbsolutePath.normalize() + !outputPath.startsWith(normalizedOutputDir) + ) if uncacheableOutputs.nonEmpty then cacheEventLog.append(ActionCacheEvent.Error) logger.error( diff --git a/util-cache/src/main/scala/sbt/util/ActionCacheStore.scala b/util-cache/src/main/scala/sbt/util/ActionCacheStore.scala index 1f16ce959..733aa32e7 100644 --- a/util-cache/src/main/scala/sbt/util/ActionCacheStore.scala +++ b/util-cache/src/main/scala/sbt/util/ActionCacheStore.scala @@ -290,7 +290,7 @@ class DiskActionCacheStore(base: Path, converter: FileConverter) extends Abstrac // On Windows, the program has be running under the Administrator privileges or the // user enable Developer Mode on Windows 10+ to create symbolic links. def writeFileAndNotify(outPath: Path): Path = - IO.createDirectory(outPath.getParent().toFile()) + Option(outPath.getParent()).foreach(parent => IO.createDirectory(parent.toFile())) val result = Retry: if Files.exists(outPath) then IO.delete(outPath.toFile()) if symlinkSupported.get() && Files.exists(casFile) then @@ -312,7 +312,10 @@ class DiskActionCacheStore(base: Path, converter: FileConverter) extends Abstrac else copyFile(outPath) afterFileWrite(ref, result, outputDirectory) result - converter.toPath(ref) match + val resolvedPath = converter.toPath(ref) match + case p if p.isAbsolute => p + case p => outputDirectory.resolve(p) + resolvedPath match case p if !Files.exists(p) => // println(s"- syncFile: $p does not exist") writeFileAndNotify(p)