mirror of https://github.com/sbt/sbt.git
Merge pull request #7949 from eed3si9n/wip/path-mapper
Add Mapper that returns VirtualFile based mappings, take 2
This commit is contained in:
commit
8dcdee8732
|
|
@ -0,0 +1,86 @@
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import sbt.io.{ AllPassFilter, FileFilter, PathFinder }
|
||||||
|
import sbt.io.Path.*
|
||||||
|
import xsbti.{ FileConverter, VirtualFile }
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object Mapper:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects all descendants of `base` directory and maps them to a path relative to `base`.
|
||||||
|
* `base` itself is not included.
|
||||||
|
*/
|
||||||
|
def allSubpaths(base: File)(using conv: FileConverter): Seq[(VirtualFile, String)] =
|
||||||
|
selectSubpaths(base, AllPassFilter)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects descendants of `base` directory matching `filter` and maps them to a path relative to `base`.
|
||||||
|
* `base` itself is not included.
|
||||||
|
*/
|
||||||
|
def selectSubpaths(base: File, filter: FileFilter)(using
|
||||||
|
conv: FileConverter
|
||||||
|
): Seq[(VirtualFile, String)] =
|
||||||
|
PathFinder(base).globRecursive(filter).get().collect {
|
||||||
|
case f if f != base =>
|
||||||
|
conv.toVirtualFile(f.toPath) -> base.toPath.relativize(f.toPath).toString
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return a Seq of mappings which effect is to add a whole directory in the generated package
|
||||||
|
*
|
||||||
|
* @example In order to create mappings for a static directory "extra" add
|
||||||
|
* {{{
|
||||||
|
* mappings ++= directory(baseDirectory.value / "extra")
|
||||||
|
* }}}
|
||||||
|
*
|
||||||
|
* The resulting mappings sequence will look something like this
|
||||||
|
*
|
||||||
|
* {{{
|
||||||
|
* File(baseDirectory/extras) -> "extras"
|
||||||
|
* File(baseDirectory/extras/file1) -> "extras/file1"
|
||||||
|
* File(baseDirectory/extras/file2) -> "extras/file2"
|
||||||
|
* ...
|
||||||
|
* }}}
|
||||||
|
*
|
||||||
|
* @param baseDirectory The directory that should be turned into a mappings sequence.
|
||||||
|
* @return mappings The `baseDirectory` and all of its contents
|
||||||
|
*/
|
||||||
|
def directory(baseDirectory: File)(using conv: FileConverter): Seq[(VirtualFile, String)] =
|
||||||
|
Option(baseDirectory.getParentFile)
|
||||||
|
.map(parent => PathFinder(baseDirectory).allPaths pair relativeTo(parent))
|
||||||
|
.getOrElse(PathFinder(baseDirectory).allPaths pair basic)
|
||||||
|
.map { case (f, s) => conv.toVirtualFile(f.toPath) -> s }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return a Seq of mappings excluding the directory itself.
|
||||||
|
*
|
||||||
|
* @example In order to create mappings for a static directory "extra" add
|
||||||
|
* {{{
|
||||||
|
* mappings ++= contentOf(baseDirectory.value / "extra")
|
||||||
|
* }}}
|
||||||
|
*
|
||||||
|
* The resulting mappings sequence will look something like this
|
||||||
|
*
|
||||||
|
* {{{
|
||||||
|
* File(baseDirectory/extras/file1) -> "file1"
|
||||||
|
* File(baseDirectory/extras/file2) -> "file2"
|
||||||
|
* ...
|
||||||
|
* }}}
|
||||||
|
*
|
||||||
|
* @example Add a static directory "extra" and re-map the destination to a different path
|
||||||
|
* {{{
|
||||||
|
* mappings ++= contentOf(baseDirectory.value / "extra").map {
|
||||||
|
* case (src, destination) => src -> s"new/path/destination"
|
||||||
|
* }
|
||||||
|
* }}}
|
||||||
|
*
|
||||||
|
* @param baseDirectory The directory that should be turned into a mappings sequence.
|
||||||
|
* @return mappings - The `basicDirectory`'s contents exlcuding `basicDirectory` itself
|
||||||
|
*/
|
||||||
|
def contentOf(baseDirectory: File)(using conv: FileConverter): Seq[(VirtualFile, String)] =
|
||||||
|
(PathFinder(baseDirectory).allPaths --- PathFinder(baseDirectory))
|
||||||
|
.pair(relativeTo(baseDirectory))
|
||||||
|
.map { case (f, s) => conv.toVirtualFile(f.toPath) -> s }
|
||||||
|
end Mapper
|
||||||
|
|
@ -0,0 +1,161 @@
|
||||||
|
/*
|
||||||
|
* sbt
|
||||||
|
* Copyright 2023, Scala center
|
||||||
|
* Copyright 2011 - 2022, Lightbend, Inc.
|
||||||
|
* Copyright 2008 - 2010, Mark Harrah
|
||||||
|
* Licensed under Apache License 2.0 (see LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import java.nio.file.Files
|
||||||
|
|
||||||
|
import sbt.internal.inc.MappedFileConverter
|
||||||
|
import sbt.io.IO
|
||||||
|
import sbt.io.syntax.*
|
||||||
|
import xsbti.FileConverter
|
||||||
|
|
||||||
|
object MapperTest extends verify.BasicTestSuite:
|
||||||
|
test("directory should create mappings including the baseDirectory") {
|
||||||
|
withTempDirectory: tempDirectory =>
|
||||||
|
given FileConverter = MappedFileConverter(Map("BASE" -> tempDirectory.toPath()), true)
|
||||||
|
val nestedFile1 = tempDirectory / "file1"
|
||||||
|
val nestedFile2 = tempDirectory / "file2"
|
||||||
|
val nestedDir = tempDirectory / "dir1"
|
||||||
|
val nestedDirFile = nestedDir / "dir1-file1"
|
||||||
|
|
||||||
|
IO.touch(nestedFile1)
|
||||||
|
IO.touch(nestedFile2)
|
||||||
|
IO.createDirectory(nestedDir)
|
||||||
|
IO.touch(nestedDirFile)
|
||||||
|
|
||||||
|
val mappings = Mapper
|
||||||
|
.directory(tempDirectory)
|
||||||
|
.map { case (h, p) =>
|
||||||
|
(h.toString, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
Predef.assert(
|
||||||
|
mappings.toSet == List(
|
||||||
|
"${BASE}/" -> s"${tempDirectory.getName}",
|
||||||
|
"${BASE}/file1" -> s"${tempDirectory.getName}/file1",
|
||||||
|
"${BASE}/file2" -> s"${tempDirectory.getName}/file2",
|
||||||
|
"${BASE}/dir1" -> s"${tempDirectory.getName}/dir1",
|
||||||
|
"${BASE}/dir1/dir1-file1" -> s"${tempDirectory.getName}/dir1/dir1-file1",
|
||||||
|
).toSet
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("it should create one mapping entry for an empty directory") {
|
||||||
|
withTempDirectory: tempDirectory =>
|
||||||
|
given FileConverter = MappedFileConverter(Map("BASE" -> tempDirectory.toPath()), true)
|
||||||
|
val mappings = Mapper
|
||||||
|
.directory(tempDirectory)
|
||||||
|
.map { case (h, p) =>
|
||||||
|
(h.toString, p)
|
||||||
|
}
|
||||||
|
Predef.assert(
|
||||||
|
mappings.toSet == List(
|
||||||
|
"${BASE}/" -> "foo"
|
||||||
|
).toSet,
|
||||||
|
s"found $mappings"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("it should create an empty mappings sequence for a non-existing directory") {
|
||||||
|
withTempDirectory: tempDirectory =>
|
||||||
|
val conv0: FileConverter = MappedFileConverter(Map("BASE" -> tempDirectory.toPath()), true)
|
||||||
|
given FileConverter = conv0
|
||||||
|
val nonExistingDirectory = tempDirectory / "imaginary"
|
||||||
|
val mappings = Mapper.directory(nonExistingDirectory)
|
||||||
|
assert(mappings.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("it should create one mapping entry if the directory is a file") {
|
||||||
|
withTempDirectory: tempDirectory =>
|
||||||
|
val conv0: FileConverter = MappedFileConverter(Map("BASE" -> tempDirectory.toPath()), true)
|
||||||
|
given FileConverter = conv0
|
||||||
|
val file = tempDirectory / "file"
|
||||||
|
IO.touch(file)
|
||||||
|
val mappings = Mapper.directory(file).map { case (h, p) =>
|
||||||
|
(h.toString, p)
|
||||||
|
}
|
||||||
|
Predef.assert(
|
||||||
|
mappings.toSet == Set("${BASE}/file" -> s"${file.getName}"),
|
||||||
|
s"actual: $mappings"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("contentOf should create mappings excluding the baseDirectory") {
|
||||||
|
withTempDirectory: tempDirectory =>
|
||||||
|
val conv0: FileConverter = MappedFileConverter(Map("BASE" -> tempDirectory.toPath()), true)
|
||||||
|
given FileConverter = conv0
|
||||||
|
val nestedFile1 = tempDirectory / "file1"
|
||||||
|
val nestedFile2 = tempDirectory / "file2"
|
||||||
|
val nestedDir = tempDirectory / "dir1"
|
||||||
|
val nestedDirFile = nestedDir / "dir1-file1"
|
||||||
|
IO.touch(nestedFile1)
|
||||||
|
IO.touch(nestedFile2)
|
||||||
|
IO.createDirectory(nestedDir)
|
||||||
|
IO.touch(nestedDirFile)
|
||||||
|
|
||||||
|
val mappings = Mapper.contentOf(tempDirectory).map { case (h, p) =>
|
||||||
|
(h.toString, p)
|
||||||
|
}
|
||||||
|
Predef.assert(
|
||||||
|
mappings.toSet == List(
|
||||||
|
"${BASE}/file1" -> "file1",
|
||||||
|
"${BASE}/file2" -> "file2",
|
||||||
|
"${BASE}/dir1" -> "dir1",
|
||||||
|
"${BASE}/dir1/dir1-file1" -> "dir1/dir1-file1",
|
||||||
|
).toSet,
|
||||||
|
s"actual: $mappings"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("it should create an empty mappings sequence for an empty directory") {
|
||||||
|
withTempDirectory: tempDirectory =>
|
||||||
|
given FileConverter = MappedFileConverter(Map("BASE" -> tempDirectory.toPath()), true)
|
||||||
|
val mappings = Mapper.contentOf(tempDirectory)
|
||||||
|
assert(mappings.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("it should create an empty mappings sequence for a non-existing directory") {
|
||||||
|
withTempDirectory: tempDirectory =>
|
||||||
|
given FileConverter = MappedFileConverter(Map("BASE" -> tempDirectory.toPath()), true)
|
||||||
|
val nonExistingDirectory = tempDirectory / "imaginary"
|
||||||
|
val mappings = Mapper.contentOf(nonExistingDirectory)
|
||||||
|
assert(mappings.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("it should create an empty mappings sequence if the directory is a file") {
|
||||||
|
withTempDirectory: tempDirectory =>
|
||||||
|
given FileConverter = MappedFileConverter(Map("BASE" -> tempDirectory.toPath()), true)
|
||||||
|
val file = tempDirectory / "file"
|
||||||
|
val mappings = Mapper.contentOf(file)
|
||||||
|
assert(mappings.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("it should create an empty mappings sequence if the directory is a file") {
|
||||||
|
withTempDirectory: tempDirectory =>
|
||||||
|
given FileConverter = MappedFileConverter(Map("BASE" -> tempDirectory.toPath()), true)
|
||||||
|
val file = tempDirectory / "file"
|
||||||
|
val mappings = Mapper.contentOf(file)
|
||||||
|
assert(mappings.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("allSubpaths should not include the base directory") {
|
||||||
|
withTempDirectory: tempDirectory =>
|
||||||
|
given FileConverter = MappedFileConverter(Map("BASE" -> tempDirectory.toPath()), true)
|
||||||
|
val file = Files.createFile((tempDirectory / "file").toPath)
|
||||||
|
val paths = Mapper.allSubpaths(tempDirectory).toVector.map(_._1.toString).toSet
|
||||||
|
assert(paths.contains("${BASE}/file"))
|
||||||
|
assert(!paths.contains("${BASE}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
def withTempDirectory[A1](f: File => A1): A1 =
|
||||||
|
IO.withTemporaryDirectory: tempDirectory0 =>
|
||||||
|
val tempDirectory = tempDirectory0 / "foo"
|
||||||
|
IO.createDirectory(tempDirectory)
|
||||||
|
f(tempDirectory)
|
||||||
|
end MapperTest
|
||||||
|
|
@ -54,7 +54,6 @@ trait Import {
|
||||||
val Hash = sbt.io.Hash
|
val Hash = sbt.io.Hash
|
||||||
val HiddenFileFilter = sbt.io.HiddenFileFilter
|
val HiddenFileFilter = sbt.io.HiddenFileFilter
|
||||||
val IO = sbt.io.IO
|
val IO = sbt.io.IO
|
||||||
type Mapper = sbt.io.Mapper
|
|
||||||
val NameFilter = sbt.io.NameFilter
|
val NameFilter = sbt.io.NameFilter
|
||||||
type NameFilter = sbt.io.NameFilter
|
type NameFilter = sbt.io.NameFilter
|
||||||
val NothingFilter = sbt.io.NothingFilter
|
val NothingFilter = sbt.io.NothingFilter
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
name := "Mappings Test"
|
||||||
|
|
||||||
|
scalaVersion := "3.3.1"
|
||||||
|
version := "0.2"
|
||||||
|
|
||||||
|
Compile / packageBin / mappings ++= {
|
||||||
|
val converter = fileConverter.value
|
||||||
|
Mapper.directory(file("test"))(using converter)
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy val unzipPackage = taskKey[Unit]("extract jar file")
|
||||||
|
unzipPackage := {
|
||||||
|
val converter = fileConverter.value
|
||||||
|
val p = converter.toPath((Compile / packageBin).value)
|
||||||
|
IO.unzip(p.toFile(), target.value / "extracted")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
> unzipPackage
|
||||||
|
$ exists target/out/jvm/scala-3.3.1/mappings-test/extracted/test
|
||||||
Loading…
Reference in New Issue