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:
parent
7a81b8169f
commit
87ce55c1ea
5 changed files with 76 additions and 52 deletions
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>())
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue