mirror of
https://github.com/ethauvin/kobalt.git
synced 2025-04-26 08:27:12 -07:00
Revamping the plug-in class loading.
This commit is contained in:
parent
bb8878ef63
commit
903f63b268
8 changed files with 260 additions and 155 deletions
|
@ -9,6 +9,7 @@ import com.beust.kobalt.kotlin.ScriptCompiler
|
||||||
import com.beust.kobalt.maven.*
|
import com.beust.kobalt.maven.*
|
||||||
import com.beust.kobalt.misc.*
|
import com.beust.kobalt.misc.*
|
||||||
import com.beust.kobalt.SystemProperties
|
import com.beust.kobalt.SystemProperties
|
||||||
|
import com.beust.kobalt.kotlin.ScriptCompiler2
|
||||||
import com.beust.kobalt.plugin.publish.JCenterApi
|
import com.beust.kobalt.plugin.publish.JCenterApi
|
||||||
import com.beust.kobalt.plugin.publish.UnauthenticatedJCenterApi
|
import com.beust.kobalt.plugin.publish.UnauthenticatedJCenterApi
|
||||||
import com.beust.kobalt.wrapper.Wrapper
|
import com.beust.kobalt.wrapper.Wrapper
|
||||||
|
@ -35,6 +36,7 @@ public fun main(argv: Array<String>) {
|
||||||
|
|
||||||
private class Main @Inject constructor(
|
private class Main @Inject constructor(
|
||||||
val scriptCompilerFactory: ScriptCompiler.IFactory,
|
val scriptCompilerFactory: ScriptCompiler.IFactory,
|
||||||
|
val script2: ScriptCompiler2.IFactory,
|
||||||
val plugins: Plugins,
|
val plugins: Plugins,
|
||||||
val taskManager: TaskManager,
|
val taskManager: TaskManager,
|
||||||
val http: Http,
|
val http: Http,
|
||||||
|
@ -115,20 +117,21 @@ private class Main @Inject constructor(
|
||||||
if (! buildFile.exists()) {
|
if (! buildFile.exists()) {
|
||||||
jc.usage()
|
jc.usage()
|
||||||
} else {
|
} else {
|
||||||
// Install all the plugins found
|
val allProjects = script2.create(arrayListOf(buildFile)).findProjects()
|
||||||
plugins.installDynamicPlugins(arrayListOf(buildFile))
|
// // Install all the plugins found
|
||||||
|
// val classLoaders = plugins.installDynamicPlugins(arrayListOf(buildFile))
|
||||||
// Compile the build script
|
//
|
||||||
val output = scriptCompilerFactory.create(plugins.pluginJarFiles,
|
// // Compile the build script
|
||||||
{ n: String, j: File? ->
|
// val output = scriptCompilerFactory.create(plugins.pluginJarFiles,
|
||||||
plugins.instantiateClassName(n, j)
|
// // @@
|
||||||
})
|
// { cl: ClassLoader, n: String -> plugins.instantiateClassName(classLoaders.get(0), n) }
|
||||||
.compile(buildFile, buildFile.lastModified(), KFiles.findBuildScriptLocation(buildFile, SCRIPT_JAR))
|
// ).compile(buildFile, buildFile.lastModified(),
|
||||||
|
// KFiles.findBuildScriptLocation(buildFile, SCRIPT_JAR))
|
||||||
|
|
||||||
//
|
//
|
||||||
// Force each project.directory to be an absolute path, if it's not already
|
// Force each project.directory to be an absolute path, if it's not already
|
||||||
//
|
//
|
||||||
output.projects.forEach {
|
allProjects.forEach {
|
||||||
val fd = File(it.directory)
|
val fd = File(it.directory)
|
||||||
if (! fd.isAbsolute) {
|
if (! fd.isAbsolute) {
|
||||||
it.directory =
|
it.directory =
|
||||||
|
@ -140,7 +143,7 @@ private class Main @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.applyPlugins(KobaltContext(args), output.projects)
|
plugins.applyPlugins(KobaltContext(args), allProjects)
|
||||||
|
|
||||||
if (args.tasks) {
|
if (args.tasks) {
|
||||||
//
|
//
|
||||||
|
@ -157,14 +160,14 @@ private class Main @Inject constructor(
|
||||||
}
|
}
|
||||||
println(sb.toString())
|
println(sb.toString())
|
||||||
} else if (args.checkVersions) {
|
} else if (args.checkVersions) {
|
||||||
checkVersions.run(output.projects)
|
checkVersions.run(allProjects)
|
||||||
} else if (args.update) {
|
} else if (args.update) {
|
||||||
updateKobalt.updateKobalt()
|
updateKobalt.updateKobalt()
|
||||||
} else {
|
} else {
|
||||||
//
|
//
|
||||||
// Launch the build
|
// Launch the build
|
||||||
//
|
//
|
||||||
taskManager.runTargets(args.targets, output.projects)
|
taskManager.runTargets(args.targets, allProjects)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@ import com.beust.kobalt.api.*
|
||||||
import com.beust.kobalt.api.annotation.Task
|
import com.beust.kobalt.api.annotation.Task
|
||||||
import com.beust.kobalt.internal.TaskManager
|
import com.beust.kobalt.internal.TaskManager
|
||||||
import com.beust.kobalt.internal.TaskResult
|
import com.beust.kobalt.internal.TaskResult
|
||||||
import com.beust.kobalt.kotlin.BuildFile
|
|
||||||
import com.beust.kobalt.kotlin.ScriptCompiler
|
|
||||||
import com.beust.kobalt.maven.DepFactory
|
import com.beust.kobalt.maven.DepFactory
|
||||||
import com.beust.kobalt.maven.IClasspathDependency
|
import com.beust.kobalt.maven.IClasspathDependency
|
||||||
import com.beust.kobalt.maven.KobaltException
|
import com.beust.kobalt.maven.KobaltException
|
||||||
|
@ -13,23 +11,17 @@ import com.beust.kobalt.maven.LocalRepo
|
||||||
import com.beust.kobalt.misc.KFiles
|
import com.beust.kobalt.misc.KFiles
|
||||||
import com.beust.kobalt.misc.KobaltExecutors
|
import com.beust.kobalt.misc.KobaltExecutors
|
||||||
import com.beust.kobalt.misc.KobaltLogger
|
import com.beust.kobalt.misc.KobaltLogger
|
||||||
import com.beust.kobalt.misc.countChar
|
|
||||||
import com.beust.kobalt.plugin.DefaultPlugin
|
import com.beust.kobalt.plugin.DefaultPlugin
|
||||||
import com.beust.kobalt.plugin.java.JavaPlugin
|
import com.beust.kobalt.plugin.java.JavaPlugin
|
||||||
import com.beust.kobalt.plugin.kotlin.KotlinPlugin
|
import com.beust.kobalt.plugin.kotlin.KotlinPlugin
|
||||||
import com.beust.kobalt.plugin.packaging.PackagingPlugin
|
import com.beust.kobalt.plugin.packaging.PackagingPlugin
|
||||||
import com.beust.kobalt.plugin.publish.PublishPlugin
|
import com.beust.kobalt.plugin.publish.PublishPlugin
|
||||||
import com.google.inject.Provider
|
import com.google.inject.Provider
|
||||||
import java.io.File
|
|
||||||
import java.io.FileInputStream
|
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.URL
|
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.nio.charset.Charset
|
import java.util.*
|
||||||
import java.nio.file.Paths
|
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.HashMap
|
|
||||||
import java.util.jar.JarInputStream
|
import java.util.jar.JarInputStream
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
@ -39,27 +31,25 @@ 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): KobaltLogger {
|
||||||
val scriptCompilerFactory: ScriptCompiler.IFactory): KobaltLogger {
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
public val MANIFEST_PLUGIN_CLASS : String = "Kobalt-Plugin-Class"
|
public val MANIFEST_PLUGIN_CLASS : String = "Kobalt-Plugin-Class"
|
||||||
|
|
||||||
private var pluginMap = hashMapOf<String, Plugin>()
|
private var pluginMap = hashMapOf<String, Plugin>()
|
||||||
private var storageMap = HashMap<String, HashMap<String, Any>>()
|
// private var storageMap = HashMap<String, HashMap<String, Any>>()
|
||||||
|
// fun storeValue(pluginName: String, key: String, value: Any) {
|
||||||
fun storeValue(pluginName: String, key: String, value: Any) {
|
// var values = storageMap.get(pluginName)
|
||||||
var values = storageMap.get(pluginName)
|
// if (values == null) {
|
||||||
if (values == null) {
|
// values = hashMapOf<String, Any>()
|
||||||
values = hashMapOf<String, Any>()
|
// storageMap.put(pluginName, values)
|
||||||
storageMap.put(pluginName, values)
|
// }
|
||||||
}
|
// values.put(key, value)
|
||||||
values.put(key, value)
|
// }
|
||||||
}
|
//
|
||||||
|
// fun getValue(pluginName: String, key: String) : Any? {
|
||||||
fun getValue(pluginName: String, key: String) : Any? {
|
// return storageMap.get(pluginName)?.get(key)
|
||||||
return storageMap.get(pluginName)?.get(key)
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
val defaultPlugin : Plugin get() = getPlugin(DefaultPlugin.NAME)!!
|
val defaultPlugin : Plugin get() = getPlugin(DefaultPlugin.NAME)!!
|
||||||
|
|
||||||
|
@ -112,7 +102,6 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider<Tas
|
||||||
plugin.apply(project, context)
|
plugin.apply(project, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var currentClass : Class<in Any> = plugin.javaClass
|
var currentClass : Class<in Any> = plugin.javaClass
|
||||||
|
|
||||||
// Tasks can come from two different places: plugin classes and build files.
|
// Tasks can come from two different places: plugin classes and build files.
|
||||||
|
@ -180,67 +169,30 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider<Tas
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Jar files for all the plugins.
|
|
||||||
*/
|
|
||||||
public val pluginJarFiles : ArrayList<String> = arrayListOf()
|
|
||||||
|
|
||||||
val dependencies = arrayListOf<IClasspathDependency>()
|
val dependencies = arrayListOf<IClasspathDependency>()
|
||||||
|
|
||||||
/**
|
public fun instantiateClassName(classLoader: ClassLoader, className : String) : Class<*> {
|
||||||
* Parse the build files, locate all the plugins, download them and make them available to be
|
try {
|
||||||
* used on the classpath of the build file.
|
log(2, "Instantiating ${className}")
|
||||||
*/
|
return classLoader.loadClass(className)
|
||||||
fun installDynamicPlugins(files: List<BuildFile>) {
|
} catch(ex: Exception) {
|
||||||
//
|
val urls = Arrays.toString((classLoader as URLClassLoader).urLs)
|
||||||
// Extract all the plugin() and repos() code into a separate script (pluginCode)
|
val message = "Couldn't instantiate ${className}\n with classLoader $urls: ${ex}"
|
||||||
//
|
println(message)
|
||||||
files.forEach {
|
throw KobaltException(message)
|
||||||
val pluginCode = arrayListOf<String>()
|
|
||||||
var parenCount = 0
|
|
||||||
it.path.toFile().forEachLine(Charset.defaultCharset()) { line ->
|
|
||||||
if (line.startsWith("import")) {
|
|
||||||
pluginCode.add(line)
|
|
||||||
}
|
|
||||||
var index = line.indexOf("plugins(")
|
|
||||||
if (index == -1) index = line.indexOf("repos(")
|
|
||||||
if (parenCount > 0 || index >= 0) {
|
|
||||||
if (index == -1) index = 0
|
|
||||||
with(line.substring(index)) {
|
|
||||||
parenCount += line countChar '('
|
|
||||||
if (parenCount > 0) {
|
|
||||||
pluginCode.add(line)
|
|
||||||
}
|
|
||||||
parenCount -= line countChar ')'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Compile and run pluginCode, which contains all the plugins() calls extracted. This
|
|
||||||
// will add all the dynamic plugins found in this code to Plugins.dynamicPlugins
|
|
||||||
//
|
|
||||||
val pluginSourceFile = KFiles.createTempFile(".kt")
|
|
||||||
pluginSourceFile.writeText(pluginCode.join("\n"), Charset.defaultCharset())
|
|
||||||
log(2, "Saved ${pluginSourceFile.absolutePath}")
|
|
||||||
scriptCompilerFactory.create(pluginJarFiles,
|
|
||||||
{ n: String, j: File? -> instantiateClassName(n, j)
|
|
||||||
}).compile(BuildFile(Paths.get(pluginSourceFile.absolutePath), "Plugins"),
|
|
||||||
it.lastModified(),
|
|
||||||
KFiles.findBuildScriptLocation(it, "preBuildScript.jar"))
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
val allTasks : List<PluginTask>
|
||||||
// Locate all the jar files for the dynamic plugins we just discovered
|
get() {
|
||||||
//
|
val result = arrayListOf<PluginTask>()
|
||||||
dependencies.addAll(dynamicPlugins.map {
|
Plugins.plugins.forEach { plugin ->
|
||||||
pluginJarFiles.add(it.jarFile.get().absolutePath)
|
result.addAll(plugin.tasks)
|
||||||
it
|
}
|
||||||
})
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
//
|
fun installPlugins(dependencies: List<IClasspathDependency>, classLoader: ClassLoader) {
|
||||||
// Materialize all the jar files, instantiate their plugin main class and add it to Plugins
|
|
||||||
//
|
|
||||||
val executor = executors.newExecutor("Plugins", 5)
|
val executor = executors.newExecutor("Plugins", 5)
|
||||||
dependencies.forEach {
|
dependencies.forEach {
|
||||||
//
|
//
|
||||||
|
@ -256,13 +208,13 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider<Tas
|
||||||
try {
|
try {
|
||||||
fis = FileInputStream(it.jarFile.get())
|
fis = FileInputStream(it.jarFile.get())
|
||||||
jis = JarInputStream(fis)
|
jis = JarInputStream(fis)
|
||||||
val manifest = jis.getManifest()
|
val manifest = jis.manifest
|
||||||
val mainClass = manifest.getMainAttributes().getValue(Plugins.MANIFEST_PLUGIN_CLASS) ?:
|
val mainClass = manifest.mainAttributes.getValue(Plugins.MANIFEST_PLUGIN_CLASS) ?:
|
||||||
throw KobaltException("Couldn't find \"${Plugins.MANIFEST_PLUGIN_CLASS}\" in the " +
|
throw KobaltException("Couldn't find \"${Plugins.MANIFEST_PLUGIN_CLASS}\" in the " +
|
||||||
"manifest of ${it}")
|
"manifest of ${it}")
|
||||||
|
|
||||||
val pluginClassName = mainClass.removeSuffix(" ")
|
val pluginClassName = mainClass.removeSuffix(" ")
|
||||||
val c = instantiateClassName(pluginClassName)
|
val c = instantiateClassName(classLoader, pluginClassName)
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
Plugins.addPlugin(c as Class<BasePlugin>)
|
Plugins.addPlugin(c as Class<BasePlugin>)
|
||||||
log(1, "Added plugin ${c}")
|
log(1, "Added plugin ${c}")
|
||||||
|
@ -274,48 +226,4 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider<Tas
|
||||||
executor.shutdown()
|
executor.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun instantiateClassName(className : String, buildScriptJarFile: File? = null) : Class<*> {
|
|
||||||
// fun jarToUrl(jarAbsolutePath: String) = URL("file://" + jarAbsolutePath)
|
|
||||||
|
|
||||||
fun jarToUrl(path: String) = URL("jar", "", "file:${path}!/")
|
|
||||||
|
|
||||||
// We need the jar files to be first in the url list otherwise the Build.kt files resolved
|
|
||||||
// might be Kobalt's own
|
|
||||||
val urls = arrayListOf<URL>()
|
|
||||||
buildScriptJarFile?.let {
|
|
||||||
urls.add(jarToUrl(it.absolutePath))
|
|
||||||
}
|
|
||||||
urls.add(jarToUrl(files.kobaltJar))
|
|
||||||
urls.addAll(pluginJarFiles.map { jarToUrl(it) })
|
|
||||||
val classLoader = URLClassLoader(urls.toArray(arrayOfNulls<URL>(urls.size())))
|
|
||||||
|
|
||||||
try {
|
|
||||||
log(2, "Instantiating ${className}")
|
|
||||||
return classLoader.loadClass(className)
|
|
||||||
} catch(ex: Exception) {
|
|
||||||
throw KobaltException("Couldn't instantiate ${className}: ${ex}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val allTasks : List<PluginTask>
|
|
||||||
get() {
|
|
||||||
val result = arrayListOf<PluginTask>()
|
|
||||||
Plugins.plugins.forEach { plugin ->
|
|
||||||
result.addAll(plugin.tasks)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the tasks accepted by at least one project
|
|
||||||
*/
|
|
||||||
fun findTasks(task: String): List<PluginTask> {
|
|
||||||
val tasks = allTasks.filter { task == it.name }
|
|
||||||
if (tasks.isEmpty()) {
|
|
||||||
throw KobaltException("Couldn't find task ${task}")
|
|
||||||
} else {
|
|
||||||
return tasks
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -8,13 +8,13 @@ import com.beust.kobalt.api.annotation.Task
|
||||||
import com.beust.kobalt.maven.KobaltException
|
import com.beust.kobalt.maven.KobaltException
|
||||||
import com.beust.kobalt.misc.KFiles
|
import com.beust.kobalt.misc.KFiles
|
||||||
import com.beust.kobalt.misc.KobaltLogger
|
import com.beust.kobalt.misc.KobaltLogger
|
||||||
import com.beust.kobalt.plugin.kotlin.kotlinCompiler
|
|
||||||
import com.beust.kobalt.plugin.kotlin.kotlinCompilePrivate
|
import com.beust.kobalt.plugin.kotlin.kotlinCompilePrivate
|
||||||
import com.google.inject.assistedinject.Assisted
|
import com.google.inject.assistedinject.Assisted
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
|
import java.net.URLClassLoader
|
||||||
import java.util.jar.JarInputStream
|
import java.util.jar.JarInputStream
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
@ -24,17 +24,17 @@ import kotlin.properties.Delegates
|
||||||
*/
|
*/
|
||||||
public class ScriptCompiler @Inject constructor(
|
public class ScriptCompiler @Inject constructor(
|
||||||
@Assisted("jarFiles") val jarFiles: List<String>,
|
@Assisted("jarFiles") val jarFiles: List<String>,
|
||||||
@Assisted("instantiate") val instantiate: (String, File?) -> Class<*>,
|
@Assisted("instantiate") val instantiate: (ClassLoader, String) -> Class<*>,
|
||||||
val files: KFiles) : KobaltLogger {
|
val files: KFiles) : KobaltLogger {
|
||||||
|
|
||||||
interface IFactory {
|
interface IFactory {
|
||||||
fun create(@Assisted("jarFiles") jarFiles: List<String>,
|
fun create(@Assisted("jarFiles") jarFiles: List<String>,
|
||||||
@Assisted("instantiate") instantiate: (String, File?) -> Class<*>) : ScriptCompiler
|
@Assisted("instantiate") instantiate: (ClassLoader, String) -> Class<*>) : ScriptCompiler
|
||||||
}
|
}
|
||||||
|
|
||||||
private var buildScriptJarFile by Delegates.notNull<File>()
|
private var buildScriptJarFile by Delegates.notNull<File>()
|
||||||
|
|
||||||
public class CompileOutput(val projects: List<Project>, val plugins: List<String>)
|
public class CompileOutput(val projects: List<Project>, val plugins: List<String>, val classLoader: ClassLoader)
|
||||||
|
|
||||||
public fun compile(buildFile: BuildFile, lastModified: Long, jarFileName: String) : CompileOutput {
|
public fun compile(buildFile: BuildFile, lastModified: Long, jarFileName: String) : CompileOutput {
|
||||||
|
|
||||||
|
@ -54,7 +54,8 @@ public class ScriptCompiler @Inject constructor(
|
||||||
log(2, "Need to recompile ${buildFile.name}")
|
log(2, "Need to recompile ${buildFile.name}")
|
||||||
generateJarFile(buildFile)
|
generateJarFile(buildFile)
|
||||||
}
|
}
|
||||||
return CompileOutput(instantiateBuildFile(), arrayListOf<String>())
|
val pi = instantiateBuildFile()
|
||||||
|
return CompileOutput(pi.projects, arrayListOf<String>(), pi.classLoader)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateJarFile(buildFile: BuildFile) {
|
private fun generateJarFile(buildFile: BuildFile) {
|
||||||
|
@ -66,10 +67,14 @@ public class ScriptCompiler @Inject constructor(
|
||||||
}.compile()
|
}.compile()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun instantiateBuildFile() : List<Project> {
|
class ProjectInfo(val projects: List<Project>, val classLoader: ClassLoader)
|
||||||
|
|
||||||
|
private fun instantiateBuildFile() : ProjectInfo {
|
||||||
val result = arrayListOf<Project>()
|
val result = arrayListOf<Project>()
|
||||||
var stream : InputStream? = null
|
var stream : InputStream? = null
|
||||||
|
val classLoader = URLClassLoader(arrayOf(buildScriptJarFile.toURI().toURL()))
|
||||||
try {
|
try {
|
||||||
|
log(1, "!!!!!!!!! CREATED CLASSLOADER FOR buildScriptJarFile: $classLoader")
|
||||||
stream = JarInputStream(FileInputStream(buildScriptJarFile))
|
stream = JarInputStream(FileInputStream(buildScriptJarFile))
|
||||||
var entry = stream.nextJarEntry
|
var entry = stream.nextJarEntry
|
||||||
|
|
||||||
|
@ -78,7 +83,7 @@ public class ScriptCompiler @Inject constructor(
|
||||||
val name = entry.name;
|
val name = entry.name;
|
||||||
if (name.endsWith(".class")) {
|
if (name.endsWith(".class")) {
|
||||||
val className = name.substring(0, name.length() - 6).replace("/", ".")
|
val className = name.substring(0, name.length() - 6).replace("/", ".")
|
||||||
var cl : Class<*>? = instantiate(className, buildScriptJarFile)
|
var cl : Class<*>? = instantiate(classLoader, className)
|
||||||
if (cl != null) {
|
if (cl != null) {
|
||||||
classes.add(cl)
|
classes.add(cl)
|
||||||
} else {
|
} else {
|
||||||
|
@ -123,7 +128,7 @@ public class ScriptCompiler @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that we all the projects, sort them topologically
|
// Now that we all the projects, sort them topologically
|
||||||
return Kobalt.sortProjects(result)
|
return ProjectInfo(Kobalt.sortProjects(result), classLoader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
178
src/main/kotlin/com/beust/kobalt/kotlin/ScriptCompiler2.kt
Normal file
178
src/main/kotlin/com/beust/kobalt/kotlin/ScriptCompiler2.kt
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
package com.beust.kobalt.kotlin
|
||||||
|
|
||||||
|
import com.beust.kobalt.Plugins
|
||||||
|
import com.beust.kobalt.api.Kobalt
|
||||||
|
import com.beust.kobalt.api.Plugin
|
||||||
|
import com.beust.kobalt.api.Project
|
||||||
|
import com.beust.kobalt.api.annotation.Task
|
||||||
|
import com.beust.kobalt.maven.KobaltException
|
||||||
|
import com.beust.kobalt.misc.KFiles
|
||||||
|
import com.beust.kobalt.misc.KobaltLogger
|
||||||
|
import com.beust.kobalt.misc.countChar
|
||||||
|
import com.beust.kobalt.plugin.kotlin.kotlinCompilePrivate
|
||||||
|
import com.google.inject.assistedinject.Assisted
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
|
import java.net.URL
|
||||||
|
import java.net.URLClassLoader
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import java.util.jar.JarInputStream
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
public class ScriptCompiler2 @Inject constructor(@Assisted("buildFiles") val buildFiles: List<BuildFile>,
|
||||||
|
val files: KFiles, val plugins: Plugins) : KobaltLogger {
|
||||||
|
|
||||||
|
interface IFactory {
|
||||||
|
fun create(@Assisted("buildFiles") buildFiles: List<BuildFile>) : ScriptCompiler2
|
||||||
|
}
|
||||||
|
|
||||||
|
private val SCRIPT_JAR = "buildScript.jar"
|
||||||
|
|
||||||
|
fun findProjects(): List<Project> {
|
||||||
|
val result = arrayListOf<Project>()
|
||||||
|
buildFiles.forEach { buildFile ->
|
||||||
|
val pluginUrls = findPlugInUrls(buildFile)
|
||||||
|
val script = File(KFiles.findBuildScriptLocation(buildFile, SCRIPT_JAR))
|
||||||
|
|
||||||
|
log(1, "Compiling main build file ${buildFile.path}")
|
||||||
|
kotlinCompilePrivate {
|
||||||
|
classpath(files.kobaltJar)
|
||||||
|
classpath(pluginUrls.map { it.file })
|
||||||
|
sourceFiles(listOf(buildFile.path.toFile().absolutePath))
|
||||||
|
output = script.absolutePath
|
||||||
|
}.compile()
|
||||||
|
|
||||||
|
val output = parseBuildScriptJarFile(script, pluginUrls)
|
||||||
|
result.addAll(output.projects)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findPlugInUrls(buildFile: BuildFile): List<URL> {
|
||||||
|
val result = arrayListOf<URL>()
|
||||||
|
val pluginCode = arrayListOf(
|
||||||
|
"import com.beust.kobalt.*",
|
||||||
|
"import com.beust.kobalt.api.*"
|
||||||
|
)
|
||||||
|
var parenCount = 0
|
||||||
|
buildFile.path.toFile().forEachLine(Charset.defaultCharset()) { line ->
|
||||||
|
var index = line.indexOf("plugins(")
|
||||||
|
if (index == -1) index = line.indexOf("repos(")
|
||||||
|
if (parenCount > 0 || index >= 0) {
|
||||||
|
if (index == -1) index = 0
|
||||||
|
with(line.substring(index)) {
|
||||||
|
parenCount += line countChar '('
|
||||||
|
if (parenCount > 0) {
|
||||||
|
pluginCode.add(line)
|
||||||
|
}
|
||||||
|
parenCount -= line countChar ')'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Compile and run pluginCode, which contains all the plugins() calls extracted. This
|
||||||
|
// will add all the dynamic plugins found in this code to Plugins.dynamicPlugins
|
||||||
|
//
|
||||||
|
val pluginSourceFile = KFiles.createTempFile(".kt")
|
||||||
|
pluginSourceFile.writeText(pluginCode.join("\n"), Charset.defaultCharset())
|
||||||
|
log(2, "Saved ${pluginSourceFile.absolutePath}")
|
||||||
|
|
||||||
|
//
|
||||||
|
// Compile to preBuildScript.jar
|
||||||
|
//
|
||||||
|
val buildScriptJar = KFiles.findBuildScriptLocation(buildFile, "preBuildScript.jar")
|
||||||
|
val buildScriptJarFile = File(buildScriptJar)
|
||||||
|
buildScriptJarFile.parentFile.mkdirs()
|
||||||
|
generateJarFile(BuildFile(Paths.get(pluginSourceFile.path), "Plugins"), buildScriptJarFile)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Run preBuildScript.jar to initialize plugins and repos
|
||||||
|
//
|
||||||
|
val projectInfo = parseBuildScriptJarFile(buildScriptJarFile, arrayListOf<URL>())
|
||||||
|
|
||||||
|
//
|
||||||
|
// All the plug-ins are now in Plugins.dynamicPlugins, download them if they're not already
|
||||||
|
//
|
||||||
|
Plugins.dynamicPlugins.forEach {
|
||||||
|
result.add(it.jarFile.get().toURI().toURL())
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateJarFile(buildFile: BuildFile, buildScriptJarFile: File) {
|
||||||
|
kotlinCompilePrivate {
|
||||||
|
classpath(files.kobaltJar)
|
||||||
|
sourceFiles(buildFile.path.toFile().absolutePath)
|
||||||
|
output = buildScriptJarFile.absolutePath
|
||||||
|
}.compile()
|
||||||
|
}
|
||||||
|
|
||||||
|
class BuildScriptInfo(val projects: List<Project>, val classLoader: ClassLoader)
|
||||||
|
|
||||||
|
private fun parseBuildScriptJarFile(buildScriptJarFile: File, urls: List<URL>) : BuildScriptInfo {
|
||||||
|
val result = arrayListOf<Project>()
|
||||||
|
var stream : InputStream? = null
|
||||||
|
val allUrls = arrayListOf<URL>().plus(urls).plus(arrayOf(
|
||||||
|
buildScriptJarFile.toURI().toURL(),
|
||||||
|
File(files.kobaltJar).toURI().toURL()))
|
||||||
|
.toTypedArray()
|
||||||
|
val classLoader = URLClassLoader(allUrls)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Install all the plugins
|
||||||
|
//
|
||||||
|
plugins.installPlugins(Plugins.dynamicPlugins, classLoader)
|
||||||
|
|
||||||
|
try {
|
||||||
|
stream = JarInputStream(FileInputStream(buildScriptJarFile))
|
||||||
|
var entry = stream.nextJarEntry
|
||||||
|
|
||||||
|
val classes = hashSetOf<Class<*>>()
|
||||||
|
while (entry != null) {
|
||||||
|
val name = entry.name;
|
||||||
|
if (name.endsWith(".class")) {
|
||||||
|
val className = name.substring(0, name.length() - 6).replace("/", ".")
|
||||||
|
var cl : Class<*>? = classLoader.loadClass(className)
|
||||||
|
if (cl != null) {
|
||||||
|
classes.add(cl)
|
||||||
|
} else {
|
||||||
|
throw KobaltException("Couldn't instantiate ${className}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry = stream.nextJarEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoke all the "val" found on the _DefaultPackage class (the Build.kt file)
|
||||||
|
classes.filter { cls ->
|
||||||
|
cls.name != "_DefaultPackage"
|
||||||
|
}.forEach { cls ->
|
||||||
|
cls.methods.forEach { method ->
|
||||||
|
// Invoke vals and see if they return a Project
|
||||||
|
if (method.name.startsWith("get") && Modifier.isStatic(method.modifiers)) {
|
||||||
|
val r = method.invoke(null)
|
||||||
|
if (r is Project) {
|
||||||
|
log(2, "Found project ${r} in class ${cls}")
|
||||||
|
result.add(r)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val taskAnnotation = method.getAnnotation(Task::class.java)
|
||||||
|
if (taskAnnotation != null) {
|
||||||
|
// Plugins.defaultPlugin.addTask(taskAnnotation, )
|
||||||
|
Plugins.defaultPlugin.methodTasks.add(Plugin.MethodTask(method, taskAnnotation))
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
stream?.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we all the projects, sort them topologically
|
||||||
|
return BuildScriptInfo(Kobalt.sortProjects(result), classLoader)
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,11 @@ public class KFiles {
|
||||||
get() {
|
get() {
|
||||||
val jar = joinDir(distributionsDir, Kobalt.version, "kobalt/wrapper/kobalt-" + Kobalt.version + ".jar")
|
val jar = joinDir(distributionsDir, Kobalt.version, "kobalt/wrapper/kobalt-" + Kobalt.version + ".jar")
|
||||||
val jarFile = File(jar)
|
val jarFile = File(jar)
|
||||||
|
val envJar = System.getenv("KOBALT_JAR")
|
||||||
|
if (! jarFile.exists() && envJar != null) {
|
||||||
|
KobaltLogger.debug("Using kobalt jar $envJar")
|
||||||
|
return File(envJar).absolutePath
|
||||||
|
}
|
||||||
if (! jarFile.exists()) {
|
if (! jarFile.exists()) {
|
||||||
// Will only happen when building kobalt itself: the jar file might not be in the dist/ directory
|
// Will only happen when building kobalt itself: the jar file might not be in the dist/ directory
|
||||||
// yet since we're currently building it. Instead, use the classes directly
|
// yet since we're currently building it. Instead, use the classes directly
|
||||||
|
@ -162,10 +167,10 @@ public class KFiles {
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun createTempFile(suffix : String = "", deleteOnExit: Boolean = false) : File =
|
public fun createTempFile(suffix : String = "", deleteOnExit: Boolean = false) : File =
|
||||||
File.createTempFile("kobalt", suffix, File(SystemProperties.tmpDir)).let {
|
File.createTempFile("kobalt", suffix, File(SystemProperties.tmpDir)).let {
|
||||||
if (deleteOnExit) it.deleteOnExit()
|
if (deleteOnExit) it.deleteOnExit()
|
||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
|
|
||||||
fun src(filePath: String): String = KFiles.joinDir(KOBALT_DIR, SRC, filePath)
|
fun src(filePath: String): String = KFiles.joinDir(KOBALT_DIR, SRC, filePath)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,10 @@ public interface KobaltLogger {
|
||||||
fun warn(s: String, e: Throwable? = null) {
|
fun warn(s: String, e: Throwable? = null) {
|
||||||
LoggerFactory.getLogger(KobaltLogger::class.java.simpleName).warn(s, e)
|
LoggerFactory.getLogger(KobaltLogger::class.java.simpleName).warn(s, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun debug(s: String) {
|
||||||
|
LoggerFactory.getLogger(KobaltLogger::class.java.simpleName).debug(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final fun log(level: Int = 1, message: String) {
|
final fun log(level: Int = 1, message: String) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.beust.kobalt.misc
|
||||||
|
|
||||||
import com.beust.kobalt.Args
|
import com.beust.kobalt.Args
|
||||||
import com.beust.kobalt.kotlin.ScriptCompiler
|
import com.beust.kobalt.kotlin.ScriptCompiler
|
||||||
|
import com.beust.kobalt.kotlin.ScriptCompiler2
|
||||||
import com.beust.kobalt.maven.ArtifactFetcher
|
import com.beust.kobalt.maven.ArtifactFetcher
|
||||||
import com.beust.kobalt.maven.LocalRepo
|
import com.beust.kobalt.maven.LocalRepo
|
||||||
import com.beust.kobalt.maven.Pom
|
import com.beust.kobalt.maven.Pom
|
||||||
|
@ -41,6 +42,7 @@ public open class MainModule(val args: Args) : AbstractModule() {
|
||||||
JCenterApi.IFactory::class.java,
|
JCenterApi.IFactory::class.java,
|
||||||
Pom.IFactory::class.java,
|
Pom.IFactory::class.java,
|
||||||
ScriptCompiler.IFactory::class.java,
|
ScriptCompiler.IFactory::class.java,
|
||||||
|
ScriptCompiler2.IFactory::class.java,
|
||||||
ArtifactFetcher.IFactory::class.java)
|
ArtifactFetcher.IFactory::class.java)
|
||||||
.forEach {
|
.forEach {
|
||||||
install(builder.build(it))
|
install(builder.build(it))
|
||||||
|
|
|
@ -17,7 +17,7 @@ import kotlin.properties.Delegates
|
||||||
* @since 08 03, 2015
|
* @since 08 03, 2015
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
private class KotlinCompiler @Inject constructor(override val localRepo : LocalRepo,
|
class KotlinCompiler @Inject constructor(override val localRepo : LocalRepo,
|
||||||
override val files: com.beust.kobalt.misc.KFiles,
|
override val files: com.beust.kobalt.misc.KFiles,
|
||||||
override val depFactory: DepFactory,
|
override val depFactory: DepFactory,
|
||||||
override val dependencyManager: DependencyManager,
|
override val dependencyManager: DependencyManager,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue