diff options
author | Som Snytt <som.snytt@gmail.com> | 2012-09-26 11:36:01 -0700 |
---|---|---|
committer | Som Snytt <som.snytt@gmail.com> | 2012-12-18 10:34:14 -0800 |
commit | fadb306fdf3d37284fd29c50aa3956cabe79480d (patch) | |
tree | 7389f4155de0aae885d6ade1bc82c80e99c8b52c /src | |
parent | e14917528e1c080a7f10785e21de36f3a7769718 (diff) | |
download | scala-fadb306fdf3d37284fd29c50aa3956cabe79480d.tar.gz scala-fadb306fdf3d37284fd29c50aa3956cabe79480d.tar.bz2 scala-fadb306fdf3d37284fd29c50aa3956cabe79480d.zip |
PluginComponent contributes description to -Xshow-phases.
In Global, SubComponent is called a phase descriptor, but it doesn't
actually have a description. (Phase itself does.) This fix adds
a description to PluginComponent so that plugins can describe what
they do in -Xshow-phases.
Elliptical descriptions
Exploded archives
Plugged-in partest
Roundup at the Little h!
Diffstat (limited to 'src')
12 files changed, 197 insertions, 125 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 34d5d10cbf..7ea49a5c86 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -572,7 +572,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val runsRightAfter = None } with Inliners - // phaseName = "inlineExceptionHandlers" + // phaseName = "inlinehandlers" object inlineExceptionHandlers extends { val global: Global.this.type = Global.this val runsAfter = List("inliner") @@ -582,7 +582,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // phaseName = "closelim" object closureElimination extends { val global: Global.this.type = Global.this - val runsAfter = List("inlineExceptionHandlers") + val runsAfter = List("inlinehandlers") val runsRightAfter = None } with ClosureElimination @@ -724,13 +724,41 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** A description of the phases that will run */ def phaseDescriptions: String = { - val width = phaseNames map (_.length) max - val fmt = "%" + width + "s %2s %s\n" + val Limit = 16 // phase names should not be absurdly long + val MaxCol = 80 // because some of us edit on green screens + val maxName = (0 /: phaseNames)(_ max _.length) + val width = maxName min Limit + val maxDesc = MaxCol - (width + 6) // descriptions not novels + val fmt = if (settings.verbose.value) s"%${maxName}s %2s %s%n" + else s"%${width}.${width}s %2s %.${maxDesc}s%n" val line1 = fmt.format("phase name", "id", "description") val line2 = fmt.format("----------", "--", "-----------") + + // built-in string precision merely truncates + import java.util.{ Formattable, FormattableFlags, Formatter } + def fmtable(s: String) = new Formattable { + override def formatTo(formatter: Formatter, flags: Int, width: Int, precision: Int) { + val p = elliptically(s, precision) + val w = if (width > 0 && p.length < width) { + import FormattableFlags.LEFT_JUSTIFY + val leftly = (flags & LEFT_JUSTIFY) == LEFT_JUSTIFY + val sb = new StringBuilder + def pad() = 1 to width - p.length foreach (_ => sb.append(' ')) + if (!leftly) pad() + sb.append(p) + if (leftly) pad() + sb.toString + } else p + formatter.out.append(w) + } + } + def elliptically(s: String, max: Int) = + if (max < 0 || s.length <= max) s + else if (max < 4) s.take(max) + else s.take(max - 3) + "..." val descs = phaseDescriptors.zipWithIndex map { - case (ph, idx) => fmt.format(ph.phaseName, idx + 1, phasesDescMap(ph)) + case (ph, idx) => fmt.format(fmtable(ph.phaseName), idx + 1, fmtable(phasesDescMap(ph))) } line1 :: line2 :: descs mkString } @@ -1302,7 +1330,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val cleanupPhase = phaseNamed("cleanup") val icodePhase = phaseNamed("icode") val inlinerPhase = phaseNamed("inliner") - val inlineExceptionHandlersPhase = phaseNamed("inlineExceptionHandlers") + val inlineExceptionHandlersPhase = phaseNamed("inlinehandlers") val closelimPhase = phaseNamed("closelim") val dcePhase = phaseNamed("dce") // val jvmPhase = phaseNamed("jvm") diff --git a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala index c534c2230c..4e65c72b0b 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala @@ -52,7 +52,7 @@ abstract class InlineExceptionHandlers extends SubComponent { import icodes._ import icodes.opcodes._ - val phaseName = "inlineExceptionHandlers" + val phaseName = "inlinehandlers" /** Create a new phase */ override def newPhase(p: Phase) = new InlineExceptionHandlersPhase(p) diff --git a/src/compiler/scala/tools/nsc/io/Jar.scala b/src/compiler/scala/tools/nsc/io/Jar.scala index 49a1ff114f..0dca75dab9 100644 --- a/src/compiler/scala/tools/nsc/io/Jar.scala +++ b/src/compiler/scala/tools/nsc/io/Jar.scala @@ -47,6 +47,20 @@ class Jar(file: File) extends Iterable[JarEntry] { case _ => Nil } + /** Invoke f with input for named jar entry (or None). */ + def withEntryStream[A](name: String)(f: Option[InputStream] => A) = { + val jarFile = new JarFile(file.jfile) + def apply() = + jarFile getEntry name match { + case null => f(None) + case entry => + val in = Some(jarFile getInputStream entry) + try f(in) + finally in map (_.close()) + } + try apply() finally jarFile.close() + } + def withJarInput[T](f: JarInputStream => T): T = { val in = new JarInputStream(file.inputStream()) try f(in) diff --git a/src/compiler/scala/tools/nsc/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala index 093f8285e1..b0113f7696 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugin.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala @@ -6,10 +6,14 @@ package scala.tools.nsc package plugins -import io.{ Path, Jar } -import java.net.URLClassLoader -import java.util.jar.JarFile +import scala.tools.nsc.io.{ Jar } +import scala.tools.nsc.util.ScalaClassLoader +import scala.reflect.io.{ Directory, File, Path } +import java.io.InputStream import java.util.zip.ZipException + +import scala.collection.mutable.ListBuffer +import scala.util.{ Try, Success, Failure } import scala.xml.XML /** Information about a plugin loaded from a jar file. @@ -34,11 +38,13 @@ abstract class Plugin { val description: String /** The compiler that this plugin uses. This is normally equated - * to a constructor parameter in the concrete subclass. */ + * to a constructor parameter in the concrete subclass. + */ val global: Global /** Handle any plugin-specific options. The `-P:plugname:` part - * will not be present. */ + * will not be present. + */ def processOptions(options: List[String], error: String => Unit) { if (!options.isEmpty) error("Error: " + name + " has no options") @@ -60,90 +66,86 @@ object Plugin { private val PluginXML = "scalac-plugin.xml" - /** Create a class loader with the specified file plus + /** Create a class loader with the specified locations plus * the loader that loaded the Scala compiler. */ - private def loaderFor(jarfiles: Seq[Path]): ClassLoader = { + private def loaderFor(locations: Seq[Path]): ScalaClassLoader = { val compilerLoader = classOf[Plugin].getClassLoader - val jarurls = jarfiles map (_.toURL) + val urls = locations map (_.toURL) - new URLClassLoader(jarurls.toArray, compilerLoader) + ScalaClassLoader fromURLs (urls, compilerLoader) } - /** Try to load a plugin description from the specified - * file, returning `None` if it does not work. + /** Try to load a plugin description from the specified location. */ - private def loadDescription(jarfile: Path): Option[PluginDescription] = - // XXX Return to this once we have some ARM support - if (!jarfile.exists) None - else try { - val jar = new JarFile(jarfile.jfile) - - try { - jar getEntry PluginXML match { - case null => None - case entry => - val in = jar getInputStream entry - val packXML = XML load in - in.close() - - PluginDescription fromXML packXML - } - } - finally jar.close() - } - catch { - case _: ZipException => None + private def loadDescriptionFromJar(jarp: Path): Try[PluginDescription] = { + // XXX Return to this once we have more ARM support + def read(is: Option[InputStream]) = is match { + case None => throw new RuntimeException(s"Missing $PluginXML in $jarp") + case _ => PluginDescription fromXML (XML load is.get) } + Try(new Jar(jarp.jfile).withEntryStream(PluginXML)(read)) + } + + private def loadDescriptionFromFile(f: Path): Try[PluginDescription] = + Try(XML loadFile f.jfile) map (PluginDescription fromXML _) type AnyClass = Class[_] - /** Loads a plugin class from the named jar file. + /** Use a class loader to load the plugin class. * - * @return `None` if the jar file has no plugin in it or - * if the plugin is badly formed. + * @return `None` on failure */ - def loadFrom(jarfile: Path, loader: ClassLoader): Option[AnyClass] = - loadDescription(jarfile) match { - case None => - println("Warning: could not load descriptor for plugin %s".format(jarfile)) - None - case Some(pdesc) => - try Some(loader loadClass pdesc.classname) catch { - case _: Exception => - println("Warning: class not found for plugin in %s (%s)".format(jarfile, pdesc.classname)) - None - } + def load(pd: PluginDescription, loader: ClassLoader): Try[AnyClass] = { + Try[AnyClass] { + loader loadClass pd.classname + } recoverWith { + case _: Exception => + Failure(new RuntimeException(s"Warning: class not found: ${pd.classname}")) } + } - /** Load all plugins found in the argument list, both in the - * jar files explicitly listed, and in the jar files in the - * directories specified. Skips all plugins in `ignoring`. + /** Load all plugins specified by the arguments. + * Each of `jars` must be a valid plugin archive or exploded archive. + * Each of `dirs` may be a directory containing arbitrary plugin archives. + * Skips all plugins named in `ignoring`. * A single classloader is created and used to load all of them. */ def loadAllFrom( jars: List[Path], dirs: List[Path], - ignoring: List[String]): List[AnyClass] = + ignoring: List[String]): List[Try[AnyClass]] = { - val alljars = (jars ::: (for { - dir <- dirs if dir.isDirectory - entry <- dir.toDirectory.files.toList sortBy (_.name) -// was: if Path.isJarOrZip(entry) - if Jar.isJarOrZip(entry) - pdesc <- loadDescription(entry) - if !(ignoring contains pdesc.name) - } yield entry)).distinct - - val loader = loaderFor(alljars) - (alljars map (loadFrom(_, loader))).flatten + // List[(jar, Success(descriptor))] in dir + def scan(d: Directory) = for { + f <- d.files.toList sortBy (_.name) + if Jar isJarOrZip f + pd = loadDescriptionFromJar(f) + if pd.isSuccess + } yield (f, pd) + // (dir, Try(descriptor)) + def explode(d: Directory) = d -> loadDescriptionFromFile(d / PluginXML) + // (j, Try(descriptor)) + def required(j: Path) = j -> loadDescriptionFromJar(j) + + type Paired = Pair[Path, Try[PluginDescription]] + val included: List[Paired] = (dirs flatMap (_ ifDirectory scan)).flatten + val exploded: List[Paired] = jars flatMap (_ ifDirectory explode) + val explicit: List[Paired] = jars flatMap (_ ifFile required) + def ignored(p: Paired) = p match { + case (path, Success(pd)) => ignoring contains pd.name + case _ => false + } + val (locs, pds) = ((explicit ::: exploded ::: included) filterNot ignored).unzip + + val loader = loaderFor(locs.distinct) + pds filter (_.isSuccess) map (_.get) map (Plugin load (_, loader)) } /** Instantiate a plugin class, given the class and * the compiler it is to be used in. */ def instantiate(clazz: AnyClass, global: Global): Plugin = { - val constructor = clazz getConstructor classOf[Global] - (constructor newInstance global).asInstanceOf[Plugin] + (clazz getConstructor classOf[Global] newInstance global).asInstanceOf[Plugin] } } diff --git a/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala b/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala index 4d98b2563c..c6e1af7ea4 100644 --- a/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala +++ b/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala @@ -18,8 +18,12 @@ abstract class PluginComponent extends SubComponent { /** Internal flag to tell external from internal phases */ final override val internal = false - /** Phases supplied by plugins should not have give the runsRightAfter constraint, - * but can override it */ + /** Phases supplied by plugins should not have to supply the + * runsRightAfter constraint, but can override it. + */ val runsRightAfter: Option[String] = None + /** Useful for -Xshow-phases. */ + def description: String = "" + } diff --git a/src/compiler/scala/tools/nsc/plugins/PluginDescription.scala b/src/compiler/scala/tools/nsc/plugins/PluginDescription.scala index f77123ba11..27693d1a45 100644 --- a/src/compiler/scala/tools/nsc/plugins/PluginDescription.scala +++ b/src/compiler/scala/tools/nsc/plugins/PluginDescription.scala @@ -13,17 +13,12 @@ import scala.xml.Node * * @author Lex Spoon * @version 1.0, 2007-5-21 + * @param name A short name of the plugin, used to identify it in + * various contexts. The phase defined by the plugin + * should have the same name. + * @param classname The name of the main Plugin class. */ -abstract class PluginDescription { - - /** A short name of the compiler, used to identify it in - * various contexts. The phase defined by the plugin - * should have the same name. - */ - val name: String - - /** The name of the main class for the plugin */ - val classname: String +case class PluginDescription(name: String, classname: String) { /** An XML representation of this description. It can be * read back using `PluginDescription.fromXML`. @@ -44,32 +39,24 @@ abstract class PluginDescription { */ object PluginDescription { - def fromXML(xml: Node): Option[PluginDescription] = { - // check the top-level tag - xml match { - case <plugin>{_*}</plugin> => () - case _ => return None - } + def fromXML(xml: Node): PluginDescription = { // extract one field def getField(field: String): Option[String] = { val text = (xml \\ field).text.trim if (text == "") None else Some(text) } - - // extract the required fields - val name1 = getField("name") match { - case None => return None - case Some(str) => str + def extracted = { + val name = "name" + val claas = "classname" + val vs = Map(name -> getField(name), claas -> getField(claas)) + if (vs.values exists (_.isEmpty)) fail() + else PluginDescription(name = vs(name).get, classname = vs(claas).get) } - val classname1 = getField("classname") match { - case None => return None - case Some(str) => str + def fail() = throw new RuntimeException("Bad plugin descriptor.") + // check the top-level tag + xml match { + case <plugin>{_*}</plugin> => extracted + case _ => fail() } - - Some(new PluginDescription { - val name = name1 - val classname = classname1 - }) } - } diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala index 736bd826e4..bb7d54d8f6 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala @@ -7,7 +7,8 @@ package scala.tools.nsc package plugins -import io.{ File, Path } +import scala.reflect.io.{ File, Path } +import scala.tools.util.PathResolver.Defaults /** Support for run-time loading of compiler plugins. * @@ -25,8 +26,14 @@ trait Plugins { */ protected def loadRoughPluginsList(): List[Plugin] = { val jars = settings.plugin.value map Path.apply - val dirs = (settings.pluginsDir.value split File.pathSeparator).toList map Path.apply - val classes = Plugin.loadAllFrom(jars, dirs, settings.disable.value) + def injectDefault(s: String) = if (s.isEmpty) Defaults.scalaPluginPath else s + val dirs = (settings.pluginsDir.value split File.pathSeparator).toList map injectDefault map Path.apply + val maybes = Plugin.loadAllFrom(jars, dirs, settings.disable.value) + val (goods, errors) = maybes partition (_.isSuccess) + errors foreach (_ recover { + case e: Exception => inform(e.getMessage) + }) + val classes = goods map (_.get) // flatten // Each plugin must only be instantiated once. A common pattern // is to register annotation checkers during object construction, so @@ -106,7 +113,7 @@ trait Plugins { * @see phasesSet */ protected def computePluginPhases(): Unit = - phasesSet ++= (plugins flatMap (_.components)) + for (p <- plugins; c <- p.components) addToPhasesSet(c, c.description) /** Summary of the options for all loaded plugins */ def pluginOptionsHelp: String = diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index f62eebaaa0..36f0253243 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -17,13 +17,14 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with import definitions._ // standard classes and methods import typer.atOwner // methods to type trees + override def description = "ANF pre-transform for @cps" + /** the following two members override abstract members in Transform */ val phaseName: String = "selectiveanf" protected def newTransformer(unit: CompilationUnit): Transformer = new ANFTransformer(unit) - class ANFTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { implicit val _unit = unit // allow code in CPSUtils.scala to report errors diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala index 801c328177..f61828debc 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala @@ -17,6 +17,8 @@ abstract class SelectiveCPSTransform extends PluginComponent with import definitions._ // standard classes and methods import typer.atOwner // methods to type trees + override def description = "@cps-driven transform of selectiveanf assignments" + /** the following two members override abstract members in Transform */ val phaseName: String = "selectivecps" diff --git a/src/partest/scala/tools/partest/nest/CompileManager.scala b/src/partest/scala/tools/partest/nest/CompileManager.scala index 3f005d143e..9a48c5ce2b 100644 --- a/src/partest/scala/tools/partest/nest/CompileManager.scala +++ b/src/partest/scala/tools/partest/nest/CompileManager.scala @@ -9,7 +9,7 @@ package scala.tools.partest package nest import scala.tools.nsc.{ Global, Settings, CompilerCommand, FatalError, io } -import scala.tools.nsc.io.{ File => SFile } +import scala.reflect.io.{ Directory, File => SFile, FileOperationException } import scala.tools.nsc.interactive.RangePositions import scala.tools.nsc.reporters.{ Reporter, ConsoleReporter } import scala.tools.nsc.util.{ ClassPath, FakePos } @@ -70,10 +70,27 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { s } - private def updatePluginPath(options: String): String = { - def absolutize(path: String) = Path(path) match { + implicit class Copier(f: SFile) { + // But what if f is bigger than CHUNK?! + def copyTo(dest: Path) { + dest.toFile writeAll f.slurp + } + } + + // plugin path can be relative to test root, or cwd is out + private def updatePluginPath(options: String, out: Option[File], srcdir: Directory): String = { + val dir = fileManager.testRootDir + def pathOrCwd(p: String) = + if (p == "." && out.isDefined) { + val plugxml = "scalac-plugin.xml" + val pout = Path(out.get) + val pd = (srcdir / plugxml).toFile + if (pd.exists) pd copyTo (pout / plugxml) + pout + } else Path(p) + def absolutize(path: String) = pathOrCwd(path) match { case x if x.isAbsolute => x.path - case x => (fileManager.testRootDir / x).toAbsolute.path + case x => (dir / x).toAbsolute.path } val (opt1, opt2) = (options split "\\s").toList partition (_ startsWith "-Xplugin:") @@ -90,17 +107,21 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { } val logWriter = new FileWriter(log) + // this api has no notion of srcdir, so fake it + val fstFile = SFile(files(0)) + val srcdir = fstFile.parent + // check whether there is a ".flags" file + def convertFlags(f: SFile) = updatePluginPath(f.slurp(), out, srcdir) val logFile = basename(log.getName) val flagsFileName = "%s.flags" format (logFile.substring(0, logFile.lastIndexOf("-"))) - val argString = (io.File(log).parent / flagsFileName) ifFile (x => updatePluginPath(x.slurp())) getOrElse "" + val argString = (SFile(log).parent / flagsFileName) ifFile (convertFlags) getOrElse "" // slurp local flags (e.g., "A_1.flags") - val fstFile = SFile(files(0)) def isInGroup(num: Int) = fstFile.stripExtension endsWith ("_" + num) val inGroup = (1 to 9) flatMap (group => if (isInGroup(group)) List(group) else List()) val localFlagsList = if (inGroup.nonEmpty) { - val localArgString = (fstFile.parent / (fstFile.stripExtension + ".flags")) ifFile (x => updatePluginPath(x.slurp())) getOrElse "" + val localArgString = (srcdir / (fstFile.stripExtension + ".flags")) ifFile (convertFlags) getOrElse "" localArgString.split(' ').toList.filter(_.length > 0) } else List() @@ -140,8 +161,10 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { NestUI.verbose("compiling "+toCompile) NestUI.verbose("with classpath: "+global.classPath.toString) NestUI.verbose("and java classpath: "+ propOrEmpty("java.class.path")) - try new global.Run compile toCompile - catch { + try { + if (command.shouldStopWithInfo) logWriter append (command getInfoMessage global) + else new global.Run compile toCompile + } catch { case FatalError(msg) => testRep.error(null, "fatal error: " + msg) return CompilerCrashed @@ -152,7 +175,7 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { } finally logWriter.close() - if (testRep.hasErrors) CompileFailed + if (testRep.hasErrors || command.shouldStopWithInfo) CompileFailed else CompileSuccess } } diff --git a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala index d3a40718c6..3446dd0f72 100644 --- a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala +++ b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala @@ -81,6 +81,9 @@ class ReflectiveRunner { val newClasspath = ClassPath.join(paths: _*) setProp("java.class.path", newClasspath) + + // don't let partest find pluginsdir; in ant build, standard plugin has dedicated test suite + //setProp("scala.home", latestLibFile.parent.parent.path) setProp("scala.home", "") if (isPartestDebug) diff --git a/src/partest/scala/tools/partest/nest/RunnerManager.scala b/src/partest/scala/tools/partest/nest/RunnerManager.scala index f2ce19a950..fbef97dab4 100644 --- a/src/partest/scala/tools/partest/nest/RunnerManager.scala +++ b/src/partest/scala/tools/partest/nest/RunnerManager.scala @@ -344,21 +344,22 @@ class RunnerManager(kind: String, val fileManager: FileManager, params: TestRunP * compiler expects and how to implement them. (see SI-1240 for the full story) * * In practice, this happens in 3 steps: - * STEP1: feed all the files to scalac - * it will parse java files and obtain their expected signatures and generate bytecode for scala files - * STEP2: feed the java files to javac - * it will generate the bytecode for the java files and link to the scalac-generated bytecode for scala - * STEP3: only if there are both scala and java files, recompile the scala sources so they link to the correct + * STEP1: Feed all the files to scalac if there are also non-Scala sources. + * It will parse java files and obtain their expected signatures and generate bytecode for scala files + * STEP2: Feed the java files to javac if there are any. + * It will generate the bytecode for the java files and link to the scalac-generated bytecode for scala + * STEP3: (Re-)compile the scala sources so they link to the correct * java signatures, in case the signatures deduced by scalac from the source files were wrong. Since the * bytecode for java is already in place, we only feed the scala files to scalac so it will take the - * java signatures from the existing javac-generated bytecode + * java signatures from the existing javac-generated bytecode. + * Note that no artifacts are deleted before this step. */ List(1, 2, 3).foldLeft(CompileSuccess: CompilationOutcome) { - case (CompileSuccess, 1) if scalaFiles.nonEmpty => + case (CompileSuccess, 1) if scalaFiles.nonEmpty && javaFiles.nonEmpty => compileMgr.attemptCompile(Some(outDir), allFiles, kind, logFile) case (CompileSuccess, 2) if javaFiles.nonEmpty => javac(outDir, javaFiles, logFile) - case (CompileSuccess, 3) if scalaFiles.nonEmpty && javaFiles.nonEmpty => + case (CompileSuccess, 3) if scalaFiles.nonEmpty => // TODO: Do we actually need this? SI-1240 is known to require this, but we don't know if other tests // require it: https://groups.google.com/forum/?fromgroups#!topic/scala-internals/rFDKAcOKciU compileMgr.attemptCompile(Some(outDir), scalaFiles, kind, logFile) |