Skip to content

Commit 40bc124

Browse files
authored
Add hooks into serializing/constructing os.Paths and launching subprocesses (#365)
Necessary for com-lihaoyi/mill#3660 in Mill. Essentially we need a way to (a) serialize absolute paths as relative paths and (b) set up the necessary symlinks in any subprocess folder such that the relative paths point to the correct absolute location
1 parent c9bcc93 commit 40bc124

File tree

6 files changed

+53
-100
lines changed

6 files changed

+53
-100
lines changed

os/src-jvm/ResourceApi.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package os
2+
trait ResourceApi {
3+
def resource(implicit resRoot: ResourceRoot = Thread.currentThread().getContextClassLoader) = {
4+
os.ResourcePath.resource(resRoot)
5+
}
6+
7+
}

os/src-jvm/package.scala

Lines changed: 0 additions & 83 deletions
This file was deleted.

os/src-native/ResourceApi.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
package os
2+
trait ResourceApi

os/src/Path.scala

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import scala.language.implicitConversions
77
import acyclic.skipped
88
import os.PathError.{InvalidSegment, NonCanonicalLiteral}
99

10-
import scala.util.Try //needed for cross-version defined macros
10+
import scala.util.{DynamicVariable, Try} //needed for cross-version defined macros
1111

1212
trait PathChunk {
1313
def segments: Seq[String]
@@ -438,6 +438,31 @@ object SubPath extends SubPathMacros {
438438
}
439439

440440
object Path extends PathMacros {
441+
@experimental trait Serializer {
442+
def serializeString(p: os.Path): String
443+
def serializeFile(p: os.Path): java.io.File
444+
def serializePath(p: os.Path): java.nio.file.Path
445+
def deserialize(s: String): java.nio.file.Path
446+
def deserialize(s: java.io.File): java.nio.file.Path
447+
def deserialize(s: java.nio.file.Path): java.nio.file.Path
448+
def deserialize(s: java.net.URI): java.nio.file.Path
449+
}
450+
@experimental val pathSerializer = new DynamicVariable[Serializer](defaultPathSerializer)
451+
@experimental object defaultPathSerializer extends Serializer {
452+
def serializeString(p: os.Path): String = p.wrapped.toString
453+
def serializeFile(p: os.Path): java.io.File = p.wrapped.toFile
454+
def serializePath(p: os.Path): java.nio.file.Path = p.wrapped
455+
def deserialize(s: String) = Paths.get(s)
456+
def deserialize(s: java.io.File) = Paths.get(s.getPath)
457+
def deserialize(s: java.nio.file.Path) = s
458+
def deserialize(s: java.net.URI) = s.getScheme() match {
459+
case "file" => Paths.get(s)
460+
case uriType =>
461+
throw new IllegalArgumentException(
462+
s"""os.Path can only be created from a "file" URI scheme, but found "${uriType}""""
463+
)
464+
}
465+
}
441466
def apply(p: FilePath, base: Path) = p match {
442467
case p: RelPath => base / p
443468
case p: SubPath => base / p
@@ -562,7 +587,7 @@ class Path private[os] (val wrapped: java.nio.file.Path)
562587
val resolved = wrapped.resolve(chunk.toString).normalize()
563588
new Path(resolved)
564589
}
565-
override def toString = wrapped.toString
590+
override def toString = Path.pathSerializer.value.serializeString(this)
566591

567592
override def equals(o: Any): Boolean = o match {
568593
case p: Path => wrapped.equals(p.wrapped)
@@ -593,8 +618,8 @@ class Path private[os] (val wrapped: java.nio.file.Path)
593618
new RelPath(segments.drop(nonUpIndex), nonUpIndex)
594619
}
595620

596-
def toIO: java.io.File = wrapped.toFile
597-
def toNIO: java.nio.file.Path = wrapped
621+
def toIO: java.io.File = Path.pathSerializer.value.serializeFile(this)
622+
def toNIO: java.nio.file.Path = Path.pathSerializer.value.serializePath(this)
598623

599624
def resolveFrom(base: os.Path) = this
600625

@@ -608,23 +633,18 @@ sealed trait PathConvertible[T] {
608633

609634
object PathConvertible {
610635
implicit object StringConvertible extends PathConvertible[String] {
611-
def apply(t: String) = Paths.get(t)
636+
def apply(t: String) = Path.pathSerializer.value.deserialize(t)
612637
}
613638
implicit object JavaIoFileConvertible extends PathConvertible[java.io.File] {
614-
def apply(t: java.io.File) = Paths.get(t.getPath)
639+
def apply(t: java.io.File) = Path.pathSerializer.value.deserialize(t)
615640
}
616641
implicit object NioPathConvertible extends PathConvertible[java.nio.file.Path] {
617-
def apply(t: java.nio.file.Path) = t
642+
def apply(t: java.nio.file.Path) = Path.pathSerializer.value.deserialize(t)
618643
override def isCustomFs(t: java.nio.file.Path): Boolean =
619644
t.getFileSystem() != java.nio.file.FileSystems.getDefault()
620645
}
621646
implicit object UriPathConvertible extends PathConvertible[URI] {
622-
def apply(uri: URI) = uri.getScheme() match {
623-
case "file" => Paths.get(uri)
624-
case uriType =>
625-
throw new IllegalArgumentException(
626-
s"""os.Path can only be created from a "file" URI scheme, but found "${uriType}""""
627-
)
628-
}
647+
def apply(uri: URI) = Path.pathSerializer.value.deserialize(uri)
648+
629649
}
630650
}

os/src/ProcessOps.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,9 @@ case class ProcGroup private[os] (commands: Seq[proc]) {
607607
def pipeTo(next: proc) = ProcGroup(commands :+ next)
608608
}
609609

610-
private[os] object ProcessOps {
610+
@experimental
611+
object ProcessOps {
612+
val spawnHook = new scala.util.DynamicVariable[os.Path => Unit]({ p => () })
611613
def buildProcess(
612614
command: Seq[String],
613615
cwd: Path = null,
@@ -644,7 +646,9 @@ private[os] object ProcessOps {
644646

645647
addToProcessEnv(env)
646648

647-
builder.directory(Option(cwd).getOrElse(os.pwd).toIO)
649+
val dir = Option(cwd).getOrElse(os.pwd)
650+
builder.directory(dir.toIO)
651+
spawnHook.value.apply(dir)
648652

649653
builder
650654
.command(command: _*)

os/src-native/package.scala renamed to os/src/package.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import scala.language.implicitConversions
12
import java.nio.file.FileSystem
23
import java.nio.file.FileSystems
4+
import java.nio.file.Paths
35
import scala.util.DynamicVariable
4-
package object os {
6+
7+
package object os extends ResourceApi {
58
type Generator[+T] = geny.Generator[T]
69
val Generator = geny.Generator
710
implicit def GlobSyntax(s: StringContext): GlobInterpolator = new GlobInterpolator(s)

0 commit comments

Comments
 (0)