summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/plugins/Plugin.scala
diff options
context:
space:
mode:
authorLex Spoon <lex@lexspoon.org>2007-06-07 09:11:46 +0000
committerLex Spoon <lex@lexspoon.org>2007-06-07 09:11:46 +0000
commitd1aed7012af7439181c4696fb33f5f4337b83684 (patch)
tree5f57aa3c6860ada20b5e4ef00debe18acf5884c1 /src/compiler/scala/tools/nsc/plugins/Plugin.scala
parent6739cacb9dbc1cbc3c459b87b8ba97923d687fbe (diff)
downloadscala-d1aed7012af7439181c4696fb33f5f4337b83684.tar.gz
scala-d1aed7012af7439181c4696fb33f5f4337b83684.tar.bz2
scala-d1aed7012af7439181c4696fb33f5f4337b83684.zip
Final merge from the plugins branch. The compiler
can now have plugins loaded at runtime via jars, and thus compiler components can be distributed indepedently of the central compiler.
Diffstat (limited to 'src/compiler/scala/tools/nsc/plugins/Plugin.scala')
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugin.scala138
1 files changed, 138 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
new file mode 100644
index 0000000000..73ebb66134
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
@@ -0,0 +1,138 @@
+package scala.tools.nsc.plugins
+import java.io.File
+import java.util.jar.JarFile
+import java.util.zip.ZipException
+import scala.xml.XML
+import java.net.URLClassLoader
+import scala.collection.mutable
+import mutable.ListBuffer
+
+/** Information about a plugin loaded from a jar file.
+ *
+ * The concrete subclass must have a one-argument constructor
+ * that accepts an instance of Global.
+ *
+ * (val global: Global)
+ */
+abstract class Plugin {
+ /** The name of this plugin */
+ val name: String
+
+ /** The components that this phase defines */
+ val components: List[PluginComponent]
+
+ /** A one-line description of the plugin */
+ val description: String
+
+ /** The compiler that this plugin uses. This is normally equated
+ * to a constructor parameter in the concrete subclass. */
+ val global: Global
+
+ /** Handle any plugin-specific options. The -P:plugname: part
+ * will not be present. */
+ def processOptions(options: List[String], error: String=>Unit) {
+ if (!options.isEmpty)
+ error("Error: " + name + " has no options")
+ }
+
+ /** A description of this plugin's options, suitable as a response
+ * to the -help command-line option. Conventionally, the
+ * options should be listed with the -P:plugname: part included.
+ */
+ val optionsHelp: Option[String] = None
+}
+
+object Plugin {
+ /** Create a class loader with the specified file plus
+ * the loader that loaded the Scala compiler.
+ */
+ private def loaderFor(jarfiles: Seq[File]): ClassLoader = {
+ val compilerLoader = classOf[Plugin].getClassLoader
+ val jarurls = jarfiles.map(.toURL).toArray
+ new URLClassLoader(jarurls, compilerLoader)
+ }
+
+
+
+ /** Try to load a plugin description from the specified
+ * file, returning None if it does not work. */
+ private def loadDescription(jarfile: File): Option[PluginDescription] = {
+ if (!jarfile.exists) return None
+
+ try {
+ val jar = new JarFile(jarfile)
+ try {
+ val ent = jar.getEntry("scalac-plugin.xml")
+ if(ent == null) return None
+
+ val inBytes = jar.getInputStream(ent)
+ val packXML = XML.load(inBytes)
+ inBytes.close()
+
+ PluginDescription.fromXML(packXML)
+ } finally {
+ jar.close()
+ }
+ } catch {
+ case _:ZipException => None
+ }
+ }
+
+
+
+ /** Loads a plugin class from the named jar file. Returns None
+ * if the jar file has no plugin in it or if the plugin
+ * is badly formed. */
+ def loadFrom(jarfile: File,
+ loader: ClassLoader): Option[Class] =
+ {
+ val pluginInfo = loadDescription(jarfile).get
+
+ try {
+ Some(loader.loadClass(pluginInfo.classname))
+ } catch {
+ case _:ClassNotFoundException =>
+ println("Warning: class not found for plugin in " + jarfile +
+ " (" + pluginInfo.classname + ")")
+ None
+ }
+ }
+
+
+
+ /** Load all plugins found in the argument list, bot hin
+ * the jar files explicitly listed, and in the jar files in
+ * the directories specified. Skips all plugins in `ignoring'.
+ * A single classloader is created and used to load all of them. */
+ def loadAllFrom(jars: List[File],
+ dirs: List[File],
+ ignoring: List[String]): List[Class] =
+ {
+ val alljars = new ListBuffer[File]
+
+ alljars ++= jars
+
+ for {
+ dir <- dirs
+ entries = dir.listFiles.toList
+ sorted = entries.sort((f1,f2)=>f1.getName <= f2.getName)
+ ent <- sorted
+ if ent.toString.toLowerCase.endsWith(".jar")
+ pdesc <- loadDescription(ent)
+ if !(ignoring contains pdesc.name)
+ } alljars += ent
+
+ val loader = loaderFor(alljars.toList)
+ alljars.toList.map(f => loadFrom(f,loader)).flatMap(x => x)
+ }
+
+
+
+ /** Instantiate a plugin class, given the class and
+ * the compiler it is to be used in.
+ */
+ def instantiate(clazz: Class, global: Global): Plugin = {
+ val constructor = clazz.getConstructor(Array(classOf[Global]))
+ constructor.newInstance(Array(global)).asInstanceOf[Plugin]
+ }
+}