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/compiler | |
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/compiler')
7 files changed, 148 insertions, 106 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 = |