Skip to content

Simplify output directory handling #4744

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions compiler/src/dotty/tools/backend/jvm/GenBCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,9 @@ class GenBCode extends Phase {

private[this] var myOutput: AbstractFile = _

protected def outputDir(implicit ctx: Context): AbstractFile = {
if (myOutput eq null) {
val path = Directory(ctx.settings.outputDir.value)
myOutput =
if (path.extension == "jar") JarArchive.create(path)
else new PlainDirectory(path)
}
private def outputDir(implicit ctx: Context): AbstractFile = {
if (myOutput eq null)
myOutput = ctx.settings.outputDir.value
myOutput
}

Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package dotty.tools.dotc
package config

import java.io.File
import dotty.tools.io.{ Directory, Path }
import dotty.tools.io.{ Directory, PlainDirectory }

import PathResolver.Defaults
import rewrite.Rewrites
Expand All @@ -20,7 +20,8 @@ class ScalaSettings extends Settings.SettingGroup {
val scansource = BooleanSetting("-scansource", "Scan source files to locate classes for which class-name != file-name")

val classpath = PathSetting("-classpath", "Specify where to find user class files.", defaultClasspath) withAbbreviation "-cp"
val outputDir = PathSetting("-d", "directory|jar", "destination for generated classfiles.", ".")
val outputDir = OutputSetting("-d", "directory|jar", "destination for generated classfiles.",
new PlainDirectory(Directory(".")))
val priorityclasspath = PathSetting("-priorityclasspath", "class path that takes precedence over all other paths (or testing only)", "")

/** Other settings */
Expand Down
24 changes: 14 additions & 10 deletions compiler/src/dotty/tools/dotc/config/Settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import scala.util.{ Try, Success, Failure }
import reflect.ClassTag
import core.Contexts._
import scala.annotation.tailrec
import dotty.tools.io.{ Directory, File, Path }
import dotty.tools.io.{ AbstractFile, Directory, JarArchive, PlainDirectory, File, Path }

// import annotation.unchecked
// Dotty deviation: Imports take precedence over definitions in enclosing package
Expand All @@ -22,6 +22,7 @@ object Settings {
val ListTag = ClassTag(classOf[List[_]])
val VersionTag = ClassTag(classOf[ScalaVersion])
val OptionTag = ClassTag(classOf[Option[_]])
val OutputTag = ClassTag(classOf[AbstractFile])

class SettingsState(initialValues: Seq[Any]) {
private[this] var values = ArrayBuffer(initialValues: _*)
Expand Down Expand Up @@ -140,14 +141,14 @@ object Settings {
else if (!choices.contains(argRest))
fail(s"$arg is not a valid choice for $name", args)
else update(argRest, args)
case (StringTag, arg :: args) if name == "-d" =>
Path(arg) match {
case _: Directory =>
update(arg, args)
case p if p.extension == "jar" =>
update(arg, args)
case _ =>
fail(s"'$arg' does not exist or is not a directory", args)
case (OutputTag, arg :: args) =>
val path = Directory(arg)
val isJar = path.extension == "jar"
if (!isJar && !path.isDirectory)
fail(s"'$arg' does not exist or is not a directory or .jar file", args)
else {
val output = if (isJar) JarArchive.create(path) else new PlainDirectory(path)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will now hold on to an open jar during the whole compiler run instead of just backend. We didn't even open the jar before if we stopped before backend

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an empty open jar, so not a big deal I think (it cannot be deleted on Windows while it's open, but who cares?)

update(output, args)
}
case (StringTag, arg2 :: args2) =>
update(arg2, args2)
Expand Down Expand Up @@ -275,11 +276,14 @@ object Settings {
def MultiStringSetting(name: String, helpArg: String, descr: String): Setting[List[String]] =
publish(Setting(name, descr, Nil, helpArg))

def OutputSetting(name: String, helpArg: String, descr: String, default: AbstractFile): Setting[AbstractFile] =
publish(Setting(name, descr, default, helpArg))

def PathSetting(name: String, descr: String, default: String): Setting[String] =
publish(Setting(name, descr, default))

def PathSetting(name: String, helpArg: String, descr: String, default: String): Setting[String] =
publish(Setting(name, descr, default, helpArg))
publish(Setting(name, descr, default, helpArg))

def PhasesSetting(name: String, descr: String, default: String = ""): Setting[List[String]] =
publish(Setting(name, descr, if (default.isEmpty) Nil else List(default)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ class DecompilationPrinter extends Phase {
override def phaseName: String = "decompilationPrinter"

override def run(implicit ctx: Context): Unit = {
val outputDir = ctx.settings.outputDir.value
if (outputDir == ".") printToOutput(System.out)
if (ctx.settings.outputDir.isDefault) printToOutput(System.out)
else {
val outputDir = ctx.settings.outputDir.value
var os: OutputStream = null
var ps: PrintStream = null
try {
os = File(outputDir + "/decompiled.scala").outputStream(append = true)
os = File(outputDir.fileNamed("decompiled.scala").path).outputStream(append = true)
ps = new PrintStream(os)
printToOutput(ps)
} finally {
Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/dotc/decompiler/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import dotty.tools.dotc.core.Contexts._
object Main extends dotc.Driver {
override protected def newCompiler(implicit ctx: Context): dotc.Compiler = {
assert(ctx.settings.fromTasty.value)
val outputDir = ctx.settings.outputDir.value
if (outputDir != ".")
Files.deleteIfExists(Paths.get(outputDir + "/decompiled.scala"))
if (!ctx.settings.outputDir.isDefault)
Files.deleteIfExists(ctx.settings.outputDir.value.fileNamed("decompiled.scala").jpath)
new TASTYDecompiler
}

Expand Down
11 changes: 1 addition & 10 deletions compiler/src/dotty/tools/dotc/quoted/QuoteCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,15 @@ import scala.quoted.{Expr, Type}
/** Compiler that takes the contents of a quoted expression `expr` and produces
* a class file with `class ' { def apply: Object = expr }`.
*/
class QuoteCompiler(directory: AbstractFile) extends Compiler {
class QuoteCompiler extends Compiler {
import tpd._

/** A GenBCode phase that outputs to a virtual directory */
private class ExprGenBCode extends GenBCode {
override def phaseName = "genBCode"
override def outputDir(implicit ctx: Context) = directory
}

override protected def frontendPhases: List[List[Phase]] =
List(List(new QuotedFrontend(putInClass = true)))

override protected def picklerPhases: List[List[Phase]] =
List(List(new ReifyQuotes))

override protected def backendPhases: List[List[Phase]] =
List(List(new ExprGenBCode))

override def newRun(implicit ctx: Context): ExprRun = {
reset()
new ExprRun(this, ctx.addMode(Mode.ReadPositions))
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/quoted/QuoteDecompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.Phases.Phase

/** Compiler that takes the contents of a quoted expression (or type) and outputs it's tree. */
class QuoteDecompiler(output: tpd.Tree => Context => Unit) extends QuoteCompiler(null) {
class QuoteDecompiler(output: tpd.Tree => Context => Unit) extends QuoteCompiler {
override def phases: List[List[Phase]] = List(
List(new QuotedFrontend(putInClass = false)), // Create class from Expr
List(new QuoteTreeOutput(output))
Expand Down
9 changes: 5 additions & 4 deletions compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,19 @@ class QuoteDriver extends Driver {
private[this] val contextBase: ContextBase = new ContextBase

def run[T](expr: Expr[T], settings: ToolboxSettings): T = {
val (_, ctx: Context) = setup(settings.compilerArgs.toArray :+ "dummy.scala", initCtx.fresh)

val outDir: AbstractFile = settings.outDir match {
case Some(out) =>
val dir = Directory(out)
dir.createDirectory()
new PlainDirectory(Directory(out))
case None =>
new VirtualDirectory("(memory)", None)
new VirtualDirectory("<quote compilation output>")
}

val driver = new QuoteCompiler(outDir)
val (_, ctx0: Context) = setup(settings.compilerArgs.toArray :+ "dummy.scala", initCtx.fresh)
val ctx = ctx0.fresh.setSetting(ctx0.settings.outputDir, outDir)

val driver = new QuoteCompiler
driver.newRun(ctx).compileExpr(expr)

val classLoader = new AbstractFileClassLoader(outDir, this.getClass.getClassLoader)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/io/VirtualDirectory.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import scala.collection.mutable
*
* ''Note: This library is considered experimental and should not be used unless you know what you are doing.''
*/
class VirtualDirectory(val name: String, maybeContainer: Option[VirtualDirectory])
class VirtualDirectory(val name: String, maybeContainer: Option[VirtualDirectory] = None)
extends AbstractFile {
def path: String =
maybeContainer match {
Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/repl/Rendering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ import dotc.core.StdNames.str
* `ReplDriver#resetToInitial` is called, the accompanying instance of
* `Rendering` is no longer valid.
*/
private[repl] class Rendering(compiler: ReplCompiler,
parentClassLoader: Option[ClassLoader] = None) {
private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None) {

private[this] var myClassLoader: ClassLoader = _

Expand All @@ -37,7 +36,7 @@ private[repl] class Rendering(compiler: ReplCompiler,
new java.net.URLClassLoader(compilerClasspath.toArray, classOf[ReplDriver].getClassLoader)
}

myClassLoader = new AbstractFileClassLoader(compiler.directory, parent)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compiler is now unused

myClassLoader = new AbstractFileClassLoader(ctx.settings.outputDir.value, parent)
// Set the current Java "context" class loader to this rendering class loader
Thread.currentThread.setContextClassLoader(myClassLoader)
myClassLoader
Expand Down
13 changes: 1 addition & 12 deletions compiler/src/dotty/tools/repl/ReplCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,10 @@ import scala.collection.mutable
* in conjunction with a specialized class loader in order to load virtual
* classfiles.
*/
class ReplCompiler(val directory: AbstractFile) extends Compiler {


/** A GenBCode phase that outputs to a virtual directory */
private class REPLGenBCode extends GenBCode {
override def phaseName = "genBCode"
override def outputDir(implicit ctx: Context) = directory
}

class ReplCompiler extends Compiler {
override protected def frontendPhases: List[List[Phase]] =
Phases.replace(classOf[FrontEnd], _ => new REPLFrontEnd :: Nil, super.frontendPhases)

override protected def backendPhases: List[List[Phase]] =
List(new REPLGenBCode) :: Nil

def newRun(initCtx: Context, objectIndex: Int) = new Run(this, initCtx) {
override protected[this] def rootContext(implicit ctx: Context) =
addMagicImports(super.rootContext)
Expand Down
16 changes: 5 additions & 11 deletions compiler/src/dotty/tools/repl/ReplDriver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,11 @@ class ReplDriver(settings: Array[String],
*/
protected[this] def resetToInitial(): Unit = {
rootCtx = initialCtx
val outDir: AbstractFile = {
if (rootCtx.settings.outputDir.isDefault(rootCtx))
new VirtualDirectory("(memory)", None)
else {
val path = Directory(rootCtx.settings.outputDir.value(rootCtx))
assert(path.isDirectory)
new PlainDirectory(path)
}
}
compiler = new ReplCompiler(outDir)
rendering = new Rendering(compiler, classLoader)
if (rootCtx.settings.outputDir.isDefault(rootCtx))
rootCtx = rootCtx.fresh
.setSetting(rootCtx.settings.outputDir, new VirtualDirectory("<REPL compilation output>"))
compiler = new ReplCompiler
rendering = new Rendering(classLoader)
}

private[this] var rootCtx: Context = _
Expand Down
49 changes: 18 additions & 31 deletions compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package dotty.tools
package dotty
package tools
package backend.jvm

import dotc.core.Contexts.{Context, ContextBase}
import dotc.core.Comments.{ContextDoc, ContextDocstrings}
import dotc.core.Phases.Phase
import dotc.Compiler

Expand All @@ -11,19 +13,13 @@ import asm._
import asm.tree._
import scala.collection.JavaConverters._

import io.JavaClassPath
import io.{AbstractFile, JavaClassPath, VirtualDirectory}
import scala.collection.JavaConverters._
import scala.tools.asm.{ClassWriter, ClassReader}
import scala.tools.asm.tree._
import java.io.{File => JFile, InputStream}

class TestGenBCode(val outDir: String) extends GenBCode {
override def phaseName: String = "testGenBCode"
val virtualDir = new Directory(outDir, None)
override def outputDir(implicit ctx: Context) = virtualDir
}

trait DottyBytecodeTest extends DottyTest {
trait DottyBytecodeTest {
import AsmNode._
import ASMConverters._

Expand All @@ -45,32 +41,23 @@ trait DottyBytecodeTest extends DottyTest {
val javaString = "java/lang/String"
}

private def bCodeCheckingComp(testPhase: TestGenBCode)(check: Directory => Unit) = {
class AssertionChecker extends Phase {
def phaseName = "assertionChecker"
def run(implicit ctx: Context): Unit = check(testPhase.virtualDir)
}
new Compiler {
override protected def backendPhases: List[List[Phase]] =
List(testPhase) ::
List(new AssertionChecker) ::
Nil
}
def initCtx = {
val ctx0 = (new ContextBase).initialCtx.fresh
val outputDir = new VirtualDirectory("<DottyBytecodeTest output>")
ctx0.setSetting(ctx0.settings.classpath, Jars.dottyLib)
ctx0.setProperty(ContextDoc, new ContextDocstrings)
ctx0.setSetting(ctx0.settings.outputDir, outputDir)
}

private def outPath(obj: Any) =
"/genBCodeTest" + math.abs(obj.hashCode) + System.currentTimeMillis

/** Checks source code from raw string */
def checkBCode(source: String)(assertion: Directory => Unit) = {
val comp = bCodeCheckingComp(new TestGenBCode(outPath(source)))(assertion)
comp.newRun.compile(source)
}
def checkBCode(source: String)(checkOutput: AbstractFile => Unit): Unit = {
implicit val ctx: Context = initCtx

val compiler = new Compiler
val run = compiler.newRun
compiler.newRun.compile(source)

/** Checks actual _files_ referenced in `sources` list */
def checkBCode(sources: List[String])(assertion: Directory => Unit) = {
val comp = bCodeCheckingComp(new TestGenBCode(outPath(sources)))(assertion)
comp.newRun.compile(sources)
checkOutput(ctx.settings.outputDir.value)
}

protected def loadClassNode(input: InputStream, skipDebugInfo: Boolean = true): ClassNode = {
Expand Down
2 changes: 1 addition & 1 deletion compiler/test/dotty/tools/dotc/SettingsTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class SettingsTests {
val options = Array("-d", "not_here")
val reporter = Main.process(options)
assertEquals(1, reporter.errorCount)
assertEquals("'not_here' does not exist or is not a directory", reporter.allErrors.head.message)
assertEquals("'not_here' does not exist or is not a directory or .jar file", reporter.allErrors.head.message)
}

@Test def jarOutput: Unit = {
Expand Down
10 changes: 3 additions & 7 deletions compiler/test/dotty/tools/dotc/SimplifyTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,9 @@ class SimplifyPosTests extends SimplifyTests(optimise = true)
class SimplifyNegTests extends SimplifyTests(optimise = false)

abstract class SimplifyTests(val optimise: Boolean) extends DottyBytecodeTest {
override protected def initializeCtx(c: FreshContext): Unit = {
super.initializeCtx(c)
if (optimise) {
val flags = Array("-optimise") // :+ "-Xprint:simplify"
val summary = CompilerCommand.distill(flags)(c)
c.setSettings(summary.sstate)
}
override def initCtx = {
val ctx0 = super.initCtx
ctx0.setSetting(ctx0.settings.optimise, optimise)
}

def check(source: String, expected: String, shared: String = ""): Unit = {
Expand Down