From 8608df4618ade4a1ef1d115c81fca0213c0648d4 Mon Sep 17 00:00:00 2001 From: Cedric Beust Date: Mon, 12 Sep 2016 13:00:48 -0700 Subject: [PATCH] Refactor build compilation in anticipation of scripting support. --- .../com/beust/kobalt/app/BuildFileCompiler.kt | 16 +- .../{ParsedBuildFile.kt => SplitBuildFile.kt} | 165 +++++++++++------- 2 files changed, 106 insertions(+), 75 deletions(-) rename src/main/kotlin/com/beust/kobalt/app/{ParsedBuildFile.kt => SplitBuildFile.kt} (71%) diff --git a/src/main/kotlin/com/beust/kobalt/app/BuildFileCompiler.kt b/src/main/kotlin/com/beust/kobalt/app/BuildFileCompiler.kt index c79f9a2e..f585ba1b 100644 --- a/src/main/kotlin/com/beust/kobalt/app/BuildFileCompiler.kt +++ b/src/main/kotlin/com/beust/kobalt/app/BuildFileCompiler.kt @@ -32,7 +32,7 @@ import javax.inject.Inject * 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, +class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val buildFiles: List, @Assisted val pluginInfo: PluginInfo, val files: KFiles, val plugins: Plugins, val dependencyManager: DependencyManager, val pluginProperties: PluginProperties, val executors: KobaltExecutors, val buildScriptUtil: BuildScriptUtil, val settings: KobaltSettings, @@ -71,7 +71,7 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b return projectResult } - val parsedBuildFiles = arrayListOf() + val compiledBuildFiles = arrayListOf() class FindProjectResult(val context: KobaltContext, val projects: List, val pluginUrls: List, val taskResult: TaskResult) @@ -80,9 +80,9 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b var errorTaskResult: TaskResult? = null val projects = arrayListOf() buildFiles.forEach { buildFile -> - val parsedBuildFile = parseBuildFile(context, buildFile) - parsedBuildFiles.add(parsedBuildFile) - val pluginUrls = parsedBuildFile.pluginUrls + val compiledBuildFile = parseBuildFile(context, buildFile) + compiledBuildFiles.add(compiledBuildFile) + val pluginUrls = compiledBuildFile.pluginUrls val buildScriptJarFile = File(KFiles.findBuildScriptLocation(buildFile, SCRIPT_JAR)) // @@ -102,7 +102,7 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b // 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", deleteOnExit = true) - KFiles.saveFile(modifiedBuildFile, parsedBuildFile.buildScriptCode) + KFiles.saveFile(modifiedBuildFile, compiledBuildFile.splitFile.buildScriptCode) val taskResult = maybeCompileBuildFile(context, BuildFile(Paths.get(modifiedBuildFile.path), "Modified ${Constants.BUILD_FILE_NAME}", buildFile.realPath), buildScriptJarFile, pluginUrls) @@ -118,7 +118,7 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b context.internalContext.absoluteDir = null } - val pluginUrls = parsedBuildFiles.flatMap { it.pluginUrls } + val pluginUrls = compiledBuildFiles.flatMap { it.pluginUrls } return FindProjectResult(context, projects, pluginUrls, if (errorTaskResult != null) errorTaskResult!! else TaskResult()) } @@ -167,5 +167,5 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b * - the URL's of all the plug-ins that were found. */ private fun parseBuildFile(context: KobaltContext, buildFile: BuildFile) = - ParsedBuildFile(buildFile, context, buildScriptUtil, dependencyManager, files) + ProcessedBuildFile(buildFile, context, buildScriptUtil, dependencyManager, files) } diff --git a/src/main/kotlin/com/beust/kobalt/app/ParsedBuildFile.kt b/src/main/kotlin/com/beust/kobalt/app/SplitBuildFile.kt similarity index 71% rename from src/main/kotlin/com/beust/kobalt/app/ParsedBuildFile.kt rename to src/main/kotlin/com/beust/kobalt/app/SplitBuildFile.kt index 9029358b..116d27ac 100644 --- a/src/main/kotlin/com/beust/kobalt/app/ParsedBuildFile.kt +++ b/src/main/kotlin/com/beust/kobalt/app/SplitBuildFile.kt @@ -2,6 +2,7 @@ package com.beust.kobalt.app import com.beust.kobalt.KobaltException import com.beust.kobalt.Plugins +import com.beust.kobalt.TaskResult import com.beust.kobalt.api.Kobalt import com.beust.kobalt.api.KobaltContext import com.beust.kobalt.api.Project @@ -18,27 +19,112 @@ import java.nio.charset.Charset import java.nio.file.Paths import java.util.* -class ParsedBuildFile(val buildFile: BuildFile, val context: KobaltContext, val buildScriptUtil: BuildScriptUtil, +/** + * Process the given build file (either with kotlinc or through scripting) and return projects and pluginUrls. + */ +class ProcessedBuildFile(val buildFile: BuildFile, val context: KobaltContext, val buildScriptUtil: BuildScriptUtil, val dependencyManager: DependencyManager, val files: KFiles) { - val pluginList = arrayListOf() - val repos = arrayListOf() - val buildFileClasspath = arrayListOf() - val profileLines = arrayListOf() val pluginUrls = arrayListOf() - val projects = arrayListOf() - val activeProfiles = arrayListOf() + val splitFile = SplitBuildFile(buildFile, context, dependencyManager, files) + + fun compile(): BuildFileCompiler.FindProjectResult { + + // Find the projects but also invoke the plugins() directive, which will initialize Plugins.dynamicPlugins + val projects = CompiledBuildFile(buildScriptUtil, dependencyManager, files) + .findProjects(splitFile, 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()) + } + + return BuildFileCompiler.FindProjectResult(context, projects, pluginUrls, TaskResult()) + } +} + +/** + * Compile a build file with kotlinc. + */ +class CompiledBuildFile(val buildScriptUtil: BuildScriptUtil, val dependencyManager: DependencyManager, + val files: KFiles) { + + fun findProjects(splitFile: SplitBuildFile, context: KobaltContext): List { + // + // 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", deleteOnExit = true) + pluginSourceFile.writeText(splitFile.preBuildScriptCode, Charset.defaultCharset()) + kobaltLog(2, "Saved ${pluginSourceFile.absolutePath}") + + // + // Compile to preBuildScript.jar + // + val buildFile = splitFile.buildFile + 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", + Paths.get(buildScriptJar)), buildScriptJarFile, buildFile) + VersionFile.generateVersionFile(buildScriptJarFile.parentFile) + Kobalt.context!!.internalContext.buildFileOutOfDate = true + } + + // + // Run preBuildScript.jar to initialize plugins and repos + // + val result = arrayListOf() + result.addAll(buildScriptUtil.runBuildScriptJarFile(buildScriptJarFile, arrayListOf(), context)) + + return result + } + + private fun generateJarFile(context: KobaltContext, buildFile: BuildFile, buildScriptJarFile: File, + originalFile: BuildFile) { + + // + // Compile the jar file + // + val kotlintDeps = dependencyManager.calculateDependencies(null, context) + val deps: List = kotlintDeps.map { it.jarFile.get().absolutePath } + val outputJar = File(buildScriptJarFile.absolutePath) + val result = kotlinCompilePrivate { + classpath(files.kobaltJar) + classpath(deps) + sourceFiles(buildFile.path.toFile().absolutePath) + output = outputJar + }.compile(context = context) + if (! result.success) { + throw KobaltException("Couldn't compile ${originalFile.realPath}:\n" + + result.errorMessage) + } + } +} + +/** + * Parse the given build file and split it into + * - A simple build file with just the repos() and plugins() lines (preBuildScriptCode) + * - The full build files with profiles applied (buildScriptCode) + */ +class SplitBuildFile(val buildFile: BuildFile, val context: KobaltContext, val dependencyManager: DependencyManager, + val files: KFiles) { + private val pluginList = arrayListOf() + private val repos = arrayListOf() + private val buildFileClasspath = arrayListOf() + private val profileLines = arrayListOf() + private val activeProfiles = arrayListOf() private val preBuildScript = arrayListOf( "import com.beust.kobalt.*", "import com.beust.kobalt.api.*") - val preBuildScriptCode : String get() = preBuildScript.joinToString("\n") + val preBuildScriptCode: String get() = preBuildScript.joinToString("\n") private val buildScript = arrayListOf() - val buildScriptCode : String get() = buildScript.joinToString("\n") + val buildScriptCode: String get() = buildScript.joinToString("\n") init { parseBuildFile() - initPluginUrls() } private fun parseBuildFile() { @@ -81,7 +167,7 @@ class ParsedBuildFile(val buildFile: BuildFile, val context: KobaltContext, val * If the current line matches one of the profiles, turn the declaration into * val profile = true, otherwise return the same line */ - fun correctProfileLine(line: String) : String { + fun correctProfileLine(line: String): String { (context.profiles as List).forEach { if (line.matches(Regex("[ \\t]*val[ \\t]+$it[ \\t]*=.*"))) { with("val $it = true") { @@ -102,61 +188,6 @@ class ParsedBuildFile(val buildFile: BuildFile, val context: KobaltContext, val pluginList.forEach { preBuildScript.add(it) } buildFileClasspath.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", deleteOnExit = true) - pluginSourceFile.writeText(preBuildScriptCode, Charset.defaultCharset()) - kobaltLog(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", - Paths.get(buildScriptJar)), buildScriptJarFile, buildFile) - VersionFile.generateVersionFile(buildScriptJarFile.parentFile) - Kobalt.context!!.internalContext.buildFileOutOfDate = true - } - - // - // Run preBuildScript.jar to initialize plugins and repos - // - projects.addAll(buildScriptUtil.runBuildScriptJarFile(buildScriptJarFile, arrayListOf(), 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, - originalFile: BuildFile) { - - // - // Compile the jar file - // - val kotlintDeps = dependencyManager.calculateDependencies(null, context) - val deps: List = kotlintDeps.map { it.jarFile.get().absolutePath } - val outputJar = File(buildScriptJarFile.absolutePath) - val result = kotlinCompilePrivate { - classpath(files.kobaltJar) - classpath(deps) - sourceFiles(buildFile.path.toFile().absolutePath) - output = outputJar - }.compile(context = context) - if (! result.success) { - throw KobaltException("Couldn't compile ${originalFile.realPath}:\n" - + result.errorMessage) - } - } } +