Skip to content

Commit eb45996

Browse files
committed
merged
1 parent 3208a7c commit eb45996

File tree

3 files changed

+157
-0
lines changed

3 files changed

+157
-0
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package mill.internal
2+
3+
import java.nio.file.Files
4+
5+
object MillPathSerializer {
6+
def setupSymlinks(wd: os.Path, workspace: os.Path): Unit = {
7+
for ((base, link) <- defaultMapping(workspace)) {
8+
os.makeDir.all(wd / link / "..")
9+
os.remove(wd / link)
10+
Files.createSymbolicLink((wd / link).toNIO, base.wrapped)
11+
}
12+
}
13+
14+
def defaultMapping(workspace: os.Path): Seq[(os.Path, os.SubPath)] = Seq(
15+
workspace -> os.sub / "out/mill-workspace",
16+
os.home -> os.sub / "out/mill-home"
17+
)
18+
}
19+
20+
class MillPathSerializer(mapping: Seq[(os.Path, os.SubPath)]) extends os.Path.Serializer {
21+
22+
private def mapPathPrefixes(path: os.Path, mapping: Seq[(os.Path, os.SubPath)]): os.FilePath = {
23+
mapping
24+
.collectFirst { case (from, to) if path.startsWith(from) => to / path.subRelativeTo(from) }
25+
.getOrElse(path)
26+
}
27+
28+
private def mapPathPrefixes(
29+
path: java.nio.file.Path,
30+
mapping: Seq[(os.SubPath, os.Path)]
31+
): java.nio.file.Path = {
32+
mapping
33+
.collectFirst {
34+
case (from, to) if path.startsWith(from.toNIO) || from.segments.isEmpty =>
35+
to.wrapped.resolve(
36+
// java.nio.Path#relativize misbehaves on empty paths
37+
if (from.segments.isEmpty) path else from.toNIO.relativize(path)
38+
)
39+
}
40+
.getOrElse(path)
41+
}
42+
43+
def normalizePath(path: os.Path): os.FilePath = mapPathPrefixes(path, mapping)
44+
45+
def denormalizePath(path: java.nio.file.Path): java.nio.file.Path =
46+
mapPathPrefixes(path, mapping.map(_.swap))
47+
48+
override def serializeString(p: os.Path): String = normalizePath(p) match {
49+
case p: os.SubPath => p.toNIO.toString
50+
case p: os.Path => p.wrapped.toString
51+
case p: os.RelPath => p.toNIO.toString
52+
}
53+
54+
override def serializeFile(p: os.Path): java.io.File = normalizePath(p) match {
55+
case p: os.SubPath => p.toNIO.toFile
56+
case p: os.Path => p.wrapped.toFile
57+
case p: os.RelPath => p.toNIO.toFile
58+
}
59+
60+
override def serializePath(p: os.Path): java.nio.file.Path = normalizePath(p) match {
61+
case p: os.SubPath => p.toNIO
62+
case p: os.Path => p.wrapped
63+
case p: os.RelPath => p.toNIO
64+
}
65+
66+
override def deserialize(s: String): java.nio.file.Path =
67+
denormalizePath(os.Path.defaultPathSerializer.deserialize(s))
68+
69+
override def deserialize(s: java.io.File): java.nio.file.Path =
70+
denormalizePath(os.Path.defaultPathSerializer.deserialize(s))
71+
72+
override def deserialize(s: java.nio.file.Path): java.nio.file.Path =
73+
denormalizePath(os.Path.defaultPathSerializer.deserialize(s))
74+
75+
override def deserialize(s: java.net.URI): java.nio.file.Path =
76+
denormalizePath(os.Path.defaultPathSerializer.deserialize(s))
77+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package build
2+
import mill.*
3+
4+
def foo = Task { 31337 }
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package mill.integration
2+
3+
import mill.api.BuildCtx
4+
import mill.testkit.UtestIntegrationTestSuite
5+
import utest.*
6+
7+
import java.util.zip.GZIPInputStream
8+
9+
object ReproducibilityTests extends UtestIntegrationTestSuite {
10+
11+
def normalize(workspacePath: os.Path): Unit = {
12+
for (p <- os.walk(workspacePath / "out")) {
13+
val sub = p.subRelativeTo(workspacePath).toString()
14+
15+
val cacheable =
16+
(sub.contains(".dest") || sub.contains(".json") || os.isDir(p)) &&
17+
!sub.replace("out/mill-build", "").contains("mill-") &&
18+
!(p.ext == "json" && ujson.read(
19+
os.read(p)
20+
).objOpt.flatMap(_.get("value")).flatMap(_.objOpt).flatMap(_.get("worker")).nonEmpty)
21+
22+
if (!cacheable) {
23+
os.remove.all(p)
24+
}
25+
}
26+
}
27+
28+
val tests: Tests = Tests {
29+
test("diff") - {
30+
def run() = integrationTest { tester =>
31+
val res = tester.eval(("show", "foo"))
32+
val lastNonEmptyLine = res.out.linesIterator.filter(_.nonEmpty).toSeq.lastOption.getOrElse("")
33+
assert(lastNonEmptyLine == "31337")
34+
tester.workspacePath
35+
}
36+
37+
val workspacePath1 = run()
38+
val workspacePath2 = run()
39+
assert(workspacePath1 != workspacePath2)
40+
normalize(workspacePath1)
41+
normalize(workspacePath2)
42+
val diff = os.call(("git", "diff", "--no-index", workspacePath1, workspacePath2)).out.text()
43+
assert(diff.isEmpty)
44+
}
45+
46+
test("inspection") - {
47+
def run() = integrationTest { tester =>
48+
tester.eval(
49+
("--meta-level", "1", "runClasspath"),
50+
env = Map("MILL_TEST_TEXT_ANALYSIS_STORE" -> "1"),
51+
check = true
52+
)
53+
tester.workspacePath
54+
}
55+
56+
val workspacePath = run()
57+
val dest = workspacePath / "out/mill-build/compile.dest/zinc.txt"
58+
val src = workspacePath / "out/mill-build/compile.dest/zinc"
59+
os.write(dest, new GZIPInputStream(os.read.inputStream(src)))
60+
normalize(workspacePath)
61+
for (p <- os.walk(workspacePath)) {
62+
if (
63+
(p.ext == "json" || p.ext == "txt")
64+
&& !p.segments.contains("enablePluginScalacOptions.super")
65+
&& !p.segments.contains("allScalacOptions.json")
66+
&& !p.segments.contains("scalacOptions.json")
67+
&& p.last != "coursierEnv.json"
68+
) {
69+
val txt = os.read(p)
70+
Predef.assert(!txt.contains(BuildCtx.workspaceRoot.toString), p)
71+
Predef.assert(!txt.contains(os.home.toString), p)
72+
}
73+
}
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)