From 57501fca3b6c2c64d32744e6d534b9de3a6674f6 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sat, 13 Jan 2018 13:00:21 -0800 Subject: Migrate most classpath-related code onto `Loose.OSet` abstraction, to enforce deduplication --- core/src/main/scala/mill/define/Graph.scala | 3 +- core/src/main/scala/mill/discover/Discovered.scala | 2 +- core/src/main/scala/mill/eval/Evaluator.scala | 1 + .../main/scala/mill/main/ReplApplyHandler.scala | 2 +- core/src/main/scala/mill/main/RunScript.scala | 3 +- core/src/main/scala/mill/modules/Jvm.scala | 13 +- core/src/main/scala/mill/package.scala | 2 + core/src/main/scala/mill/util/MultiBiMap.scala | 2 +- core/src/main/scala/mill/util/OSet.scala | 192 +++++++++++---------- 9 files changed, 116 insertions(+), 104 deletions(-) (limited to 'core/src/main/scala') diff --git a/core/src/main/scala/mill/define/Graph.scala b/core/src/main/scala/mill/define/Graph.scala index e7844691..68fb65ed 100644 --- a/core/src/main/scala/mill/define/Graph.scala +++ b/core/src/main/scala/mill/define/Graph.scala @@ -1,7 +1,8 @@ package mill.define import mill.eval.Tarjans -import mill.util.{MultiBiMap, OSet} +import mill.util.MultiBiMap +import mill.util.Strict.OSet object Graph { class TopoSorted private[Graph](val values: OSet[Task[_]]) diff --git a/core/src/main/scala/mill/discover/Discovered.scala b/core/src/main/scala/mill/discover/Discovered.scala index c5ca4843..b7ec2140 100644 --- a/core/src/main/scala/mill/discover/Discovered.scala +++ b/core/src/main/scala/mill/discover/Discovered.scala @@ -5,7 +5,7 @@ import ammonite.main.Router import ammonite.main.Router.EntryPoint import mill.discover.Mirror.TargetPoint import mill.util.Ctx.Loader -import mill.util.OSet +import mill.util.Strict.OSet import scala.language.experimental.macros import scala.reflect.macros.blackbox.Context diff --git a/core/src/main/scala/mill/eval/Evaluator.scala b/core/src/main/scala/mill/eval/Evaluator.scala index efb10d36..2afaec6e 100644 --- a/core/src/main/scala/mill/eval/Evaluator.scala +++ b/core/src/main/scala/mill/eval/Evaluator.scala @@ -9,6 +9,7 @@ import mill.discover.{Discovered, Mirror} import mill.define.Segment import mill.util import mill.util._ +import mill.util.Strict.OSet import scala.collection.mutable import scala.util.control.NonFatal diff --git a/core/src/main/scala/mill/main/ReplApplyHandler.scala b/core/src/main/scala/mill/main/ReplApplyHandler.scala index d4236487..b83453ee 100644 --- a/core/src/main/scala/mill/main/ReplApplyHandler.scala +++ b/core/src/main/scala/mill/main/ReplApplyHandler.scala @@ -5,7 +5,7 @@ import mill.define.Applicative.ApplyHandler import mill.define._ import mill.discover.{Discovered, Mirror} import mill.eval.{Evaluator, Result} -import mill.util.OSet +import mill.util.Strict.OSet import scala.collection.mutable object ReplApplyHandler{ diff --git a/core/src/main/scala/mill/main/RunScript.scala b/core/src/main/scala/mill/main/RunScript.scala index b8793b40..9f73fbc8 100644 --- a/core/src/main/scala/mill/main/RunScript.scala +++ b/core/src/main/scala/mill/main/RunScript.scala @@ -11,7 +11,8 @@ import mill.define.Task import mill.define.Segment import mill.discover.Discovered import mill.eval.{Evaluator, Result} -import mill.util.{EitherOps, Logger, OSet} +import mill.util.{EitherOps, Logger} +import mill.util.Strict.OSet import upickle.Js /** diff --git a/core/src/main/scala/mill/modules/Jvm.scala b/core/src/main/scala/mill/modules/Jvm.scala index e6806923..6e3fe45f 100644 --- a/core/src/main/scala/mill/modules/Jvm.scala +++ b/core/src/main/scala/mill/modules/Jvm.scala @@ -9,14 +9,15 @@ import ammonite.ops._ import mill.define.Task import mill.eval.PathRef import mill.util.Ctx +import mill.util.Loose.OSet import scala.annotation.tailrec import scala.collection.mutable object Jvm { - def gatherClassloaderJars(): Seq[Path] = { - val allJars = collection.mutable.Buffer.empty[Path] + def gatherClassloaderJars(): OSet[Path] = { + val allJars = new OSet.Mutable[Path]() var currentClassloader = Thread.currentThread().getContextClassLoader while(currentClassloader != null){ currentClassloader match{ @@ -29,14 +30,14 @@ object Jvm { } def interactiveSubprocess(mainClass: String, - classPath: Seq[Path], + classPath: OSet[Path], options: Seq[String] = Seq.empty): Unit = { import ammonite.ops.ImplicitWd._ %("java", "-cp", classPath.mkString(":"), mainClass, options) } def subprocess(mainClass: String, - classPath: Seq[Path], + classPath: OSet[Path], jvmOptions: Seq[String] = Seq.empty, options: Seq[String] = Seq.empty, workingDir: Path = null) @@ -100,7 +101,7 @@ object Jvm { m } - def createJar(inputPaths: Seq[Path], mainClass: Option[String] = None) + def createJar(inputPaths: OSet[Path], mainClass: Option[String] = None) (implicit ctx: Ctx.DestCtx): PathRef = { val outputPath = ctx.dest rm(outputPath) @@ -134,7 +135,7 @@ object Jvm { PathRef(outputPath) } - def createAssembly(inputPaths: Seq[Path], + def createAssembly(inputPaths: OSet[Path], mainClass: Option[String] = None, prependShellScript: String = "") (implicit ctx: Ctx.DestCtx): PathRef = { diff --git a/core/src/main/scala/mill/package.scala b/core/src/main/scala/mill/package.scala index 3463b63f..0b4f4873 100644 --- a/core/src/main/scala/mill/package.scala +++ b/core/src/main/scala/mill/package.scala @@ -8,4 +8,6 @@ package object mill extends JsonFormatters{ type Module = define.Module val Module = define.Module type Cross[T] = define.Cross[T] + type OSet[T] = util.Loose.OSet[T] + val OSet = util.Loose.OSet } diff --git a/core/src/main/scala/mill/util/MultiBiMap.scala b/core/src/main/scala/mill/util/MultiBiMap.scala index ef5359bc..abbaa752 100644 --- a/core/src/main/scala/mill/util/MultiBiMap.scala +++ b/core/src/main/scala/mill/util/MultiBiMap.scala @@ -1,7 +1,7 @@ package mill.util import scala.collection.mutable - +import Strict.OSet /** * A map from keys to collections of values: you can assign multiple values * to any particular key. Also allows lookups in both directions: what values diff --git a/core/src/main/scala/mill/util/OSet.scala b/core/src/main/scala/mill/util/OSet.scala index 52b47cde..a4fcc406 100644 --- a/core/src/main/scala/mill/util/OSet.scala +++ b/core/src/main/scala/mill/util/OSet.scala @@ -3,108 +3,114 @@ package mill.util import scala.collection.mutable - -/** - * A collection with enforced uniqueness, fast contains and deterministic - * ordering. Raises an exception if a duplicate is found; call - * `toSeq.distinct` if you explicitly want to make it swallow duplicates - */ -trait OSet[V] extends TraversableOnce[V]{ - def contains(v: V): Boolean - def items: Iterator[V] - def indexed: IndexedSeq[V] - def flatMap[T](f: V => TraversableOnce[T]): OSet[T] - def map[T](f: V => T): OSet[T] - def filter(f: V => Boolean): OSet[V] - def withFilter(f: V => Boolean): OSet[V] - def collect[T](f: PartialFunction[V, T]): OSet[T] - def zipWithIndex: OSet[(V, Int)] - def reverse: OSet[V] - def zip[T](other: OSet[T]): OSet[(V, T)] -} - -object OSet{ - implicit def jsonFormat[T: upickle.default.ReadWriter]: upickle.default.ReadWriter[OSet[T]] = - upickle.default.ReadWriter[OSet[T]] ( - oset => upickle.default.writeJs(oset.toList), - {case json => OSet.from(upickle.default.readJs[Seq[T]](json))} - ) - def apply[V](items: V*) = from(items) - - def from[V](items: TraversableOnce[V]): OSet[V] = { - val set = new OSet.Mutable[V]() - items.foreach(set.append) - set +object Strict extends OSetWrapper(true) +object Loose extends OSetWrapper(false) +sealed class OSetWrapper(strictUniqueness: Boolean){ + /** + * A collection with enforced uniqueness, fast contains and deterministic + * ordering. Raises an exception if a duplicate is found; call + * `toSeq.distinct` if you explicitly want to make it swallow duplicates + */ + trait OSet[V] extends TraversableOnce[V]{ + def contains(v: V): Boolean + def items: Iterator[V] + def indexed: IndexedSeq[V] + def flatMap[T](f: V => TraversableOnce[T]): OSet[T] + def map[T](f: V => T): OSet[T] + def filter(f: V => Boolean): OSet[V] + def withFilter(f: V => Boolean): OSet[V] + def collect[T](f: PartialFunction[V, T]): OSet[T] + def zipWithIndex: OSet[(V, Int)] + def reverse: OSet[V] + def zip[T](other: OSet[T]): OSet[(V, T)] + def ++[T >: V](other: TraversableOnce[T]): OSet[T] } + object OSet{ + def empty[V]: OSet[V] = new OSet.Mutable[V] + implicit def jsonFormat[T: upickle.default.ReadWriter]: upickle.default.ReadWriter[OSet[T]] = + upickle.default.ReadWriter[OSet[T]] ( + oset => upickle.default.writeJs(oset.toList), + {case json => OSet.from(upickle.default.readJs[Seq[T]](json))} + ) + def apply[V](items: V*) = from(items) + + def from[V](items: TraversableOnce[V]): OSet[V] = { + val set = new OSet.Mutable[V]() + items.foreach(set.append) + set + } - class Mutable[V]() extends OSet[V]{ - private[this] val set0 = mutable.LinkedHashSet.empty[V] - def contains(v: V) = set0.contains(v) - def append(v: V) = if (!contains(v)){ - set0.add(v) + class Mutable[V]() extends OSet[V]{ - }else { - throw new Exception("Duplicated item inserted into OrderedSet: " + v) - } - def appendAll(vs: Seq[V]) = vs.foreach(append) - def items = set0.iterator - def indexed: IndexedSeq[V] = items.toIndexedSeq - def set: collection.Set[V] = set0 - - def map[T](f: V => T): OSet[T] = { - val output = new OSet.Mutable[T] - for(i <- items) output.append(f(i)) - output - } - def flatMap[T](f: V => TraversableOnce[T]): OSet[T] = { - val output = new OSet.Mutable[T] - for(i <- items) for(i0 <- f(i)) output.append(i0) - output - } - def filter(f: V => Boolean): OSet[V] = { - val output = new OSet.Mutable[V] - for(i <- items) if (f(i)) output.append(i) - output - } - def withFilter(f: V => Boolean): OSet[V] = filter(f) + private[this] val set0 = mutable.LinkedHashSet.empty[V] + def contains(v: V) = set0.contains(v) + def append(v: V) = if (!contains(v)){ + set0.add(v) - def collect[T](f: PartialFunction[V, T]) = this.filter(f.isDefinedAt).map(x => f(x)) + }else if (strictUniqueness){ + throw new Exception("Duplicated item inserted into OrderedSet: " + v) + } + def appendAll(vs: Seq[V]) = vs.foreach(append) + def items = set0.iterator + def indexed: IndexedSeq[V] = items.toIndexedSeq + def set: collection.Set[V] = set0 + + def map[T](f: V => T): OSet[T] = { + val output = new OSet.Mutable[T] + for(i <- items) output.append(f(i)) + output + } + def flatMap[T](f: V => TraversableOnce[T]): OSet[T] = { + val output = new OSet.Mutable[T] + for(i <- items) for(i0 <- f(i)) output.append(i0) + output + } + def filter(f: V => Boolean): OSet[V] = { + val output = new OSet.Mutable[V] + for(i <- items) if (f(i)) output.append(i) + output + } + def withFilter(f: V => Boolean): OSet[V] = filter(f) - def zipWithIndex = { - var i = 0 - this.map{ x => - i += 1 - (x, i-1) + def collect[T](f: PartialFunction[V, T]) = this.filter(f.isDefinedAt).map(x => f(x)) + + def zipWithIndex = { + var i = 0 + this.map{ x => + i += 1 + (x, i-1) + } } - } - def reverse = OSet.from(indexed.reverseIterator) - - def zip[T](other: OSet[T]) = OSet.from(items.zip(other.items)) - - // Members declared in scala.collection.GenTraversableOnce - def isTraversableAgain: Boolean = items.isTraversableAgain - def toIterator: Iterator[V] = items.toIterator - def toStream: Stream[V] = items.toStream - - // Members declared in scala.collection.TraversableOnce - def copyToArray[B >: V](xs: Array[B],start: Int,len: Int): Unit = items.copyToArray(xs, start, len) - def exists(p: V => Boolean): Boolean = items.exists(p) - def find(p: V => Boolean): Option[V] = items.find(p) - def forall(p: V => Boolean): Boolean = items.forall(p) - def foreach[U](f: V => U): Unit = items.foreach(f) - def hasDefiniteSize: Boolean = items.hasDefiniteSize - def isEmpty: Boolean = items.isEmpty - def seq: scala.collection.TraversableOnce[V] = items - def toTraversable: Traversable[V] = items.toTraversable - - override def hashCode() = items.hashCode() - override def equals(other: Any) = other match{ - case s: OSet[_] => items.sameElements(s.items) - case _ => super.equals(other) + def reverse = OSet.from(indexed.reverseIterator) + + def zip[T](other: OSet[T]) = OSet.from(items.zip(other.items)) + def ++[T >: V](other: TraversableOnce[T]) = OSet.from(items ++ other) + + // Members declared in scala.collection.GenTraversableOnce + def isTraversableAgain: Boolean = items.isTraversableAgain + def toIterator: Iterator[V] = items.toIterator + def toStream: Stream[V] = items.toStream + + // Members declared in scala.collection.TraversableOnce + def copyToArray[B >: V](xs: Array[B], start: Int,len: Int): Unit = items.copyToArray(xs, start, len) + def exists(p: V => Boolean): Boolean = items.exists(p) + def find(p: V => Boolean): Option[V] = items.find(p) + def forall(p: V => Boolean): Boolean = items.forall(p) + def foreach[U](f: V => U): Unit = items.foreach(f) + def hasDefiniteSize: Boolean = items.hasDefiniteSize + def isEmpty: Boolean = items.isEmpty + def seq: scala.collection.TraversableOnce[V] = items + def toTraversable: Traversable[V] = items.toTraversable + + override def hashCode() = items.map(_.hashCode()).sum + override def equals(other: Any) = other match{ + case s: OSet[_] => items.sameElements(s.items) + case _ => super.equals(other) + } + override def toString = items.mkString("OSet(", ", ", ")") } - override def toString = items.mkString("OSet(", ", ", ")") } } -- cgit v1.2.3