summaryrefslogtreecommitdiff
path: root/src/main/scala/forge
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2017-10-22 09:37:50 -0700
committerLi Haoyi <haoyi.sg@gmail.com>2017-10-22 09:37:50 -0700
commitd5b903d44cbd29f838db3ea475d1efa101ad810f (patch)
tree3cb29f86f7be07ab2aaf3cf74fc66e41e9d3ed94 /src/main/scala/forge
parent06afaf6d3cf65cc7b42fa5f31aa6c48421626b6e (diff)
downloadmill-d5b903d44cbd29f838db3ea475d1efa101ad810f.tar.gz
mill-d5b903d44cbd29f838db3ea475d1efa101ad810f.tar.bz2
mill-d5b903d44cbd29f838db3ea475d1efa101ad810f.zip
rename hbt -> forge
Diffstat (limited to 'src/main/scala/forge')
-rw-r--r--src/main/scala/forge/Args.scala11
-rw-r--r--src/main/scala/forge/DefCtx.scala35
-rw-r--r--src/main/scala/forge/Evaluator.scala78
-rw-r--r--src/main/scala/forge/Implicits.scala71
-rw-r--r--src/main/scala/forge/ReadWrite.scala20
-rw-r--r--src/main/scala/forge/Target.scala72
-rw-r--r--src/main/scala/forge/Tarjans.scala61
-rw-r--r--src/main/scala/forge/Util.scala52
8 files changed, 400 insertions, 0 deletions
diff --git a/src/main/scala/forge/Args.scala b/src/main/scala/forge/Args.scala
new file mode 100644
index 00000000..c7cc455c
--- /dev/null
+++ b/src/main/scala/forge/Args.scala
@@ -0,0 +1,11 @@
+package forge
+
+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/forge/DefCtx.scala b/src/main/scala/forge/DefCtx.scala
new file mode 100644
index 00000000..57f2e71e
--- /dev/null
+++ b/src/main/scala/forge/DefCtx.scala
@@ -0,0 +1,35 @@
+package forge
+
+
+import scala.language.experimental.macros
+import scala.reflect.macros._
+
+
+case class DefCtx(staticEnclosing: Option[String])
+object DefCtx{
+ case class StaticContext(value: Boolean)
+ object StaticContext {
+ implicit def default: StaticContext = macro applyImpl
+ def rec(c: Context)(expr: c.Symbol): Boolean = {
+ import c.universe._
+ // Classes and traits and such
+ if(!expr.isModuleClass && expr.isClass) false
+ // Method contents
+ else if(expr.isMethod) false
+ else if(expr.owner == NoSymbol) true
+ else rec(c)(expr.owner)
+ }
+
+ def applyImpl(c: Context): c.Expr[StaticContext] = {
+ import c.universe._
+ val staticContext = rec(c)(c.internal.enclosingOwner)
+ c.Expr[StaticContext](q"hbt.DefCtx.StaticContext($staticContext)")
+ }
+ }
+
+ implicit def default(implicit enc: sourcecode.Enclosing,
+ sc: StaticContext) = {
+ if (sc.value) DefCtx(Some(enc.value))
+ else DefCtx(None)
+ }
+} \ No newline at end of file
diff --git a/src/main/scala/forge/Evaluator.scala b/src/main/scala/forge/Evaluator.scala
new file mode 100644
index 00000000..137943f7
--- /dev/null
+++ b/src/main/scala/forge/Evaluator.scala
@@ -0,0 +1,78 @@
+package forge
+
+import java.nio.{file => jnio}
+
+import sourcecode.Enclosing
+
+import scala.collection.mutable
+
+class Evaluator(workspacePath: jnio.Path,
+ enclosingBase: DefCtx){
+
+ /**
+ * Takes the given targets, finds
+ */
+ def prepareTransitiveTargets(targets: Seq[Target[_]]) = {
+
+ val targetIndices = targets.zipWithIndex.toMap
+
+ val transitiveTargetSet = mutable.Set.empty[Target[_]]
+ def rec(t: Target[_]): Unit = {
+ if (transitiveTargetSet.contains(t)) () // do nothing
+ else {
+ transitiveTargetSet.add(t)
+ t.inputs.foreach(rec)
+ }
+ }
+
+ targets.foreach(rec)
+ val transitiveTargets = transitiveTargetSet.toVector
+ val numberedEdges =
+ for(i <- transitiveTargets.indices)
+ yield targets(i).inputs.map(targetIndices)
+
+ val sortedClusters = Tarjans(numberedEdges)
+ val nonTrivialClusters = sortedClusters.filter(_.length > 1)
+ assert(nonTrivialClusters.isEmpty, nonTrivialClusters)
+ (transitiveTargets, sortedClusters.flatten)
+ }
+
+ def apply[T](t: Target[T])
+ (implicit enclosing: Enclosing): T = {
+ jnio.Files.createDirectories(workspacePath)
+
+ val (transitiveTargets, sortedTargetIndices) = prepareTransitiveTargets(Seq(t))
+ val results = mutable.Map.empty[Target[_], Any]
+ for (index <- sortedTargetIndices){
+
+ val target = transitiveTargets(index)
+ val inputResults = target.inputs.map(results)
+
+ for(enclosing <- target.defCtx.staticEnclosing){
+ val targetDestPath = workspacePath.resolve(
+ jnio.Paths.get(enclosing.stripSuffix(enclosingBase.staticEnclosing.getOrElse("")))
+ )
+ deleteRec(targetDestPath)
+ }
+
+
+
+
+
+ results(target) = target.evaluate(
+ new Args(inputResults.toIndexedSeq, targetDestPath)
+ )
+ }
+ results(t).asInstanceOf[T]
+ }
+ def deleteRec(path: jnio.Path) = {
+ if (jnio.Files.exists(path)){
+ import collection.JavaConverters._
+ jnio.Files.walk(path).iterator()
+ .asScala
+ .toArray
+ .reverseIterator
+ .map(jnio.Files.deleteIfExists)
+ }
+ }
+}
diff --git a/src/main/scala/forge/Implicits.scala b/src/main/scala/forge/Implicits.scala
new file mode 100644
index 00000000..3295ce38
--- /dev/null
+++ b/src/main/scala/forge/Implicits.scala
@@ -0,0 +1,71 @@
+package forge
+
+
+import scala.collection.mutable
+
+/**
+ * Container for all the type-level logic around appending things
+ * to tuples or flattening `Seq[Unit]`s into `Unit`s.
+ *
+ * Some of these implicits make liberal use of mutable state, so as
+ * to minimize allocations while parsing.
+ */
+object Implicits {
+ trait Sequencer[-T, V, R]{
+ def apply(t: T, v: V): R
+ }
+ object Sequencer extends LowPriSequencer{
+ def apply[T, V, R](f: (T, V) => R) = new Sequencer[T, V, R]{
+ def apply(t: T, v: V): R = f(t, v)
+ }
+ implicit def SingleSequencer[T]: Sequencer[Unit, T, T] = Sequencer( (_, t) => t )
+ }
+ trait LowPriSequencer extends LowerPriSequencer{
+ implicit def UnitSequencer[T]: Sequencer[T, Unit, T] = Sequencer( (t, _) => t )
+ }
+ trait LowerPriSequencer extends SequencerGen[Sequencer]{
+ protected[this] def Sequencer0[A, B, C](f: (A, B) => C) = Sequencer(f)
+ }
+ trait Repeater[-T, R]{
+ type Acc
+ def initial: Acc
+ def accumulate(t: T, acc: Acc): Unit
+ def result(acc: Acc): R
+ }
+ object Repeater extends LowPriRepeater{
+ implicit object UnitRepeater extends Repeater[Unit, Unit]{
+ type Acc = Unit
+ def initial = ()
+ def accumulate(t: Unit, acc: Unit) = acc
+ def result(acc: Unit) = ()
+ }
+ }
+ trait LowPriRepeater{
+ implicit def GenericRepeaterImplicit[T] = GenericRepeater[T]()
+ case class GenericRepeater[T]() extends Repeater[T, Seq[T]]{
+ type Acc = mutable.Buffer[T]
+ def initial = mutable.Buffer.empty[T]
+ def accumulate(t: T, acc: mutable.Buffer[T]) = acc += t
+ def result(acc: mutable.Buffer[T]) = acc
+ }
+ }
+
+ trait Optioner[-T, R]{
+ def none: R
+ def some(value: T): R
+ }
+
+ object Optioner extends LowPriOptioner{
+ implicit object UnitOptioner extends Optioner[Unit, Unit]{
+ def none = ()
+ def some(value: Unit) = ()
+ }
+ }
+ trait LowPriOptioner{
+ implicit def GenericOptionerImplicit[T] = GenericOptioner[T]()
+ case class GenericOptioner[T]() extends Optioner[T, Option[T]]{
+ def none = None
+ def some(value: T) = Some(value)
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/scala/forge/ReadWrite.scala b/src/main/scala/forge/ReadWrite.scala
new file mode 100644
index 00000000..ee74f35f
--- /dev/null
+++ b/src/main/scala/forge/ReadWrite.scala
@@ -0,0 +1,20 @@
+package forge
+import java.nio.{file => jnio}
+
+trait ReadWrite[T] {
+ def write(t: T, p: jnio.Path): Unit
+ def read(p: jnio.Path): T
+}
+
+object ReadWrite{
+ implicit object String extends ReadWrite[java.lang.String]{
+ def write(t: String, p: jnio.Path) = {
+ jnio.Files.createDirectories(p.getParent)
+ jnio.Files.deleteIfExists(p)
+ jnio.Files.write(p, t.getBytes)
+ }
+ def read(p: jnio.Path) = {
+ new String(jnio.Files.readAllBytes(p))
+ }
+ }
+}
diff --git a/src/main/scala/forge/Target.scala b/src/main/scala/forge/Target.scala
new file mode 100644
index 00000000..80ff98c4
--- /dev/null
+++ b/src/main/scala/forge/Target.scala
@@ -0,0 +1,72 @@
+package forge
+
+import java.nio.{file => jnio}
+
+trait Target[T]{
+ val defCtx: DefCtx
+
+ val inputs: Seq[Target[_]]
+ def evaluate(args: Args): T
+
+ def map[V](f: T => V)(implicit defCtx: DefCtx) = {
+ Target.Mapped(this, f, defCtx)
+ }
+ def zip[V](other: Target[V])(implicit defCtx: DefCtx) = {
+ Target.Zipped(this, other, defCtx)
+ }
+ def ~[V, R](other: Target[V])
+ (implicit s: Implicits.Sequencer[T, V, R], defCtx: DefCtx): Target[R] = {
+ this.zip(other).map(s.apply _ tupled)
+ }
+
+}
+
+object Target{
+ def noop(inputs: Target[_]*)(implicit defCtx: DefCtx) = NoopTarget(inputs, defCtx)
+ case class NoopTarget(inputs: Seq[Target[_]], defCtx: DefCtx) extends Target[Unit]{
+ def evaluate(args: Args) = ()
+ }
+ def traverse[T](source: Seq[Target[T]])(implicit defCtx: DefCtx) = {
+ Traverse[T](source, defCtx)
+ }
+ case class Traverse[T](inputs: Seq[Target[T]], defCtx: DefCtx) 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,
+ defCtx: DefCtx) 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],
+ defCtx: DefCtx) extends Target[(T, V)]{
+ def evaluate(args: Args) = (args(0), args(0))
+ val inputs = List(source1, source1)
+ }
+
+ def path(path: jnio.Path)(implicit defCtx: DefCtx) = Path(path, defCtx)
+ case class Path(path: jnio.Path, defCtx: DefCtx) extends Target[jnio.Path]{
+ def evaluate(args: Args) = path
+ val inputs = Nil
+ }
+ case class Subprocess(inputs: Seq[Target[_]],
+ command: Args => Seq[String],
+ defCtx: DefCtx) extends Target[Subprocess.Result] {
+
+ def evaluate(args: Args) = {
+ jnio.Files.createDirectories(args.dest)
+ import ammonite.ops._
+ implicit val path = ammonite.ops.Path(args.dest, pwd)
+ val output = %%(command(args))
+ assert(output.exitCode == 0)
+ Subprocess.Result(output, args.dest)
+ }
+ }
+ object Subprocess{
+ case class Result(result: ammonite.ops.CommandResult, dest: jnio.Path)
+ }
+}
diff --git a/src/main/scala/forge/Tarjans.scala b/src/main/scala/forge/Tarjans.scala
new file mode 100644
index 00000000..78dea34a
--- /dev/null
+++ b/src/main/scala/forge/Tarjans.scala
@@ -0,0 +1,61 @@
+package forge
+
+import collection.mutable
+// Adapted from
+// https://github.com/indy256/codelibrary/blob/c52247216258e84aac442a23273b7d8306ef757b/java/src/SCCTarjan.java
+object Tarjans {
+ def main(args: Array[String]) = {
+ val components = Tarjans(
+ Vector(
+ Vector(1),
+ Vector(0),
+ Vector(0, 1)
+ )
+ )
+ println(components)
+ }
+
+ def apply(graph0: Seq[Seq[Int]]): Seq[Seq[Int]] = {
+ val graph = graph0.map(_.toArray).toArray
+ val n = graph.length
+ val visited = new Array[Boolean](n)
+ val stack = mutable.ArrayBuffer.empty[Integer]
+ var time = 0
+ val lowlink = new Array[Int](n)
+ val components = mutable.ArrayBuffer.empty[Seq[Int]]
+
+
+ for (u <- 0 until n) {
+ if (!visited(u)) dfs(u)
+ }
+
+ def dfs(u: Int): Unit = {
+ lowlink(u) = time
+ time += 1
+ visited(u) = true
+ stack.append(u)
+ var isComponentRoot = true
+ for (v <- graph(u)) {
+ if (!visited(v)) dfs(v)
+ if (lowlink(u) > lowlink(v)) {
+ lowlink(u) = lowlink(v)
+ isComponentRoot = false
+ }
+ }
+ if (isComponentRoot) {
+ val component = mutable.Buffer.empty[Int]
+
+ var done = false
+ while (!done) {
+ val x = stack.last
+ stack.remove(stack.length - 1)
+ component.append(x)
+ lowlink(x) = Integer.MAX_VALUE
+ if (x == u) done = true
+ }
+ components.append(component)
+ }
+ }
+ components
+ }
+} \ No newline at end of file
diff --git a/src/main/scala/forge/Util.scala b/src/main/scala/forge/Util.scala
new file mode 100644
index 00000000..89a3eafe
--- /dev/null
+++ b/src/main/scala/forge/Util.scala
@@ -0,0 +1,52 @@
+package forge
+
+import java.io.FileOutputStream
+import java.nio.{file => jnio}
+import java.util.jar.JarEntry
+
+import sourcecode.Enclosing
+
+import scala.collection.JavaConverters._
+
+object Util{
+ def compileAll(sources: Target[Seq[jnio.Path]])
+ (implicit defCtx: DefCtx) = {
+ Target.Subprocess(
+ Seq(sources),
+ args =>
+ Seq("javac") ++
+ args[Seq[jnio.Path]](0).map(_.toAbsolutePath.toString) ++
+ Seq("-d", args.dest.toAbsolutePath.toString),
+ defCtx
+ ).map(_.dest)
+ }
+
+ def list(root: Target[jnio.Path])(implicit defCtx: DefCtx): Target[Seq[jnio.Path]] = {
+ root.map(jnio.Files.list(_).iterator().asScala.toArray[jnio.Path])
+ }
+ case class jarUp(roots: Target[jnio.Path]*)(implicit val defCtx: DefCtx) extends Target[jnio.Path]{
+
+ val inputs = roots
+ def evaluate(args: Args): jnio.Path = {
+
+ val output = new java.util.jar.JarOutputStream(new FileOutputStream(args.dest.toFile))
+ for{
+ root0 <- args.args
+ root = root0.asInstanceOf[jnio.Path]
+
+ path <- jnio.Files.walk(root).iterator().asScala
+ if jnio.Files.isRegularFile(path)
+ }{
+ val relative = root.relativize(path)
+ output.putNextEntry(new JarEntry(relative.toString))
+ output.write(jnio.Files.readAllBytes(path))
+ }
+ output.close()
+ args.dest
+ }
+
+
+ }
+
+
+} \ No newline at end of file