mirror of
https://github.com/ethauvin/kobalt.git
synced 2025-04-26 08:27:12 -07:00
Revert "Move files into the internal.build package."
This reverts commit 146d6702c5
.
This commit is contained in:
parent
ed09aa7e54
commit
93f30ad4fe
9 changed files with 437 additions and 6 deletions
|
@ -7,8 +7,8 @@ import com.beust.kobalt.internal.PluginInfo
|
|||
import com.beust.kobalt.internal.TaskManager
|
||||
import com.beust.kobalt.internal.remote.KobaltClient
|
||||
import com.beust.kobalt.internal.remote.KobaltServer
|
||||
import com.beust.kobalt.kotlin.internal.build.BuildFile
|
||||
import com.beust.kobalt.kotlin.internal.build.BuildFileCompiler
|
||||
import com.beust.kobalt.kotlin.BuildFile
|
||||
import com.beust.kobalt.kotlin.BuildFileCompiler
|
||||
import com.beust.kobalt.maven.DepFactory
|
||||
import com.beust.kobalt.maven.Http
|
||||
import com.beust.kobalt.maven.LocalRepo
|
||||
|
|
|
@ -3,8 +3,8 @@ package com.beust.kobalt.internal.remote
|
|||
import com.beust.kobalt.Args
|
||||
import com.beust.kobalt.api.Project
|
||||
import com.beust.kobalt.internal.PluginInfo
|
||||
import com.beust.kobalt.kotlin.internal.build.BuildFile
|
||||
import com.beust.kobalt.kotlin.internal.build.BuildFileCompiler
|
||||
import com.beust.kobalt.kotlin.BuildFile
|
||||
import com.beust.kobalt.kotlin.BuildFileCompiler
|
||||
import com.beust.kobalt.maven.DependencyManager
|
||||
import com.beust.kobalt.maven.IClasspathDependency
|
||||
import com.beust.kobalt.maven.MavenDependency
|
||||
|
|
18
src/main/kotlin/com/beust/kobalt/kotlin/BuildFile.kt
Normal file
18
src/main/kotlin/com/beust/kobalt/kotlin/BuildFile.kt
Normal file
|
@ -0,0 +1,18 @@
|
|||
package com.beust.kobalt.kotlin
|
||||
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
|
||||
/**
|
||||
* Sometimes, build files are moved to temporary files, so we give them a specific name for clarity.
|
||||
*/
|
||||
public class BuildFile(val path: Path, val name: String) {
|
||||
public fun exists() : Boolean = Files.exists(path)
|
||||
|
||||
public val lastModified : Long
|
||||
get() = Files.readAttributes(path, BasicFileAttributes::class.java).lastModifiedTime().toMillis()
|
||||
|
||||
public val directory : File get() = path.toFile().directory
|
||||
}
|
128
src/main/kotlin/com/beust/kobalt/kotlin/BuildFileCompiler.kt
Normal file
128
src/main/kotlin/com/beust/kobalt/kotlin/BuildFileCompiler.kt
Normal file
|
@ -0,0 +1,128 @@
|
|||
package com.beust.kobalt.kotlin
|
||||
|
||||
import com.beust.kobalt.Args
|
||||
import com.beust.kobalt.KobaltException
|
||||
import com.beust.kobalt.Plugins
|
||||
import com.beust.kobalt.api.Kobalt
|
||||
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.kotlin.internal.BuildScriptUtil
|
||||
import com.beust.kobalt.kotlin.internal.ParsedBuildFile
|
||||
import com.beust.kobalt.kotlin.internal.VersionFile
|
||||
import com.beust.kobalt.maven.DependencyManager
|
||||
import com.beust.kobalt.misc.KFiles
|
||||
import com.beust.kobalt.misc.KobaltExecutors
|
||||
import com.beust.kobalt.misc.log
|
||||
import com.beust.kobalt.plugin.kotlin.kotlinCompilePrivate
|
||||
import com.google.inject.assistedinject.Assisted
|
||||
import rx.subjects.PublishSubject
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.nio.file.Paths
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Manage the compilation of Build.kt. There are two passes for this processing:
|
||||
* 1) Extract the repos() and plugins() statements in a separate .kt and compile it into preBuildScript.jar.
|
||||
* 2) Actually build the whole Build.kt file after adding to the classpath whatever phase 1 found (plugins, repos)
|
||||
*/
|
||||
public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val buildFiles: List<BuildFile>,
|
||||
@Assisted val pluginInfo: PluginInfo, val files: KFiles, val plugins: Plugins,
|
||||
val dependencyManager: DependencyManager, val pluginProperties: PluginProperties,
|
||||
val executors: KobaltExecutors, val buildScriptUtil: BuildScriptUtil) {
|
||||
|
||||
interface IFactory {
|
||||
fun create(@Assisted("buildFiles") buildFiles: List<BuildFile>, pluginInfo: PluginInfo) : BuildFileCompiler
|
||||
}
|
||||
|
||||
val observable = PublishSubject.create<List<Project>>()
|
||||
|
||||
private val SCRIPT_JAR = "buildScript.jar"
|
||||
|
||||
fun compileBuildFiles(args: Args): List<Project> {
|
||||
//
|
||||
// Create the KobaltContext
|
||||
//
|
||||
val context = KobaltContext(args)
|
||||
context.pluginInfo = pluginInfo
|
||||
context.pluginProperties = pluginProperties
|
||||
context.dependencyManager = dependencyManager
|
||||
context.executors = executors
|
||||
Kobalt.context = context
|
||||
|
||||
//
|
||||
// Find all the projects in the build file, possibly compiling them
|
||||
//
|
||||
val allProjects = findProjects(context)
|
||||
plugins.applyPlugins(context, allProjects)
|
||||
|
||||
return allProjects
|
||||
}
|
||||
|
||||
private fun findProjects(context: KobaltContext): List<Project> {
|
||||
val result = arrayListOf<Project>()
|
||||
buildFiles.forEach { buildFile ->
|
||||
val processBuildFile = parseBuildFile(context, buildFile)
|
||||
val pluginUrls = processBuildFile.pluginUrls
|
||||
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
|
||||
// changed in-between
|
||||
buildScriptJarFile.parentFile.let { dir ->
|
||||
if (! VersionFile.isSameVersionFile(dir)) {
|
||||
log(1, "Detected new installation, wiping $dir")
|
||||
dir.listFiles().map { it.delete() }
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
val modifiedBuildFile = KFiles.createTempFile(".kt")
|
||||
KFiles.saveFile(modifiedBuildFile, processBuildFile.buildScriptCode)
|
||||
maybeCompileBuildFile(context, BuildFile(Paths.get(modifiedBuildFile.path), "Modified Build.kt"),
|
||||
buildScriptJarFile, pluginUrls)
|
||||
val projects = buildScriptUtil.runBuildScriptJarFile(buildScriptJarFile, pluginUrls, context)
|
||||
result.addAll(projects)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun maybeCompileBuildFile(context: KobaltContext, buildFile: BuildFile, buildScriptJarFile: File,
|
||||
pluginUrls: List<URL>) {
|
||||
log(2, "Running build file ${buildFile.name} jar: $buildScriptJarFile")
|
||||
|
||||
if (buildScriptUtil.isUpToDate(buildFile, buildScriptJarFile)) {
|
||||
log(2, "Build file is up to date")
|
||||
} else {
|
||||
log(2, "Need to recompile ${buildFile.name}")
|
||||
|
||||
buildScriptJarFile.delete()
|
||||
kotlinCompilePrivate {
|
||||
classpath(files.kobaltJar)
|
||||
classpath(pluginUrls.map { it.file })
|
||||
sourceFiles(listOf(buildFile.path.toFile().absolutePath))
|
||||
output = buildScriptJarFile
|
||||
}.compile(context = context)
|
||||
|
||||
if (! buildScriptJarFile.exists()) {
|
||||
throw KobaltException("Could not compile ${buildFile.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the script file with only the plugins()/repos() directives and run it. Then return
|
||||
* - the source code for the modified Build.kt (after profiles are applied)
|
||||
* - the URL's of all the plug-ins that were found.
|
||||
*/
|
||||
private fun parseBuildFile(context: KobaltContext, buildFile: BuildFile) : ParsedBuildFile {
|
||||
// Parse the build file so we can generate preBuildScript and buildScript from it.
|
||||
with(ParsedBuildFile(buildFile, context, buildScriptUtil, dependencyManager, files)) {
|
||||
// Notify possible listeners (e.g. KobaltServer) we now have all the projects
|
||||
observable.onNext(projects)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
package com.beust.kobalt.kotlin.internal
|
||||
|
||||
import com.beust.kobalt.Plugins
|
||||
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.log
|
||||
import com.beust.kobalt.plugin.kotlin.kotlinCompilePrivate
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
|
||||
class ParsedBuildFile(val buildFile: BuildFile, val context: KobaltContext, val buildScriptUtil: BuildScriptUtil,
|
||||
val dependencyManager: DependencyManager, val files: KFiles) {
|
||||
val pluginList = arrayListOf<String>()
|
||||
val repos = arrayListOf<String>()
|
||||
val profileLines = arrayListOf<String>()
|
||||
val pluginUrls = arrayListOf<URL>()
|
||||
val projects = arrayListOf<Project>()
|
||||
|
||||
private val preBuildScript = arrayListOf(
|
||||
"import com.beust.kobalt.*",
|
||||
"import com.beust.kobalt.api.*")
|
||||
val preBuildScriptCode : String get() = preBuildScript.joinToString("\n")
|
||||
|
||||
private val buildScript = arrayListOf<String>()
|
||||
val buildScriptCode : String get() = buildScript.joinToString("\n")
|
||||
|
||||
init {
|
||||
parseBuildFile()
|
||||
initPluginUrls()
|
||||
}
|
||||
|
||||
private fun parseBuildFile() {
|
||||
var parenCount = 0
|
||||
buildFile.path.toFile().forEachLine(Charset.defaultCharset()) { line ->
|
||||
var current: ArrayList<String>? = null
|
||||
var index = line.indexOf("plugins(")
|
||||
if (index >= 0) {
|
||||
current = pluginList
|
||||
} else {
|
||||
index = line.indexOf("repos(")
|
||||
if (index >= 0) {
|
||||
current = repos
|
||||
}
|
||||
}
|
||||
if (parenCount > 0 || current != null) {
|
||||
if (index == -1) index = 0
|
||||
with(line.substring(index)) {
|
||||
parenCount += line countChar '('
|
||||
if (parenCount > 0) {
|
||||
current!!.add(line)
|
||||
}
|
||||
parenCount -= line countChar ')'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current line matches one of the profile, turns the declaration into
|
||||
* val profile = true, otherwise return the same line
|
||||
*/
|
||||
fun correctProfileLine(line: String) : String {
|
||||
context.profiles.forEach {
|
||||
if (line.matches(kotlin.text.Regex("[ \\t]*val[ \\t]+$it[ \\t]+=.*"))) {
|
||||
with("val $it = true") {
|
||||
profileLines.add(this)
|
||||
return this
|
||||
}
|
||||
}
|
||||
}
|
||||
return line
|
||||
}
|
||||
|
||||
buildScript.add(correctProfileLine(line))
|
||||
}
|
||||
|
||||
repos.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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ package com.beust.kobalt.misc
|
|||
import com.beust.kobalt.SystemProperties
|
||||
import com.beust.kobalt.api.Kobalt
|
||||
import com.beust.kobalt.homeDir
|
||||
import com.beust.kobalt.kotlin.internal.build.BuildFile
|
||||
import com.beust.kobalt.kotlin.BuildFile
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.beust.kobalt.misc
|
|||
|
||||
import com.beust.kobalt.Args
|
||||
import com.beust.kobalt.internal.PluginInfo
|
||||
import com.beust.kobalt.kotlin.internal.build.BuildFileCompiler
|
||||
import com.beust.kobalt.kotlin.BuildFileCompiler
|
||||
import com.beust.kobalt.maven.*
|
||||
import com.beust.kobalt.plugin.publish.JCenterApi
|
||||
import com.google.inject.*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue