1
0
Fork 0
mirror of https://github.com/ethauvin/kobalt.git synced 2025-04-27 00:38:11 -07:00

Expect plug-ins to use the new plug-in architecture.

This commit is contained in:
Cedric Beust 2015-11-07 18:02:40 -08:00
parent 7a81b8169f
commit 87ce55c1ea
5 changed files with 76 additions and 52 deletions

View file

@ -12,13 +12,12 @@ import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltExecutors import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.log import com.beust.kobalt.misc.log
import com.beust.kobalt.plugin.KobaltDefaultPlugin import com.beust.kobalt.plugin.KobaltDefaultPlugin
import com.beust.kobalt.plugin.packaging.JarUtils
import com.google.inject.Provider import com.google.inject.Provider
import java.io.FileInputStream
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import java.net.URLClassLoader
import java.util.* import java.util.*
import java.util.jar.JarInputStream import java.util.jar.JarFile
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -27,17 +26,12 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider<Tas
val files: KFiles, val files: KFiles,
val depFactory: DepFactory, val depFactory: DepFactory,
val localRepo: LocalRepo, val localRepo: LocalRepo,
val executors: KobaltExecutors) { val executors: KobaltExecutors,
val pluginInfo: PluginInfo) {
companion object { companion object {
public val MANIFEST_PLUGIN_CLASS : String = "Kobalt-Plugin-Class"
private var pluginMap = hashMapOf<String, Plugin>() private var pluginMap = hashMapOf<String, Plugin>()
fun addPlugin(pluginClass : Class<out Plugin>) {
addPluginInstance(Kobalt.INJECTOR.getInstance(pluginClass))
}
fun addPluginInstance(plugin: Plugin) { fun addPluginInstance(plugin: Plugin) {
pluginMap.put(plugin.name, plugin) pluginMap.put(plugin.name, plugin)
} }
@ -141,18 +135,6 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider<Tas
val dependencies = arrayListOf<IClasspathDependency>() val dependencies = arrayListOf<IClasspathDependency>()
public fun instantiateClassName(classLoader: ClassLoader, className : String) : Class<*> {
try {
log(2, "Instantiating $className")
return classLoader.loadClass(className)
} catch(ex: Exception) {
val urls = Arrays.toString((classLoader as URLClassLoader).urLs)
val message = "Couldn't instantiate $className\n with classLoader $urls: $ex"
println(message)
throw KobaltException(message)
}
}
val allTasks : List<PluginTask> val allTasks : List<PluginTask>
get() = Plugins.plugins.flatMap { it.tasks } get() = Plugins.plugins.flatMap { it.tasks }
@ -165,21 +147,17 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider<Tas
depFactory.create(it.id, executor) depFactory.create(it.id, executor)
// //
// Inspect the jar, open the manifest, instantiate the main class and add it to the plugin repo // Open the jar, parse its plugin.xml and add the resulting PluginInfo to pluginInfo
// //
FileInputStream(it.jarFile.get()).use { fis -> val pluginXml = JarUtils.extractTextFile(JarFile(it.jarFile.get()), PluginInfo.PLUGIN_XML)
JarInputStream(fis).use { jis -> if (pluginXml != null) {
val manifest = jis.manifest val thisPluginInfo = PluginInfo.readPluginXml(pluginXml, classLoader)
val mainClass = manifest.mainAttributes.getValue(Plugins.MANIFEST_PLUGIN_CLASS) ?: pluginInfo.addPluginInfo(thisPluginInfo)
throw KobaltException("Couldn't find \"${Plugins.MANIFEST_PLUGIN_CLASS}\" in the " + thisPluginInfo.plugins.forEach {
"manifest of $it") Plugins.addPluginInstance(it)
val pluginClassName = mainClass.removeSuffix(" ")
val c = instantiateClassName(classLoader, pluginClassName)
@Suppress("UNCHECKED_CAST")
Plugins.addPlugin(c as Class<BasePlugin>)
log(1, "Added plugin $c")
} }
} else {
throw KobaltException("Plugin $it doesn't contain a ${PluginInfo.PLUGIN_XML} file")
} }
} }
executor.shutdown() executor.shutdown()

View file

@ -1,6 +1,8 @@
package com.beust.kobalt.api package com.beust.kobalt.api
import com.beust.kobalt.maven.IClasspathDependency import com.beust.kobalt.maven.IClasspathDependency
import com.beust.kobalt.misc.log
import java.io.ByteArrayInputStream
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
@ -41,7 +43,10 @@ interface IFactory {
fun <T> instanceOf(c: Class<T>) : T fun <T> instanceOf(c: Class<T>) : T
} }
class ContributorFactory : IFactory { /**
* If a plug-in didn't specify a factory, we use our own injector to instantiate all its components.
*/
class GuiceFactory : IFactory {
override fun <T> instanceOf(c: Class<T>) : T = Kobalt.INJECTOR.getInstance(c) override fun <T> instanceOf(c: Class<T>) : T = Kobalt.INJECTOR.getInstance(c)
} }
@ -116,7 +121,7 @@ class ClassNameXml {
* all the contributors instantiated and other information that Kobalt can actually use. Kobalt code that * all the contributors instantiated and other information that Kobalt can actually use. Kobalt code that
* needs to access plug-in info can then just inject a PluginInfo object. * needs to access plug-in info can then just inject a PluginInfo object.
*/ */
class PluginInfo(val xml: KobaltPluginXml) { class PluginInfo(val xml: KobaltPluginXml, val classLoader: ClassLoader?) {
val plugins = arrayListOf<Plugin>() val plugins = arrayListOf<Plugin>()
val projectContributors = arrayListOf<IProjectContributor>() val projectContributors = arrayListOf<IProjectContributor>()
val classpathContributors = arrayListOf<IClasspathContributor>() val classpathContributors = arrayListOf<IClasspathContributor>()
@ -130,48 +135,71 @@ class PluginInfo(val xml: KobaltPluginXml) {
// repos // repos
companion object { companion object {
val PLUGIN_XML = "META-INF/plugin.xml" // Plugins.PLUGIN_XML)
/** /**
* Read Kobalt's own plugin.xml. * Read Kobalt's own plugin.xml.
*/ */
fun readKobaltPluginXml() : PluginInfo { fun readKobaltPluginXml(): PluginInfo {
// Note: use forward slash here since we're looking up this file in a .jar file // Note: use forward slash here since we're looking up this file in a .jar file
val pluginXml = "META-INF/plugin.xml" // Plugins.PLUGIN_XML) val url = Kobalt::class.java.classLoader.getResource(PLUGIN_XML)
val url = Kobalt::class.java.classLoader.getResource(pluginXml)
if (url != null) { if (url != null) {
return readPluginXml(url.openConnection().inputStream) return readPluginXml(url.openConnection().inputStream)
} else { } else {
throw AssertionError("Couldn't find $pluginXml") throw AssertionError("Couldn't find $PLUGIN_XML")
} }
} }
/** /**
* Read a general plugin.xml. * Read a general plugin.xml.
*/ */
private fun readPluginXml(ins: InputStream): PluginInfo { fun readPluginXml(ins: InputStream, classLoader: ClassLoader? = null): PluginInfo {
val jaxbContext = JAXBContext.newInstance(KobaltPluginXml::class.java) val jaxbContext = JAXBContext.newInstance(KobaltPluginXml::class.java)
val kotlinPlugin: KobaltPluginXml = jaxbContext.createUnmarshaller().unmarshal(ins) val kotlinPlugin: KobaltPluginXml = jaxbContext.createUnmarshaller().unmarshal(ins)
as KobaltPluginXml as KobaltPluginXml
return PluginInfo(kotlinPlugin) return PluginInfo(kotlinPlugin, classLoader)
} }
fun readPluginXml(s: String, classLoader: ClassLoader? = null)
= readPluginXml(ByteArrayInputStream(s.toByteArray(Charsets.UTF_8)), classLoader)
} }
init { init {
val factory = Class.forName(xml.factoryClassName).newInstance() as IFactory val factory = if (xml.factoryClassName != null) {
Class.forName(xml.factoryClassName).newInstance() as IFactory
} else {
GuiceFactory()
}
fun forName(className: String) =
if (classLoader != null) classLoader.loadClass(className)
else Class.forName(className)
xml.plugins?.className?.forEach { xml.plugins?.className?.forEach {
plugins.add(factory.instanceOf(Class.forName(it)) as Plugin) plugins.add(factory.instanceOf(forName(it)) as Plugin)
} }
xml.classpathClassName?.className?.forEach { xml.classpathClassName?.className?.forEach {
classpathContributors.add(factory.instanceOf(Class.forName(it)) as IClasspathContributor) classpathContributors.add(factory.instanceOf(forName(it)) as IClasspathContributor)
} }
xml.projectClassName?.className?.forEach { xml.projectClassName?.className?.forEach {
projectContributors.add(factory.instanceOf(Class.forName(it)) as IProjectContributor) projectContributors.add(factory.instanceOf(forName(it)) as IProjectContributor)
} }
xml.initClassName?.className?.forEach { xml.initClassName?.className?.forEach {
initContributors.add(factory.instanceOf(Class.forName(it)) as IInitContributor) initContributors.add(factory.instanceOf(forName(it)) as IInitContributor)
} }
xml.repoClassName?.className?.forEach { xml.repoClassName?.className?.forEach {
repoContributors.add(factory.instanceOf(Class.forName(it)) as IRepoContributor) repoContributors.add(factory.instanceOf(forName(it)) as IRepoContributor)
} }
} }
fun addPluginInfo(pluginInfo: PluginInfo) {
log(2, "Found new plug-in, adding it to pluginInfo: $pluginInfo")
plugins.addAll(pluginInfo.plugins)
classpathContributors.addAll(pluginInfo.classpathContributors)
projectContributors.addAll(pluginInfo.projectContributors)
initContributors.addAll(pluginInfo.initContributors)
repoContributors.addAll(pluginInfo.repoContributors)
}
} }

View file

@ -172,7 +172,9 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b
} }
private fun isSameVersionFile(directory: File) = private fun isSameVersionFile(directory: File) =
File(directory, VERSION_FILE).readText() == Kobalt.version with(File(directory, VERSION_FILE)) {
! exists() || (exists() && readText() == Kobalt.version)
}
private fun generateJarFile(context: KobaltContext, buildFile: BuildFile, buildScriptJarFile: File) { private fun generateJarFile(context: KobaltContext, buildFile: BuildFile, buildScriptJarFile: File) {
val kotlintDeps = jvmCompiler.calculateDependencies(null, context, listOf<IClasspathDependency>()) val kotlintDeps = jvmCompiler.calculateDependencies(null, context, listOf<IClasspathDependency>())

View file

@ -2,11 +2,13 @@ package com.beust.kobalt.plugin.packaging
import com.beust.kobalt.IFileSpec import com.beust.kobalt.IFileSpec
import com.beust.kobalt.misc.log import com.beust.kobalt.misc.log
import com.google.common.io.CharStreams
import java.io.* import java.io.*
import java.util.jar.JarEntry import java.util.jar.JarEntry
import java.util.jar.JarFile import java.util.jar.JarFile
import java.util.jar.JarInputStream import java.util.jar.JarInputStream
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
public class JarUtils { public class JarUtils {
@ -92,6 +94,20 @@ public class JarUtils {
} }
} }
fun extractTextFile(zip : ZipFile, fileName: String) : String? {
val enumEntries = zip.entries()
while (enumEntries.hasMoreElements()) {
val file = enumEntries.nextElement()
if (file.name == fileName) {
log(2, "Found $fileName in $zip")
zip.getInputStream(file).use { ins ->
return CharStreams.toString(InputStreamReader(ins, "UTF-8"))
}
}
}
return null
}
fun extractJarFile(jarFile: File, destDir: File) { fun extractJarFile(jarFile: File, destDir: File) {
val jar = java.util.jar.JarFile(jarFile) val jar = java.util.jar.JarFile(jarFile)
val enumEntries = jar.entries() val enumEntries = jar.entries()

View file

@ -1,6 +1,6 @@
<kobalt-plugin> <kobalt-plugin>
<name>kobalt</name> <name>kobalt</name>
<factory-class-name>com.beust.kobalt.api.ContributorFactory</factory-class-name> <factory-class-name>com.beust.kobalt.api.GuiceFactory</factory-class-name>
<plugins> <plugins>
<class-name>com.beust.kobalt.plugin.android.AndroidPlugin</class-name> <class-name>com.beust.kobalt.plugin.android.AndroidPlugin</class-name>
<class-name>com.beust.kobalt.plugin.application.ApplicationPlugin</class-name> <class-name>com.beust.kobalt.plugin.application.ApplicationPlugin</class-name>