From 33ee29edc7dcf580099dfc7482737129ac2bddd0 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Tue, 31 Oct 2017 08:35:46 -0700 Subject: Sketched out structure of Scala subprojects --- src/main/scala/forge/Discovered.scala | 4 +- src/main/scala/forge/Target.scala | 32 ++----- src/main/scala/forge/scalaplugin/Compile.scala | 107 ++++++++++++++++++++++ src/main/scala/forge/scalaplugin/Subproject.scala | 31 +++++++ src/test/scala/forge/EvaluationTests.scala | 6 +- src/test/scala/forge/GraphTests.scala | 6 +- src/test/scala/forge/TestGraphs.scala | 2 +- src/test/scala/forge/TestUtil.scala | 20 +++- 8 files changed, 175 insertions(+), 33 deletions(-) create mode 100644 src/main/scala/forge/scalaplugin/Compile.scala create mode 100644 src/main/scala/forge/scalaplugin/Subproject.scala (limited to 'src') diff --git a/src/main/scala/forge/Discovered.scala b/src/main/scala/forge/Discovered.scala index 6003f3b8..75e82c25 100644 --- a/src/main/scala/forge/Discovered.scala +++ b/src/main/scala/forge/Discovered.scala @@ -1,7 +1,7 @@ package forge -import scala.language.experimental.macros -import scala.reflect.macros.blackbox.Context +import language.experimental.macros +import reflect.macros.blackbox.Context class Discovered[T](val value: Seq[(Seq[String], T => Target[_])]){ def apply(t: T) = value.map{case (a, b) => (a, b(t)) } diff --git a/src/main/scala/forge/Target.scala b/src/main/scala/forge/Target.scala index c6908d1d..f95a2dda 100644 --- a/src/main/scala/forge/Target.scala +++ b/src/main/scala/forge/Target.scala @@ -24,37 +24,25 @@ abstract class Target[T](implicit formatter: Format[T]) extends Target.Ops[T]{ } object Target{ + class Target0[T: Format](t: T) extends Target[T]{ + val inputs = Nil + def evaluate(args: Args) = t + } + implicit def apply[T: Format](t: T): Target[T] = new Target0(t) abstract class Ops[T](implicit val formatter: Format[T]){ this: Target[T] => def evaluateAndWrite(args: Args): (T, JsValue) = { val res = evaluate(args) val str = formatter.writes(res) (res, str) } - def map[V: Format](f: T => V) = { - new Target.Mapped(this, f) - } - def zip[V: Format](other: Target[V]) = { - new Target.Zipped(this, other) - } - } - def test(inputs: Target[Int]*) = { - new Test(inputs, pure = inputs.nonEmpty) - } + def map[V: Format](f: T => V) = new Target.Mapped(this, f) - /** - * A dummy target that takes any number of inputs, and whose output can be - * controlled externally, so you can construct arbitrary dataflow graphs and - * test how changes propagate. - */ - class Test(val inputs: Seq[Target[Int]], - val pure: Boolean) extends Target[Int]{ - var counter = 0 - def evaluate(args: Args) = { - counter + args.args.map(_.asInstanceOf[Int]).sum - } + def filter(f: T => Boolean) = this + def withFilter(f: T => Boolean) = this + def zip[V: Format](other: Target[V]) = new Target.Zipped(this, other) - override def sideHash = counter } + def traverse[T: Format](source: Seq[Target[T]]) = { new Traverse[T](source) } diff --git a/src/main/scala/forge/scalaplugin/Compile.scala b/src/main/scala/forge/scalaplugin/Compile.scala new file mode 100644 index 00000000..c4220ddd --- /dev/null +++ b/src/main/scala/forge/scalaplugin/Compile.scala @@ -0,0 +1,107 @@ +package forge.scalaplugin +import ammonite.ops._ +import java.io.File + +import coursier._ +import forge.Target +import forge.util.OSet +import sbt.internal.inc.{FreshCompilerCache, ScalaInstance, ZincUtil} +import sbt.internal.util.{ConsoleOut, MainAppender} +import sbt.util.LogExchange +import xsbti.api.{ClassLike, DependencyContext} +import xsbti.compile._ + +import scalaz.concurrent.Task +object Compile { + + def apply(scalaVersion: Target[String], + dependencies: Target[OSet[Dependency]]) = { + + } + def underlying(scalaVersion: String, + dependencies: OSet[Dependency]) = { + + val binaryScalaVersion = scalaVersion.split('.').dropRight(1).mkString(".") + val start = Resolution( + Set( + Dependency(Module("org.scala-lang", "scala-compiler"), scalaVersion), + Dependency(Module("org.scala-lang", "scala-library"), scalaVersion), + Dependency(Module("org.scala-sbt", s"compiler-bridge_$binaryScalaVersion"), "1.0.3"), + Dependency(Module("com.typesafe.play", s"play-json_$binaryScalaVersion"), "2.6.6") + ) + ) + val repositories = Seq( + Cache.ivy2Local, + MavenRepository("https://repo1.maven.org/maven2") + ) + + val fetch = Fetch.from(repositories, Cache.fetch()) + val resolution = start.process.run(fetch).unsafePerformSync + + + val localArtifacts: Seq[File] = Task.gatherUnordered( + resolution.artifacts.map(Cache.file(_).run) + ).unsafePerformSync.flatMap(_.toOption) + + pprint.log(localArtifacts) + def grepJar(s: String) = localArtifacts.find(_.toString.endsWith(s)).get + + val scalac = ZincUtil.scalaCompiler( + new ScalaInstance( + version = scalaVersion, + loader = getClass.getClassLoader, + libraryJar = grepJar(s"scala-library-$scalaVersion.jar"), + compilerJar = grepJar(s"scala-compiler-$scalaVersion.jar"), + allJars = localArtifacts.toArray, + explicitActual = None + ), + grepJar(s"compiler-bridge_$binaryScalaVersion-1.0.3.jar") + ) + + val outputDir = pwd/'target/'zinc + mkdir(outputDir) + + val scalaFiles = Array(pwd/"Test.scala") + + + scalac.apply( + sources = scalaFiles.map(_.toIO), + changes = new DependencyChanges { + def isEmpty = true + def modifiedBinaries() = Array[File]() + def modifiedClasses() = Array[String]() + }, + classpath = localArtifacts.toArray, + singleOutput = outputDir.toIO, + options = Array(), + callback = new xsbti.AnalysisCallback { + def startSource(source: File) = () + def apiPhaseCompleted() = () + def enabled() = true + def binaryDependency(onBinaryEntry: File, onBinaryClassName: String, fromClassName: String, fromSourceFile: File, context: DependencyContext) = () + def generatedNonLocalClass(source: File, classFile: File, binaryClassName: String, srcClassName: String) = () + def problem(what: String, pos: xsbti.Position, msg: String, severity: xsbti.Severity, reported: Boolean) = () + def dependencyPhaseCompleted() = () + def classDependency(onClassName: String, sourceClassName: String, context: DependencyContext) = () + def generatedLocalClass(source: File, classFile: File) = () + def api(sourceFile: File, classApi: ClassLike) = () + + def mainClass(sourceFile: File, className: String) = () + def usedName(className: String, name: String, useScopes: java.util.EnumSet[xsbti.UseScope]) = () + }, + maximumErrors = 10, + cache = new FreshCompilerCache(), + log = { + val console = ConsoleOut.systemOut + val consoleAppender = MainAppender.defaultScreen(console) + val l = LogExchange.logger("Hello") + LogExchange.unbindLoggerAppenders("Hello") + LogExchange.bindLoggerAppenders("Hello", (consoleAppender -> sbt.util.Level.Warn) :: Nil) + l + } + ) + + %('java,"-cp", localArtifacts.mkString(":") + ":.", "Test")(pwd/'target/'zinc) + + } +} diff --git a/src/main/scala/forge/scalaplugin/Subproject.scala b/src/main/scala/forge/scalaplugin/Subproject.scala new file mode 100644 index 00000000..a91389ab --- /dev/null +++ b/src/main/scala/forge/scalaplugin/Subproject.scala @@ -0,0 +1,31 @@ +package forge +package scalaplugin + +import ammonite.ops.Path +import forge.{Target => T} +import forge.util.PathRef +object Subproject{ + def compileScala(sources: T[PathRef], + dependencyClasspath: T[Seq[PathRef]], + outputPath: T[Path]): T[PathRef] = ??? + def createJar(sourceDirs: T[Seq[PathRef]]) = ??? + def resolveDependencies(deps: T[Seq[coursier.Dependency]]): T[Seq[PathRef]] = ??? +} +import Subproject._ +abstract class Subproject { + val scalaVersion: T[String] + + val compileDeps: T[Seq[coursier.Dependency]] + val runDeps: T[Seq[coursier.Dependency]] + val basePath: T[Path] + + val compileDepClasspath: T[Seq[PathRef]] = resolveDependencies(compileDeps) + val runDepClasspath: T[Seq[PathRef]] = resolveDependencies(runDeps) + val sources: T[PathRef] = basePath.map(p => PathRef(p / 'src)) + val outputPath: T[Path] = basePath.map(p => p / 'out) + val resources: T[PathRef] = basePath.map(p => PathRef(p / 'resources)) + val compiledPath: T[Path] = outputPath.map(p => p / 'classpath) + val compiled: T[PathRef] = compileScala(sources, compileDepClasspath, outputPath) + val classpath: T[Seq[PathRef]] = for((r, c) <- resources.zip(compiled)) yield Seq(r, c) + val jar: T[PathRef] = createJar(classpath) +} diff --git a/src/test/scala/forge/EvaluationTests.scala b/src/test/scala/forge/EvaluationTests.scala index 313ceaaa..fe5d0b09 100644 --- a/src/test/scala/forge/EvaluationTests.scala +++ b/src/test/scala/forge/EvaluationTests.scala @@ -78,7 +78,7 @@ object EvaluationTests extends TestSuite{ up.counter += 1 check(down, expValue = 2, expEvaled = OSet(up, middle, down)) - middle.asInstanceOf[Target.Test].counter += 1 + middle.asInstanceOf[TestUtil.Test].counter += 1 check(down, expValue = 3, expEvaled = OSet(middle, down)) } @@ -103,8 +103,8 @@ object EvaluationTests extends TestSuite{ 'anonDiamond - { import anonDiamond._ val check = new Checker(anonDiamond) - val left = down.inputs(0).asInstanceOf[Target.Test] - val right = down.inputs(1).asInstanceOf[Target.Test] + val left = down.inputs(0).asInstanceOf[TestUtil.Test] + val right = down.inputs(1).asInstanceOf[TestUtil.Test] check(down, expValue = 0, expEvaled = OSet(up, left, right, down)) down.counter += 1 diff --git a/src/test/scala/forge/GraphTests.scala b/src/test/scala/forge/GraphTests.scala index d197fb5f..ec84218f 100644 --- a/src/test/scala/forge/GraphTests.scala +++ b/src/test/scala/forge/GraphTests.scala @@ -1,7 +1,7 @@ package forge import utest._ -import Target.test +import TestUtil.test import forge.util.OSet object GraphTests extends TestSuite{ @@ -79,8 +79,8 @@ object GraphTests extends TestSuite{ 'groupAroundNamedTargets - { def check[T: Discovered](base: T, - target: Target.Test, - expected: OSet[(OSet[Target.Test], Int)]) = { + target: TestUtil.Test, + expected: OSet[(OSet[TestUtil.Test], Int)]) = { val mapping = Discovered.mapping(base) val topoSortedTransitive = Evaluator.topoSortedTransitiveTargets(OSet(target)) diff --git a/src/test/scala/forge/TestGraphs.scala b/src/test/scala/forge/TestGraphs.scala index 8dac142c..0ee48a18 100644 --- a/src/test/scala/forge/TestGraphs.scala +++ b/src/test/scala/forge/TestGraphs.scala @@ -1,6 +1,6 @@ package forge -import forge.Target.test +import forge.TestUtil.test class TestGraphs(){ // single diff --git a/src/test/scala/forge/TestUtil.scala b/src/test/scala/forge/TestUtil.scala index 8405c87c..9337fbe0 100644 --- a/src/test/scala/forge/TestUtil.scala +++ b/src/test/scala/forge/TestUtil.scala @@ -1,12 +1,28 @@ package forge -import forge.util.OSet +import forge.util.{Args, OSet} import utest.assert - import scala.collection.mutable object TestUtil { + def test(inputs: Target[Int]*) = { + new Test(inputs, pure = inputs.nonEmpty) + } + /** + * A dummy target that takes any number of inputs, and whose output can be + * controlled externally, so you can construct arbitrary dataflow graphs and + * test how changes propagate. + */ + class Test(val inputs: Seq[Target[Int]], + val pure: Boolean) extends Target[Int]{ + var counter = 0 + def evaluate(args: Args) = { + counter + args.args.map(_.asInstanceOf[Int]).sum + } + + override def sideHash = counter + } def checkTopological(targets: OSet[Target[_]]) = { val seen = mutable.Set.empty[Target[_]] for(t <- targets.items.reverseIterator){ -- cgit v1.2.3