diff --git a/core/api/src/mill/api/JsonFormatters.scala b/core/api/src/mill/api/JsonFormatters.scala
index 2ebb0bdf127a..3caa447db858 100644
--- a/core/api/src/mill/api/JsonFormatters.scala
+++ b/core/api/src/mill/api/JsonFormatters.scala
@@ -23,8 +23,8 @@ trait JsonFormatters {
implicit val pathReadWrite: RW[os.Path] = upickle.readwriter[String]
.bimap[os.Path](
- _.toString,
- os.Path(_)
+ p => MappedRoots.encodeKnownRootsInPath(p),
+ s => os.Path(MappedRoots.decodeKnownRootsInPath(s))
)
implicit val relPathRW: RW[os.RelPath] = upickle.readwriter[String]
diff --git a/core/api/src/mill/api/MappedRoots.scala b/core/api/src/mill/api/MappedRoots.scala
new file mode 100644
index 000000000000..96c23ae89ce3
--- /dev/null
+++ b/core/api/src/mill/api/MappedRoots.scala
@@ -0,0 +1,95 @@
+package mill.api
+
+import mill.api.internal.NamedParameterOnlyDummy
+import mill.constants.PathVars
+
+import scala.annotation.unused
+import scala.util.DynamicVariable
+
+type MappedRoots = Seq[(key: String, path: os.Path)]
+
+object MappedRoots extends MappedRootsImpl
+
+trait MappedRootsImpl {
+
+ private val rootMapping: DynamicVariable[MappedRoots] = DynamicVariable(Seq())
+
+ def get: MappedRoots = rootMapping.value
+
+ def toMap: Map[String, os.Path] = get.map(m => (m.key, m.path)).toMap
+
+ def withMillDefaults[T](
+ @unused t: NamedParameterOnlyDummy = new NamedParameterOnlyDummy,
+ outPath: os.Path,
+ workspacePath: os.Path = BuildCtx.workspaceRoot,
+ homePath: os.Path = os.home
+ )(thunk: => T): T = withMapping(
+ Seq(
+ ("MILL_OUT", outPath),
+ ("WORKSPACE", workspacePath),
+ // TODO: add coursier here
+ ("HOME", homePath)
+ )
+ )(thunk)
+
+ def withMapping[T](mapping: MappedRoots)(thunk: => T): T = withMapping(_ => mapping)(thunk)
+
+ def withMapping[T](mapping: MappedRoots => MappedRoots)(thunk: => T): T = {
+ val newMapping = mapping(rootMapping.value)
+ var seenKeys = Set[String]()
+ var seenPaths = Set[os.Path]()
+ newMapping.foreach { case m =>
+ require(!m.key.startsWith("$"), "Key must not start with a `$`.")
+ require(m.key != PathVars.ROOT, s"Invalid key, '${PathVars.ROOT}' is a reserved key name.")
+ require(
+ !seenKeys.contains(m.key),
+ s"Key must be unique, but '${m.key}' was given multiple times."
+ )
+ require(
+ !seenPaths.contains(m.path),
+ s"Paths must be unique, but '${m.path}' was given multiple times."
+ )
+ seenKeys += m.key
+ seenPaths += m.path
+ }
+ rootMapping.withValue(newMapping)(thunk)
+ }
+
+ def encodeKnownRootsInPath(p: os.Path): String = {
+ MappedRoots.get.collectFirst {
+ case rep if p.startsWith(rep.path) =>
+ s"$$${rep.key}${
+ if (p != rep.path) {
+ s"/${p.subRelativeTo(rep.path).toString()}"
+ } else ""
+ }"
+ }.getOrElse(p.toString)
+ }
+
+ def decodeKnownRootsInPath(encoded: String): String = {
+ if (encoded.startsWith("$")) {
+ val offset = 1 // "$".length
+ MappedRoots.get.collectFirst {
+ case mapping if encoded.startsWith(mapping.key, offset) =>
+ s"${mapping.path.toString}${encoded.substring(mapping.key.length + offset)}"
+ }.getOrElse(encoded)
+ } else {
+ encoded
+ }
+ }
+
+ /**
+ * Use this to assert at runtime, that a root path with the given `key` is defined.
+ * @throws NoSuchElementException when no path was mapped under the given `key`.
+ */
+ def requireMappedPaths(key: String*): Unit = {
+ val map = toMap
+ for {
+ singleKey <- key
+ } {
+ if (!map.contains(singleKey))
+ throw new NoSuchElementException(s"No root path mapping defined for '${key}'")
+ }
+ }
+
+}
diff --git a/core/api/src/mill/api/PathRef.scala b/core/api/src/mill/api/PathRef.scala
index 83bbc3b14cfe..29d72057ea09 100644
--- a/core/api/src/mill/api/PathRef.scala
+++ b/core/api/src/mill/api/PathRef.scala
@@ -2,7 +2,6 @@ package mill.api
import mill.api.DummyOutputStream
import mill.api.daemon.internal.PathRefApi
-import upickle.ReadWriter as RW
import java.nio.file as jnio
import java.security.{DigestOutputStream, MessageDigest}
@@ -10,11 +9,12 @@ import java.util.concurrent.ConcurrentHashMap
import scala.annotation.nowarn
import scala.language.implicitConversions
import scala.util.DynamicVariable
+import scala.util.hashing.MurmurHash3
/**
- * A wrapper around `os.Path` that calculates it's hashcode based
- * on the contents of the filesystem underneath it. Used to ensure filesystem
- * changes can bust caches which are keyed off hashcodes.
+ * A wrapper around `os.Path` that calculates a `sig` (which ends up in the [[hashCode]])
+ * based on the contents of the filesystem underneath it.
+ * Used to ensure filesystem changes can bust caches which are keyed off hashcodes.
*/
case class PathRef private[mill] (
path: os.Path,
@@ -24,6 +24,18 @@ case class PathRef private[mill] (
) extends PathRefApi {
private[mill] def javaPath = path.toNIO
+ /**
+ * The path with common mapped path roots replaced, to make it relocatable.
+ * See [[MappedRoots]].
+ */
+ private val mappedPath: String = MappedRoots.encodeKnownRootsInPath(path)
+
+ /**
+ * Apply the current contextual path mapping to this PathRef.
+ * Updates [[mappedPath]] but does not recalculate the [[sig]].
+ */
+ def remap: PathRef = PathRef(path, quick, sig, revalidate)
+
def recomputeSig(): Int = PathRef.apply(path, quick).sig
def validate(): Boolean = recomputeSig() == sig
@@ -38,16 +50,33 @@ case class PathRef private[mill] (
def withRevalidate(revalidate: PathRef.Revalidate): PathRef = copy(revalidate = revalidate)
def withRevalidateOnce: PathRef = copy(revalidate = PathRef.Revalidate.Once)
- override def toString: String = {
+ private def toStringPrefix: String = {
val quick = if (this.quick) "qref:" else "ref:"
+
val valid = revalidate match {
case PathRef.Revalidate.Never => "v0:"
case PathRef.Revalidate.Once => "v1:"
case PathRef.Revalidate.Always => "vn:"
}
val sig = String.format("%08x", this.sig: Integer)
- quick + valid + sig + ":" + path.toString()
+ s"${quick}${valid}${sig}:"
+ }
+
+ override def toString: String = {
+ toStringPrefix + path.toString()
}
+
+ // Instead of using `path` we need to use `mappedPath` to make the hashcode stable as cache key
+ override def hashCode(): Int = {
+ var h = MurmurHash3.productSeed
+ h = MurmurHash3.mix(h, "PathRef".hashCode)
+ h = MurmurHash3.mix(h, mappedPath.hashCode)
+ h = MurmurHash3.mix(h, quick.##)
+ h = MurmurHash3.mix(h, sig.##)
+ h = MurmurHash3.mix(h, revalidate.##)
+ MurmurHash3.finalizeHash(h, 4)
+ }
+
}
object PathRef {
@@ -192,37 +221,38 @@ object PathRef {
/**
* Default JSON formatter for [[PathRef]].
*/
- implicit def jsonFormatter: RW[PathRef] = upickle.readwriter[String].bimap[PathRef](
- p => {
- storeSerializedPaths(p)
- p.toString()
- },
- {
- case s"$prefix:$valid0:$hex:$pathString" if prefix == "ref" || prefix == "qref" =>
-
- val path = os.Path(pathString)
- val quick = prefix match {
- case "qref" => true
- case "ref" => false
- }
- val validOrig = valid0 match {
- case "v0" => Revalidate.Never
- case "v1" => Revalidate.Once
- case "vn" => Revalidate.Always
- }
- // Parsing to a long and casting to an int is the only way to make
- // round-trip handling of negative numbers work =(
- val sig = java.lang.Long.parseLong(hex, 16).toInt
- val pr = PathRef(path, quick, sig, revalidate = validOrig)
- validatedPaths.value.revalidateIfNeededOrThrow(pr)
- storeSerializedPaths(pr)
- pr
- case s =>
- mill.api.BuildCtx.withFilesystemCheckerDisabled(
- PathRef(os.Path(s, currentOverrideModulePath.value))
- )
- }
- )
+ implicit def jsonFormatter: upickle.ReadWriter[PathRef] =
+ upickle.readwriter[String].bimap[PathRef](
+ p => {
+ storeSerializedPaths(p)
+ p.toStringPrefix + MappedRoots.encodeKnownRootsInPath(p.path)
+ },
+ {
+ case s"$prefix:$valid0:$hex:$pathVal" if prefix == "ref" || prefix == "qref" =>
+
+ val path = os.Path(MappedRoots.decodeKnownRootsInPath(pathVal))
+ val quick = prefix match {
+ case "qref" => true
+ case "ref" => false
+ }
+ val validOrig = valid0 match {
+ case "v0" => Revalidate.Never
+ case "v1" => Revalidate.Once
+ case "vn" => Revalidate.Always
+ }
+ // Parsing to a long and casting to an int is the only way to make
+ // round-trip handling of negative numbers work =(
+ val sig = java.lang.Long.parseLong(hex, 16).toInt
+ val pr = PathRef(path, quick, sig, revalidate = validOrig)
+ validatedPaths.value.revalidateIfNeededOrThrow(pr)
+ storeSerializedPaths(pr)
+ pr
+ case s =>
+ mill.api.BuildCtx.withFilesystemCheckerDisabled(
+ PathRef(os.Path(MappedRoots.decodeKnownRootsInPath(s), currentOverrideModulePath.value))
+ )
+ }
+ )
private[mill] val currentOverrideModulePath = DynamicVariable[os.Path](null)
// scalafix:off; we want to hide the unapply method
diff --git a/core/api/test/src/mill/api/MappedRootsTests.scala b/core/api/test/src/mill/api/MappedRootsTests.scala
new file mode 100644
index 000000000000..63ac9a391fff
--- /dev/null
+++ b/core/api/test/src/mill/api/MappedRootsTests.scala
@@ -0,0 +1,48 @@
+package mill.api
+
+import utest.*
+
+import java.nio.file.Files
+import mill.api.{MappedRoots => MR}
+
+object MappedRootsTests extends TestSuite {
+ val tests: Tests = Tests {
+ test("encode") {
+ withTmpDir { tmpDir =>
+ val workspaceDir = tmpDir / "workspace"
+ val outDir = workspaceDir / "out"
+ MR.withMillDefaults(outPath = outDir, workspacePath = workspaceDir) {
+
+ def check(path: os.Path, encContains: Seq[String], containsNot: Seq[String]) = {
+ val enc = MR.encodeKnownRootsInPath(path)
+ val dec = MR.decodeKnownRootsInPath(enc)
+ assert(path.toString == dec)
+ encContains.foreach(s => assert(enc.containsSlice(s)))
+ containsNot.foreach(s => assert(!enc.containsSlice(s)))
+
+ path -> enc
+ }
+
+ val file1 = tmpDir / "file1"
+ val file2 = workspaceDir / "file2"
+ val file3 = outDir / "file3"
+
+ Seq(
+ "mapping" -> MR.get,
+ check(file1, Seq(file1.toString), Seq("$WORKSPACE", "$MILL_OUT")),
+ check(file2, Seq("$WORKSPACE/file2"), Seq("$MILL_OUT")),
+ check(file3, Seq("$MILL_OUT/file3"), Seq("$WORKSPACE"))
+ )
+ }
+ }
+ }
+ }
+
+ private def withTmpDir[T](body: os.Path => T): T = {
+ val tmpDir = os.Path(Files.createTempDirectory(""))
+ val res = body(tmpDir)
+ os.remove.all(tmpDir)
+ res
+ }
+
+}
diff --git a/core/api/test/src/mill/api/PathRefTests.scala b/core/api/test/src/mill/api/PathRefTests.scala
index dd8717a6949d..5c4f9790cd8c 100644
--- a/core/api/test/src/mill/api/PathRefTests.scala
+++ b/core/api/test/src/mill/api/PathRefTests.scala
@@ -19,6 +19,7 @@ object PathRefTests extends TestSuite {
val sig2 = PathRef(file, quick).sig
assert(sig1 != sig2)
}
+
test("qref") - check(quick = true)
test("ref") - check(quick = false)
}
@@ -43,12 +44,18 @@ object PathRefTests extends TestSuite {
val file = tmpDir / "foo.txt"
val content = "hello"
os.write.over(file, content)
- Files.setPosixFilePermissions(file.wrapped, PosixFilePermissions.fromString("rw-rw----"))
+ Files.setPosixFilePermissions(
+ file.wrapped,
+ PosixFilePermissions.fromString("rw-rw----")
+ )
val rwSig = PathRef(file, quick).sig
val rwSigb = PathRef(file, quick).sig
assert(rwSig == rwSigb)
- Files.setPosixFilePermissions(file.wrapped, PosixFilePermissions.fromString("rwxrw----"))
+ Files.setPosixFilePermissions(
+ file.wrapped,
+ PosixFilePermissions.fromString("rwxrw----")
+ )
val rwxSig = PathRef(file, quick).sig
assert(rwSig != rwxSig)
@@ -76,6 +83,7 @@ object PathRefTests extends TestSuite {
val sig2 = PathRef(tmpDir, quick).sig
assert(sig1 == sig2)
}
+
test("qref") - check(quick = true)
test("ref") - check(quick = false)
}
diff --git a/core/constants/src/mill/constants/PathVars.java b/core/constants/src/mill/constants/PathVars.java
new file mode 100644
index 000000000000..574f5c61c5a1
--- /dev/null
+++ b/core/constants/src/mill/constants/PathVars.java
@@ -0,0 +1,22 @@
+package mill.constants;
+
+/**
+ * Central place containing all the path variables that Mill uses in PathRef or os.Path.
+ */
+public interface PathVars {
+
+ /**
+ * Output directory where Mill workers' state and Mill tasks output should be
+ * written to
+ */
+ String MILL_OUT = "MILL_OUT";
+
+ /**
+ * The Mill project workspace root directory.
+ */
+ String WORKSPACE = "WORKSPACE";
+
+ String HOME = "HOME";
+
+ String ROOT = "ROOT";
+}
diff --git a/core/exec/src/mill/exec/Execution.scala b/core/exec/src/mill/exec/Execution.scala
index 5caa217abb4d..40219e333df4 100644
--- a/core/exec/src/mill/exec/Execution.scala
+++ b/core/exec/src/mill/exec/Execution.scala
@@ -54,24 +54,24 @@ private[mill] case class Execution(
offline: Boolean,
enableTicker: Boolean
) = this(
- baseLogger,
- new JsonArrayLogger.Profile(os.Path(outPath) / millProfile),
- os.Path(workspace),
- os.Path(outPath),
- os.Path(externalOutPath),
- rootModule,
- classLoaderSigHash,
- classLoaderIdentityHash,
- workerCache,
- env,
- failFast,
- ec,
- codeSignatures,
- systemExit,
- exclusiveSystemStreams,
- getEvaluator,
- offline,
- enableTicker
+ baseLogger = baseLogger,
+ profileLogger = new JsonArrayLogger.Profile(os.Path(outPath) / millProfile),
+ workspace = os.Path(workspace),
+ outPath = os.Path(outPath),
+ externalOutPath = os.Path(externalOutPath),
+ rootModule = rootModule,
+ classLoaderSigHash = classLoaderSigHash,
+ classLoaderIdentityHash = classLoaderIdentityHash,
+ workerCache = workerCache,
+ env = env,
+ failFast = failFast,
+ ec = ec,
+ codeSignatures = codeSignatures,
+ systemExit = systemExit,
+ exclusiveSystemStreams = exclusiveSystemStreams,
+ getEvaluator = getEvaluator,
+ offline = offline,
+ enableTicker = enableTicker
)
def withBaseLogger(newBaseLogger: Logger) = this.copy(baseLogger = newBaseLogger)
diff --git a/core/exec/src/mill/exec/GroupExecution.scala b/core/exec/src/mill/exec/GroupExecution.scala
index 2ffe980ff0a8..93c45df2b3ab 100644
--- a/core/exec/src/mill/exec/GroupExecution.scala
+++ b/core/exec/src/mill/exec/GroupExecution.scala
@@ -88,7 +88,7 @@ trait GroupExecution {
executionContext: mill.api.TaskCtx.Fork.Api,
exclusive: Boolean,
upstreamPathRefs: Seq[PathRef]
- ): GroupExecution.Results = {
+ ): GroupExecution.Results = MappedRoots.withMillDefaults(outPath = outPath) {
val inputsHash = {
val externalInputsHash = MurmurHash3.orderedHash(
@@ -244,8 +244,8 @@ trait GroupExecution {
newEvaluated = newEvaluated.toSeq,
cached = if (labelled.isInstanceOf[Task.Input[?]]) null else false,
inputsHash = inputsHash,
- previousInputsHash = cached.map(_._1).getOrElse(-1),
- valueHashChanged = !cached.map(_._3).contains(valueHash),
+ previousInputsHash = cached.map(_.inputsHash).getOrElse(-1),
+ valueHashChanged = !cached.map(_.valueHash).contains(valueHash),
serializedPaths = serializedPaths
)
}
@@ -455,7 +455,11 @@ trait GroupExecution {
inputsHash: Int,
labelled: Task.Named[?],
paths: ExecutionPaths
- ): Option[(Int, Option[(Val, Seq[PathRef])], Int)] = {
+ ): Option[(
+ inputsHash: Int,
+ valOpt: Option[(Val, Seq[PathRef])],
+ valueHash: Int
+ )] = {
for {
cached <-
try Some(upickle.read[Cached](paths.meta.toIO, trace = false))
@@ -463,8 +467,8 @@ trait GroupExecution {
case NonFatal(_) => None
}
} yield (
- cached.inputsHash,
- for {
+ inputsHash = cached.inputsHash,
+ valOpt = for {
_ <- Option.when(cached.inputsHash == inputsHash)(())
reader <- labelled.readWriterOpt
(parsed, serializedPaths) <-
@@ -480,7 +484,7 @@ trait GroupExecution {
case NonFatal(_) => None
}
} yield (Val(parsed), serializedPaths),
- cached.valueHash
+ valueHash = cached.valueHash
)
}
@@ -620,7 +624,7 @@ object GroupExecution {
classLoader: ClassLoader
)(t: => T): T = {
// Tasks must be allowed to write to upstream worker's dest folders, because
- // the point of workers is to manualy manage long-lived state which includes
+ // the point of workers is to manually manage long-lived state which includes
// state on disk.
val validWriteDests =
deps.collect { case n: Task.Worker[?] =>
diff --git a/example/androidlib/java/1-hello-world/build.mill b/example/androidlib/java/1-hello-world/build.mill
index 3d6a9be7d269..4edd80e4d573 100644
--- a/example/androidlib/java/1-hello-world/build.mill
+++ b/example/androidlib/java/1-hello-world/build.mill
@@ -64,7 +64,7 @@ object app extends AndroidAppModule {
/** Usage
> ./mill show app.androidApk
-".../out/app/androidApk.dest/app.apk"
+"...$MILL_OUT/app/androidApk.dest/app.apk"
*/
diff --git a/example/androidlib/java/2-app-bundle/build.mill b/example/androidlib/java/2-app-bundle/build.mill
index f05d58782d5d..007a41475265 100644
--- a/example/androidlib/java/2-app-bundle/build.mill
+++ b/example/androidlib/java/2-app-bundle/build.mill
@@ -37,7 +37,7 @@ object bundle extends AndroidAppBundle {
/** Usage
> ./mill show bundle.androidBundle
-".../out/bundle/androidBundle.dest/signedBundle.aab"
+"...$MILL_OUT/bundle/androidBundle.dest/signedBundle.aab"
*/
diff --git a/example/androidlib/java/4-sum-lib-java/build.mill b/example/androidlib/java/4-sum-lib-java/build.mill
index 20ef34f00105..97dfd203dc93 100644
--- a/example/androidlib/java/4-sum-lib-java/build.mill
+++ b/example/androidlib/java/4-sum-lib-java/build.mill
@@ -125,7 +125,7 @@ Publish ... to /home/.../.m2/repository/...
},
"payload": [
[
- ".../out/lib/androidAar.dest/library.aar",
+ "...$MILL_OUT/lib/androidAar.dest/library.aar",
"lib-0.0.1.aar"
],
...
diff --git a/example/androidlib/java/6-native-libs/build.mill b/example/androidlib/java/6-native-libs/build.mill
index 3ffb464d1258..bb29666daab1 100644
--- a/example/androidlib/java/6-native-libs/build.mill
+++ b/example/androidlib/java/6-native-libs/build.mill
@@ -66,7 +66,7 @@ object app extends AndroidNativeAppModule { // <1>
/** Usage
> ./mill show app.androidApk
-".../out/app/androidApk.dest/app.apk"
+"...$MILL_OUT/app/androidApk.dest/app.apk"
> ./mill show app.createAndroidVirtualDevice
...Name: java-test, DeviceId: medium_phone...
diff --git a/example/androidlib/kotlin/1-hello-kotlin/build.mill b/example/androidlib/kotlin/1-hello-kotlin/build.mill
index eccccd80c698..9d639729af04 100644
--- a/example/androidlib/kotlin/1-hello-kotlin/build.mill
+++ b/example/androidlib/kotlin/1-hello-kotlin/build.mill
@@ -72,7 +72,7 @@ object app extends AndroidAppKotlinModule {
/** Usage
> ./mill show app.androidApk
-".../out/app/androidApk.dest/app.apk"
+"...$MILL_OUT/app/androidApk.dest/app.apk"
*/
diff --git a/example/androidlib/kotlin/2-compose/build.mill b/example/androidlib/kotlin/2-compose/build.mill
index 64fb8212e99e..4d3e9b9b8326 100644
--- a/example/androidlib/kotlin/2-compose/build.mill
+++ b/example/androidlib/kotlin/2-compose/build.mill
@@ -60,7 +60,7 @@ object app extends AndroidAppKotlinModule {
/** Usage
> ./mill show app.androidApk
-".../out/app/androidApk.dest/app.apk"
+"...$MILL_OUT/app/androidApk.dest/app.apk"
> ./mill show app.createAndroidVirtualDevice
diff --git a/example/androidlib/kotlin/4-sum-lib-kotlin/build.mill b/example/androidlib/kotlin/4-sum-lib-kotlin/build.mill
index f38252c4c6ca..bc88718f9d46 100644
--- a/example/androidlib/kotlin/4-sum-lib-kotlin/build.mill
+++ b/example/androidlib/kotlin/4-sum-lib-kotlin/build.mill
@@ -129,7 +129,7 @@ Publish ... to /home/.../.m2/repository/...
},
"payload": [
[
- ".../out/lib/androidAar.dest/library.aar",
+ "...$MILL_OUT/lib/androidAar.dest/library.aar",
"lib-0.0.1.aar"
],
...
diff --git a/example/cli/builtins/1-builtin-commands/build.mill b/example/cli/builtins/1-builtin-commands/build.mill
index 4ac5ba3b98a4..e33ca895b722 100644
--- a/example/cli/builtins/1-builtin-commands/build.mill
+++ b/example/cli/builtins/1-builtin-commands/build.mill
@@ -146,8 +146,8 @@ Inputs:
> ./mill show foo.compile
{
- "analysisFile": ".../out/foo/compile.dest/...",
- "classes": ".../out/foo/compile.dest/classes"
+ "analysisFile": "...$MILL_OUT/foo/compile.dest/...",
+ "classes": "...$MILL_OUT/foo/compile.dest/classes"
}
*/
diff --git a/example/depth/sandbox/2-test/build.mill b/example/depth/sandbox/2-test/build.mill
index 70f409653e32..3288e086f056 100644
--- a/example/depth/sandbox/2-test/build.mill
+++ b/example/depth/sandbox/2-test/build.mill
@@ -47,8 +47,8 @@ object bar extends MyModule
/** Usage
> find . | grep generated.html
-.../out/foo/test/testForked.dest/sandbox/generated.html
-.../out/bar/test/testForked.dest/sandbox/generated.html
+./out/foo/test/testForked.dest/sandbox/generated.html
+./out/bar/test/testForked.dest/sandbox/generated.html
> cat out/foo/test/testForked.dest/sandbox/generated.html
hello
@@ -81,7 +81,7 @@ object qux extends JavaModule {
> find . | grep .html
...
-.../out/qux/test/testForked.dest/sandbox/foo.html
+./out/qux/test/testForked.dest/sandbox/foo.html
> cat out/qux/test/testForked.dest/sandbox/foo.html
foo
diff --git a/example/extending/imports/1-mvn-deps/build.mill b/example/extending/imports/1-mvn-deps/build.mill
index fda9104f287d..b47f2011ff0c 100644
--- a/example/extending/imports/1-mvn-deps/build.mill
+++ b/example/extending/imports/1-mvn-deps/build.mill
@@ -44,7 +44,7 @@ compiling 1 Java source...
generated snippet.txt resource:
> ./mill show foo.assembly
-".../out/foo/assembly.dest/out.jar"
+"...$MILL_OUT/foo/assembly.dest/out.jar"
> ./out/foo/assembly.dest/out.jar # mac/linux
generated snippet.txt resource:
diff --git a/example/extending/imports/2-mvn-deps-scala/build.mill b/example/extending/imports/2-mvn-deps-scala/build.mill
index da924f68e671..96352e65d657 100644
--- a/example/extending/imports/2-mvn-deps-scala/build.mill
+++ b/example/extending/imports/2-mvn-deps-scala/build.mill
@@ -34,7 +34,7 @@ compiling 1 Scala source...
generated snippet.txt resource:
> ./mill show bar.assembly
-".../out/bar/assembly.dest/out.jar"
+"...$MILL_OUT/bar/assembly.dest/out.jar"
> ./out/bar/assembly.dest/out.jar # mac/linux
generated snippet.txt resource:
diff --git a/example/extending/metabuild/4-meta-build/build.mill b/example/extending/metabuild/4-meta-build/build.mill
index b8b0a56cfd43..4beaa6e39c7e 100644
--- a/example/extending/metabuild/4-meta-build/build.mill
+++ b/example/extending/metabuild/4-meta-build/build.mill
@@ -48,7 +48,7 @@ Build-time HTML snippet: hello
Run-time HTML snippet: world
> ./mill show assembly
-".../out/assembly.dest/out.jar"
+"...$MILL_OUT/assembly.dest/out.jar"
> ./out/assembly.dest/out.jar # mac/linux
Build-time HTML snippet: hello
diff --git a/example/extending/python/4-python-libs-bundle/build.mill b/example/extending/python/4-python-libs-bundle/build.mill
index b1da6f0571a3..cfb86a1cf5b1 100644
--- a/example/extending/python/4-python-libs-bundle/build.mill
+++ b/example/extending/python/4-python-libs-bundle/build.mill
@@ -109,7 +109,7 @@ object qux extends PythonModule {
Numpy : Sum: 150 | Pandas: Mean: 30.0, Max: 50
> ./mill show qux.bundle
-".../out/qux/bundle.dest/bundle.pex"
+"...$MILL_OUT/qux/bundle.dest/bundle.pex"
> out/qux/bundle.dest/bundle.pex # running the PEX binary outside of Mill
Numpy : Sum: 150 | Pandas: Mean: 30.0, Max: 50
diff --git a/example/extending/typescript/4-npm-deps-bundle/build.mill b/example/extending/typescript/4-npm-deps-bundle/build.mill
index 006a4749c8a0..d44808e68ffb 100644
--- a/example/extending/typescript/4-npm-deps-bundle/build.mill
+++ b/example/extending/typescript/4-npm-deps-bundle/build.mill
@@ -117,7 +117,7 @@ object qux extends TypeScriptModule {
Hello James Bond Professor
> ./mill show qux.bundle
-".../out/qux/bundle.dest/bundle.js"
+"...$MILL_OUT/qux/bundle.dest/bundle.js"
> node out/qux/bundle.dest/bundle.js James Bond prof
Hello James Bond Professor
diff --git a/example/fundamentals/cross/10-static-blog/build.mill b/example/fundamentals/cross/10-static-blog/build.mill
index 0cb186843ed6..77d53d2886cd 100644
--- a/example/fundamentals/cross/10-static-blog/build.mill
+++ b/example/fundamentals/cross/10-static-blog/build.mill
@@ -113,7 +113,7 @@ def dist = Task {
/** Usage
> ./mill show "post[1-My-First-Post.md].render"
-".../out/post/1-My-First-Post.md/render.dest/1-my-first-post.html"
+"...$MILL_OUT/post/1-My-First-Post.md/render.dest/1-my-first-post.html"
> cat out/post/1-My-First-Post.md/render.dest/1-my-first-post.html
...
diff --git a/example/fundamentals/libraries/2-upickle/build.mill b/example/fundamentals/libraries/2-upickle/build.mill
index fcac91f6de98..1a0dc0aa6a92 100644
--- a/example/fundamentals/libraries/2-upickle/build.mill
+++ b/example/fundamentals/libraries/2-upickle/build.mill
@@ -74,7 +74,7 @@ def taskPath = Task {
/** Usage
> ./mill show taskPath
-".../out/taskPath.dest/file.txt"
+"...$MILL_OUT/taskPath.dest/file.txt"
*/
@@ -92,7 +92,7 @@ def taskPathRef = Task {
/** Usage
> ./mill show taskPathRef
-"ref.../out/taskPathRef.dest/file.txt"
+"ref...$MILL_OUT/taskPathRef.dest/file.txt"
*/
diff --git a/example/fundamentals/modules/8-diy-java-modules/build.mill b/example/fundamentals/modules/8-diy-java-modules/build.mill
index 4b6a508fd561..524bf846a5c4 100644
--- a/example/fundamentals/modules/8-diy-java-modules/build.mill
+++ b/example/fundamentals/modules/8-diy-java-modules/build.mill
@@ -150,7 +150,7 @@ object qux extends DiyJavaModule {
}
> ./mill show qux.assembly
-".../out/qux/assembly.dest/assembly.jar"
+"...$MILL_OUT/qux/assembly.dest/assembly.jar"
> java -jar out/qux/assembly.dest/assembly.jar
Foo.value: 31337
@@ -158,7 +158,7 @@ Bar.value: 271828
Qux.value: 9000
> ./mill show foo.assembly
-".../out/foo/assembly.dest/assembly.jar"
+"...$MILL_OUT/foo/assembly.dest/assembly.jar"
> java -jar out/foo/assembly.dest/assembly.jar
Foo.value: 31337
diff --git a/example/fundamentals/tasks/1-task-graph/build.mill b/example/fundamentals/tasks/1-task-graph/build.mill
index a512437f7652..1c855464b708 100644
--- a/example/fundamentals/tasks/1-task-graph/build.mill
+++ b/example/fundamentals/tasks/1-task-graph/build.mill
@@ -55,7 +55,7 @@ def run(args: String*) = Task.Command {
/** Usage
> ./mill show assembly
-".../out/assembly.dest/assembly.jar"
+"...$MILL_OUT/assembly.dest/assembly.jar"
> java -jar out/assembly.dest/assembly.jar i am cow
Foo.value: 31337
diff --git a/example/fundamentals/tasks/2-primary-tasks/build.mill b/example/fundamentals/tasks/2-primary-tasks/build.mill
index 570dd57b59ce..115a64d1657f 100644
--- a/example/fundamentals/tasks/2-primary-tasks/build.mill
+++ b/example/fundamentals/tasks/2-primary-tasks/build.mill
@@ -167,7 +167,7 @@ Generating classfiles
Generating jar
> ./mill show jar
-".../out/jar.dest/foo.jar"
+"...$MILL_OUT/jar.dest/foo.jar"
*/
diff --git a/example/javalib/basic/1-script/build.mill b/example/javalib/basic/1-script/build.mill
index 9f7c5dadb44d..9ca3528a3096 100644
--- a/example/javalib/basic/1-script/build.mill
+++ b/example/javalib/basic/1-script/build.mill
@@ -18,7 +18,7 @@ compiling 1 Java source to...
/** Usage
> ./mill show Foo.java:assembly # show the output of the assembly task
-".../out/Foo.java/assembly.dest/out.jar"
+"...$MILL_OUT/Foo.java/assembly.dest/out.jar"
> java -jar ./out/Foo.java/assembly.dest/out.jar --text hello
hello
diff --git a/example/javalib/basic/10-realistic/build.mill b/example/javalib/basic/10-realistic/build.mill
index f23038a5b082..5a10ccda2820 100644
--- a/example/javalib/basic/10-realistic/build.mill
+++ b/example/javalib/basic/10-realistic/build.mill
@@ -101,7 +101,7 @@ Publishing Artifact(com.lihaoyi,qux,0.0.1) to ivy repo...
...
> ./mill show foo.assembly # mac/linux
-".../out/foo/assembly.dest/out.jar"
+"...$MILL_OUT/foo/assembly.dest/out.jar"
> ./out/foo/assembly.dest/out.jar # mac/linux
foo version 0.0.1
diff --git a/example/javalib/publishing/5-jlink/build.mill b/example/javalib/publishing/5-jlink/build.mill
index d9031d13d04f..f44598072418 100644
--- a/example/javalib/publishing/5-jlink/build.mill
+++ b/example/javalib/publishing/5-jlink/build.mill
@@ -47,7 +47,7 @@ object foo extends JavaModule, JlinkModule {
> ./mill foo.jlinkAppImage
> ./mill show foo.jlinkAppImage
-".../out/foo/jlinkAppImage.dest/jlink-runtime"
+"...$MILL_OUT/foo/jlinkAppImage.dest/jlink-runtime"
> ./out/foo/jlinkAppImage.dest/jlink-runtime/bin/jlink
... foo.Bar main
diff --git a/example/javalib/publishing/6-jpackage/build.mill b/example/javalib/publishing/6-jpackage/build.mill
index 150c9f8be48a..db1d7535fb3a 100644
--- a/example/javalib/publishing/6-jpackage/build.mill
+++ b/example/javalib/publishing/6-jpackage/build.mill
@@ -50,7 +50,7 @@ object foo extends JavaModule, JpackageModule {
> ./mill foo.jpackageAppImage
> ./mill show foo.jpackageAppImage
-".../out/foo/jpackageAppImage.dest/image"
+"...$MILL_OUT/foo/jpackageAppImage.dest/image"
*/
//
diff --git a/example/javalib/publishing/9-repackage-config/build.mill b/example/javalib/publishing/9-repackage-config/build.mill
index 26d2b4706319..68b5b61fab2a 100644
--- a/example/javalib/publishing/9-repackage-config/build.mill
+++ b/example/javalib/publishing/9-repackage-config/build.mill
@@ -45,7 +45,7 @@ Qux.value: 31337
...Test run bar.BarTests finished: 0 failed, 0 ignored, 1 total, ...s
> ./mill show foo.repackagedJar
-".../out/foo/repackagedJar.dest/out.jar"
+"...$MILL_OUT/foo/repackagedJar.dest/out.jar"
> ./out/foo/repackagedJar.dest/out.jar
Foo.value: hello
diff --git a/example/javalib/script/10-resources/build.mill b/example/javalib/script/10-resources/build.mill
index e511c0b7ae96..a197381daf17 100644
--- a/example/javalib/script/10-resources/build.mill
+++ b/example/javalib/script/10-resources/build.mill
@@ -13,7 +13,7 @@ Hello World Resource File
/** Usage
> ./mill show Foo.java:assembly
-".../out/Foo.java/assembly.dest/out.jar"
+"...$MILL_OUT/Foo.java/assembly.dest/out.jar"
> out/Foo.java/assembly.dest/out.jar
Hello World Resource File
diff --git a/example/kotlinlib/basic/1-script/build.mill b/example/kotlinlib/basic/1-script/build.mill
index 679cd5d33ce1..b76ba7996a1b 100644
--- a/example/kotlinlib/basic/1-script/build.mill
+++ b/example/kotlinlib/basic/1-script/build.mill
@@ -18,7 +18,7 @@ Compiling 1 Kotlin sources to...
/** Usage
> ./mill show Foo.kt:assembly # show the output of the assembly task
-".../out/Foo.kt/assembly.dest/out.jar"
+"...$MILL_OUT/Foo.kt/assembly.dest/out.jar"
> java -jar ./out/Foo.kt/assembly.dest/out.jar --text hello
hello
diff --git a/example/kotlinlib/basic/10-realistic/build.mill b/example/kotlinlib/basic/10-realistic/build.mill
index 250c18f6f16c..89ecc57ee291 100644
--- a/example/kotlinlib/basic/10-realistic/build.mill
+++ b/example/kotlinlib/basic/10-realistic/build.mill
@@ -109,7 +109,7 @@ Publishing Artifact(com.lihaoyi,qux,0.0.1) to ivy repo...
...
> ./mill show foo.assembly # mac/linux
-".../out/foo/assembly.dest/out.jar"
+"...$MILL_OUT/foo/assembly.dest/out.jar"
> ./out/foo/assembly.dest/out.jar # mac/linux
foo version 0.0.1
diff --git a/example/kotlinlib/linting/4-kover/build.mill b/example/kotlinlib/linting/4-kover/build.mill
index 1006a229c9f1..296aa141c7c0 100644
--- a/example/kotlinlib/linting/4-kover/build.mill
+++ b/example/kotlinlib/linting/4-kover/build.mill
@@ -57,7 +57,7 @@ kover.xmlReport
> ./mill show kover.htmlReport
...
-...out/kover/htmlReport.dest/kover-report...
+...$MILL_OUT/kover/htmlReport.dest/kover-report...
> cat out/kover/htmlReport.dest/kover-report/index.html
...
@@ -65,7 +65,7 @@ kover.xmlReport
> ./mill show mill.kotlinlib.kover/htmlReportAll # collect reports from all modules
...
-...out/mill.kotlinlib.kover.Kover/htmlReportAll.dest/kover-report...
+...$MILL_OUT/mill.kotlinlib.kover.Kover/htmlReportAll.dest/kover-report...
> cat out/mill.kotlinlib.kover.Kover/htmlReportAll.dest/kover-report/index.html
...
diff --git a/example/kotlinlib/module/10-dependency-injection/build.mill b/example/kotlinlib/module/10-dependency-injection/build.mill
index 788946dcf6b4..a3a0b2af6aa9 100644
--- a/example/kotlinlib/module/10-dependency-injection/build.mill
+++ b/example/kotlinlib/module/10-dependency-injection/build.mill
@@ -47,8 +47,8 @@ object dagger extends KspModule {
> ./mill show dagger.generatedSources
[
- "ref:v0:.../out/dagger/generatedSourcesWithKsp2.dest/generated/java",
- "ref:v0:.../out/dagger/generatedSourcesWithKsp2.dest/generated/kotlin"
+ "ref:v0:...$MILL_OUT/dagger/generatedSourcesWithKsp2.dest/generated/java",
+ "ref:v0:...$MILL_OUT/dagger/generatedSourcesWithKsp2.dest/generated/kotlin"
]
> ls out/dagger/generatedSourcesWithKsp2.dest/generated/java/com/example/dagger/
diff --git a/example/kotlinlib/publishing/9-repackage-config/build.mill b/example/kotlinlib/publishing/9-repackage-config/build.mill
index 83a97c99ff14..96627654f8d8 100644
--- a/example/kotlinlib/publishing/9-repackage-config/build.mill
+++ b/example/kotlinlib/publishing/9-repackage-config/build.mill
@@ -47,7 +47,7 @@ Qux.value: 31337
...Test run bar.BarTests finished: 0 failed, 0 ignored, 1 total, ...s
> ./mill show foo.repackagedJar
-".../out/foo/repackagedJar.dest/out.jar"
+"...$MILL_OUT/foo/repackagedJar.dest/out.jar"
> ./out/foo/repackagedJar.dest/out.jar
Foo.value: hello
diff --git a/example/kotlinlib/script/10-resources/build.mill b/example/kotlinlib/script/10-resources/build.mill
index 6985db0a0b9b..e5104f1f1955 100644
--- a/example/kotlinlib/script/10-resources/build.mill
+++ b/example/kotlinlib/script/10-resources/build.mill
@@ -13,7 +13,7 @@ Hello World Resource File
/** Usage
> ./mill show Foo.kt:assembly
-".../out/Foo.kt/assembly.dest/out.jar"
+"...$MILL_OUT/Foo.kt/assembly.dest/out.jar"
> out/Foo.kt/assembly.dest/out.jar
Hello World Resource File
diff --git a/example/pythonlib/basic/1-simple/build.mill b/example/pythonlib/basic/1-simple/build.mill
index 21ee94f61468..0982f38afd7a 100644
--- a/example/pythonlib/basic/1-simple/build.mill
+++ b/example/pythonlib/basic/1-simple/build.mill
@@ -87,7 +87,7 @@ OK
...
> ./mill show foo.bundle # Creates Bundle for the python file
-".../out/foo/bundle.dest/bundle.pex"
+"...$MILL_OUT/foo/bundle.dest/bundle.pex"
> out/foo/bundle.dest/bundle.pex --text "Hello Mill" # running the PEX binary outside of Mill
Hello Mill
diff --git a/example/pythonlib/module/1-common-config/build.mill b/example/pythonlib/module/1-common-config/build.mill
index a399d898f067..95bf44f45294 100644
--- a/example/pythonlib/module/1-common-config/build.mill
+++ b/example/pythonlib/module/1-common-config/build.mill
@@ -75,7 +75,7 @@ MY_CUSTOM_ENV: my-env-value
...
> ./mill show foo.bundle
-".../out/foo/bundle.dest/bundle.pex"
+"...$MILL_OUT/foo/bundle.dest/bundle.pex"
> out/foo/bundle.dest/bundle.pex
...
diff --git a/example/pythonlib/module/6-pex-config/build.mill b/example/pythonlib/module/6-pex-config/build.mill
index ef6454532025..f27112c56e7a 100644
--- a/example/pythonlib/module/6-pex-config/build.mill
+++ b/example/pythonlib/module/6-pex-config/build.mill
@@ -44,7 +44,7 @@ object foo extends PythonModule {
/** Usage
> ./mill show foo.bundle
-".../out/foo/bundle.dest/bundle.pex"
+"...$MILL_OUT/foo/bundle.dest/bundle.pex"
> out/foo/bundle.dest/bundle.pex
...
diff --git a/example/pythonlib/publishing/1-publish-module/build.mill b/example/pythonlib/publishing/1-publish-module/build.mill
index 75f91333158e..b6df9a67e92b 100644
--- a/example/pythonlib/publishing/1-publish-module/build.mill
+++ b/example/pythonlib/publishing/1-publish-module/build.mill
@@ -44,10 +44,10 @@ object `package` extends PythonModule, PublishModule {
/** Usage
> ./mill show sdist
-".../out/sdist.dest/dist/testpkg_mill-0.0.2.tar.gz"
+"...$MILL_OUT/sdist.dest/dist/testpkg_mill-0.0.2.tar.gz"
> ./mill show wheel
-".../out/wheel.dest/dist/testpkg_mill-0.0.2-py3-none-any.whl"
+"...$MILL_OUT/wheel.dest/dist/testpkg_mill-0.0.2-py3-none-any.whl"
*/
// These files can then be `pip-installed` by other projects, or, if you're using Mill, you can
diff --git a/example/scalalib/basic/1-script/build.mill b/example/scalalib/basic/1-script/build.mill
index 4a1032593878..975b59718ca7 100644
--- a/example/scalalib/basic/1-script/build.mill
+++ b/example/scalalib/basic/1-script/build.mill
@@ -70,7 +70,7 @@ Jvm Version: 11.0.28
/** Usage
> ./mill show Foo.scala:assembly # show the output of the assembly task
-".../out/Foo.scala/assembly.dest/out.jar"
+"...$MILL_OUT/Foo.scala/assembly.dest/out.jar"
> java -jar ./out/Foo.scala/assembly.dest/out.jar --text hello
hello
diff --git a/example/scalalib/basic/10-realistic/build.mill b/example/scalalib/basic/10-realistic/build.mill
index b9ea519a6e6e..2ec7435d5e2f 100644
--- a/example/scalalib/basic/10-realistic/build.mill
+++ b/example/scalalib/basic/10-realistic/build.mill
@@ -126,7 +126,7 @@ Publishing Artifact(com.lihaoyi,bar_3,0.0.1) to ivy repo...
Publishing Artifact(com.lihaoyi,qux,0.0.1) to ivy repo...
> ./mill show foo[2.13.16].assembly # mac/linux
-".../out/foo/2.13.16/assembly.dest/out.jar"
+"...$MILL_OUT/foo/2.13.16/assembly.dest/out.jar"
> ./out/foo/2.13.16/assembly.dest/out.jar # mac/linux
foo version 0.0.1
diff --git a/example/scalalib/basic/3-simple/build.mill b/example/scalalib/basic/3-simple/build.mill
index 1138995a9734..976dd2c6bab3 100644
--- a/example/scalalib/basic/3-simple/build.mill
+++ b/example/scalalib/basic/3-simple/build.mill
@@ -101,7 +101,7 @@ compiling 1 Scala source to...
> ./mill assembly # bundle classfiles and libraries into a jar for deployment
> ./mill show assembly # show the output of the assembly task
-".../out/assembly.dest/out.jar"
+"...$MILL_OUT/assembly.dest/out.jar"
> java -jar ./out/assembly.dest/out.jar --text hello
hello
diff --git a/example/scalalib/basic/6-programmatic/build.mill b/example/scalalib/basic/6-programmatic/build.mill
index 8a2bcb597a9c..85824ede4b1c 100644
--- a/example/scalalib/basic/6-programmatic/build.mill
+++ b/example/scalalib/basic/6-programmatic/build.mill
@@ -83,7 +83,7 @@ foo.run
> ./mill foo.assembly # bundle classfiles and libraries into a jar for deployment
> ./mill show foo.assembly # show the output of the assembly task
-".../out/foo/assembly.dest/out.jar"
+"...$MILL_OUT/foo/assembly.dest/out.jar"
> java -jar ./out/foo/assembly.dest/out.jar --text hello
hello
diff --git a/example/scalalib/config/1-common-config/build.mill b/example/scalalib/config/1-common-config/build.mill
index 0a8cee6fc8f7..40240b0f8319 100644
--- a/example/scalalib/config/1-common-config/build.mill
+++ b/example/scalalib/config/1-common-config/build.mill
@@ -14,7 +14,7 @@ my.custom.property: my-prop-value
MY_CUSTOM_ENV: my-env-value
> ./mill show assembly
-".../out/assembly.dest/out.jar"
+"...$MILL_OUT/assembly.dest/out.jar"
> ./out/assembly.dest/out.jar # mac/linux
Foo2.value: hello2
diff --git a/example/scalalib/module/15-unidoc/build.mill b/example/scalalib/module/15-unidoc/build.mill
index 94f1f27fb1d2..38d594591792 100644
--- a/example/scalalib/module/15-unidoc/build.mill
+++ b/example/scalalib/module/15-unidoc/build.mill
@@ -34,7 +34,7 @@ object foo extends ScalaModule, UnidocModule {
/** Usage
> ./mill show foo.unidocLocal
-".../out/foo/unidocLocal.dest"
+"...$MILL_OUT/foo/unidocLocal.dest"
> cat out/foo/unidocLocal.dest/foo/Foo.html
...
diff --git a/example/scalalib/module/2-common-config/build.mill b/example/scalalib/module/2-common-config/build.mill
index 6103b6ff4cea..30d516f56333 100644
--- a/example/scalalib/module/2-common-config/build.mill
+++ b/example/scalalib/module/2-common-config/build.mill
@@ -105,7 +105,7 @@ my.custom.property: my-prop-value
MY_CUSTOM_ENV: my-env-value
> ./mill show assembly
-".../out/assembly.dest/out.jar"
+"...$MILL_OUT/assembly.dest/out.jar"
> ./out/assembly.dest/out.jar # mac/linux
Foo2.value: hello2
diff --git a/example/scalalib/native/1-simple/build.mill b/example/scalalib/native/1-simple/build.mill
index f6a43a8451e8..0566d3473c52 100644
--- a/example/scalalib/native/1-simple/build.mill
+++ b/example/scalalib/native/1-simple/build.mill
@@ -26,7 +26,7 @@ object `package` extends ScalaNativeModule {
hello
> ./mill show nativeLink # Build and link native binary
-".../out/nativeLink.dest/out"
+"...$MILL_OUT/nativeLink.dest/out"
> ./out/nativeLink.dest/out --text hello # Run the executable
hello
diff --git a/example/scalalib/publishing/9-repackage-config/build.mill b/example/scalalib/publishing/9-repackage-config/build.mill
index 48cda4ce64b7..30e22ab17008 100644
--- a/example/scalalib/publishing/9-repackage-config/build.mill
+++ b/example/scalalib/publishing/9-repackage-config/build.mill
@@ -59,7 +59,7 @@ Qux.value: 31337
...Test run bar.BarTests finished: 0 failed, 0 ignored, 1 total, ...s
> ./mill show foo.repackagedJar
-".../out/foo/repackagedJar.dest/out.jar"
+"...$MILL_OUT/foo/repackagedJar.dest/out.jar"
> ./out/foo/repackagedJar.dest/out.jar
Foo.value: hello
diff --git a/example/scalalib/script/10-resources/build.mill b/example/scalalib/script/10-resources/build.mill
index 664f45838f8e..ae6cb540c380 100644
--- a/example/scalalib/script/10-resources/build.mill
+++ b/example/scalalib/script/10-resources/build.mill
@@ -23,7 +23,7 @@ Hello World Resource File
/** Usage
> ./mill show Foo.scala:assembly
-".../out/Foo.scala/assembly.dest/out.jar"
+"...$MILL_OUT/Foo.scala/assembly.dest/out.jar"
> out/Foo.scala/assembly.dest/out.jar
Hello World Resource File
diff --git a/example/scalalib/spark/3-semi-realistic/build.mill b/example/scalalib/spark/3-semi-realistic/build.mill
index 73668b29a008..66d4c8678555 100644
--- a/example/scalalib/spark/3-semi-realistic/build.mill
+++ b/example/scalalib/spark/3-semi-realistic/build.mill
@@ -46,7 +46,7 @@ Summary Statistics by Category:
> chmod +x spark-submit.sh
> ./mill show assembly # prepare for spark-submit
-".../out/assembly.dest/out.jar"
+"...$MILL_OUT/assembly.dest/out.jar"
> ./spark-submit.sh out/assembly.dest/out.jar foo.Foo resources/transactions.csv
...
diff --git a/example/scalalib/web/4-scalajs-module/build.mill b/example/scalalib/web/4-scalajs-module/build.mill
index b9eae71f61c6..6d56f90ae0d4 100644
--- a/example/scalalib/web/4-scalajs-module/build.mill
+++ b/example/scalalib/web/4-scalajs-module/build.mill
@@ -41,7 +41,7 @@ stringifiedJsObject: ["hello","world","!"]
{
...
..."jsFileName": "main.js",
- "dest": ".../out/foo/fullLinkJS.dest"
+ "dest": "...$MILL_OUT/foo/fullLinkJS.dest"
}
> node out/foo/fullLinkJS.dest/main.js # mac/linux
diff --git a/example/scalalib/web/9-wasm/build.mill b/example/scalalib/web/9-wasm/build.mill
index f85403e81e10..a8443d6e1643 100644
--- a/example/scalalib/web/9-wasm/build.mill
+++ b/example/scalalib/web/9-wasm/build.mill
@@ -27,7 +27,7 @@ object wasm extends ScalaJSModule {
...
..."jsFileName": "main.js",
...
- "dest": ".../out/wasm/fastLinkJS.dest"
+ "dest": "...$MILL_OUT/wasm/fastLinkJS.dest"
}
> node --experimental-wasm-exnref out/wasm/fastLinkJS.dest/main.js # mac/linux
diff --git a/example/springboot/java/1-web-initializr/build.mill b/example/springboot/java/1-web-initializr/build.mill
index c22f48d4b9b5..729995098fdb 100644
--- a/example/springboot/java/1-web-initializr/build.mill
+++ b/example/springboot/java/1-web-initializr/build.mill
@@ -50,6 +50,6 @@ object `package` extends MavenModule {
> ./mill clean runBackground
> ./mill show assembly
-.../out/assembly.dest/out.jar...
+...$MILL_OUT/assembly.dest/out.jar...
*/
diff --git a/example/springboot/kotlin/1-web-initializr/build.mill b/example/springboot/kotlin/1-web-initializr/build.mill
index 62785513d86e..3c31ae30bd24 100644
--- a/example/springboot/kotlin/1-web-initializr/build.mill
+++ b/example/springboot/kotlin/1-web-initializr/build.mill
@@ -59,6 +59,6 @@ object `package` extends KotlinMavenModule {
> ./mill clean runBackground
> ./mill show assembly
-.../out/assembly.dest/out.jar...
+...$MILL_OUT/assembly.dest/out.jar...
*/
diff --git a/example/thirdparty/android-endless-tunnel/build.mill b/example/thirdparty/android-endless-tunnel/build.mill
index 7bbec86eb8fc..081b3aefb782 100644
--- a/example/thirdparty/android-endless-tunnel/build.mill
+++ b/example/thirdparty/android-endless-tunnel/build.mill
@@ -53,7 +53,7 @@ object `endless-tunnel` extends mill.api.Module {
/** Usage
> ./mill show endless-tunnel.app.androidApk
-".../out/endless-tunnel/app/androidApk.dest/app.apk"
+"...$MILL_OUT/endless-tunnel/app/androidApk.dest/app.apk"
> ./mill show endless-tunnel.app.createAndroidVirtualDevice
...Name: cpp-test, DeviceId: medium_phone...
diff --git a/integration/ide/build-classpath-contents/src/BuildClasspathContentsTests.scala b/integration/ide/build-classpath-contents/src/BuildClasspathContentsTests.scala
index fad8eaa2ccec..14c7e1484118 100644
--- a/integration/ide/build-classpath-contents/src/BuildClasspathContentsTests.scala
+++ b/integration/ide/build-classpath-contents/src/BuildClasspathContentsTests.scala
@@ -1,4 +1,4 @@
-import mill.api.BuildCtx
+import mill.api.{BuildCtx, MappedRoots, PathRef}
import mill.testkit.UtestIntegrationTestSuite
import utest.*
@@ -6,56 +6,61 @@ object BuildClasspathContentsTests extends UtestIntegrationTestSuite {
val tests: Tests = Tests {
test("test") - integrationTest { tester =>
- val result1 =
- tester.eval(("--meta-level", "1", "show", "compileClasspath"), stderr = os.Inherit)
- val deserialized = upickle.read[Seq[mill.api.PathRef]](result1.out)
- val millPublishedJars = deserialized
- .map(_.path.last)
- .filter(_.startsWith("mill-"))
- .sorted
- val millLocalClasspath = deserialized
- .map(_.path)
- .filter(_.startsWith(BuildCtx.workspaceRoot))
- .map(_.subRelativeTo(BuildCtx.workspaceRoot))
- .filter(!_.startsWith("out/integration"))
- .filter(!_.startsWith("out/dist/localRepo.dest"))
- .map(_.toString)
- .sorted
- if (sys.env("MILL_INTEGRATION_IS_PACKAGED_LAUNCHER") == "true") {
- assertGoldenLiteral(
- millPublishedJars,
- List(
- "mill-core-api-daemon_3-SNAPSHOT.jar",
- "mill-core-api_3-SNAPSHOT.jar",
- "mill-core-constants-SNAPSHOT.jar",
- "mill-libs-androidlib-databinding_3-SNAPSHOT.jar",
- "mill-libs-androidlib_3-SNAPSHOT.jar",
- "mill-libs-daemon-client-SNAPSHOT.jar",
- "mill-libs-daemon-server_3-SNAPSHOT.jar",
- "mill-libs-javalib-api_3-SNAPSHOT.jar",
- "mill-libs-javalib-testrunner-entrypoint-SNAPSHOT.jar",
- "mill-libs-javalib-testrunner_3-SNAPSHOT.jar",
- "mill-libs-javalib_3-SNAPSHOT.jar",
- "mill-libs-javascriptlib_3-SNAPSHOT.jar",
- "mill-libs-kotlinlib-api_3-SNAPSHOT.jar",
- "mill-libs-kotlinlib-ksp2-api_3-SNAPSHOT.jar",
- "mill-libs-kotlinlib_3-SNAPSHOT.jar",
- "mill-libs-pythonlib_3-SNAPSHOT.jar",
- "mill-libs-rpc_3-SNAPSHOT.jar",
- "mill-libs-scalajslib-api_3-SNAPSHOT.jar",
- "mill-libs-scalajslib_3-SNAPSHOT.jar",
- "mill-libs-scalalib_3-SNAPSHOT.jar",
- "mill-libs-scalanativelib-api_3-SNAPSHOT.jar",
- "mill-libs-scalanativelib_3-SNAPSHOT.jar",
- "mill-libs-script_3-SNAPSHOT.jar",
- "mill-libs-util_3-SNAPSHOT.jar",
- "mill-libs_3-SNAPSHOT.jar",
- "mill-moduledefs_3-0.11.10.jar"
+ MappedRoots.withMapping(Seq(
+ "HOME" -> os.home,
+ "WORKSPACE" -> tester.workspacePath
+ )) {
+ val result1 =
+ tester.eval(("--meta-level", "1", "show", "compileClasspath"), stderr = os.Inherit)
+ val deserialized = upickle.read[Seq[mill.api.PathRef]](result1.out)
+ val millPublishedJars = deserialized
+ .map(_.path.last)
+ .filter(_.startsWith("mill-"))
+ .sorted
+ val millLocalClasspath = deserialized
+ .map(_.path)
+ .filter(_.startsWith(BuildCtx.workspaceRoot))
+ .map(_.subRelativeTo(BuildCtx.workspaceRoot))
+ .filter(!_.startsWith("out/integration"))
+ .filter(!_.startsWith("out/dist/localRepo.dest"))
+ .map(_.toString)
+ .sorted
+ if (sys.env("MILL_INTEGRATION_IS_PACKAGED_LAUNCHER") == "true") {
+ assertGoldenLiteral(
+ millPublishedJars,
+ List(
+ "mill-core-api-daemon_3-SNAPSHOT.jar",
+ "mill-core-api_3-SNAPSHOT.jar",
+ "mill-core-constants-SNAPSHOT.jar",
+ "mill-libs-androidlib-databinding_3-SNAPSHOT.jar",
+ "mill-libs-androidlib_3-SNAPSHOT.jar",
+ "mill-libs-daemon-client-SNAPSHOT.jar",
+ "mill-libs-daemon-server_3-SNAPSHOT.jar",
+ "mill-libs-javalib-api_3-SNAPSHOT.jar",
+ "mill-libs-javalib-testrunner-entrypoint-SNAPSHOT.jar",
+ "mill-libs-javalib-testrunner_3-SNAPSHOT.jar",
+ "mill-libs-javalib_3-SNAPSHOT.jar",
+ "mill-libs-javascriptlib_3-SNAPSHOT.jar",
+ "mill-libs-kotlinlib-api_3-SNAPSHOT.jar",
+ "mill-libs-kotlinlib-ksp2-api_3-SNAPSHOT.jar",
+ "mill-libs-kotlinlib_3-SNAPSHOT.jar",
+ "mill-libs-pythonlib_3-SNAPSHOT.jar",
+ "mill-libs-rpc_3-SNAPSHOT.jar",
+ "mill-libs-scalajslib-api_3-SNAPSHOT.jar",
+ "mill-libs-scalajslib_3-SNAPSHOT.jar",
+ "mill-libs-scalalib_3-SNAPSHOT.jar",
+ "mill-libs-scalanativelib-api_3-SNAPSHOT.jar",
+ "mill-libs-scalanativelib_3-SNAPSHOT.jar",
+ "mill-libs-script_3-SNAPSHOT.jar",
+ "mill-libs-util_3-SNAPSHOT.jar",
+ "mill-libs_3-SNAPSHOT.jar",
+ "mill-moduledefs_3-0.11.10.jar"
+ )
)
- )
- assert(millLocalClasspath == Nil)
- } else {
- sys.error("This test must be run in `packaged` mode, not `local`")
+ assert(millLocalClasspath == Nil)
+ } else {
+ sys.error("This test must be run in `packaged` mode, not `local`")
+ }
}
}
}
diff --git a/integration/invalidation/multi-level-editing/src/MultiLevelBuildTests.scala b/integration/invalidation/multi-level-editing/src/MultiLevelBuildTests.scala
index 8fabf73fab97..4625dba1a362 100644
--- a/integration/invalidation/multi-level-editing/src/MultiLevelBuildTests.scala
+++ b/integration/invalidation/multi-level-editing/src/MultiLevelBuildTests.scala
@@ -1,5 +1,6 @@
package mill.integration
+import mill.api.MappedRoots
import mill.testkit.{IntegrationTester, UtestIntegrationTestSuite}
import mill.constants.OutFiles.*
import mill.daemon.RunnerState
@@ -48,7 +49,13 @@ trait MultiLevelBuildTests extends UtestIntegrationTestSuite {
yield {
val path =
tester.workspacePath / "out" / Seq.fill(depth)(millBuild) / millRunnerState
- if (os.exists(path)) upickle.read[RunnerState.Frame.Logged](os.read(path)) -> path
+ if (os.exists(path))
+ MappedRoots.withMillDefaults(
+ outPath = tester.workspacePath / "out",
+ workspacePath = tester.workspacePath
+ ) {
+ upickle.read[RunnerState.Frame.Logged](os.read(path)) -> path
+ }
else RunnerState.Frame.Logged(Map(), Seq(), Seq(), None, Seq(), 0) -> path
}
}
diff --git a/libs/javalib/src/mill/javalib/TestModule.scala b/libs/javalib/src/mill/javalib/TestModule.scala
index 3925b9bd78bd..1f58c779bdc3 100644
--- a/libs/javalib/src/mill/javalib/TestModule.scala
+++ b/libs/javalib/src/mill/javalib/TestModule.scala
@@ -1,14 +1,10 @@
package mill.javalib
import mill.T
-import mill.api.Result
+import mill.api.{DefaultTaskModule, MappedRoots, PathRef, Result, Task, TaskCtx}
import mill.api.daemon.internal.TestModuleApi
import mill.api.daemon.internal.TestReporter
import mill.api.daemon.internal.bsp.{BspBuildTarget, BspModuleApi}
-import mill.api.PathRef
-import mill.api.Task
-import mill.api.TaskCtx
-import mill.api.DefaultTaskModule
import mill.javalib.bsp.BspModule
import mill.api.JsonFormatters.given
import mill.constants.EnvVars
@@ -195,7 +191,10 @@ trait TestModule
)
val argsFile = Task.dest / "testargs"
- os.write(argsFile, upickle.write(testArgs))
+ MappedRoots.withMapping(Seq()) {
+ // Don't use placeholders, so we only have local absolute paths
+ os.write(argsFile, upickle.write(testArgs))
+ }
val testRunnerClasspathArg =
jvmWorker().scalalibClasspath()
diff --git a/libs/javalib/src/mill/javalib/TestModuleUtil.scala b/libs/javalib/src/mill/javalib/TestModuleUtil.scala
index f80e59c3e435..227e27f58ab6 100644
--- a/libs/javalib/src/mill/javalib/TestModuleUtil.scala
+++ b/libs/javalib/src/mill/javalib/TestModuleUtil.scala
@@ -1,7 +1,6 @@
package mill.javalib
-import mill.api.{PathRef, TaskCtx}
-import mill.api.Result
+import mill.api.{Logger, MappedRoots, PathRef, Result, TaskCtx}
import mill.api.daemon.internal.TestReporter
import mill.util.Jvm
import mill.api.internal.Util
@@ -13,8 +12,6 @@ import java.time.temporal.ChronoUnit
import java.time.{Instant, LocalDateTime, ZoneId}
import scala.xml.Elem
import scala.collection.mutable
-import mill.api.Logger
-
import java.util.concurrent.ConcurrentHashMap
import mill.api.BuildCtx
import mill.javalib.api.internal.ZincOp
@@ -136,7 +133,10 @@ final class TestModuleUtil(
val argsFile = baseFolder / "testargs"
val sandbox = baseFolder / "sandbox"
- os.write(argsFile, upickle.write(testArgs), createFolders = true)
+ MappedRoots.withMapping(Seq()) {
+ // Don't use placeholders, so we only have local absolute paths
+ os.write(argsFile, upickle.write(testArgs), createFolders = true)
+ }
os.makeDir.all(sandbox)
diff --git a/libs/javalib/test/src/mill/javalib/HelloJavaTests.scala b/libs/javalib/test/src/mill/javalib/HelloJavaTests.scala
index 303dc58ad854..fada1674bca3 100644
--- a/libs/javalib/test/src/mill/javalib/HelloJavaTests.scala
+++ b/libs/javalib/test/src/mill/javalib/HelloJavaTests.scala
@@ -40,9 +40,9 @@ object HelloJavaTests extends TestSuite {
assert(
result1.value == result2.value,
+ result1.evalCount != 0,
result2.evalCount == 0,
result3.evalCount != 0,
- result3.evalCount != 0,
os.walk(result1.value.classes.path).exists(_.last == "Core.class"),
!os.walk(result1.value.classes.path).exists(_.last == "Main.class"),
os.walk(result3.value.classes.path).exists(_.last == "Main.class"),
diff --git a/libs/javalib/testrunner/entrypoint/src/mill/javalib/testrunner/entrypoint/TestRunnerMain.java b/libs/javalib/testrunner/entrypoint/src/mill/javalib/testrunner/entrypoint/TestRunnerMain.java
index 5930e98e6198..18e8d21283ec 100644
--- a/libs/javalib/testrunner/entrypoint/src/mill/javalib/testrunner/entrypoint/TestRunnerMain.java
+++ b/libs/javalib/testrunner/entrypoint/src/mill/javalib/testrunner/entrypoint/TestRunnerMain.java
@@ -14,6 +14,10 @@
* nested classloaders.
*/
public class TestRunnerMain {
+ /**
+ *
+ * @param args arg1: classpath, arg2 testArgs-file
+ */
public static void main(String[] args) throws Exception {
URL[] testRunnerClasspath = Stream.of(args[0].split(","))
.map(s -> {
diff --git a/libs/javalib/worker/src/mill/javalib/worker/NoMappedRootsMillRcpWireTransport.scala b/libs/javalib/worker/src/mill/javalib/worker/NoMappedRootsMillRcpWireTransport.scala
new file mode 100644
index 000000000000..ce3f418714d0
--- /dev/null
+++ b/libs/javalib/worker/src/mill/javalib/worker/NoMappedRootsMillRcpWireTransport.scala
@@ -0,0 +1,25 @@
+package mill.javalib.worker
+
+import mill.api.MappedRoots
+import mill.rpc.MillRpcWireTransport
+
+import java.io.{BufferedReader, PrintStream}
+
+class NoMappedRootsMillRcpWireTransport(
+ name: String,
+ serverToClient: BufferedReader,
+ clientToServer: PrintStream,
+ writeSynchronizer: AnyRef
+) extends MillRpcWireTransport(
+ name = name,
+ serverToClient = serverToClient,
+ clientToServer = clientToServer,
+ writeSynchronizer = writeSynchronizer
+ ) {
+ override def writeSerialized[A: upickle.Writer](message: A, log: String => Unit): Unit = {
+ // RPC communication is local and uncached, so we don't want to use any root mapping
+ MappedRoots.withMapping(Seq()) {
+ super.writeSerialized(message, log)
+ }
+ }
+}
diff --git a/libs/javalib/worker/src/mill/javalib/worker/SubprocessZincApi.scala b/libs/javalib/worker/src/mill/javalib/worker/SubprocessZincApi.scala
index 28bdc91d9443..f3c67fb83759 100644
--- a/libs/javalib/worker/src/mill/javalib/worker/SubprocessZincApi.scala
+++ b/libs/javalib/worker/src/mill/javalib/worker/SubprocessZincApi.scala
@@ -1,4 +1,5 @@
package mill.javalib.worker
+
import mill.api.*
import mill.api.daemon.internal.CompileProblemReporter
import mill.client.{LaunchedServer, ServerLauncher}
@@ -6,7 +7,7 @@ import mill.javalib.api.internal.*
import mill.javalib.internal.{RpcProblemMessage, ZincCompilerBridgeProvider}
import mill.javalib.zinc.ZincWorkerRpcServer.ReporterMode
import mill.javalib.zinc.{ZincApi, ZincWorker, ZincWorkerRpcServer}
-import mill.rpc.{MillRpcChannel, MillRpcClient, MillRpcWireTransport}
+import mill.rpc.{MillRpcChannel, MillRpcClient}
import mill.util.CachedFactoryWithInitData
import java.io.*
@@ -93,13 +94,12 @@ class SubprocessZincApi(
(in, out) => {
val serverToClient = use(BufferedReader(InputStreamReader(in)))
val clientToServer = use(PrintStream(out))
- val wireTransport =
- MillRpcWireTransport(
- debugName,
- serverToClient,
- clientToServer,
- writeSynchronizer = clientToServer
- )
+ val wireTransport = NoMappedRootsMillRcpWireTransport(
+ name = debugName,
+ serverToClient = serverToClient,
+ clientToServer = clientToServer,
+ writeSynchronizer = clientToServer
+ )
val init =
ZincWorkerRpcServer.Initialize(compilerBridgeWorkspace = compilerBridge.workspace)
diff --git a/libs/javalib/worker/src/mill/javalib/zinc/ZincWorkerMain.scala b/libs/javalib/worker/src/mill/javalib/zinc/ZincWorkerMain.scala
index 322e229a949d..9b5beb209bb3 100644
--- a/libs/javalib/worker/src/mill/javalib/zinc/ZincWorkerMain.scala
+++ b/libs/javalib/worker/src/mill/javalib/zinc/ZincWorkerMain.scala
@@ -1,9 +1,9 @@
package mill.javalib.zinc
-import mill.api.SystemStreamsUtils
import mill.api.daemon.{DummyInputStream, SystemStreams}
+import mill.api.SystemStreamsUtils
import mill.client.lock.Locks
-import mill.rpc.MillRpcWireTransport
+import mill.javalib.worker.NoMappedRootsMillRcpWireTransport
import mill.server.Server
import mill.server.Server.ConnectionData
import pprint.{TPrint, TPrintColors}
@@ -63,7 +63,8 @@ object ZincWorkerMain {
Using.Manager { use =>
val stdin = use(BufferedReader(InputStreamReader(connectionData.clientToServer)))
val stdout = use(PrintStream(connectionData.serverToClient))
- val transport = MillRpcWireTransport(serverName, stdin, stdout, writeSynchronizer)
+ val transport =
+ NoMappedRootsMillRcpWireTransport(serverName, stdin, stdout, writeSynchronizer)
val server = ZincWorkerRpcServer(worker, serverName, transport, setIdle, serverLog)
// Make sure stdout and stderr is sent to the client
diff --git a/libs/rpc/src/mill/rpc/MillRpcWireTransport.scala b/libs/rpc/src/mill/rpc/MillRpcWireTransport.scala
index abb6d3a31924..d859c4347604 100644
--- a/libs/rpc/src/mill/rpc/MillRpcWireTransport.scala
+++ b/libs/rpc/src/mill/rpc/MillRpcWireTransport.scala
@@ -5,6 +5,7 @@ import upickle.{Reader, Writer}
import java.io.{BufferedReader, PrintStream}
import scala.annotation.tailrec
+
class MillRpcWireTransport(
val name: String,
serverToClient: BufferedReader,
diff --git a/libs/scalalib/test/src/mill/scalalib/HelloWorldTests.scala b/libs/scalalib/test/src/mill/scalalib/HelloWorldTests.scala
index edfa402c6639..eb2a7e5f2d4a 100644
--- a/libs/scalalib/test/src/mill/scalalib/HelloWorldTests.scala
+++ b/libs/scalalib/test/src/mill/scalalib/HelloWorldTests.scala
@@ -159,101 +159,121 @@ object HelloWorldTests extends TestSuite {
test("compile") {
test("fromScratch") - UnitTester(HelloWorld, sourceRoot = resourcePath).scoped { eval =>
- val Right(result) = eval.apply(HelloWorld.core.compile): @unchecked
-
- val classesPath = eval.outPath / "core/compile.dest/classes"
- val analysisFile = result.value.analysisFile
- val outputFiles = os.walk(result.value.classes.path)
- val expectedClassfiles = compileClassfiles.map(classesPath / _)
- assert(
- result.value.classes.path == classesPath,
- os.exists(analysisFile),
- outputFiles.nonEmpty,
- outputFiles.forall(expectedClassfiles.contains),
- result.evalCount > 0
- )
+ if (scala.util.Properties.isJavaAtLeast(21))
+ "Skipping on Java 21+ due to too old Scala version"
+ else {
+ val Right(result) = eval.apply(HelloWorld.core.compile): @unchecked
+
+ val classesPath = eval.outPath / "core/compile.dest/classes"
+ val analysisFile = result.value.analysisFile
+ val outputFiles = os.walk(result.value.classes.path)
+ val expectedClassfiles = compileClassfiles.map(classesPath / _)
+ assert(
+ result.value.classes.path == classesPath,
+ os.exists(analysisFile),
+ outputFiles.nonEmpty,
+ outputFiles.forall(expectedClassfiles.contains),
+ result.evalCount > 0
+ )
- // don't recompile if nothing changed
- val Right(result2) = eval.apply(HelloWorld.core.compile): @unchecked
+ // don't recompile if nothing changed
+ val Right(result2) = eval.apply(HelloWorld.core.compile): @unchecked
- assert(result2.evalCount == 0)
+ assert(result2.evalCount == 0)
- // Make sure we *do not* end up compiling the compiler bridge, since
- // it's using a pre-compiled bridge value
- assert(!os.exists(
- eval.outPath / "mill/scalalib/JvmWorkerModule/internalWorker.dest" / s"zinc-${zincVersion}"
- ))
+ // Make sure we *do not* end up compiling the compiler bridge, since
+ // it's using a pre-compiled bridge value
+ assert(!os.exists(
+ eval.outPath / "mill/scalalib/JvmWorkerModule/internalWorker.dest" / s"zinc-${zincVersion}"
+ ))
+ }
}
test("nonPreCompiledBridge") - UnitTester(
HelloWorldNonPrecompiledBridge,
sourceRoot = resourcePath
).scoped { eval =>
- val Right(result) = eval.apply(HelloWorldNonPrecompiledBridge.core.compile): @unchecked
+ if (scala.util.Properties.isJavaAtLeast(21))
+ "Skipping on Java 21+ due to too old Scala version"
+ else {
+ val Right(result) = eval.apply(HelloWorldNonPrecompiledBridge.core.compile): @unchecked
- val classesPath = eval.outPath / "core/compile.dest/classes"
+ val classesPath = eval.outPath / "core/compile.dest/classes"
- val analysisFile = result.value.analysisFile
- val outputFiles = os.walk(result.value.classes.path)
- val expectedClassfiles = compileClassfiles.map(classesPath / _)
- assert(
- result.value.classes.path == classesPath,
- os.exists(analysisFile),
- outputFiles.nonEmpty,
- outputFiles.forall(expectedClassfiles.contains),
- result.evalCount > 0
- )
+ val analysisFile = result.value.analysisFile
+ val outputFiles = os.walk(result.value.classes.path)
+ val expectedClassfiles = compileClassfiles.map(classesPath / _)
+ assert(
+ result.value.classes.path == classesPath,
+ os.exists(analysisFile),
+ outputFiles.nonEmpty,
+ outputFiles.forall(expectedClassfiles.contains),
+ result.evalCount > 0
+ )
- // don't recompile if nothing changed
- val Right(result2) = eval.apply(HelloWorldNonPrecompiledBridge.core.compile): @unchecked
+ // don't recompile if nothing changed
+ val Right(result2) = eval.apply(HelloWorldNonPrecompiledBridge.core.compile): @unchecked
- assert(result2.evalCount == 0)
+ assert(result2.evalCount == 0)
- // Make sure we *do* end up compiling the compiler bridge, since it's
- // *not* using a pre-compiled bridge value
- assert(os.exists(
- eval.outPath / "mill.javalib.JvmWorkerModule/internalWorker.dest" / s"zinc-${zincVersion}"
- ))
+ // Make sure we *do* end up compiling the compiler bridge, since it's
+ // *not* using a pre-compiled bridge value
+ assert(os.exists(
+ eval.outPath / "mill.javalib.JvmWorkerModule/internalWorker.dest" / s"zinc-${zincVersion}"
+ ))
+ }
}
test("recompileOnChange") - UnitTester(HelloWorld, sourceRoot = resourcePath).scoped { eval =>
- val Right(result) = eval.apply(HelloWorld.core.compile): @unchecked
- assert(result.evalCount > 0)
+ if (scala.util.Properties.isJavaAtLeast(21))
+ "Skipping on Java 21+ due to too old Scala version"
+ else {
+ val Right(result) = eval.apply(HelloWorld.core.compile): @unchecked
+ assert(result.evalCount > 0)
- os.write.append(HelloWorld.moduleDir / "core/src/Main.scala", "\n")
+ os.write.append(HelloWorld.moduleDir / "core/src/Main.scala", "\n")
- val Right(result2) = eval.apply(HelloWorld.core.compile): @unchecked
- assert(result2.evalCount > 0, result2.evalCount < result.evalCount)
+ val Right(result2) = eval.apply(HelloWorld.core.compile): @unchecked
+ assert(result2.evalCount > 0, result2.evalCount < result.evalCount)
+ }
}
test("failOnError") - UnitTester(HelloWorld, sourceRoot = resourcePath).scoped { eval =>
- os.write.append(HelloWorld.moduleDir / "core/src/Main.scala", "val x: ")
+ if (scala.util.Properties.isJavaAtLeast(21))
+ "Skipping on Java 21+ due to too old Scala version"
+ else {
+ os.write.append(HelloWorld.moduleDir / "core/src/Main.scala", "val x: ")
- val Left(ExecResult.Failure("Compilation failed")) =
- eval.apply(HelloWorld.core.compile): @unchecked
+ val Left(ExecResult.Failure("Compilation failed")) =
+ eval.apply(HelloWorld.core.compile): @unchecked
- val paths = ExecutionPaths.resolve(eval.outPath, HelloWorld.core.compile)
+ val paths = ExecutionPaths.resolve(eval.outPath, HelloWorld.core.compile)
- assert(
- os.walk(paths.dest / "classes").isEmpty,
- !os.exists(paths.meta)
- )
- // Works when fixed
- os.write.over(
- HelloWorld.moduleDir / "core/src/Main.scala",
- os.read(HelloWorld.moduleDir / "core/src/Main.scala").dropRight(
- "val x: ".length
+ assert(
+ os.walk(paths.dest / "classes").isEmpty,
+ !os.exists(paths.meta)
+ )
+ // Works when fixed
+ os.write.over(
+ HelloWorld.moduleDir / "core/src/Main.scala",
+ os.read(HelloWorld.moduleDir / "core/src/Main.scala").dropRight(
+ "val x: ".length
+ )
)
- )
- val Right(_) = eval.apply(HelloWorld.core.compile): @unchecked
+ val Right(_) = eval.apply(HelloWorld.core.compile): @unchecked
+ }
}
test("passScalacOptions") - UnitTester(
HelloWorldFatalWarnings,
sourceRoot = resourcePath
).scoped { eval =>
- // compilation fails because of "-Xfatal-warnings" flag
- val Left(ExecResult.Failure("Compilation failed")) =
- eval.apply(HelloWorldFatalWarnings.core.compile): @unchecked
+ if (scala.util.Properties.isJavaAtLeast(21))
+ "Skipping on Java 21+ due to too old Scala version"
+ else {
+ // compilation fails because of "-Xfatal-warnings" flag
+ val Left(ExecResult.Failure("Compilation failed")) =
+ eval.apply(HelloWorldFatalWarnings.core.compile): @unchecked
+ }
}
}
@@ -266,39 +286,47 @@ object HelloWorldTests extends TestSuite {
test("jar") {
test("nonEmpty") - UnitTester(HelloWorldWithMain, resourcePath).scoped { eval =>
- val Right(result) = eval.apply(HelloWorldWithMain.core.jar): @unchecked
+ if (scala.util.Properties.isJavaAtLeast(21))
+ "Skipping on Java 21+ due to too old Scala version"
+ else {
+ val Right(result) = eval.apply(HelloWorldWithMain.core.jar): @unchecked
- assert(
- os.exists(result.value.path),
- result.evalCount > 0
- )
+ assert(
+ os.exists(result.value.path),
+ result.evalCount > 0
+ )
- Using.resource(new JarFile(result.value.path.toIO)) { jarFile =>
- val entries = jarFile.entries().asScala.map(_.getName).toSeq.sorted
+ Using.resource(new JarFile(result.value.path.toIO)) { jarFile =>
+ val entries = jarFile.entries().asScala.map(_.getName).toSeq.sorted
- val otherFiles = Seq(
- "META-INF/",
- "META-INF/MANIFEST.MF",
- "reference.conf"
- )
- val expectedFiles = (compileClassfiles.map(_.toString()) ++ otherFiles).sorted
+ val otherFiles = Seq(
+ "META-INF/",
+ "META-INF/MANIFEST.MF",
+ "reference.conf"
+ )
+ val expectedFiles = (compileClassfiles.map(_.toString()) ++ otherFiles).sorted
- assert(
- entries.nonEmpty,
- entries == expectedFiles
- )
+ assert(
+ entries.nonEmpty,
+ entries == expectedFiles
+ )
- val mainClass = jarMainClass(jarFile)
- assert(mainClass.contains("Main"))
+ val mainClass = jarMainClass(jarFile)
+ assert(mainClass.contains("Main"))
+ }
}
}
test("logOutputToFile") - UnitTester(HelloWorld, resourcePath).scoped { eval =>
- val outPath = eval.outPath
- eval.apply(HelloWorld.core.compile)
-
- val logFile = outPath / "core/compile.log"
- assert(os.exists(logFile))
+ if (scala.util.Properties.isJavaAtLeast(21))
+ "Skipping on Java 21+ due to too old Scala version"
+ else {
+ val outPath = eval.outPath
+ eval.apply(HelloWorld.core.compile)
+
+ val logFile = outPath / "core/compile.log"
+ assert(os.exists(logFile))
+ }
}
}
}
diff --git a/runner/daemon/src/mill/daemon/MillBuildBootstrap.scala b/runner/daemon/src/mill/daemon/MillBuildBootstrap.scala
index 3a19d2ce9721..10e668f3739f 100644
--- a/runner/daemon/src/mill/daemon/MillBuildBootstrap.scala
+++ b/runner/daemon/src/mill/daemon/MillBuildBootstrap.scala
@@ -7,12 +7,12 @@ import mill.api.daemon.internal.{
PathRefApi,
RootModuleApi
}
-import mill.api.{Logger, Result, SystemStreams, Val}
+import mill.api.{BuildCtx, Logger, MappedRoots, PathRef, Result, SelectMode, SystemStreams, Val}
import mill.constants.CodeGenConstants.*
import mill.constants.OutFiles.{millBuild, millRunnerState}
import mill.api.daemon.Watchable
import mill.api.internal.RootModule
-import mill.api.{BuildCtx, PathRef, SelectMode}
+import mill.constants.PathVars
import mill.internal.PrefixLogger
import mill.meta.MillBuildRootModule
import mill.meta.CliImports
@@ -70,11 +70,13 @@ class MillBuildBootstrap(
val runnerState = evaluateRec(0)
for ((frame, depth) <- runnerState.frames.zipWithIndex) {
- os.write.over(
- recOut(output, depth) / millRunnerState,
- upickle.write(frame.loggedData, indent = 4),
- createFolders = true
- )
+ MappedRoots.withMillDefaults(outPath = output) {
+ os.write.over(
+ recOut(output, depth) / millRunnerState,
+ upickle.write(frame.loggedData, indent = 4),
+ createFolders = true
+ )
+ }
}
Watching.Result(
@@ -85,6 +87,10 @@ class MillBuildBootstrap(
}
def evaluateRec(depth: Int): RunnerState = {
+
+ // We need relocatable PathRef for meta-builds for a stable classpathSig
+ MappedRoots.requireMappedPaths(PathVars.WORKSPACE, PathVars.HOME, PathVars.MILL_OUT)
+
logger.withChromeProfile(s"meta-level $depth") {
// println(s"+evaluateRec($depth) " + recRoot(projectRoot, depth))
val currentRoot = recRoot(projectRoot, depth)
@@ -253,24 +259,25 @@ class MillBuildBootstrap(
case Result.Success((buildFileApi)) =>
Using.resource(makeEvaluator(
- projectRoot,
- output,
- keepGoing,
- env,
- logger,
- ec,
- allowPositionalCommandArgs,
- systemExit,
- streams0,
- selectiveExecution,
- offline,
- newWorkerCache,
- nestedState.frames.headOption.map(_.codeSignatures).getOrElse(Map.empty),
- buildFileApi.rootModule,
+ projectRoot = projectRoot,
+ output = output,
+ keepGoing = keepGoing,
+ env = env,
+ logger = logger,
+ ec = ec,
+ allowPositionalCommandArgs = allowPositionalCommandArgs,
+ systemExit = systemExit,
+ streams0 = streams0,
+ selectiveExecution = selectiveExecution,
+ offline = offline,
+ workerCache = newWorkerCache,
+ codeSignatures =
+ nestedState.frames.headOption.map(_.codeSignatures).getOrElse(Map.empty),
+ rootModule = buildFileApi.rootModule,
// We want to use the grandparent buildHash, rather than the parent
// buildHash, because the parent build changes are instead detected
// by analyzing the scriptImportGraph in a more fine-grained manner.
- nestedState
+ millClassloaderSigHash = nestedState
.frames
.dropRight(1)
.headOption
@@ -278,13 +285,13 @@ class MillBuildBootstrap(
.getOrElse(millBootClasspathPathRefs)
.map(p => (os.Path(p.javaPath), p.sig))
.hashCode(),
- nestedState
+ millClassloaderIdentityHash = nestedState
.frames
.headOption
.flatMap(_.classLoaderOpt)
.map(_.hashCode())
.getOrElse(0),
- depth,
+ depth = depth,
actualBuildFileName = nestedState.buildFile,
enableTicker = enableTicker
)) { evaluator =>
diff --git a/runner/daemon/src/mill/daemon/MillDaemonMain.scala b/runner/daemon/src/mill/daemon/MillDaemonMain.scala
index 0eeaaecb876e..00ea81c6e782 100644
--- a/runner/daemon/src/mill/daemon/MillDaemonMain.scala
+++ b/runner/daemon/src/mill/daemon/MillDaemonMain.scala
@@ -1,27 +1,29 @@
package mill.daemon
-import mill.api.{BuildCtx, SystemStreams}
+import mill.api.{MappedRoots, SystemStreams}
import mill.client.lock.{Lock, Locks}
-import mill.constants.{OutFiles, OutFolderMode}
+import mill.constants.OutFolderMode
import mill.server.Server
import scala.concurrent.duration.*
import scala.util.{Failure, Properties, Success, Try}
object MillDaemonMain {
- case class Args(daemonDir: os.Path, outMode: OutFolderMode, rest: Seq[String])
+ case class Args(daemonDir: os.Path, outMode: OutFolderMode, outDir: os.Path, rest: Seq[String])
+
object Args {
def apply(appName: String, args: Array[String]): Either[String, Args] = {
def usage(extra: String = "") =
- s"usage: $appName $extra"
+ s"usage: $appName $extra"
args match {
- case Array(daemonDir, outModeStr, rest*) =>
+ case Array(daemonDir, outModeStr, outDir, rest*) =>
Try(OutFolderMode.fromString(outModeStr)) match {
case Failure(_) =>
val possibleValues = OutFolderMode.values.map(_.asString).mkString(", ")
Left(usage(s"\n\n must be one of $possibleValues but was '$outModeStr'"))
- case Success(outMode) => Right(apply(os.Path(daemonDir), outMode, rest))
+ case Success(outMode) =>
+ Right(apply(os.Path(daemonDir), outMode, os.Path(outDir), rest))
}
case _ => Left(usage())
}
@@ -36,27 +38,30 @@ object MillDaemonMain {
val args =
Args(getClass.getName, args0).fold(err => throw IllegalArgumentException(err), identity)
- // temporarily disabling FFM use by coursier, which has issues with the way
- // Mill manages class loaders, throwing things like
- // UnsatisfiedLinkError: Native Library C:\Windows\System32\ole32.dll already loaded in another classloader
- if (Properties.isWin) sys.props("coursier.windows.disable-ffm") = "true"
+ MappedRoots.withMillDefaults(outPath = args.outDir) {
+ // temporarily disabling FFM use by coursier, which has issues with the way
+ // Mill manages class loaders, throwing things like
+ // UnsatisfiedLinkError: Native Library C:\Windows\System32\ole32.dll already loaded in another classloader
+ if (Properties.isWin) sys.props("coursier.windows.disable-ffm") = "true"
- coursier.Resolve.proxySetup() // Take into account proxy-related Java properties
+ coursier.Resolve.proxySetup() // Take into account proxy-related Java properties
- mill.api.SystemStreamsUtils.withTopLevelSystemStreamProxy {
- Server.overrideSigIntHandling()
+ mill.api.SystemStreamsUtils.withTopLevelSystemStreamProxy {
+ Server.overrideSigIntHandling()
- val acceptTimeout =
- Try(System.getProperty("mill.server_timeout").toInt.millis).getOrElse(30.minutes)
+ val acceptTimeout =
+ Try(System.getProperty("mill.server_timeout").toInt.millis).getOrElse(30.minutes)
- val exitCode = new MillDaemonMain(
- daemonDir = args.daemonDir,
- acceptTimeout = acceptTimeout,
- Locks.files(args.daemonDir.toString),
- outMode = args.outMode
- ).run().getOrElse(0)
+ val exitCode = new MillDaemonMain(
+ daemonDir = args.daemonDir,
+ acceptTimeout = acceptTimeout,
+ Locks.files(args.daemonDir.toString),
+ outMode = args.outMode,
+ outDir = args.outDir
+ ).run().getOrElse(0)
- System.exit(exitCode)
+ System.exit(exitCode)
+ }
}
}
}
@@ -65,18 +70,18 @@ class MillDaemonMain(
daemonDir: os.Path,
acceptTimeout: FiniteDuration,
locks: Locks,
- outMode: OutFolderMode
+ outMode: OutFolderMode,
+ outDir: os.Path
) extends mill.server.MillDaemonServer[RunnerState](
- daemonDir,
- acceptTimeout,
- locks
+ daemonDir = daemonDir,
+ acceptTimeout = acceptTimeout,
+ locks = locks,
+ outDir = outDir
) {
def initialStateCache = RunnerState.empty
- val outFolder: os.Path = os.Path(OutFiles.outFor(outMode), BuildCtx.workspaceRoot)
-
- val outLock = MillMain0.doubleLock(outFolder)
+ val outLock = MillMain0.doubleLock(outDir)
def main0(
args: Array[String],
@@ -100,7 +105,8 @@ class MillDaemonMain(
initialSystemProperties = initialSystemProperties,
systemExit = systemExit,
daemonDir = daemonDir,
- outLock = outLock
+ outLock = outLock,
+ outDir = outDir
)
catch MillMain0.handleMillException(streams.err, stateCache)
}
diff --git a/runner/daemon/src/mill/daemon/MillMain0.scala b/runner/daemon/src/mill/daemon/MillMain0.scala
index a4d721231e2a..6688cca76943 100644
--- a/runner/daemon/src/mill/daemon/MillMain0.scala
+++ b/runner/daemon/src/mill/daemon/MillMain0.scala
@@ -3,11 +3,10 @@ package mill.daemon
import ch.epfl.scala.bsp4j.BuildClient
import mill.api.daemon.internal.bsp.BspServerHandle
import mill.api.daemon.internal.{CompileProblemReporter, EvaluatorApi}
-import mill.api.{Logger, MillException, Result, SystemStreams}
+import mill.api.{BuildCtx, Logger, MappedRoots, MillException, Result, SystemStreams}
import mill.bsp.BSP
import mill.client.lock.{DoubleLock, Lock}
-import mill.constants.{DaemonFiles, OutFiles, OutFolderMode}
-import mill.api.BuildCtx
+import mill.constants.{DaemonFiles, OutFiles}
import mill.internal.{
Colors,
JsonArrayLogger,
@@ -65,12 +64,12 @@ object MillMain0 {
private def withStreams[T](
bspMode: Boolean,
- streams: SystemStreams
+ streams: SystemStreams,
+ outDir: os.Path
)(thunk: SystemStreams => T): T =
if (bspMode) {
// In BSP mode, don't let anything other than the BSP server write to stdout and read from stdin
- val outDir = BuildCtx.workspaceRoot / os.RelPath(OutFiles.outFor(OutFolderMode.BSP))
val outFileStream = os.write.outputStream(
outDir / "mill-bsp/out.log",
createFolders = true
@@ -109,8 +108,9 @@ object MillMain0 {
initialSystemProperties: Map[String, String],
systemExit: Server.StopServer,
daemonDir: os.Path,
- outLock: Lock
- ): (Boolean, RunnerState) =
+ outLock: Lock,
+ outDir: os.Path
+ ): (Boolean, RunnerState) = MappedRoots.withMillDefaults(outPath = outDir) {
mill.api.daemon.internal.MillScalaParser.current.withValue(MillScalaParserImpl) {
os.SubProcess.env.withValue(env) {
val parserResult = MillCliConfig.parse(args)
@@ -121,7 +121,7 @@ object MillMain0 {
// This is especially helpful if anything unexpectedly goes wrong
// early on, when developing on Mill or debugging things for example.
val bspMode = parserResult.toOption.exists(_.bsp.value)
- withStreams(bspMode, streams0) { streams =>
+ withStreams(bspMode, streams0, outDir) { streams =>
parserResult match {
// Cannot parse args
case Result.Failure(msg) =>
@@ -178,7 +178,7 @@ object MillMain0 {
// special BSP mode, in which we spawn a server and register the current evaluator when-ever we start to eval a dedicated command
val bspMode = config.bsp.value && config.leftoverArgs.value.isEmpty
- val outMode = if (bspMode) OutFolderMode.BSP else OutFolderMode.REGULAR
+
val bspInstallModeJobCountOpt = {
def defaultJobCount =
maybeThreadCount.toOption.getOrElse(BSP.defaultJobCount)
@@ -241,7 +241,6 @@ object MillMain0 {
if (threadCount == 1) None
else Some(mill.exec.ExecutionContexts.createExecutor(threadCount))
- val out = os.Path(OutFiles.outFor(outMode), BuildCtx.workspaceRoot)
Using.resources(new TailManager(daemonDir), createEc()) { (tailManager, ec) =>
def runMillBootstrap(
skipSelectiveExecution: Boolean,
@@ -257,7 +256,7 @@ object MillMain0 {
): Watching.Result[RunnerState] = MillDaemonServer.withOutLock(
noBuildLock = config.noBuildLock.value,
noWaitForBuildLock = config.noWaitForBuildLock.value,
- out = out,
+ out = outDir,
millActiveCommandMessage = millActiveCommandMessage,
streams = streams,
outLock = outLock,
@@ -270,7 +269,8 @@ object MillMain0 {
// Do this by removing the file rather than disabling selective execution,
// because we still want to generate the selective execution metadata json
// for subsequent runs that may use it
- if (skipSelectiveExecution) os.remove(out / OutFiles.millSelectiveExecution)
+ if (skipSelectiveExecution)
+ os.remove(outDir / OutFiles.millSelectiveExecution)
mill.api.SystemStreamsUtils.withStreams(logger.streams) {
mill.api.FilesystemCheckerEnabled.withValue(
!config.noFilesystemChecker.value
@@ -278,7 +278,7 @@ object MillMain0 {
tailManager.withOutErr(logger.streams.out, logger.streams.err) {
new MillBuildBootstrap(
projectRoot = BuildCtx.workspaceRoot,
- output = out,
+ output = outDir,
// In BSP server, we want to evaluate as many tasks as possible,
// in order to give as many results as available in BSP responses
keepGoing = bspMode || config.keepGoing.value,
@@ -313,7 +313,7 @@ object MillMain0 {
daemonDir = daemonDir,
colored = colored,
colors = colors,
- out = out
+ out = outDir
)) { logger =>
proceed(logger)
}
@@ -358,7 +358,7 @@ object MillMain0 {
val bspLogger = getBspLogger(streams, config)
var prevRunnerStateOpt = Option.empty[RunnerState]
val (bspServerHandle, buildClient) =
- startBspServer(streams0, outLock, bspLogger)
+ startBspServer(streams0, outLock, bspLogger, outDir)
var keepGoing = true
var errored = false
val initCommandLogger = new PrefixLogger(bspLogger, Seq("init"))
@@ -510,6 +510,7 @@ object MillMain0 {
}
}
}
+
if (config.ringBell.value) {
if (success) println("\u0007")
else {
@@ -524,6 +525,7 @@ object MillMain0 {
}
}
}
+ }
/**
* Starts the BSP server
@@ -533,13 +535,13 @@ object MillMain0 {
def startBspServer(
bspStreams: SystemStreams,
outLock: Lock,
- bspLogger: Logger
+ bspLogger: Logger,
+ outDir: os.Path
): (BspServerHandle, BuildClient) = {
bspLogger.info("Trying to load BSP server...")
- val wsRoot = BuildCtx.workspaceRoot
- val outFolder = wsRoot / os.RelPath(OutFiles.outFor(OutFolderMode.BSP))
- val logDir = outFolder / "mill-bsp"
+ BuildCtx.workspaceRoot
+ val logDir = outDir / "mill-bsp"
os.makeDir.all(logDir)
val bspServerHandleRes =
@@ -550,7 +552,7 @@ object MillMain0 {
true,
outLock,
bspLogger,
- outFolder
+ outDir
).get
bspLogger.info("BSP server started")
diff --git a/runner/daemon/src/mill/daemon/MillNoDaemonMain.scala b/runner/daemon/src/mill/daemon/MillNoDaemonMain.scala
index f70dbd2282a7..bcbb5754876a 100644
--- a/runner/daemon/src/mill/daemon/MillNoDaemonMain.scala
+++ b/runner/daemon/src/mill/daemon/MillNoDaemonMain.scala
@@ -2,13 +2,13 @@ package mill.daemon
import mill.constants.{DaemonFiles, OutFiles, Util}
import mill.daemon.MillMain0.{handleMillException, main0}
-import mill.api.BuildCtx
import mill.server.Server
import scala.jdk.CollectionConverters.*
import scala.util.Properties
object MillNoDaemonMain {
+
def main(args0: Array[String]): Unit = mill.api.SystemStreamsUtils.withTopLevelSystemStreamProxy {
val initialSystemStreams = mill.api.SystemStreams.original
@@ -28,9 +28,8 @@ object MillNoDaemonMain {
.fold(err => throw IllegalArgumentException(err), identity)
val processId = Server.computeProcessId()
- val out = os.Path(OutFiles.outFor(args.outMode), BuildCtx.workspaceRoot)
Server.watchProcessIdFile(
- out / OutFiles.millNoDaemon / s"pid-$processId" / DaemonFiles.processId,
+ args.outDir / OutFiles.millNoDaemon / s"pid-$processId" / DaemonFiles.processId,
processId,
running = () => true,
exit = msg => {
@@ -39,7 +38,7 @@ object MillNoDaemonMain {
}
)
- val outLock = MillMain0.doubleLock(out)
+ val outLock = MillMain0.doubleLock(args.outDir)
val (result, _) =
try main0(
@@ -53,7 +52,8 @@ object MillNoDaemonMain {
initialSystemProperties = sys.props.toMap,
systemExit = ( /*reason*/ _, exitCode) => sys.exit(exitCode),
daemonDir = args.daemonDir,
- outLock = outLock
+ outLock = outLock,
+ outDir = args.outDir
)
catch handleMillException(initialSystemStreams.err, ())
diff --git a/runner/launcher/src/mill/launcher/MillLauncherMain.java b/runner/launcher/src/mill/launcher/MillLauncherMain.java
index a914102421af..233a0369424b 100644
--- a/runner/launcher/src/mill/launcher/MillLauncherMain.java
+++ b/runner/launcher/src/mill/launcher/MillLauncherMain.java
@@ -1,5 +1,6 @@
package mill.launcher;
+import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -54,6 +55,7 @@ public static void main(String[] args) throws Exception {
var outMode = bspMode ? OutFolderMode.BSP : OutFolderMode.REGULAR;
exitInTestsAfterBspCheck();
var outDir = OutFiles.outFor(outMode);
+ var outPath = new File(outDir).getAbsoluteFile();
if (outMode == OutFolderMode.BSP) {
System.err.println(
@@ -87,8 +89,8 @@ public static void main(String[] args) throws Exception {
if (runNoDaemon) {
String mainClass = bspMode ? "mill.daemon.MillBspMain" : "mill.daemon.MillNoDaemonMain";
// start in no-server mode
- int exitCode =
- MillProcessLauncher.launchMillNoDaemon(args, outMode, runnerClasspath, mainClass);
+ int exitCode = MillProcessLauncher.launchMillNoDaemon(
+ args, outMode, outPath, runnerClasspath, mainClass);
System.exit(exitCode);
} else {
var logs = new java.util.ArrayList();
@@ -107,9 +109,9 @@ public static void main(String[] args) throws Exception {
Optional.empty(),
-1) {
public LaunchedServer initServer(Path daemonDir, Locks locks) throws Exception {
- return new LaunchedServer.OsProcess(
- MillProcessLauncher.launchMillDaemon(daemonDir, outMode, runnerClasspath)
- .toHandle());
+ return new LaunchedServer.OsProcess(MillProcessLauncher.launchMillDaemon(
+ daemonDir, outMode, outPath, runnerClasspath)
+ .toHandle());
}
};
diff --git a/runner/launcher/src/mill/launcher/MillProcessLauncher.java b/runner/launcher/src/mill/launcher/MillProcessLauncher.java
index 2042ec335ce9..3e8d7291a19d 100644
--- a/runner/launcher/src/mill/launcher/MillProcessLauncher.java
+++ b/runner/launcher/src/mill/launcher/MillProcessLauncher.java
@@ -20,7 +20,7 @@
public class MillProcessLauncher {
static int launchMillNoDaemon(
- String[] args, OutFolderMode outMode, String[] runnerClasspath, String mainClass)
+ String[] args, OutFolderMode outMode, File outDir, String[] runnerClasspath, String mainClass)
throws Exception {
final String sig = String.format("%08x", UUID.randomUUID().hashCode());
final Path processDir =
@@ -35,6 +35,7 @@ static int launchMillNoDaemon(
l.add(mainClass);
l.add(processDir.toAbsolutePath().toString());
l.add(outMode.asString());
+ l.add(outDir.toString());
l.addAll(millOpts(outMode));
l.addAll(Arrays.asList(args));
@@ -60,12 +61,14 @@ static int launchMillNoDaemon(
}
}
- static Process launchMillDaemon(Path daemonDir, OutFolderMode outMode, String[] runnerClasspath)
+ static Process launchMillDaemon(
+ Path daemonDir, OutFolderMode outMode, File outDir, String[] runnerClasspath)
throws Exception {
List l = new ArrayList<>(millLaunchJvmCommand(outMode, runnerClasspath));
l.add("mill.daemon.MillDaemonMain");
l.add(daemonDir.toFile().getCanonicalPath());
l.add(outMode.asString());
+ l.add(outDir.toString());
ProcessBuilder builder = new ProcessBuilder()
.command(l)
@@ -265,11 +268,6 @@ static List millLaunchJvmCommand(OutFolderMode outMode, String[] runnerC
return vmOptions;
}
- static String[] cachedComputedValue(
- OutFolderMode outMode, String name, String key, Supplier block) {
- return cachedComputedValue0(outMode, name, key, block, arr -> true);
- }
-
static String[] cachedComputedValue0(
OutFolderMode outMode,
String name,
@@ -307,6 +305,11 @@ static String[] cachedComputedValue0(
}
}
+ static String[] cachedComputedValue(
+ OutFolderMode outMode, String name, String key, Supplier block) {
+ return cachedComputedValue0(outMode, name, key, block, arr -> true);
+ }
+
static int getTerminalDim(String s, boolean inheritError) throws Exception {
Process proc = new ProcessBuilder()
.command("tput", s)
diff --git a/runner/server/src/mill/server/MillDaemonServer.scala b/runner/server/src/mill/server/MillDaemonServer.scala
index 76a9c386661b..a9dab2f857b4 100644
--- a/runner/server/src/mill/server/MillDaemonServer.scala
+++ b/runner/server/src/mill/server/MillDaemonServer.scala
@@ -25,6 +25,7 @@ abstract class MillDaemonServer[State](
daemonDir: os.Path,
acceptTimeout: FiniteDuration,
locks: Locks,
+ outDir: os.Path,
testLogEvenWhenServerIdWrong: Boolean = false
) extends Server[DaemonServerData, Int](Server.Args(
daemonDir = daemonDir,
@@ -35,7 +36,6 @@ abstract class MillDaemonServer[State](
)) {
def outLock: mill.client.lock.Lock
- def outFolder: os.Path
private var stateCache: State = initialStateCache
@@ -90,7 +90,7 @@ abstract class MillDaemonServer[State](
MillDaemonServer.withOutLock(
noBuildLock = false,
noWaitForBuildLock = false,
- out = outFolder,
+ out = outDir,
millActiveCommandMessage = "checking server mill version and java version",
streams = new mill.api.daemon.SystemStreams(
new PrintStream(mill.api.daemon.DummyOutputStream),
diff --git a/runner/server/test/src/mill/server/ClientServerTests.scala b/runner/server/test/src/mill/server/ClientServerTests.scala
index d5ce210e4399..b0d87e9989fd 100644
--- a/runner/server/test/src/mill/server/ClientServerTests.scala
+++ b/runner/server/test/src/mill/server/ClientServerTests.scala
@@ -26,16 +26,15 @@ object ClientServerTests extends TestSuite {
testLogEvenWhenServerIdWrong: Boolean,
commandSleepMillis: Int = 0
) extends MillDaemonServer[Option[Int]](
- daemonDir,
- 1000.millis,
- locks,
- testLogEvenWhenServerIdWrong
+ daemonDir = daemonDir,
+ acceptTimeout = 1000.millis,
+ locks = locks,
+ outDir = os.temp.dir(),
+ testLogEvenWhenServerIdWrong = testLogEvenWhenServerIdWrong
) {
override def outLock = mill.client.lock.Lock.memory()
- override def outFolder = os.temp.dir()
-
def initialStateCache = None
override def serverLog0(s: String) = {
diff --git a/testkit/src/mill/testkit/UnitTester.scala b/testkit/src/mill/testkit/UnitTester.scala
index 3b90d4771683..8b9e8b3d6dc9 100644
--- a/testkit/src/mill/testkit/UnitTester.scala
+++ b/testkit/src/mill/testkit/UnitTester.scala
@@ -1,12 +1,20 @@
package mill.testkit
import mill.Task
-import mill.api.{BuildCtx, DummyInputStream, ExecResult, Result, SystemStreams, Val}
+import mill.api.{
+ BuildCtx,
+ DummyInputStream,
+ Evaluator,
+ ExecResult,
+ MappedRoots,
+ Result,
+ SelectMode,
+ SystemStreams,
+ Val
+}
import mill.api.ExecResult.OuterStack
import mill.constants.OutFiles.millChromeProfile
import mill.constants.OutFiles.millProfile
-import mill.api.Evaluator
-import mill.api.SelectMode
import mill.internal.JsonArrayLogger
import mill.resolve.Resolve
@@ -229,7 +237,9 @@ class UnitTester(
def scoped[T](tester: UnitTester => T): T = {
try {
BuildCtx.workspaceRoot0.withValue(module.moduleDir) {
- tester(this)
+ MappedRoots.withMillDefaults(outPath = outPath) {
+ tester(this)
+ }
}
} finally close()
}
diff --git a/website/blog/modules/ROOT/pages/12-direct-style-build-tool.adoc b/website/blog/modules/ROOT/pages/12-direct-style-build-tool.adoc
index a58837e17ede..d16806d8ee01 100644
--- a/website/blog/modules/ROOT/pages/12-direct-style-build-tool.adoc
+++ b/website/blog/modules/ROOT/pages/12-direct-style-build-tool.adoc
@@ -74,7 +74,7 @@ Test foo.FooTest.testSimple finished, ...
0 failed, 0 ignored, 2 total, ...
> ./mill show foo.assembly
-".../out/foo/assembly.dest/out.jar"
+"...$MILL_OUT/foo/assembly.dest/out.jar"
> ./out/foo/assembly.dest/out.jar --text hello
hello
diff --git a/website/blog/modules/ROOT/pages/7-graal-native-executables.adoc b/website/blog/modules/ROOT/pages/7-graal-native-executables.adoc
index 2eeb3eff3cf9..1e952c1aae48 100644
--- a/website/blog/modules/ROOT/pages/7-graal-native-executables.adoc
+++ b/website/blog/modules/ROOT/pages/7-graal-native-executables.adoc
@@ -107,7 +107,7 @@ outside of the build tool:
[source,console]
----
$ ./mill show foo.assembly
-".../out/foo/assembly.dest/out.jar"
+"...$MILL_OUT/foo/assembly.dest/out.jar"
$ out/foo/assembly.dest/out.jar --text "hello world"
hello world
@@ -159,7 +159,7 @@ Now, we can use build a native image using `foo.nativeImage`:
[source,console]
----
$ ./mill show foo.nativeImage
-".../out/foo/nativeImage.dest/native-executable"
+"...$MILL_OUT/foo/nativeImage.dest/native-executable"
$ out/foo/nativeImage.dest/native-executable --text "hello world"
hello world
@@ -229,7 +229,7 @@ _Executable Assembly_
----
$ time ./mill show foo.assembly
[1-41] [info] compiling 1 Java source...
-".../out/foo/assembly.dest/out.jar"
+"...$MILL_OUT/foo/assembly.dest/out.jar"
./mill show foo.assembly 0.12s user 0.06s system 21% cpu 0.818 total
----
@@ -243,7 +243,7 @@ $ time ./mill show foo.nativeImage
[1-50] [2/8] Performing analysis... [****] (7.9s @ 0.77GB)
...
[1-50] Finished generating 'native-executable' in 26.0s.
-".../out/foo/nativeImage.dest/native-executable"
+"...$MILL_OUT/foo/nativeImage.dest/native-executable"
./mill show foo.nativeImage 0.70s user 1.11s system 7% cpu 24.762 total
----
diff --git a/website/blog/modules/ROOT/pages/9-mill-faster-assembly-jars.adoc b/website/blog/modules/ROOT/pages/9-mill-faster-assembly-jars.adoc
index 2818d48fb406..69f00125d926 100644
--- a/website/blog/modules/ROOT/pages/9-mill-faster-assembly-jars.adoc
+++ b/website/blog/modules/ROOT/pages/9-mill-faster-assembly-jars.adoc
@@ -124,7 +124,7 @@ to build an assembly that we can run using `java -jar`:
[source,console]
----
> ./mill show assembly
-".../out/assembly.dest/out.jar"
+"...$MILL_OUT/assembly.dest/out.jar"
Total time: 27s
$ ls -lh out/assembly.dest/out.jar
@@ -325,7 +325,7 @@ the code and re-build the assembly:
> echo "class dummy" >> src/main/scala/foo/Foo.scala
> ./mill show assembly
-".../out/assembly.dest/out.jar"
+"...$MILL_OUT/assembly.dest/out.jar"
Total time: 1s
> sbt assembly