diff --git a/build.sbt b/build.sbt index 9a8c3a1bd627..354792e17e5f 100644 --- a/build.sbt +++ b/build.sbt @@ -17,7 +17,10 @@ val `scala-library` = Build.`scala-library` val `scala-compiler` = Build.`scala-compiler` val `scala-reflect` = Build.`scala-reflect` val scalap = Build.scalap -val tasty4scalac = Build.tasty4scalac + +val `tasty4scalac-plugin` = Build.`tasty4scalac-plugin` +val `tasty4scalac-integration` = Build.`tasty4scalac-integration` + val dist = Build.dist val `dist-bootstrapped` = Build.`dist-bootstrapped` val `community-build` = Build.`community-build` diff --git a/project/Build.scala b/project/Build.scala index 8d152f7bfb85..7789e4c78d8d 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -808,44 +808,53 @@ object Build { libraryDependencies := Seq("org.scala-lang" % "scalap" % scalacVersion) ) - lazy val tasty4scalac = project. + lazy val scalacPluginSettings = commonSettings ++ Seq( + version := dottyVersion, + scalaVersion := scalacVersion + ) + + lazy val `tasty4scalac-plugin` = project. + in(file("tasty4scalac/plugin")). dependsOn(`dotty-compiler`). - settings(commonSettings). + settings(scalacPluginSettings). settings( libraryDependencies := Seq("org.scala-lang" % "scala-compiler" % scalacVersion), + ) + + lazy val `tasty4scalac-integration` = project. + in(file("tasty4scalac/integration")). + dependsOn(`tasty4scalac-plugin`). + settings(scalacPluginSettings). + settings( fork in Test := true, + javaOptions ++= { + val attList = (dependencyClasspath in Runtime).value + def lib(name: String): String = findLib(attList, name) + + def toClasspath(strings: String*): String = strings.mkString(File.pathSeparator) + + val scalaLibrary = lib("scala-library") + val scalaCompiler = lib("scala-compiler") + val scalaReflect = lib("scala-reflect") + + val dottyLibrary = packageBin.in(`dotty-library`, Compile).value.absolutePath + val dottyCompiler = packageBin.in(`dotty-compiler`, Compile).value.absolutePath + val dottyInterfaces = packageBin.in(`dotty-interfaces`, Compile).value.absolutePath + + val pluginJar = packageBin.in(`tasty4scalac-plugin`, Compile).value.absolutePath + + val scalacClasspath = toClasspath(scalaLibrary, scalaCompiler, scalaReflect) + val pluginClasspath = toClasspath(pluginJar, dottyLibrary, dottyCompiler, dottyInterfaces) + val dottyClasspath = toClasspath(scalaLibrary, dottyLibrary, dottyCompiler, dottyInterfaces) - // From kind-projector - scalacOptions in Test ++= { - val pluginJar = (packageBin in Compile).value - val interfacesJar = packageBin.in(`dotty-interfaces`, Compile).value - val libraryJar = packageBin.in(`dotty-library`, Compile).value - val compilerJar = packageBin.in(`dotty-compiler`, Compile).value Seq( - s"-Xplugin:${pluginJar.getAbsolutePath}:${libraryJar.getAbsolutePath}:${compilerJar.getAbsolutePath}:${interfacesJar.getAbsolutePath}", - s"-Jdummy=${pluginJar.lastModified}" // ensures recompile + "-Dscalac.classpath=" + scalacClasspath, + "-Dscalac.plugin.classpath=" + pluginClasspath, + "-Ddotty.classpath=" + dottyClasspath, + "-Dtest.root.directory=" + (baseDirectory.value / "test-resources") ) - }, - scalacOptions in Test += "-Yrangepos" + } ) - // lazy val tasty4scalacTests = project. - // dependsOn(tasty4scalac % "plugin->default(compile)"). - // settings(commonSettings). - // settings( - // autoCompilerPlugins := true, - // scalacOptions ++= { - // val jar = (packageBin in Compile in tasty4scalac).value - - // val libraryJar = packageBin.in(`dotty-library`, Compile).value - // val compilerJar = packageBin.in(`dotty-compiler`, Compile).value - // Seq( - // "-Yrangepos", - // s"-Xplugin:${jar.getAbsolutePath}:${compilerJar}:${libraryJar}", - // s"-Jdummy=${jar.lastModified}" // ensures recompile - // ) - // } - // ) - // sbt plugin to use Dotty in your own build, see // https://github.com/lampepfl/dotty-example-project for usage. diff --git a/tasty4scalac/integration/src/tasty4scalac/CompileSource.scala b/tasty4scalac/integration/src/tasty4scalac/CompileSource.scala new file mode 100644 index 000000000000..33a4ad9294a2 --- /dev/null +++ b/tasty4scalac/integration/src/tasty4scalac/CompileSource.scala @@ -0,0 +1,7 @@ +package tasty4scalac + +import java.nio.file.Path + +final class CompileSource(val name: String, val source: Path) { + override def toString: String = name +} diff --git a/tasty4scalac/integration/src/tasty4scalac/Compiler.scala b/tasty4scalac/integration/src/tasty4scalac/Compiler.scala new file mode 100644 index 000000000000..1994fbd319d6 --- /dev/null +++ b/tasty4scalac/integration/src/tasty4scalac/Compiler.scala @@ -0,0 +1,17 @@ +package tasty4scalac + + +trait Compiler { + def compile(code: String): Unit + + final override def toString: String = getClass.getSimpleName +} + +object Compiler { + def dotty(): Compiler = Dotty() + def scalac(): Compiler = Scalac() + + trait Factory { + def apply(): Compiler + } +} diff --git a/tasty4scalac/integration/src/tasty4scalac/Dotty.scala b/tasty4scalac/integration/src/tasty4scalac/Dotty.scala new file mode 100644 index 000000000000..9af5096eb7b3 --- /dev/null +++ b/tasty4scalac/integration/src/tasty4scalac/Dotty.scala @@ -0,0 +1,28 @@ +package tasty4scalac + +import dotty.tools.dotc.core.Comments.{ContextDoc, ContextDocstrings} +import dotty.tools.dotc.core.Contexts +import dotty.tools.dotc.core.Contexts.ContextBase +import dotty.tools.dotc.reporting.Reporter.NoReporter +import dotty.tools.io.VirtualDirectory +import tasty4scalac.Compiler.Factory + +final class Dotty(val ctx: Contexts.Context) extends dotty.tools.dotc.Compiler with Compiler { + override def compile(code: String): Unit = newRun(ctx.fresh).compile(code) +} + +object Dotty extends Factory { + private val classpath = System.getProperty("dotty.classpath") + + override def apply(): Compiler = { + implicit val ctx: Contexts.FreshContext = new ContextBase().initialCtx.fresh + ctx.setSetting(ctx.settings.classpath, classpath) + ctx.setSetting(ctx.settings.YtestPickler, true) + ctx.setSetting(ctx.settings.encoding, "UTF8") + ctx.setSetting(ctx.settings.outputDir, new VirtualDirectory("")) + + ctx.setReporter(NoReporter) + ctx.setProperty(ContextDoc, new ContextDocstrings) + new Dotty(ctx) + } +} \ No newline at end of file diff --git a/tasty4scalac/integration/src/tasty4scalac/Provider.scala b/tasty4scalac/integration/src/tasty4scalac/Provider.scala new file mode 100644 index 000000000000..31abff922f49 --- /dev/null +++ b/tasty4scalac/integration/src/tasty4scalac/Provider.scala @@ -0,0 +1,47 @@ +package tasty4scalac + +import java.nio.file.{Files, Path, Paths} +import java.util +import java.util.regex.Pattern +import scala.collection.JavaConverters._ + +trait Provider { + def testCases(): util.Collection[Array[CompileSource]] +} + +object Providers { + private val root = Paths.get(System.getProperty("test.root.directory")) + + def compiledSourceProvider: Provider = new CompiledSources(root) + + private final class CompiledSources(root: Path) extends Provider { + private val negativeTests = Seq( + Pattern.compile(".*/neg(-.*)?/.*"), + Pattern.compile(".*/run-with-compiler/.*"), + Pattern.compile(".*/vulpix-tests/.*"), + Pattern.compile(".*/disabled/.*"), + ) + + def testCases(): util.Collection[Array[CompileSource]] = { + findTestCases(root) + .map(createTestCase) + .map(Array(_)) + .asJavaCollection + } + + private def findTestCases(path: Path): Seq[Path] = { + Files.walk(path) + .iterator() + .asScala + .filterNot(path => negativeTests.exists(p => p.matcher(path.toString).matches())) + .filter(_.getFileName.toString.endsWith(".scala")) + .toSeq + } + + private def createTestCase(path: Path): CompileSource = { + val name = root.relativize(path).toString.dropRight(6) // drop ".scala" + new CompileSource(name, path) + } + } + +} diff --git a/tasty4scalac/integration/src/tasty4scalac/Scalac.scala b/tasty4scalac/integration/src/tasty4scalac/Scalac.scala new file mode 100644 index 000000000000..c9e7b8a21d7b --- /dev/null +++ b/tasty4scalac/integration/src/tasty4scalac/Scalac.scala @@ -0,0 +1,38 @@ +package tasty4scalac + +import java.io._ +import java.net.URL +import java.nio.file._ + +import scala.reflect.internal.util.{BatchSourceFile, NoFile, SourceFile} +import scala.tools.nsc.{Global, Settings} +import scala.tools.nsc.reporters.NoReporter + +final class Scalac(settings: Settings) extends Global(settings, NoReporter) with Compiler { + override def compile(code: String): Unit = { + val file = new BatchSourceFile(NoFile, code.toCharArray) + compile(file) + } + + private def compile(files: SourceFile*): Unit = { + new Run().compileSources(files.toList) + } +} + +object Scalac extends Compiler.Factory { + private val classpath = System.getProperty("scalac.classpath") + private val pluginPath = System.getProperty("scalac.plugin.classpath") + + def apply(): Scalac = { + val settings = new Settings() + settings.classpath.value = classpath + settings.plugin.value = List(pluginPath) + settings.stopAfter.value = List("tasty") + settings.d.value = { + val path = Files.createTempDirectory("tasty-generation") + Files.createDirectories(path).toString + } + + new Scalac(settings) + } +} \ No newline at end of file diff --git a/tasty4scalac/integration/test/tasty4scalac/CompilerTest.java b/tasty4scalac/integration/test/tasty4scalac/CompilerTest.java new file mode 100644 index 000000000000..54a3d8eaf457 --- /dev/null +++ b/tasty4scalac/integration/test/tasty4scalac/CompilerTest.java @@ -0,0 +1,31 @@ +package tasty4scalac; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; + +@RunWith(Parameterized.class) +public final class CompilerTest { + private final Compiler compiler; + + public CompilerTest(Compiler factory) { + this.compiler = factory; + } + + @Parameterized.Parameters(name = "{index}: {0}") + public static Collection parameters() { + return Arrays.asList(new Compiler[][]{ + {Scalac$.MODULE$.apply()}, + {Dotty$.MODULE$.apply()} + }); + } + + @Test + public void compilesSimpleClass() { + String code = "class A"; + compiler.compile(code); + } +} diff --git a/tasty4scalac/integration/test/tasty4scalac/Runner.java b/tasty4scalac/integration/test/tasty4scalac/Runner.java new file mode 100644 index 000000000000..ee4495f8d50e --- /dev/null +++ b/tasty4scalac/integration/test/tasty4scalac/Runner.java @@ -0,0 +1,31 @@ +package tasty4scalac; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.Collection; + +@RunWith(Parameterized.class) +public final class Runner { + private static final Compiler compiler = Compiler$.MODULE$.scalac(); + private final CompileSource test; + + public Runner(CompileSource test) { + this.test = test; + } + + @Parameterized.Parameters(name = "{index}: {0}") + public static Collection parameters() { + return Providers.compiledSourceProvider().testCases(); + } + + @Test + public void compile() throws IOException { + String code = new String(Files.readAllBytes(test.source())); + compiler.compile(code); + } +} + diff --git a/tasty4scalac/resources/scalac-plugin.xml b/tasty4scalac/plugin/resources/scalac-plugin.xml similarity index 100% rename from tasty4scalac/resources/scalac-plugin.xml rename to tasty4scalac/plugin/resources/scalac-plugin.xml diff --git a/tasty4scalac/src/tasty4scalac/Plugin.scala b/tasty4scalac/plugin/src/tasty4scalac/Plugin.scala similarity index 100% rename from tasty4scalac/src/tasty4scalac/Plugin.scala rename to tasty4scalac/plugin/src/tasty4scalac/Plugin.scala diff --git a/tasty4scalac/src/tasty4scalac/ScalacTastyPickler.scala b/tasty4scalac/plugin/src/tasty4scalac/ScalacTastyPickler.scala similarity index 100% rename from tasty4scalac/src/tasty4scalac/ScalacTastyPickler.scala rename to tasty4scalac/plugin/src/tasty4scalac/ScalacTastyPickler.scala diff --git a/tasty4scalac/src/tasty4scalac/ScalacTreePickler.scala b/tasty4scalac/plugin/src/tasty4scalac/ScalacTreePickler.scala similarity index 100% rename from tasty4scalac/src/tasty4scalac/ScalacTreePickler.scala rename to tasty4scalac/plugin/src/tasty4scalac/ScalacTreePickler.scala