From 0c6ab69119308a4248e416ce325d1a9e1c649516 Mon Sep 17 00:00:00 2001 From: Philipp Haller Date: Mon, 25 Feb 2008 20:39:53 +0000 Subject: Fixed #535 and #539 --- .../scala/tools/partest/nest/CompileManager.scala | 59 ++++- .../scala/tools/partest/nest/FileManager.scala | 11 +- .../scala/tools/partest/nest/NestRunner.scala | 11 +- .../scala/tools/partest/nest/StreamAppender.scala | 2 + src/partest/scala/tools/partest/nest/Worker.scala | 241 +++++++++++++++------ 5 files changed, 244 insertions(+), 80 deletions(-) diff --git a/src/partest/scala/tools/partest/nest/CompileManager.scala b/src/partest/scala/tools/partest/nest/CompileManager.scala index 81bbf076db..3f9a20959d 100644 --- a/src/partest/scala/tools/partest/nest/CompileManager.scala +++ b/src/partest/scala/tools/partest/nest/CompileManager.scala @@ -8,7 +8,6 @@ import scala.tools.nsc.{Global, Settings} import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} import java.io.{File, BufferedReader, PrintWriter, FileWriter} -import java.net.URLClassLoader class ExtConsoleReporter(override val settings: Settings, reader: BufferedReader, var writer: PrintWriter) extends ConsoleReporter(settings, reader, writer) { def this(settings: Settings) = { @@ -106,10 +105,13 @@ class DirectCompiler extends SimpleCompiler { } class ReflectiveCompiler extends SimpleCompiler { - import FileManager.{latestCompFile, latestPartestFile} + import FileManager.{latestCompFile, latestPartestFile, latestFjbgFile} - val sepUrls = Array(latestCompFile.toURL, latestPartestFile.toURL) - val sepLoader = new URLClassLoader(sepUrls) + val sepUrls = Array(latestCompFile.toURL, latestPartestFile.toURL, + latestFjbgFile.toURL) + //NestUI.verbose("constructing URLClassLoader from URLs "+latestCompFile+" and "+latestPartestFile) + + val sepLoader = new java.net.URLClassLoader(sepUrls, null) val sepCompilerClass = sepLoader.loadClass("scala.tools.partest.nest.DirectCompiler") @@ -123,12 +125,22 @@ class ReflectiveCompiler extends SimpleCompiler { val sepCompileMethod2 = sepCompilerClass.getMethod("compile", Array(fileClass, stringClass, fileClass)) + /* This method throws java.lang.reflect.InvocationTargetException + * if the compiler crashes. + * This exception is handled in the shouldCompile and shouldFailCompile + * methods of class CompileManager. + */ def compile(file: File, kind: String): Boolean = { val fileArgs: Array[AnyRef] = Array(file, kind) val res = sepCompileMethod.invoke(sepCompiler, fileArgs).asInstanceOf[java.lang.Boolean] res.booleanValue() } + /* This method throws java.lang.reflect.InvocationTargetException + * if the compiler crashes. + * This exception is handled in the shouldCompile and shouldFailCompile + * methods of class CompileManager. + */ def compile(file: File, kind: String, log: File): Boolean = { val fileArgs: Array[AnyRef] = Array(file, kind, log) val res = sepCompileMethod2.invoke(sepCompiler, fileArgs).asInstanceOf[java.lang.Boolean] @@ -145,20 +157,47 @@ class CompileManager { compiler = new ReflectiveCompiler } + /* This method returns true iff compilation succeeds. + */ def shouldCompile(file: File, kind: String): Boolean = { createSeparateCompiler() - compiler.compile(file, kind) - /*compiler.compile(file, kind) || { - NestUI.verbose("creating new separate compiler") - createSeparateCompiler() + try { compiler.compile(file, kind) - }*/ + } catch { + case ite: java.lang.reflect.InvocationTargetException => + NestUI.verbose("while invoking compiler ("+file+"):") + NestUI.verbose("caught "+ite) + ite.printStackTrace + ite.getCause.printStackTrace + false + } } + /* This method returns true iff compilation fails + * _and_ the compiler does _not_ crash. + * + * If the compiler crashes, this method returns false. + */ def shouldFailCompile(file: File, kind: String, log: File): Boolean = { // always create new separate compiler createSeparateCompiler() - !compiler.compile(file, kind, log) + + try { + // simulating compiler crash + /*if (file.getName().endsWith("bug752.scala")) { + NestUI.verbose("simulating compiler crash") + throw new java.lang.reflect.InvocationTargetException(new Throwable) + }*/ + + !compiler.compile(file, kind, log) + } catch { + case ite: java.lang.reflect.InvocationTargetException => + NestUI.verbose("while invoking compiler ("+file+"):") + NestUI.verbose("caught "+ite) + ite.printStackTrace + ite.getCause.printStackTrace + false + } } } diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala index b55ebdcfea..0d45f8ae1f 100644 --- a/src/partest/scala/tools/partest/nest/FileManager.scala +++ b/src/partest/scala/tools/partest/nest/FileManager.scala @@ -40,7 +40,7 @@ else (libs.listFiles(new FilenameFilter { def accept(dir: File, name: String) = name.endsWith(".jar") - }) map {file => file.getAbsolutePath}).mkString(""+PATH_SEP) + }) map {file => file.getCanonicalFile.getAbsolutePath}).mkString(""+PATH_SEP) } /* @@ -87,6 +87,7 @@ elif [ -d "$PREFIX/bin" ]; then latestLibFile = prefixFile("build/quick/lib/library") latestCompFile = prefixFile("build/quick/lib/compiler") latestPartestFile = prefixFile("build/quick/lib/partest") + latestFjbgFile = prefixFile("build/quick/lib/fjbg.jar") } else if (bin.exists && bin.isDirectory) { latestFile = prefixFile("bin") @@ -118,6 +119,7 @@ elif [ -d "$PREFIX/bin" ]; then var latestLibFile: File = _ var latestCompFile: File = _ var latestPartestFile: File = _ + var latestFjbgFile: File = _ // initialize above fields findLatest() @@ -188,9 +190,9 @@ class FileManager { var testFiles: List[File] = List() - def getFiles(kind: String, doCheck: Boolean): List[File] = { + def getFiles(kind: String, doCheck: Boolean, ending: String): List[File] = { val filter = new FilenameFilter { - def accept(dir: File, name: String): Boolean = name endsWith ".scala" + def accept(dir: File, name: String): Boolean = name endsWith ending } val dir = new File(FileManager.srcDir, kind) NestUI.verbose("look in "+dir+" for tests") @@ -207,4 +209,7 @@ class FileManager { Nil } } + + def getFiles(kind: String, doCheck: Boolean): List[File] = + getFiles(kind, doCheck, ".scala") } diff --git a/src/partest/scala/tools/partest/nest/NestRunner.scala b/src/partest/scala/tools/partest/nest/NestRunner.scala index f9f2a44af4..5598db3a15 100644 --- a/src/partest/scala/tools/partest/nest/NestRunner.scala +++ b/src/partest/scala/tools/partest/nest/NestRunner.scala @@ -20,6 +20,7 @@ object NestRunner { private var jvmCheck = false private var jvm5Check = false private var runCheck = false + private var resCheck = false private var shootoutCheck = false private var conservative = false @@ -48,6 +49,7 @@ object NestRunner { case "--jvm" => jvmCheck = true case "--jvm5" => jvm5Check = true case "--run" => runCheck = true + case "--res" => resCheck = true case "--shootout" => shootoutCheck = true case "--conservative" => conservative = true case "--verbose" => NestUI._verbose = true @@ -82,6 +84,7 @@ object NestRunner { NestUI.outline("Scala binaries in : "+FileManager.BIN_DIR+"\n") // obtain scalac version + //TODO: this does not work under Windows! val cmd = FileManager.SCALAC_CMD+" -version" NestUI.verbose("running "+cmd) val proc = Runtime.getRuntime.exec(cmd) @@ -138,7 +141,10 @@ object NestRunner { NestUI.verbose("testing "+testFiles) testFiles } - else fileMgr.getFiles(kind, check) + else if (kind == "res") //TODO: is there a nicer way? + fileMgr.getFiles(kind, check, ".res") + else + fileMgr.getFiles(kind, check) if (!kindFiles.isEmpty) { NestUI.outline("\n"+msg+"\n") @@ -180,7 +186,8 @@ object NestRunner { runTests("neg", negCheck, "Testing compiler (on files whose compilation should fail)"), runTests("run", runCheck, "Testing JVM backend"), runTests("jvm", jvmCheck, "Testing JVM backend"), - runTests("jvm5", jvm5Check, "Testing JVM backend")) + runTests("jvm5", jvm5Check, "Testing JVM backend"), + runTests("res", resCheck, "Testing resident compiler")) results reduceLeft { (p: (Int, Int), q: (Int, Int)) => (p._1+q._1, p._2+q._2) } } diff --git a/src/partest/scala/tools/partest/nest/StreamAppender.scala b/src/partest/scala/tools/partest/nest/StreamAppender.scala index eddf47cbfc..4a5e5c2f8b 100644 --- a/src/partest/scala/tools/partest/nest/StreamAppender.scala +++ b/src/partest/scala/tools/partest/nest/StreamAppender.scala @@ -17,7 +17,9 @@ class StreamAppender(from: Reader, to: Writer) extends Thread { writer.println(line) line = reader.readLine() } + reader.close() writer.flush() + writer.close() } catch { case e: IOException => e.printStackTrace() diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala index bf02c1fb9b..3dc0ecf82c 100644 --- a/src/partest/scala/tools/partest/nest/Worker.scala +++ b/src/partest/scala/tools/partest/nest/Worker.scala @@ -6,7 +6,7 @@ package scala.tools.partest.nest import java.io.{File, FileInputStream, FileOutputStream, PrintStream, PrintWriter, StringWriter, FileWriter, InputStreamReader, - FileReader} + FileReader, OutputStreamWriter} import java.net.URL @@ -87,7 +87,6 @@ class Worker extends Actor { proc.waitFor() inApp.join() errApp.join() - writer.close() if (NestRunner.showLog) { // produce log as string in `log` @@ -96,7 +95,6 @@ class Worker extends Actor { val appender = new StreamAppender(reader, writer) appender.start() appender.join() - reader.close() log = writer.toString } @@ -148,53 +146,61 @@ class Worker extends Actor { diff = "" printInfoStart(file, wr) - if (!compileMgr.shouldCompile(file, kind)) { - NestUI.verbose("compilation of "+file+" failed\n") - success = false - } else { - // -------- run test -------- + try { // *catch-all* - //TODO: detect whether we have to use Runtime.exec - val useRuntime = true - - if (useRuntime) { - execTest(outDir, logFile) + if (!compileMgr.shouldCompile(file, kind)) { + NestUI.verbose("compilation of "+file+" failed\n") + success = false } else { - val classpath: List[URL] = - outDir.toURL :: - List(file.getParentFile.toURL) ::: - (List.fromString(CLASSPATH, PATH_SEP) map { x => - (new File(x)).toURL }) ::: - (List.fromString(EXT_CLASSPATH, PATH_SEP) map { x => - (new File(x)).toURL }) - try { - NestUI.verbose("classpath: "+classpath) - val out = new FileOutputStream(logFile, true) - Console.withOut(new PrintStream(out)) { - ObjectRunner.run(classpath, "Test", List("jvm")) + // -------- run test -------- + + //TODO: detect whether we have to use Runtime.exec + val useRuntime = true + + if (useRuntime) { + execTest(outDir, logFile) + } else { + val classpath: List[URL] = + outDir.toURL :: + List(file.getParentFile.toURL) ::: + (List.fromString(CLASSPATH, PATH_SEP) map { x => + (new File(x)).toURL }) ::: + (List.fromString(EXT_CLASSPATH, PATH_SEP) map { x => + (new File(x)).toURL }) + try { + NestUI.verbose("classpath: "+classpath) + val out = new FileOutputStream(logFile, true) + Console.withOut(new PrintStream(out)) { + ObjectRunner.run(classpath, "Test", List("jvm")) + } + out.flush + out.close + } catch { + case e: Exception => + NestUI.verbose(e+" ("+file.getPath+")") } - out.flush - out.close - } catch { - case e: Exception => - NestUI.verbose(e+" ("+file.getPath+")") } - } - NestUI.verbose(this+" finished running "+fileBase) + NestUI.verbose(this+" finished running "+fileBase) - diff = compareOutput(dir, fileBase, kind, logFile) - if (!diff.equals("")) { - NestUI.verbose("output differs from log file\n") - success = false - } + diff = compareOutput(dir, fileBase, kind, logFile) + if (!diff.equals("")) { + NestUI.verbose("output differs from log file\n") + success = false + } - // delete output dir - FileManager.deleteRecursive(outDir) + // delete output dir + FileManager.deleteRecursive(outDir) + + // delete log file only if test was successful + if (success) + FileManager.deleteRecursive(logFile) + } // successful compile + } catch { // *catch-all* + case e: Exception => + NestUI.verbose("caught "+e) + success = false + } - // delete log file only if test was successful - if (success) - FileManager.deleteRecursive(logFile) - } // successful compile if (!success) { errors += 1 NestUI.verbose("incremented errors: "+errors) @@ -247,9 +253,15 @@ class Worker extends Actor { case "pos" => { def posTestRun(file: File, wr: PrintWriter) = new CompileTestRun(file, wr) { def runTest() { - if (!compileMgr.shouldCompile(file, kind)) { - succeeded = false - errors += 1 + try { + if (!compileMgr.shouldCompile(file, kind)) { + succeeded = false + errors += 1 + } + } catch { + case e: Exception => + succeeded = false + errors += 1 } } } @@ -278,29 +290,37 @@ class Worker extends Actor { succeeded = true; diff = ""; log = "" printInfoStart(file, wr) - if (!compileMgr.shouldFailCompile(file, kind, logFile)) { - succeeded = false - errors += 1 - } else { //TODO: else compare log file to check file - val fileBase: String = basename(file.getName) - val dir = file.getParentFile - val outDir = new File(dir, fileBase + "-" + kind + ".obj") - - NestUI.verbose("comparing output with check file...") - diff = compareOutput(dir, fileBase, kind, logFile) - if (!diff.equals("")) { - NestUI.verbose("output differs from log file\n") + try { + if (!compileMgr.shouldFailCompile(file, kind, logFile)) { succeeded = false errors += 1 + } else { // compare log file to check file + val fileBase: String = basename(file.getName) + val dir = file.getParentFile + val outDir = new File(dir, fileBase + "-" + kind + ".obj") + + NestUI.verbose("comparing output with check file...") + diff = compareOutput(dir, fileBase, kind, logFile) + if (!diff.equals("")) { + NestUI.verbose("output differs from log file\n") + succeeded = false + errors += 1 + } + + // delete output dir + FileManager.deleteRecursive(outDir) + + // delete log file only if test was successful + if (succeeded) + FileManager.deleteRecursive(logFile) } - - // delete output dir - FileManager.deleteRecursive(outDir) - - // delete log file only if test was successful - if (succeeded) - FileManager.deleteRecursive(logFile) + } catch { + case e: Exception => + NestUI.verbose("caught "+e) + succeeded = false + errors += 1 } + printInfoEnd(succeeded, wr) wr.flush() swr.flush() @@ -332,6 +352,97 @@ class Worker extends Actor { case "jvm5" => { runJvmTests(kind, files) } + case "res" => { + for (file <- files) { + val swr = new StringWriter + val wr = new PrintWriter(swr) + succeeded = true; diff = ""; log = "" + printInfoStart(file, wr) + + val fileBase: String = basename(file.getName) + NestUI.verbose(this+" running test "+fileBase) + val dir = file.getParentFile + val outDir = new File(dir, fileBase + "-" + kind + ".obj") + if (!outDir.exists) + outDir.mkdir() + val logFile = new File(dir, fileBase + "-" + kind + ".log") + val resFile = new File(dir, fileBase + ".res") + + // run scalac in resident mode: + // $SCALAC -d "$os_dstbase".obj -Xresident -sourcepath . "$@" + val cmd = + JAVACMD+ + " -classpath "+outDir+PATH_SEP+CLASSPATH+PATH_SEP+EXT_CLASSPATH+ + " -Djavacmd="+JAVACMD+ + " scala.tools.nsc.Main"+ + " -d "+outDir.getCanonicalFile.getAbsolutePath+ + " -Xresident"+ + " -sourcepath "+logFile.getParentFile.getCanonicalFile.getAbsolutePath + NestUI.verbose(cmd) + + val proc = Runtime.getRuntime.exec(cmd, null, dir) + + val in = proc.getInputStream + val err = proc.getErrorStream + val out = proc.getOutputStream + + val writer = new FileWriter(logFile) + val reader = new FileReader(resFile) + val errWriter = new StringWriter + + val inApp = new StreamAppender(new InputStreamReader(in), writer) + val errApp = new StreamAppender(new InputStreamReader(err), errWriter) + val outApp = new StreamAppender(reader, new OutputStreamWriter(out)) + + inApp.start() + errApp.start() + outApp.start() + + val exitCode = proc.waitFor() + NestUI.verbose("finished with exit code "+exitCode) + + inApp.join() + errApp.join() + outApp.join() + + //writer.close() + //reader.close() + + // compare log file with check file + diff = compareOutput(dir, fileBase, kind, logFile) + if (!diff.equals("")) { + NestUI.verbose("output differs from log file\n") + succeeded = false + errors += 1 + } + + if (!succeeded && NestRunner.showDiff) NestUI.normal(diff) + if (!succeeded && NestRunner.showLog) { + // output log file + val logReader = new FileReader(logFile) + val logWriter = new StringWriter + val logAppender = new StreamAppender(logReader, logWriter) + logAppender.start() + logAppender.join() + val log = logWriter.toString + NestUI.normal(log) + } + + // delete output dir + FileManager.deleteRecursive(outDir) + // delete log file only if test was successful + if (succeeded) + FileManager.deleteRecursive(logFile) + + printInfoEnd(succeeded, wr) + wr.flush() + swr.flush() + NestUI.normal(swr.toString) + } + NestUI.verbose("finished testing "+kind+" with "+errors+" errors") + NestUI.verbose("created "+compileMgr.numSeparateCompilers+" separate compilers") + (files.length-errors, errors) + } } } } -- cgit v1.2.3