1
0
Fork 0
mirror of https://github.com/ethauvin/kobalt.git synced 2025-04-26 08:27:12 -07:00

Move files into the internal.build package.

This commit is contained in:
Cedric Beust 2015-11-27 09:16:12 -08:00
parent 43ccceb71e
commit 146d6702c5
9 changed files with 6 additions and 437 deletions

View file

@ -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.BuildFile
import com.beust.kobalt.kotlin.BuildFileCompiler
import com.beust.kobalt.kotlin.internal.build.BuildFile
import com.beust.kobalt.kotlin.internal.build.BuildFileCompiler
import com.beust.kobalt.maven.DepFactory
import com.beust.kobalt.maven.Http
import com.beust.kobalt.maven.LocalRepo

View file

@ -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.BuildFile
import com.beust.kobalt.kotlin.BuildFileCompiler
import com.beust.kobalt.kotlin.internal.build.BuildFile
import com.beust.kobalt.kotlin.internal.build.BuildFileCompiler
import com.beust.kobalt.maven.DependencyManager
import com.beust.kobalt.maven.IClasspathDependency
import com.beust.kobalt.maven.MavenDependency

View file

@ -1,18 +0,0 @@
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
}

View file

@ -1,128 +0,0 @@
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
}
}
}

View file

@ -1,135 +0,0 @@
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)
}
}
}
}

View file

@ -1,130 +0,0 @@
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)
}
}

View file

@ -1,20 +0,0 @@
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)
}
}
}

View file

@ -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.BuildFile
import com.beust.kobalt.kotlin.internal.build.BuildFile
import java.io.File
import java.io.IOException
import java.nio.file.Files

View file

@ -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.BuildFileCompiler
import com.beust.kobalt.kotlin.internal.build.BuildFileCompiler
import com.beust.kobalt.maven.*
import com.beust.kobalt.plugin.publish.JCenterApi
import com.google.inject.*