summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/plugins
diff options
context:
space:
mode:
authorSom Snytt <som.snytt@gmail.com>2012-09-26 11:36:01 -0700
committerSom Snytt <som.snytt@gmail.com>2012-12-18 10:34:14 -0800
commitfadb306fdf3d37284fd29c50aa3956cabe79480d (patch)
tree7389f4155de0aae885d6ade1bc82c80e99c8b52c /src/compiler/scala/tools/nsc/plugins
parente14917528e1c080a7f10785e21de36f3a7769718 (diff)
downloadscala-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/scala/tools/nsc/plugins')
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugin.scala128
-rw-r--r--src/compiler/scala/tools/nsc/plugins/PluginComponent.scala8
-rw-r--r--src/compiler/scala/tools/nsc/plugins/PluginDescription.scala47
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugins.scala15
4 files changed, 99 insertions, 99 deletions
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 =