From 5dc0aa5cf59c2f9c6c95b4a200926794a6c75fa9 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sat, 7 Sep 2024 17:57:01 +0800 Subject: [PATCH] Make `os.pwd` modifiable via the `os.pwd0` dynamic variable (#298) Basically the same thing we did in https://github.com/com-lihaoyi/os-lib/pull/295 and https://github.com/com-lihaoyi/os-lib/pull/283, except rather than being `os.SubProcess`-specific this applies to all usage of `os.pwd`. And with similar limitations: this works for code paths that go through OS-Lib, but not for code that uses `java.io` or `java.nio` directly. AFAIK this is the last of the "global" things we need to redirect in Mill tasks, after env variables and default subprocess streams. I'd like it to go into Mill 0.12.0 together with the other sandboxing-related changes Added unit tests and updated the documentation Pull Request: https://github.com/com-lihaoyi/os-lib/pull/298 --- Readme.adoc | 4 ++-- os/src-jvm/package.scala | 5 ++++- os/src-native/package.scala | 5 ++++- os/test/src/PathTests.scala | 8 ++++++++ os/test/src/SubprocessTests.scala | 19 +++++++++++++++++++ 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/Readme.adoc b/Readme.adoc index 4c36e35b..125fe60e 100644 --- a/Readme.adoc +++ b/Readme.adoc @@ -1828,8 +1828,8 @@ wd / os.up wd / os.up / os.up ---- -Note that there are no in-built operations to change the `os.pwd`. In general, -you should not need to: simply defining a new path, e.g. +`os.pwd` can be modified in certain scopes via the `os.dynamicPwd` dynamic variable, but +best practice is not to change it. Instead simply define a new path, e.g. [source,scala] ---- diff --git a/os/src-jvm/package.scala b/os/src-jvm/package.scala index e5053657..d11a54d6 100644 --- a/os/src-jvm/package.scala +++ b/os/src-jvm/package.scala @@ -2,6 +2,7 @@ import scala.language.implicitConversions import java.nio.file.FileSystem import java.nio.file.FileSystems import java.nio.file.Paths +import scala.util.DynamicVariable package object os { type Generator[+T] = geny.Generator[T] @@ -38,7 +39,9 @@ package object os { /** * The current working directory for this process. */ - val pwd: Path = os.Path(java.nio.file.Paths.get(".").toAbsolutePath) + def pwd: Path = dynamicPwd.value + val dynamicPwd: DynamicVariable[Path] = + new DynamicVariable(os.Path(java.nio.file.Paths.get(".").toAbsolutePath)) val up: RelPath = RelPath.up diff --git a/os/src-native/package.scala b/os/src-native/package.scala index ea021c90..e71aac1a 100644 --- a/os/src-native/package.scala +++ b/os/src-native/package.scala @@ -1,5 +1,6 @@ import java.nio.file.FileSystem import java.nio.file.FileSystems +import scala.util.DynamicVariable package object os { type Generator[+T] = geny.Generator[T] val Generator = geny.Generator @@ -31,7 +32,9 @@ package object os { /** * The current working directory for this process. */ - val pwd: Path = os.Path(java.nio.file.Paths.get(".").toAbsolutePath) + def pwd: Path = dynamicPwd.value + val dynamicPwd: DynamicVariable[Path] = + new DynamicVariable(os.Path(java.nio.file.Paths.get(".").toAbsolutePath)) val up: RelPath = RelPath.up diff --git a/os/test/src/PathTests.scala b/os/test/src/PathTests.scala index 98aa6ae4..365b1647 100644 --- a/os/test/src/PathTests.scala +++ b/os/test/src/PathTests.scala @@ -434,6 +434,14 @@ object PathTests extends TestSuite { System.err.printf("p[%s]\n", posix(p)) assert(posix(p) contains "/omg") } + test("dynamicPwd") { + val x = os.pwd + val y = os.dynamicPwd.withValue(os.pwd / "hello") { + os.pwd + } + + assert(x / "hello" == y) + } } // compare absolute paths def sameFile(a: java.nio.file.Path, b: java.nio.file.Path): Boolean = { diff --git a/os/test/src/SubprocessTests.scala b/os/test/src/SubprocessTests.scala index ac01bd41..0a240ba2 100644 --- a/os/test/src/SubprocessTests.scala +++ b/os/test/src/SubprocessTests.scala @@ -225,5 +225,24 @@ object SubprocessTests extends TestSuite { assert(output.out.lines() == Seq("HELLO /usr")) } } + test("dynamicPwd") { + // Windows doesnt have bash installed so a bit inconvenient + // to run these subprocesses for testing + if (!scala.util.Properties.isWin) { + val outsidePwd = os.pwd + val tmp0 = os.temp.dir() + val tmp = os.followLink(tmp0).getOrElse(tmp0) + val x = proc("bash", "-c", "pwd").call() + val y = os.dynamicPwd.withValue(tmp) { + proc("bash", "-c", "pwd").call() + } + + val z = proc("bash", "-c", "pwd").call() + assert(outsidePwd.toString != tmp.toString) + assert(x.out.trim() == outsidePwd.toString) + assert(y.out.trim() == tmp.toString) + assert(z.out.trim() == outsidePwd.toString) + } + } } }