1
0
Fork 0
mirror of https://github.com/ethauvin/kobalt.git synced 2025-04-26 08:27:12 -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.log
import com.beust.kobalt.plugin.KobaltDefaultPlugin
import com.beust.kobalt.plugin.packaging.JarUtils
import com.google.inject.Provider
import java.io.FileInputStream
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.net.URLClassLoader
import java.util.*
import java.util.jar.JarInputStream
import java.util.jar.JarFile
import javax.inject.Inject
import javax.inject.Singleton
@ -27,17 +26,12 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider<Tas
val files: KFiles,
val depFactory: DepFactory,
val localRepo: LocalRepo,
val executors: KobaltExecutors) {
val executors: KobaltExecutors,
val pluginInfo: PluginInfo) {
companion object {
public val MANIFEST_PLUGIN_CLASS : String = "Kobalt-Plugin-Class"
private var pluginMap = hashMapOf<String, Plugin>()
fun addPlugin(pluginClass : Class<out Plugin>) {
addPluginInstance(Kobalt.INJECTOR.getInstance(pluginClass))
}
fun addPluginInstance(plugin: Plugin) {
pluginMap.put(plugin.name, plugin)
}
@ -141,18 +135,6 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider<Tas
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>
get() = Plugins.plugins.flatMap { it.tasks }
@ -165,23 +147,19 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider<Tas
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 ->
JarInputStream(fis).use { jis ->
val manifest = jis.manifest
val mainClass = manifest.mainAttributes.getValue(Plugins.MANIFEST_PLUGIN_CLASS) ?:
throw KobaltException("Couldn't find \"${Plugins.MANIFEST_PLUGIN_CLASS}\" in the " +
"manifest of $it")
val pluginClassName = mainClass.removeSuffix(" ")
val c = instantiateClassName(classLoader, pluginClassName)
@Suppress("UNCHECKED_CAST")
Plugins.addPlugin(c as Class<BasePlugin>)
log(1, "Added plugin $c")
val pluginXml = JarUtils.extractTextFile(JarFile(it.jarFile.get()), PluginInfo.PLUGIN_XML)
if (pluginXml != null) {
val thisPluginInfo = PluginInfo.readPluginXml(pluginXml, classLoader)
pluginInfo.addPluginInfo(thisPluginInfo)
thisPluginInfo.plugins.forEach {
Plugins.addPluginInstance(it)
}
} else {
throw KobaltException("Plugin $it doesn't contain a ${PluginInfo.PLUGIN_XML} file")
}
}
}
executor.shutdown()
}

View file

@ -1,6 +1,8 @@
package com.beust.kobalt.api
import com.beust.kobalt.maven.IClasspathDependency
import com.beust.kobalt.misc.log
import java.io.ByteArrayInputStream
import java.io.File
import java.io.InputStream
import java.io.OutputStream
@ -41,7 +43,10 @@ interface IFactory {
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)
}
@ -116,7 +121,7 @@ class ClassNameXml {
* 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.
*/
class PluginInfo(val xml: KobaltPluginXml) {
class PluginInfo(val xml: KobaltPluginXml, val classLoader: ClassLoader?) {
val plugins = arrayListOf<Plugin>()
val projectContributors = arrayListOf<IProjectContributor>()
val classpathContributors = arrayListOf<IClasspathContributor>()
@ -130,48 +135,71 @@ class PluginInfo(val xml: KobaltPluginXml) {
// repos
companion object {
val PLUGIN_XML = "META-INF/plugin.xml" // Plugins.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
val pluginXml = "META-INF/plugin.xml" // Plugins.PLUGIN_XML)
val url = Kobalt::class.java.classLoader.getResource(pluginXml)
val url = Kobalt::class.java.classLoader.getResource(PLUGIN_XML)
if (url != null) {
return readPluginXml(url.openConnection().inputStream)
} else {
throw AssertionError("Couldn't find $pluginXml")
throw AssertionError("Couldn't find $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 kotlinPlugin: KobaltPluginXml = jaxbContext.createUnmarshaller().unmarshal(ins)
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 {
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 {
plugins.add(factory.instanceOf(Class.forName(it)) as Plugin)
plugins.add(factory.instanceOf(forName(it)) as Plugin)
}
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 {
projectContributors.add(factory.instanceOf(Class.forName(it)) as IProjectContributor)
projectContributors.add(factory.instanceOf(forName(it)) as IProjectContributor)
}
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 {
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) =
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) {
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.misc.log
import com.google.common.io.CharStreams
import java.io.*
import java.util.jar.JarEntry
import java.util.jar.JarFile
import java.util.jar.JarInputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
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) {
val jar = java.util.jar.JarFile(jarFile)
val enumEntries = jar.entries()

View file

@ -1,6 +1,6 @@
<kobalt-plugin>
<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>
<class-name>com.beust.kobalt.plugin.android.AndroidPlugin</class-name>
<class-name>com.beust.kobalt.plugin.application.ApplicationPlugin</class-name>