mirror of
https://github.com/ethauvin/kobalt.git
synced 2025-04-26 16:28:12 -07:00
Refactoring of BuildScript.
This commit is contained in:
parent
947c620e56
commit
43ccceb71e
5 changed files with 246 additions and 198 deletions
|
@ -1,6 +1,7 @@
|
||||||
package com.beust.kobalt.internal.remote
|
package com.beust.kobalt.internal.remote
|
||||||
|
|
||||||
import com.beust.kobalt.Args
|
import com.beust.kobalt.Args
|
||||||
|
import com.beust.kobalt.api.Project
|
||||||
import com.beust.kobalt.internal.PluginInfo
|
import com.beust.kobalt.internal.PluginInfo
|
||||||
import com.beust.kobalt.kotlin.BuildFile
|
import com.beust.kobalt.kotlin.BuildFile
|
||||||
import com.beust.kobalt.kotlin.BuildFileCompiler
|
import com.beust.kobalt.kotlin.BuildFileCompiler
|
||||||
|
@ -28,16 +29,16 @@ class GetDependenciesCommand @Inject constructor(val executors: KobaltExecutors,
|
||||||
val buildFile = BuildFile(Paths.get(received.get("buildFile").asString), "GetDependenciesCommand")
|
val buildFile = BuildFile(Paths.get(received.get("buildFile").asString), "GetDependenciesCommand")
|
||||||
val scriptCompiler = buildFileCompilerFactory.create(listOf(buildFile), pluginInfo)
|
val scriptCompiler = buildFileCompilerFactory.create(listOf(buildFile), pluginInfo)
|
||||||
scriptCompiler.observable.subscribe {
|
scriptCompiler.observable.subscribe {
|
||||||
buildScriptInfo -> if (buildScriptInfo.projects.size > 0) {
|
projects -> if (projects.size > 0) {
|
||||||
sender.sendData(toData(buildScriptInfo))
|
sender.sendData(toData(projects))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scriptCompiler.compileBuildFiles(args)
|
scriptCompiler.compileBuildFiles(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toData(info: BuildFileCompiler.BuildScriptInfo) : CommandData {
|
private fun toData(projects: List<Project>) : CommandData {
|
||||||
|
val projectDatas = arrayListOf<ProjectData>()
|
||||||
val executor = executors.miscExecutor
|
val executor = executors.miscExecutor
|
||||||
val projects = arrayListOf<ProjectData>()
|
|
||||||
|
|
||||||
fun toDependencyData(d: IClasspathDependency, scope: String) : DependencyData {
|
fun toDependencyData(d: IClasspathDependency, scope: String) : DependencyData {
|
||||||
val dep = MavenDependency.create(d.id, executor)
|
val dep = MavenDependency.create(d.id, executor)
|
||||||
|
@ -46,7 +47,7 @@ class GetDependenciesCommand @Inject constructor(val executors: KobaltExecutors,
|
||||||
|
|
||||||
fun allDeps(l: List<IClasspathDependency>) = dependencyManager.transitiveClosure(l)
|
fun allDeps(l: List<IClasspathDependency>) = dependencyManager.transitiveClosure(l)
|
||||||
|
|
||||||
info.projects.forEach { project ->
|
projects.forEach { project ->
|
||||||
val allDependencies =
|
val allDependencies =
|
||||||
allDeps(project.compileDependencies).map { toDependencyData(it, "compile") } +
|
allDeps(project.compileDependencies).map { toDependencyData(it, "compile") } +
|
||||||
allDeps(project.compileProvidedDependencies).map { toDependencyData(it, "provided") } +
|
allDeps(project.compileProvidedDependencies).map { toDependencyData(it, "provided") } +
|
||||||
|
@ -54,10 +55,10 @@ class GetDependenciesCommand @Inject constructor(val executors: KobaltExecutors,
|
||||||
allDeps(project.testDependencies).map { toDependencyData(it, "testCompile") } +
|
allDeps(project.testDependencies).map { toDependencyData(it, "testCompile") } +
|
||||||
allDeps(project.testProvidedDependencies).map { toDependencyData(it, "testProvided") }
|
allDeps(project.testProvidedDependencies).map { toDependencyData(it, "testProvided") }
|
||||||
|
|
||||||
projects.add(ProjectData(project.name, allDependencies))
|
projectDatas.add(ProjectData(project.name, allDependencies))
|
||||||
}
|
}
|
||||||
log(1, "Returning BuildScriptInfo")
|
log(1, "Returning BuildScriptInfo")
|
||||||
val result = toCommandData(Gson().toJson(GetDependenciesData(projects)))
|
val result = toCommandData(Gson().toJson(GetDependenciesData(projectDatas)))
|
||||||
log(2, " $result")
|
log(2, " $result")
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,28 +3,24 @@ package com.beust.kobalt.kotlin
|
||||||
import com.beust.kobalt.Args
|
import com.beust.kobalt.Args
|
||||||
import com.beust.kobalt.KobaltException
|
import com.beust.kobalt.KobaltException
|
||||||
import com.beust.kobalt.Plugins
|
import com.beust.kobalt.Plugins
|
||||||
import com.beust.kobalt.api.*
|
import com.beust.kobalt.api.Kobalt
|
||||||
import com.beust.kobalt.api.annotation.Task
|
import com.beust.kobalt.api.KobaltContext
|
||||||
|
import com.beust.kobalt.api.PluginProperties
|
||||||
|
import com.beust.kobalt.api.Project
|
||||||
import com.beust.kobalt.internal.PluginInfo
|
import com.beust.kobalt.internal.PluginInfo
|
||||||
|
import com.beust.kobalt.kotlin.internal.BuildScriptUtil
|
||||||
import com.beust.kobalt.kotlin.internal.ParsedBuildFile
|
import com.beust.kobalt.kotlin.internal.ParsedBuildFile
|
||||||
|
import com.beust.kobalt.kotlin.internal.VersionFile
|
||||||
import com.beust.kobalt.maven.DependencyManager
|
import com.beust.kobalt.maven.DependencyManager
|
||||||
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.Topological
|
|
||||||
import com.beust.kobalt.misc.log
|
import com.beust.kobalt.misc.log
|
||||||
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 rx.subjects.PublishSubject
|
import rx.subjects.PublishSubject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.lang.reflect.Modifier
|
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLClassLoader
|
|
||||||
import java.nio.charset.Charset
|
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.*
|
|
||||||
import java.util.jar.JarInputStream
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,13 +31,13 @@ import javax.inject.Inject
|
||||||
public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val buildFiles: List<BuildFile>,
|
public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val buildFiles: List<BuildFile>,
|
||||||
@Assisted val pluginInfo: PluginInfo, val files: KFiles, val plugins: Plugins,
|
@Assisted val pluginInfo: PluginInfo, val files: KFiles, val plugins: Plugins,
|
||||||
val dependencyManager: DependencyManager, val pluginProperties: PluginProperties,
|
val dependencyManager: DependencyManager, val pluginProperties: PluginProperties,
|
||||||
val executors: KobaltExecutors) {
|
val executors: KobaltExecutors, val buildScriptUtil: BuildScriptUtil) {
|
||||||
|
|
||||||
interface IFactory {
|
interface IFactory {
|
||||||
fun create(@Assisted("buildFiles") buildFiles: List<BuildFile>, pluginInfo: PluginInfo) : BuildFileCompiler
|
fun create(@Assisted("buildFiles") buildFiles: List<BuildFile>, pluginInfo: PluginInfo) : BuildFileCompiler
|
||||||
}
|
}
|
||||||
|
|
||||||
val observable = PublishSubject.create<BuildScriptInfo>()
|
val observable = PublishSubject.create<List<Project>>()
|
||||||
|
|
||||||
private val SCRIPT_JAR = "buildScript.jar"
|
private val SCRIPT_JAR = "buildScript.jar"
|
||||||
|
|
||||||
|
@ -65,31 +61,17 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b
|
||||||
return allProjects
|
return allProjects
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Make sure all the projects have a unique name.
|
|
||||||
*/
|
|
||||||
private fun validateProjects(projects: List<Project>) {
|
|
||||||
val seen = hashSetOf<String>()
|
|
||||||
projects.forEach {
|
|
||||||
if (seen.contains(it.name)) {
|
|
||||||
throw KobaltException("Duplicate project name: $it")
|
|
||||||
} else {
|
|
||||||
seen.add(it.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findProjects(context: KobaltContext): List<Project> {
|
private fun findProjects(context: KobaltContext): List<Project> {
|
||||||
val result = arrayListOf<Project>()
|
val result = arrayListOf<Project>()
|
||||||
buildFiles.forEach { buildFile ->
|
buildFiles.forEach { buildFile ->
|
||||||
val pair = processBuildFile(context, buildFile)
|
val processBuildFile = parseBuildFile(context, buildFile)
|
||||||
val pluginUrls = pair.second
|
val pluginUrls = processBuildFile.pluginUrls
|
||||||
val buildScriptJarFile = File(KFiles.findBuildScriptLocation(buildFile, SCRIPT_JAR))
|
val buildScriptJarFile = File(KFiles.findBuildScriptLocation(buildFile, SCRIPT_JAR))
|
||||||
|
|
||||||
// If the script jar files were generated by a different version, wipe them in case the API
|
// If the script jar files were generated by a different version, wipe them in case the API
|
||||||
// changed in-between
|
// changed in-between
|
||||||
buildScriptJarFile.parentFile.let { dir ->
|
buildScriptJarFile.parentFile.let { dir ->
|
||||||
if (! isSameVersionFile(dir)) {
|
if (! VersionFile.isSameVersionFile(dir)) {
|
||||||
log(1, "Detected new installation, wiping $dir")
|
log(1, "Detected new installation, wiping $dir")
|
||||||
dir.listFiles().map { it.delete() }
|
dir.listFiles().map { it.delete() }
|
||||||
}
|
}
|
||||||
|
@ -98,24 +80,20 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b
|
||||||
// Write the modified Build.kt (e.g. maybe profiles were applied) to a temporary file,
|
// Write the modified Build.kt (e.g. maybe profiles were applied) to a temporary file,
|
||||||
// compile it, jar it in buildScript.jar and run it
|
// compile it, jar it in buildScript.jar and run it
|
||||||
val modifiedBuildFile = KFiles.createTempFile(".kt")
|
val modifiedBuildFile = KFiles.createTempFile(".kt")
|
||||||
KFiles.saveFile(modifiedBuildFile, pair.first.buildScriptCode)
|
KFiles.saveFile(modifiedBuildFile, processBuildFile.buildScriptCode)
|
||||||
maybeCompileBuildFile(context, BuildFile(Paths.get(modifiedBuildFile.path), "Modified Build.kt"),
|
maybeCompileBuildFile(context, BuildFile(Paths.get(modifiedBuildFile.path), "Modified Build.kt"),
|
||||||
buildScriptJarFile, pluginUrls)
|
buildScriptJarFile, pluginUrls)
|
||||||
val buildScriptInfo = runBuildScriptJarFile(buildScriptJarFile, pluginUrls)
|
val projects = buildScriptUtil.runBuildScriptJarFile(buildScriptJarFile, pluginUrls, context)
|
||||||
result.addAll(buildScriptInfo.projects)
|
result.addAll(projects)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isUpToDate(buildFile: BuildFile, jarFile: File) =
|
|
||||||
buildFile.exists() && jarFile.exists()
|
|
||||||
&& buildFile.lastModified < jarFile.lastModified()
|
|
||||||
|
|
||||||
private fun maybeCompileBuildFile(context: KobaltContext, buildFile: BuildFile, buildScriptJarFile: File,
|
private fun maybeCompileBuildFile(context: KobaltContext, buildFile: BuildFile, buildScriptJarFile: File,
|
||||||
pluginUrls: List<URL>) {
|
pluginUrls: List<URL>) {
|
||||||
log(2, "Running build file ${buildFile.name} jar: $buildScriptJarFile")
|
log(2, "Running build file ${buildFile.name} jar: $buildScriptJarFile")
|
||||||
|
|
||||||
if (isUpToDate(buildFile, buildScriptJarFile)) {
|
if (buildScriptUtil.isUpToDate(buildFile, buildScriptJarFile)) {
|
||||||
log(2, "Build file is up to date")
|
log(2, "Build file is up to date")
|
||||||
} else {
|
} else {
|
||||||
log(2, "Need to recompile ${buildFile.name}")
|
log(2, "Need to recompile ${buildFile.name}")
|
||||||
|
@ -139,156 +117,12 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b
|
||||||
* - the source code for the modified Build.kt (after profiles are applied)
|
* - the source code for the modified Build.kt (after profiles are applied)
|
||||||
* - the URL's of all the plug-ins that were found.
|
* - the URL's of all the plug-ins that were found.
|
||||||
*/
|
*/
|
||||||
private fun processBuildFile(context: KobaltContext, buildFile: BuildFile): Pair<ParsedBuildFile, List<URL>> {
|
private fun parseBuildFile(context: KobaltContext, buildFile: BuildFile) : ParsedBuildFile {
|
||||||
val result = arrayListOf<URL>()
|
|
||||||
|
|
||||||
// Parse the build file so we can generate preBuildScript and buildScript from it.
|
// Parse the build file so we can generate preBuildScript and buildScript from it.
|
||||||
val parsedBuildFile = ParsedBuildFile(buildFile.path.toFile(), context)
|
with(ParsedBuildFile(buildFile, context, buildScriptUtil, dependencyManager, files)) {
|
||||||
|
|
||||||
//
|
|
||||||
// Compile and run preBuildScriptCode, 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(parsedBuildFile.preBuildScriptCode, Charset.defaultCharset())
|
|
||||||
log(2, "Saved ${pluginSourceFile.absolutePath}")
|
|
||||||
|
|
||||||
//
|
|
||||||
// Compile to preBuildScript.jar
|
|
||||||
//
|
|
||||||
val buildScriptJar = KFiles.findBuildScriptLocation(buildFile, "preBuildScript.jar")
|
|
||||||
val buildScriptJarFile = File(buildScriptJar)
|
|
||||||
if (! isUpToDate(buildFile, File(buildScriptJar))) {
|
|
||||||
buildScriptJarFile.parentFile.mkdirs()
|
|
||||||
generateJarFile(context, BuildFile(Paths.get(pluginSourceFile.path), "Plugins"), buildScriptJarFile)
|
|
||||||
generateVersionFile(buildScriptJarFile.parentFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Run preBuildScript.jar to initialize plugins and repos
|
|
||||||
//
|
|
||||||
runBuildScriptJarFile(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 Pair(parsedBuildFile, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val VERSION_FILE = "version.txt"
|
|
||||||
|
|
||||||
private fun generateVersionFile(directory: File) {
|
|
||||||
KFiles.saveFile(File(directory, VERSION_FILE), Kobalt.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isSameVersionFile(directory: File) =
|
|
||||||
with(File(directory, VERSION_FILE)) {
|
|
||||||
! exists() || (exists() && readText() == Kobalt.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateJarFile(context: KobaltContext, buildFile: BuildFile, buildScriptJarFile: File) {
|
|
||||||
val kotlintDeps = dependencyManager.calculateDependencies(null, context)
|
|
||||||
val deps: List<String> = kotlintDeps.map { it.jarFile.get().absolutePath }
|
|
||||||
kotlinCompilePrivate {
|
|
||||||
classpath(files.kobaltJar)
|
|
||||||
classpath(deps)
|
|
||||||
sourceFiles(buildFile.path.toFile().absolutePath)
|
|
||||||
output = File(buildScriptJarFile.absolutePath)
|
|
||||||
}.compile(context = context)
|
|
||||||
}
|
|
||||||
|
|
||||||
class BuildScriptInfo(val projects: List<Project>)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the given preBuildScript (or buildScript) jar file, using a classloader made of the passed URL's.
|
|
||||||
*/
|
|
||||||
private fun runBuildScriptJarFile(buildScriptJarFile: File, urls: List<URL>) : BuildScriptInfo {
|
|
||||||
val projects = arrayListOf<Project>()
|
|
||||||
var stream : InputStream? = null
|
|
||||||
val allUrls = (urls + 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)) {
|
|
||||||
try {
|
|
||||||
val r = method.invoke(null)
|
|
||||||
if (r is Project) {
|
|
||||||
log(2, "Found project $r in class $cls")
|
|
||||||
projects.add(r)
|
|
||||||
}
|
|
||||||
} catch(ex: Throwable) {
|
|
||||||
throw ex.cause ?: KobaltException(ex)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val taskAnnotation = method.getAnnotation(Task::class.java)
|
|
||||||
if (taskAnnotation != null) {
|
|
||||||
Plugins.defaultPlugin.methodTasks.add(IPlugin.MethodTask(method, taskAnnotation))
|
|
||||||
}
|
|
||||||
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
stream?.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
validateProjects(projects)
|
|
||||||
|
|
||||||
//
|
|
||||||
// Now that the build file has run, fetch all the project contributors, grab the projects from them and sort
|
|
||||||
// them topologically
|
|
||||||
//
|
|
||||||
Topological<Project>().let { topologicalProjects ->
|
|
||||||
val all = hashSetOf<Project>()
|
|
||||||
pluginInfo.projectContributors.forEach { contributor ->
|
|
||||||
val descriptions = contributor.projects()
|
|
||||||
descriptions.forEach { pd ->
|
|
||||||
all.add(pd.project)
|
|
||||||
pd.dependsOn.forEach { dependsOn ->
|
|
||||||
topologicalProjects.addEdge(pd.project, dependsOn)
|
|
||||||
all.add(dependsOn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val result = BuildScriptInfo(topologicalProjects.sort(ArrayList(all)))
|
|
||||||
|
|
||||||
// Notify possible listeners (e.g. KobaltServer) we now have all the projects
|
// Notify possible listeners (e.g. KobaltServer) we now have all the projects
|
||||||
observable.onNext(result)
|
observable.onNext(projects)
|
||||||
return result
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
package com.beust.kobalt.kotlin.internal
|
||||||
|
|
||||||
|
import com.beust.kobalt.KobaltException
|
||||||
|
import com.beust.kobalt.Plugins
|
||||||
|
import com.beust.kobalt.api.IPlugin
|
||||||
|
import com.beust.kobalt.api.KobaltContext
|
||||||
|
import com.beust.kobalt.api.Project
|
||||||
|
import com.beust.kobalt.api.annotation.Task
|
||||||
|
import com.beust.kobalt.kotlin.BuildFile
|
||||||
|
import com.beust.kobalt.misc.KFiles
|
||||||
|
import com.beust.kobalt.misc.Topological
|
||||||
|
import com.beust.kobalt.misc.log
|
||||||
|
import com.google.inject.Inject
|
||||||
|
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.util.*
|
||||||
|
import java.util.jar.JarInputStream
|
||||||
|
|
||||||
|
class BuildScriptUtil @Inject constructor(val plugins: Plugins, val files: KFiles){
|
||||||
|
val projects = arrayListOf<Project>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the given preBuildScript (or buildScript) jar file, using a classloader made of the passed URL's.
|
||||||
|
* This list is empty when we run preBuildScript.jar but for buildScript.jar, it contains the list of
|
||||||
|
* URL's found from running preBuildScript.jar.
|
||||||
|
*/
|
||||||
|
fun runBuildScriptJarFile(buildScriptJarFile: File, urls: List<URL>,
|
||||||
|
context: KobaltContext) : List<Project> {
|
||||||
|
var stream : InputStream? = null
|
||||||
|
val allUrls = (urls + arrayOf(
|
||||||
|
buildScriptJarFile.toURI().toURL()) + File(files.kobaltJar).toURI().toURL())
|
||||||
|
.toTypedArray()
|
||||||
|
val classLoader = URLClassLoader(allUrls)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Install all the plugins
|
||||||
|
//
|
||||||
|
plugins.installPlugins(Plugins.dynamicPlugins, classLoader)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Classload all the jar files and invoke their methods
|
||||||
|
//
|
||||||
|
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)) {
|
||||||
|
try {
|
||||||
|
val r = method.invoke(null)
|
||||||
|
if (r is Project) {
|
||||||
|
log(2, "Found project $r in class $cls")
|
||||||
|
projects.add(r)
|
||||||
|
}
|
||||||
|
} catch(ex: Throwable) {
|
||||||
|
throw ex.cause ?: KobaltException(ex)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val taskAnnotation = method.getAnnotation(Task::class.java)
|
||||||
|
if (taskAnnotation != null) {
|
||||||
|
Plugins.defaultPlugin.methodTasks.add(IPlugin.MethodTask(method, taskAnnotation))
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
stream?.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
validateProjects(projects)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Now that the build file has run, fetch all the project contributors, grab the projects from them and sort
|
||||||
|
// them topologically
|
||||||
|
//
|
||||||
|
Topological<Project>().let { topologicalProjects ->
|
||||||
|
val all = hashSetOf<Project>()
|
||||||
|
context.pluginInfo.projectContributors.forEach { contributor ->
|
||||||
|
val descriptions = contributor.projects()
|
||||||
|
descriptions.forEach { pd ->
|
||||||
|
all.add(pd.project)
|
||||||
|
pd.dependsOn.forEach { dependsOn ->
|
||||||
|
topologicalProjects.addEdge(pd.project, dependsOn)
|
||||||
|
all.add(dependsOn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val result = topologicalProjects.sort(ArrayList(all))
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isUpToDate(buildFile: BuildFile, jarFile: File) =
|
||||||
|
buildFile.exists() && jarFile.exists()
|
||||||
|
&& buildFile.lastModified < jarFile.lastModified()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure all the projects have a unique name.
|
||||||
|
*/
|
||||||
|
private fun validateProjects(projects: List<Project>) {
|
||||||
|
val seen = hashSetOf<String>()
|
||||||
|
projects.forEach {
|
||||||
|
if (seen.contains(it.name)) {
|
||||||
|
throw KobaltException("Duplicate project name: $it")
|
||||||
|
} else {
|
||||||
|
seen.add(it.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,30 @@
|
||||||
package com.beust.kobalt.kotlin.internal
|
package com.beust.kobalt.kotlin.internal
|
||||||
|
|
||||||
|
import com.beust.kobalt.Plugins
|
||||||
import com.beust.kobalt.api.KobaltContext
|
import com.beust.kobalt.api.KobaltContext
|
||||||
|
import com.beust.kobalt.api.Project
|
||||||
|
import com.beust.kobalt.kotlin.BuildFile
|
||||||
|
import com.beust.kobalt.maven.DependencyManager
|
||||||
|
import com.beust.kobalt.misc.KFiles
|
||||||
import com.beust.kobalt.misc.countChar
|
import com.beust.kobalt.misc.countChar
|
||||||
|
import com.beust.kobalt.misc.log
|
||||||
|
import com.beust.kobalt.plugin.kotlin.kotlinCompilePrivate
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
import java.nio.file.Paths
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class ParsedBuildFile(val file: File, val context: KobaltContext) {
|
class ParsedBuildFile(val buildFile: BuildFile, val context: KobaltContext, val buildScriptUtil: BuildScriptUtil,
|
||||||
val plugins = arrayListOf<String>()
|
val dependencyManager: DependencyManager, val files: KFiles) {
|
||||||
|
val pluginList = arrayListOf<String>()
|
||||||
val repos = arrayListOf<String>()
|
val repos = arrayListOf<String>()
|
||||||
val profileLines = arrayListOf<String>()
|
val profileLines = arrayListOf<String>()
|
||||||
|
val pluginUrls = arrayListOf<URL>()
|
||||||
|
val projects = arrayListOf<Project>()
|
||||||
|
|
||||||
private val preBuildScript = arrayListOf("import com.beust.kobalt.*",
|
private val preBuildScript = arrayListOf(
|
||||||
|
"import com.beust.kobalt.*",
|
||||||
"import com.beust.kobalt.api.*")
|
"import com.beust.kobalt.api.*")
|
||||||
val preBuildScriptCode : String get() = preBuildScript.joinToString("\n")
|
val preBuildScriptCode : String get() = preBuildScript.joinToString("\n")
|
||||||
|
|
||||||
|
@ -20,15 +33,16 @@ class ParsedBuildFile(val file: File, val context: KobaltContext) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
parseBuildFile()
|
parseBuildFile()
|
||||||
|
initPluginUrls()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseBuildFile() {
|
private fun parseBuildFile() {
|
||||||
var parenCount = 0
|
var parenCount = 0
|
||||||
file.forEachLine(Charset.defaultCharset()) { line ->
|
buildFile.path.toFile().forEachLine(Charset.defaultCharset()) { line ->
|
||||||
var current: ArrayList<String>? = null
|
var current: ArrayList<String>? = null
|
||||||
var index = line.indexOf("plugins(")
|
var index = line.indexOf("plugins(")
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
current = plugins
|
current = pluginList
|
||||||
} else {
|
} else {
|
||||||
index = line.indexOf("repos(")
|
index = line.indexOf("repos(")
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
|
@ -66,7 +80,51 @@ class ParsedBuildFile(val file: File, val context: KobaltContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
repos.forEach { preBuildScript.add(it) }
|
repos.forEach { preBuildScript.add(it) }
|
||||||
plugins.forEach { preBuildScript.add(it) }
|
pluginList.forEach { preBuildScript.add(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initPluginUrls() {
|
||||||
|
//
|
||||||
|
// Compile and run preBuildScriptCode, 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(preBuildScriptCode, Charset.defaultCharset())
|
||||||
|
log(2, "Saved ${pluginSourceFile.absolutePath}")
|
||||||
|
|
||||||
|
//
|
||||||
|
// Compile to preBuildScript.jar
|
||||||
|
//
|
||||||
|
val buildScriptJar = KFiles.findBuildScriptLocation(buildFile, "preBuildScript.jar")
|
||||||
|
val buildScriptJarFile = File(buildScriptJar)
|
||||||
|
if (! buildScriptUtil.isUpToDate(buildFile, File(buildScriptJar))) {
|
||||||
|
buildScriptJarFile.parentFile.mkdirs()
|
||||||
|
generateJarFile(context, BuildFile(Paths.get(pluginSourceFile.path), "Plugins"), buildScriptJarFile)
|
||||||
|
VersionFile.generateVersionFile(buildScriptJarFile.parentFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Run preBuildScript.jar to initialize plugins and repos
|
||||||
|
//
|
||||||
|
projects.addAll(buildScriptUtil.runBuildScriptJarFile(buildScriptJarFile, arrayListOf<URL>(), context))
|
||||||
|
|
||||||
|
//
|
||||||
|
// All the plug-ins are now in Plugins.dynamicPlugins, download them if they're not already
|
||||||
|
//
|
||||||
|
Plugins.dynamicPlugins.forEach {
|
||||||
|
pluginUrls.add(it.jarFile.get().toURI().toURL())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateJarFile(context: KobaltContext, buildFile: BuildFile, buildScriptJarFile: File) {
|
||||||
|
val kotlintDeps = dependencyManager.calculateDependencies(null, context)
|
||||||
|
val deps: List<String> = kotlintDeps.map { it.jarFile.get().absolutePath }
|
||||||
|
kotlinCompilePrivate {
|
||||||
|
classpath(files.kobaltJar)
|
||||||
|
classpath(deps)
|
||||||
|
sourceFiles(buildFile.path.toFile().absolutePath)
|
||||||
|
output = File(buildScriptJarFile.absolutePath)
|
||||||
|
}.compile(context = context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.beust.kobalt.kotlin.internal
|
||||||
|
|
||||||
|
import com.beust.kobalt.api.Kobalt
|
||||||
|
import com.beust.kobalt.misc.KFiles
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class VersionFile {
|
||||||
|
companion object {
|
||||||
|
private val VERSION_FILE = "version.txt"
|
||||||
|
|
||||||
|
fun generateVersionFile(directory: File) {
|
||||||
|
KFiles.saveFile(File(directory, VERSION_FILE), Kobalt.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSameVersionFile(directory: File) =
|
||||||
|
with(File(directory, VERSION_FILE)) {
|
||||||
|
! exists() || (exists() && readText() == Kobalt.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue