From 738104aafa8ac0f0dd09e9ef015d909b3f016158 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Wed, 18 Oct 2017 21:54:37 -0700 Subject: - Add a configurable workspace to `evaluateTargetGraph` - Break up `Hbt.scala` into `Args.scala` `Target.scala` and `Evaluator.scala` --- src/main/scala/hbt/Args.scala | 11 ++++ src/main/scala/hbt/Evaluator.scala | 63 ++++++++++++++++++++ src/main/scala/hbt/Hbt.scala | 119 ------------------------------------- src/main/scala/hbt/Main.scala | 2 +- src/main/scala/hbt/Target.scala | 61 +++++++++++++++++++ 5 files changed, 136 insertions(+), 120 deletions(-) create mode 100644 src/main/scala/hbt/Args.scala create mode 100644 src/main/scala/hbt/Evaluator.scala delete mode 100644 src/main/scala/hbt/Hbt.scala create mode 100644 src/main/scala/hbt/Target.scala (limited to 'src/main/scala/hbt') diff --git a/src/main/scala/hbt/Args.scala b/src/main/scala/hbt/Args.scala new file mode 100644 index 00000000..c87ec96b --- /dev/null +++ b/src/main/scala/hbt/Args.scala @@ -0,0 +1,11 @@ +package hbt + +import java.nio.{file => jnio} + +class Args(val args: IndexedSeq[_], val dest: jnio.Path){ + def length = args.length + def apply[T](index: Int): T = { + if (index >= 0 && index < args.length) args(index).asInstanceOf[T] + else throw new IndexOutOfBoundsException(s"Index $index outside of range 0 - ${args.length}") + } +} diff --git a/src/main/scala/hbt/Evaluator.scala b/src/main/scala/hbt/Evaluator.scala new file mode 100644 index 00000000..442ec633 --- /dev/null +++ b/src/main/scala/hbt/Evaluator.scala @@ -0,0 +1,63 @@ +package hbt + +import java.nio.{file => jnio} + +import sourcecode.Enclosing + +import scala.collection.mutable + +object Evaluator{ + + + def apply[T](t: Target[T], + workspacePath: jnio.Path) + (implicit enclosing: Enclosing): T = { + jnio.Files.createDirectories(workspacePath) + val targetPaths = mutable.Map.empty[Target[_], List[String]] + def rec(t: Target[_], path: List[String]): Unit = { + if (targetPaths.contains(t)) () // do nothing + else { + val currentPath = + if (!t.label.startsWith(enclosing.value)) path.reverse + else t.label.stripPrefix(enclosing.value).drop(1).split('.').toList + + targetPaths(t) = currentPath + t.inputs.zipWithIndex.foreach{case (c, i) => rec(c, i.toString :: currentPath)} + } + } + rec(t, Nil) + val targets = targetPaths.keys.toIndexedSeq + val targetIndices = targets.zipWithIndex.toMap + + val numberedEdges = + for(i <- targets.indices) + yield targets(i).inputs.map(targetIndices) + + val sortedClusters = Tarjans(numberedEdges) + val nonTrivialClusters = sortedClusters.filter(_.length > 1) + assert(nonTrivialClusters.isEmpty, nonTrivialClusters) + + val results = mutable.Map.empty[Target[_], Any] + for (cluster <- sortedClusters){ + val Seq(singletonIndex) = cluster + val target = targets(singletonIndex) + val inputResults = target.inputs.map(results) + val targetDestPath = workspacePath.resolve( + jnio.Paths.get(targetPaths(target).mkString("/")) + ) + import collection.JavaConverters._ + if (jnio.Files.exists(targetDestPath)){ + jnio.Files.walk(targetDestPath).iterator() + .asScala + .toArray + .reverseIterator + .map(jnio.Files.deleteIfExists) + } + + results(target) = target.evaluate( + new Args(inputResults.toIndexedSeq, targetDestPath) + ) + } + results(t).asInstanceOf[T] + } +} diff --git a/src/main/scala/hbt/Hbt.scala b/src/main/scala/hbt/Hbt.scala deleted file mode 100644 index b321920b..00000000 --- a/src/main/scala/hbt/Hbt.scala +++ /dev/null @@ -1,119 +0,0 @@ -package hbt - -import java.nio.{file => jnio} - -import sourcecode.Enclosing - -import scala.collection.mutable -class Args(val args: IndexedSeq[_], val dest: jnio.Path){ - def length = args.length - def apply[T](index: Int): T = { - if (index >= 0 && index < args.length) args(index).asInstanceOf[T] - else throw new IndexOutOfBoundsException(s"Index $index outside of range 0 - ${args.length}") - } -} -trait Target[T]{ - val label: String - val inputs: Seq[Target[_]] - def evaluate(args: Args): T - - def map[V](f: T => V)(implicit path: Enclosing) = { - Target.Mapped(this, f, path.value) - } - def zip[V](other: Target[V])(implicit path: Enclosing) = { - Target.Zipped(this, other, path.value) - } - def ~[V, R](other: Target[V]) - (implicit s: Implicits.Sequencer[T, V, R]): Target[R] = { - this.zip(other).map(s.apply _ tupled) - } - -} - -object Target{ - def traverse[T](source: Seq[Target[T]])(implicit path: Enclosing) = { - Traverse[T](source, path.value) - } - case class Traverse[T](inputs: Seq[Target[T]], label: String) extends Target[Seq[T]]{ - def evaluate(args: Args) = { - for (i <- 0 until args.length) - yield args(i).asInstanceOf[T] - } - - } - case class Mapped[T, V](source: Target[T], f: T => V, - label: String) extends Target[V]{ - def evaluate(args: Args) = f(args(0)) - val inputs = List(source) - } - case class Zipped[T, V](source1: Target[T], - source2: Target[V], - label: String) extends Target[(T, V)]{ - def evaluate(args: Args) = (args(0), args(0)) - val inputs = List(source1, source1) - } - - def path(path: jnio.Path)(implicit label: Enclosing) = Path(path, label.value) - case class Path(path: jnio.Path, label: String) extends Target[jnio.Path]{ - def evaluate(args: Args) = path - val inputs = Nil - } -// case class Command(inputs: Seq[Target[jnio.Path]], -// output: Seq[Target[jnio.Path]], -// label: String) extends Target[Command.Result] -// object Command{ -// case class Result(stdout: String, -// stderr: String, -// writtenFiles: Seq[jnio.Path]) -// } -} -object Hbt{ - - - def evaluateTargetGraph[T](t: Target[T])(implicit enclosing: Enclosing): T = { - val targetPaths = mutable.Map.empty[Target[_], List[String]] - def rec(t: Target[_], path: List[String]): Unit = { - if (targetPaths.contains(t)) () // do nothing - else { - val currentPath = - if (!t.label.startsWith(enclosing.value)) path.reverse - else t.label.stripPrefix(enclosing.value).drop(1).split('.').toList - - targetPaths(t) = currentPath - t.inputs.zipWithIndex.foreach{case (c, i) => rec(c, i.toString :: currentPath)} - } - } - rec(t, Nil) - val targets = targetPaths.keys.toIndexedSeq - val targetIndices = targets.zipWithIndex.toMap - - val numberedEdges = - for(i <- targets.indices) - yield targets(i).inputs.map(targetIndices) - - val sortedClusters = Tarjans(numberedEdges) - val nonTrivialClusters = sortedClusters.filter(_.length > 1) - assert(nonTrivialClusters.isEmpty, nonTrivialClusters) - - val results = mutable.Map.empty[Target[_], Any] - for (cluster <- sortedClusters){ - val Seq(singletonIndex) = cluster - val target = targets(singletonIndex) - val inputResults = target.inputs.map(results) - val targetDestPath = jnio.Paths.get(targetPaths(target).mkString("/")) - import collection.JavaConverters._ - if (jnio.Files.exists(targetDestPath)){ - jnio.Files.walk(targetDestPath).iterator() - .asScala - .toArray - .reverseIterator - .map(jnio.Files.deleteIfExists) - } - - results(target) = target.evaluate( - new Args(inputResults.toIndexedSeq, targetDestPath) - ) - } - results(t).asInstanceOf[T] - } -} diff --git a/src/main/scala/hbt/Main.scala b/src/main/scala/hbt/Main.scala index 3baadda1..db5e2820 100644 --- a/src/main/scala/hbt/Main.scala +++ b/src/main/scala/hbt/Main.scala @@ -8,6 +8,6 @@ object Main{ val allSources = list(sourceRoot) val classFiles = compileAll(allSources) val jar = jarUp(resourceRoot, classFiles) - Hbt.evaluateTargetGraph(jar) + Evaluator.apply(jar, jnio.Paths.get("target/workspace")) } } \ No newline at end of file diff --git a/src/main/scala/hbt/Target.scala b/src/main/scala/hbt/Target.scala new file mode 100644 index 00000000..99f6e27d --- /dev/null +++ b/src/main/scala/hbt/Target.scala @@ -0,0 +1,61 @@ +package hbt + +import java.nio.{file => jnio} + +import sourcecode.Enclosing + +trait Target[T]{ + val label: String + val inputs: Seq[Target[_]] + def evaluate(args: Args): T + + def map[V](f: T => V)(implicit path: Enclosing) = { + Target.Mapped(this, f, path.value) + } + def zip[V](other: Target[V])(implicit path: Enclosing) = { + Target.Zipped(this, other, path.value) + } + def ~[V, R](other: Target[V]) + (implicit s: Implicits.Sequencer[T, V, R]): Target[R] = { + this.zip(other).map(s.apply _ tupled) + } + +} + +object Target{ + def traverse[T](source: Seq[Target[T]])(implicit path: Enclosing) = { + Traverse[T](source, path.value) + } + case class Traverse[T](inputs: Seq[Target[T]], label: String) extends Target[Seq[T]]{ + def evaluate(args: Args) = { + for (i <- 0 until args.length) + yield args(i).asInstanceOf[T] + } + + } + case class Mapped[T, V](source: Target[T], f: T => V, + label: String) extends Target[V]{ + def evaluate(args: Args) = f(args(0)) + val inputs = List(source) + } + case class Zipped[T, V](source1: Target[T], + source2: Target[V], + label: String) extends Target[(T, V)]{ + def evaluate(args: Args) = (args(0), args(0)) + val inputs = List(source1, source1) + } + + def path(path: jnio.Path)(implicit label: Enclosing) = Path(path, label.value) + case class Path(path: jnio.Path, label: String) extends Target[jnio.Path]{ + def evaluate(args: Args) = path + val inputs = Nil + } +// case class Command(inputs: Seq[Target[jnio.Path]], +// output: Seq[Target[jnio.Path]], +// label: String) extends Target[Command.Result] +// object Command{ +// case class Result(stdout: String, +// stderr: String, +// writtenFiles: Seq[jnio.Path]) +// } +} -- cgit v1.2.3