From 919f5b2e5c5a091326a0693db49ac30ca1e6bf0f Mon Sep 17 00:00:00 2001 From: vsalvis Date: Wed, 24 Jun 2015 16:14:59 +0200 Subject: [PATCH 1/3] Partest 1/3: Pass PrintWriter as argument for context creation --- src/dotty/tools/dotc/Bench.scala | 11 ++++++----- src/dotty/tools/dotc/Compiler.scala | 10 +++++----- src/dotty/tools/dotc/Driver.scala | 9 +++++---- src/dotty/tools/dotc/FromTasty.scala | 3 ++- src/dotty/tools/dotc/Main.scala | 4 ++-- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/dotty/tools/dotc/Bench.scala b/src/dotty/tools/dotc/Bench.scala index 2e0e15e83d58..6abad7e8574b 100644 --- a/src/dotty/tools/dotc/Bench.scala +++ b/src/dotty/tools/dotc/Bench.scala @@ -29,14 +29,15 @@ object Bench extends Driver { private def ntimes(n: Int)(op: => Reporter): Reporter = (emptyReporter /: (0 until n)) ((_, _) => op) - override def doCompile(compiler: Compiler, fileNames: List[String])(implicit ctx: Context): Reporter = + override def doCompile(compiler: Compiler, fileNames: List[String], reporter: Option[Reporter] = None) + (implicit ctx: Context): Reporter = if (new config.Settings.Setting.SettingDecorator[Boolean](ctx.base.settings.resident).value(ctx)) resident(compiler) else ntimes(numRuns) { val start = System.nanoTime() - val r = super.doCompile(compiler, fileNames) - println(s"time elapsed: ${(System.nanoTime - start) / 1000000}ms") + val r = super.doCompile(compiler, fileNames, reporter) + ctx.println(s"time elapsed: ${(System.nanoTime - start) / 1000000}ms") r } @@ -46,11 +47,11 @@ object Bench extends Driver { else (args(pos + 1).toInt, (args take pos) ++ (args drop (pos + 2))) } - override def process(args: Array[String], rootCtx: Context): Reporter = { + override def process(args: Array[String], rootCtx: Context, reporter: Option[Reporter] = None): Reporter = { val (numCompilers, args1) = extractNumArg(args, "#compilers") val (numRuns, args2) = extractNumArg(args1, "#runs") this.numRuns = numRuns - ntimes(numCompilers)(super.process(args2, rootCtx)) + ntimes(numCompilers)(super.process(args2, rootCtx, reporter)) } } diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 386f976cf1c7..94be0342b653 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -7,7 +7,7 @@ import Periods._ import Symbols._ import Scopes._ import typer.{FrontEnd, Typer, Mode, ImportInfo, RefChecks} -import reporting.ConsoleReporter +import reporting.{ConsoleReporter, Reporter} import Phases.Phase import dotty.tools.dotc.transform._ import dotty.tools.dotc.transform.TreeTransforms.{TreeTransform, TreeTransformer} @@ -93,7 +93,7 @@ class Compiler { * for type checking. * imports For each element of RootImports, an import context */ - def rootContext(implicit ctx: Context): Context = { + def rootContext(implicit ctx: Context, r: Option[Reporter] = None): Context = { ctx.definitions.init(ctx) ctx.setPhasePlan(phases) val rootScope = new MutableScope @@ -105,7 +105,7 @@ class Compiler { .setOwner(defn.RootClass) .setTyper(new Typer) .setMode(Mode.ImplicitsEnabled) - .setTyperState(new MutableTyperState(ctx.typerState, new ConsoleReporter()(ctx), isCommittable = true)) + .setTyperState(new MutableTyperState(ctx.typerState, r.getOrElse(new ConsoleReporter()(ctx)), isCommittable = true)) ctx.definitions.init(start) // set context of definitions to start def addImport(ctx: Context, symf: () => Symbol) = ctx.fresh.setImportInfo(ImportInfo.rootImport(symf)(ctx)) @@ -117,8 +117,8 @@ class Compiler { ctx.runInfo.clear() } - def newRun(implicit ctx: Context): Run = { + def newRun(implicit ctx: Context, r: Option[Reporter] = None): Run = { reset() - new Run(this)(rootContext) + new Run(this)(rootContext(ctx, r)) } } diff --git a/src/dotty/tools/dotc/Driver.scala b/src/dotty/tools/dotc/Driver.scala index dc1431acada3..f5e41cbef771 100644 --- a/src/dotty/tools/dotc/Driver.scala +++ b/src/dotty/tools/dotc/Driver.scala @@ -14,21 +14,22 @@ abstract class Driver extends DotClass { protected def emptyReporter: Reporter = new StoreReporter - protected def doCompile(compiler: Compiler, fileNames: List[String])(implicit ctx: Context): Reporter = + protected def doCompile(compiler: Compiler, fileNames: List[String], reporter: Option[Reporter] = None) + (implicit ctx: Context): Reporter = if (fileNames.nonEmpty) { - val run = compiler.newRun + val run = compiler.newRun(ctx, reporter) run.compile(fileNames) run.printSummary() } else emptyReporter protected def initCtx = (new ContextBase).initialCtx - def process(args: Array[String], rootCtx: Context): Reporter = { + def process(args: Array[String], rootCtx: Context, reporter: Option[Reporter] = None): Reporter = { val summary = CompilerCommand.distill(args)(rootCtx) implicit val ctx: Context = initCtx.fresh.setSettings(summary.sstate) val fileNames = CompilerCommand.checkUsage(summary) try { - doCompile(newCompiler(), fileNames) + doCompile(newCompiler(), fileNames, reporter) } catch { case ex: FatalError => ctx.error(ex.getMessage) // signals that we should fail compilation. diff --git a/src/dotty/tools/dotc/FromTasty.scala b/src/dotty/tools/dotc/FromTasty.scala index 019846396277..1662d57e7866 100644 --- a/src/dotty/tools/dotc/FromTasty.scala +++ b/src/dotty/tools/dotc/FromTasty.scala @@ -12,6 +12,7 @@ import SymDenotations._ import typer.FrontEnd import Phases.Phase import util._ +import reporting.Reporter import Decorators._ import dotty.tools.dotc.transform.Pickler import tasty.DottyUnpickler @@ -41,7 +42,7 @@ object FromTasty extends Driver { List(new ReadTastyTreesFromClasses) :: backendPhases } - override def newRun(implicit ctx: Context): Run = { + override def newRun(implicit ctx: Context, reporter: Option[Reporter] = None): Run = { reset() new TASTYRun(this)(rootContext) } diff --git a/src/dotty/tools/dotc/Main.scala b/src/dotty/tools/dotc/Main.scala index 0b136d72f7b9..9aafc83ef55b 100644 --- a/src/dotty/tools/dotc/Main.scala +++ b/src/dotty/tools/dotc/Main.scala @@ -19,10 +19,10 @@ object Main extends Driver { override def newCompiler(): Compiler = new Compiler - override def doCompile(compiler: Compiler, fileNames: List[String])(implicit ctx: Context): Reporter = { + override def doCompile(compiler: Compiler, fileNames: List[String], reporter: Option[Reporter] = None)(implicit ctx: Context): Reporter = { if (new config.Settings.Setting.SettingDecorator[Boolean](ctx.base.settings.resident).value(ctx)) resident(compiler) else - super.doCompile(compiler, fileNames) + super.doCompile(compiler, fileNames, reporter) } } From 5e4e0cf35036906423e13eb74093af07a29f0521 Mon Sep 17 00:00:00 2001 From: vsalvis Date: Wed, 24 Jun 2015 16:33:44 +0200 Subject: [PATCH 2/3] Partest 2/3: Compiler prints via context instead of println --- src/dotty/tools/dotc/Run.scala | 8 ++++---- src/dotty/tools/dotc/transform/TreeChecker.scala | 12 ++++++------ src/dotty/tools/dotc/typer/FrontEnd.scala | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala index 401608a73ac6..553805d95130 100644 --- a/src/dotty/tools/dotc/Run.scala +++ b/src/dotty/tools/dotc/Run.scala @@ -33,7 +33,7 @@ class Run(comp: Compiler)(implicit ctx: Context) { compileSources(sources) } catch { case NonFatal(ex) => - println(i"exception occurred while compiling $units%, %") + ctx.println(i"exception occurred while compiling $units%, %") throw ex } @@ -55,7 +55,7 @@ class Run(comp: Compiler)(implicit ctx: Context) { ctx.usePhases(phases) for (phase <- ctx.allPhases) if (!ctx.reporter.hasErrors) { - if (ctx.settings.verbose.value) println(s"[$phase]") + if (ctx.settings.verbose.value) ctx.println(s"[$phase]") units = phase.runOn(units) def foreachUnit(op: Context => Unit)(implicit ctx: Context): Unit = for (unit <- units) op(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit)) @@ -69,8 +69,8 @@ class Run(comp: Compiler)(implicit ctx: Context) { val prevPhase = ctx.phase.prev // can be a mini-phase val squashedPhase = ctx.squashed(prevPhase) - println(s"result of $unit after ${squashedPhase}:") - println(unit.tpdTree.show(ctx)) + ctx.println(s"result of $unit after ${squashedPhase}:") + ctx.println(unit.tpdTree.show(ctx)) } def compile(sourceCode: String): Unit = { diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 19e6aca62b46..347eb8c01989 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -42,8 +42,8 @@ class TreeChecker extends Phase with SymTransformer { private val seenClasses = collection.mutable.HashMap[String, Symbol]() private val seenModuleVals = collection.mutable.HashMap[String, Symbol]() - def printError(str: String) = { - println(Console.RED + "[error] " + Console.WHITE + str) + def printError(str: String)(implicit ctx: Context) = { + ctx.println(Console.RED + "[error] " + Console.WHITE + str) } val NoSuperClass = Trait | Package @@ -109,7 +109,7 @@ class TreeChecker extends Phase with SymTransformer { def check(phasesToRun: Seq[Phase], ctx: Context) = { val prevPhase = ctx.phase.prev // can be a mini-phase val squahsedPhase = ctx.squashed(prevPhase) - println(s"checking ${ctx.compilationUnit} after phase ${squahsedPhase}") + ctx.println(s"checking ${ctx.compilationUnit} after phase ${squahsedPhase}") val checkingCtx = ctx.fresh .setTyperState(ctx.typerState.withReporter(new ThrowingReporter(ctx.typerState.reporter))) val checker = new Checker(previousPhases(phasesToRun.toList)(ctx)) @@ -117,7 +117,7 @@ class TreeChecker extends Phase with SymTransformer { catch { case NonFatal(ex) => implicit val ctx: Context = checkingCtx - println(i"*** error while checking after phase ${checkingCtx.phase.prev} ***") + ctx.println(i"*** error while checking after phase ${checkingCtx.phase.prev} ***") throw ex } } @@ -151,10 +151,10 @@ class TreeChecker extends Phase with SymTransformer { } nowDefinedSyms += tree.symbol - //println(i"defined: ${tree.symbol}") + //ctx.println(i"defined: ${tree.symbol}") val res = op nowDefinedSyms -= tree.symbol - //println(i"undefined: ${tree.symbol}") + //ctx.println(i"undefined: ${tree.symbol}") res case _ => op } diff --git a/src/dotty/tools/dotc/typer/FrontEnd.scala b/src/dotty/tools/dotc/typer/FrontEnd.scala index 056a57215739..bd597f008192 100644 --- a/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -18,7 +18,7 @@ class FrontEnd extends Phase { try body catch { case NonFatal(ex) => - println(s"exception occurred while $doing ${ctx.compilationUnit}") + ctx.println(s"exception occurred while $doing ${ctx.compilationUnit}") throw ex } From 4b42a198a1db7bb2613e9553e7e4250e6c66f4e3 Mon Sep 17 00:00:00 2001 From: vsalvis Date: Wed, 24 Jun 2015 16:41:37 +0200 Subject: [PATCH 3/3] Partest 3/3: Proper compiler output redirection --- test/dotty/partest/DPConfig.scala | 7 ++++-- test/dotty/partest/DPConsoleRunner.scala | 29 +++++++++-------------- test/dotty/partest/DPDirectCompiler.scala | 19 ++++++++++----- test/test/CompilerTest.scala | 2 +- 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/test/dotty/partest/DPConfig.scala b/test/dotty/partest/DPConfig.scala index ad9c271ef8ea..640dfd0213c3 100644 --- a/test/dotty/partest/DPConfig.scala +++ b/test/dotty/partest/DPConfig.scala @@ -1,7 +1,8 @@ package dotty.partest -import java.io.File import scala.collection.JavaConversions._ +import scala.reflect.io.Path +import java.io.File /** Dotty Partest runs all tests in the provided testDirs located under @@ -14,7 +15,9 @@ import scala.collection.JavaConversions._ * otherwise pos/__defaultFlags.flags are used if the file exists). */ object DPConfig { - val testRoot = "./tests/partest-generated" + val testRoot = (Path(".") / Path("tests") / Path("partest-generated")).toString + val genLog = Path(testRoot) / Path("gen.log") + lazy val testDirs = { val root = new File(testRoot) val dirs = if (!root.exists) Array.empty[String] else root.listFiles.filter(_.isDirectory).map(_.getName) diff --git a/test/dotty/partest/DPConsoleRunner.scala b/test/dotty/partest/DPConsoleRunner.scala index 13ffed05007c..fa6256398d47 100644 --- a/test/dotty/partest/DPConsoleRunner.scala +++ b/test/dotty/partest/DPConsoleRunner.scala @@ -9,7 +9,7 @@ import scala.tools.partest._ import scala.tools.partest.nest._ import scala.util.matching.Regex import tools.nsc.io.{ File => NSCFile } -import java.io.{ File, PrintStream, FileOutputStream } +import java.io.{ File, PrintStream, FileOutputStream, PrintWriter, FileWriter } import java.net.URLClassLoader /** Runs dotty partest from the Console, discovering test sources in @@ -91,23 +91,11 @@ extends SuiteRunner(testSourcePath, fileManager, updateCheck, failed, javaCmdPat val state = try { - // IO redirection is messy, there are no concurrency guarantees. - // Parts of test output might end up in the wrong file or get lost. - Console.out.flush - Console.err.flush - val clog = runner.cLogFile - val stream = new PrintStream(new FileOutputStream(clog.jfile), true) - val result = Console.withOut(stream)({ Console.withErr(stream)({ - val res = runner.run() - Console.err.flush - Console.out.flush - res - })}) - result match { + runner.run match { // Append compiler output to transcript if compilation failed, // printed with --verbose option case TestState.Fail(f, r@"compilation failed", transcript) => - TestState.Fail(f, r, transcript ++ clog.fileLines.dropWhile(_ == "")) + TestState.Fail(f, r, transcript ++ runner.cLogFile.fileLines.dropWhile(_ == "")) case res => res } } catch { @@ -261,11 +249,16 @@ class DPTestRunner(testFile: File, suiteRunner: DPSuiteRunner) extends nest.Runn override def groupedFiles(sources: List[File]): List[List[File]] = { val grouped = sources groupBy (_.group) val flatGroup = List(grouped.keys.toList.sorted.map({ k => grouped(k) sortBy (_.getName) }).flatten) - try { // try/catch because of bug in partest + try { // try/catch because of bug in partest that throws exception if (flatGroup != super.groupedFiles(sources)) - NestUI.echoWarning("Warning: Overriding compilation groups for tests: " + sources) + throw new java.lang.UnsupportedOperationException() } catch { - case e: java.lang.UnsupportedOperationException => NestUI.echoWarning("Warning: Overriding compilation groups for tests: " + sources) + case e: java.lang.UnsupportedOperationException => + val genlogFWriter = new FileWriter(DPConfig.genLog.jfile, true) + val genlogWriter = new PrintWriter(genlogFWriter, true) + genlogWriter.println("Warning: Overriding compilation groups for tests: " + sources) + genlogWriter.close + genlogFWriter.close } flatGroup } diff --git a/test/dotty/partest/DPDirectCompiler.scala b/test/dotty/partest/DPDirectCompiler.scala index 86e7665053b0..b04214893241 100644 --- a/test/dotty/partest/DPDirectCompiler.scala +++ b/test/dotty/partest/DPDirectCompiler.scala @@ -1,14 +1,17 @@ package dotty.partest +import dotty.tools.dotc.reporting.ConsoleReporter import scala.tools.partest.{ TestState, nest } -import java.io.File +import java.io.{ File, PrintWriter, FileWriter } /* NOTE: Adapted from partest.DirectCompiler and DottyTest */ -class DPDirectCompiler(runner: nest.Runner) extends nest.DirectCompiler(runner) { +class DPDirectCompiler(runner: DPTestRunner) extends nest.DirectCompiler(runner) { override def compile(opts0: List[String], sources: List[File]): TestState = { - println("\ncompiling " + sources.mkString(" ") + "\noptions: " + opts0.mkString(" ")) + val clogFWriter = new FileWriter(runner.cLogFile.jfile, true) + val clogWriter = new PrintWriter(clogFWriter, true) + clogWriter.println("\ncompiling " + sources.mkString(" ") + "\noptions: " + opts0.mkString(" ")) implicit var ctx: dotty.tools.dotc.core.Contexts.Context = { val base = new dotty.tools.dotc.core.Contexts.ContextBase @@ -18,17 +21,21 @@ class DPDirectCompiler(runner: nest.Runner) extends nest.DirectCompiler(runner) base.definitions.init(ctx) ctx } - + try { val processor = if (opts0.exists(_.startsWith("#"))) dotty.tools.dotc.Bench else dotty.tools.dotc.Main - val reporter = processor.process((sources.map(_.toString) ::: opts0).toArray, ctx) + val clogger = new ConsoleReporter(writer = clogWriter)(ctx) + val reporter = processor.process((sources.map(_.toString) ::: opts0).toArray, ctx, Some(clogger)) if (!reporter.hasErrors) runner.genPass() else { reporter.printSummary(ctx) runner.genFail(s"compilation failed with ${reporter.errorCount} errors") } - } catch { + } catch { case t: Throwable => runner.genCrash(t) + } finally { + clogFWriter.close + clogWriter.close } } } diff --git a/test/test/CompilerTest.scala b/test/test/CompilerTest.scala index 4a8ec6da0759..09b608f22e1a 100644 --- a/test/test/CompilerTest.scala +++ b/test/test/CompilerTest.scala @@ -344,7 +344,7 @@ object CompilerTest extends App { lazy val init: SFile = { scala.reflect.io.Directory(DPConfig.testRoot).deleteRecursively new JFile(DPConfig.testRoot).mkdirs - val log = (Path(DPConfig.testRoot) / Path("gen.log")).createFile(true) + val log = DPConfig.genLog.createFile(true) println(s"CompilerTest is generating tests for partest, log: $log") log }