diff --git a/src/main/kotlin/com/beust/kobalt/Args.kt b/src/main/kotlin/com/beust/kobalt/Args.kt index dfd2e9b7..26491a07 100644 --- a/src/main/kotlin/com/beust/kobalt/Args.kt +++ b/src/main/kotlin/com/beust/kobalt/Args.kt @@ -42,6 +42,9 @@ class Args { @Parameter(names = arrayOf("--port"), description = "Port, if --server was specified") var port: Int = DEFAULT_SERVER_PORT + @Parameter(names = arrayOf("--profiles"), description = "Comma-separate list of profiles to run") + var profiles: String? = null + @Parameter(names = arrayOf("--resolve"), description = "Resolve the given dependency and display its tree") var dependency: String? = null diff --git a/src/main/kotlin/com/beust/kobalt/api/KobaltContext.kt b/src/main/kotlin/com/beust/kobalt/api/KobaltContext.kt index 98dc6ce9..896ca753 100644 --- a/src/main/kotlin/com/beust/kobalt/api/KobaltContext.kt +++ b/src/main/kotlin/com/beust/kobalt/api/KobaltContext.kt @@ -2,17 +2,29 @@ package com.beust.kobalt.api import com.beust.kobalt.Args import com.beust.kobalt.Plugins -import com.beust.kobalt.internal.PluginInfo import com.beust.kobalt.Variant +import com.beust.kobalt.internal.PluginInfo import com.beust.kobalt.maven.DependencyManager import com.beust.kobalt.misc.KobaltExecutors public class KobaltContext(val args: Args) { + var variant: Variant = Variant() + val profiles = arrayListOf() + + init { + args.profiles?.split(",")?.filterNotNull()?.forEach { + profiles.add(it) + } + } + fun findPlugin(name: String) = Plugins.findPlugin(name) + + // + // Injected + // lateinit var pluginInfo: PluginInfo lateinit var pluginProperties: PluginProperties lateinit var dependencyManager: DependencyManager lateinit var executors: KobaltExecutors - var variant: Variant = Variant() } diff --git a/src/main/kotlin/com/beust/kobalt/kotlin/BuildFileCompiler.kt b/src/main/kotlin/com/beust/kobalt/kotlin/BuildFileCompiler.kt index bf2522bc..2a31f5f8 100644 --- a/src/main/kotlin/com/beust/kobalt/kotlin/BuildFileCompiler.kt +++ b/src/main/kotlin/com/beust/kobalt/kotlin/BuildFileCompiler.kt @@ -6,8 +6,12 @@ import com.beust.kobalt.Plugins import com.beust.kobalt.api.* import com.beust.kobalt.api.annotation.Task import com.beust.kobalt.internal.PluginInfo +import com.beust.kobalt.kotlin.internal.ParsedBuildFile import com.beust.kobalt.maven.DependencyManager -import com.beust.kobalt.misc.* +import com.beust.kobalt.misc.KFiles +import com.beust.kobalt.misc.KobaltExecutors +import com.beust.kobalt.misc.Topological +import com.beust.kobalt.misc.log import com.beust.kobalt.plugin.kotlin.kotlinCompilePrivate import com.google.inject.assistedinject.Assisted import rx.subjects.PublishSubject @@ -75,7 +79,8 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b private fun findProjects(context: KobaltContext): List { val result = arrayListOf() buildFiles.forEach { buildFile -> - val pluginUrls = findPlugInUrls(context, buildFile) + val pair = processBuildFile(context, buildFile) + val pluginUrls = pair.second 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 @@ -87,8 +92,13 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b } } - maybeCompileBuildFile(context, buildFile, buildScriptJarFile, pluginUrls) - val buildScriptInfo = parseBuildScriptJarFile(buildScriptJarFile, pluginUrls) + // Write the modified Build.kt (e.g. maybe profiles were applied) to a temporary file, + // compile it and run it + val modifiedBuildFile = KFiles.createTempFile(".kt") + KFiles.saveFile(modifiedBuildFile, pair.first.buildScriptCode) + maybeCompileBuildFile(context, BuildFile(Paths.get(modifiedBuildFile.path), "Modified Build.kt"), + buildScriptJarFile, pluginUrls) + val buildScriptInfo = runBuildScriptJarFile(buildScriptJarFile, pluginUrls) result.addAll(buildScriptInfo.projects) } return result @@ -123,36 +133,21 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b /** * Generate the script file with only the plugins()/repos() directives and run it. Then return - * the URL's of all the plug-ins that were found. + * - 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 findPlugInUrls(context: KobaltContext, buildFile: BuildFile): List { + private fun processBuildFile(context: KobaltContext, buildFile: BuildFile): Pair> { val result = arrayListOf() - 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 ')' - } - } - } + + // Parse the build file so we can generate preBuildScript and buildScript from it. + val parsedBuildFile = ParsedBuildFile(buildFile.path.toFile(), context) // - // Compile and run pluginCode, which contains all the plugins() calls extracted. This + // 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(pluginCode.joinToString("\n"), Charset.defaultCharset()) + pluginSourceFile.writeText(parsedBuildFile.preBuildScriptCode, Charset.defaultCharset()) log(2, "Saved ${pluginSourceFile.absolutePath}") // @@ -169,7 +164,7 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b // // Run preBuildScript.jar to initialize plugins and repos // - parseBuildScriptJarFile(buildScriptJarFile, arrayListOf()) + runBuildScriptJarFile(buildScriptJarFile, arrayListOf()) // // All the plug-ins are now in Plugins.dynamicPlugins, download them if they're not already @@ -178,7 +173,7 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b result.add(it.jarFile.get().toURI().toURL()) } - return result + return Pair(parsedBuildFile, result) } private val VERSION_FILE = "version.txt" @@ -205,7 +200,10 @@ public class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val b class BuildScriptInfo(val projects: List) - private fun parseBuildScriptJarFile(buildScriptJarFile: File, urls: List) : BuildScriptInfo { + /** + * Run the given preBuildScript (or buildScript) jar file, using a classloader made of the passed URL's. + */ + private fun runBuildScriptJarFile(buildScriptJarFile: File, urls: List) : BuildScriptInfo { val projects = arrayListOf() var stream : InputStream? = null val allUrls = (urls + arrayOf( diff --git a/src/main/kotlin/com/beust/kobalt/kotlin/internal/ParsedBuildFile.kt b/src/main/kotlin/com/beust/kobalt/kotlin/internal/ParsedBuildFile.kt new file mode 100644 index 00000000..95ec4c9f --- /dev/null +++ b/src/main/kotlin/com/beust/kobalt/kotlin/internal/ParsedBuildFile.kt @@ -0,0 +1,75 @@ +package com.beust.kobalt.kotlin.internal + +import com.beust.kobalt.api.KobaltContext +import com.beust.kobalt.misc.countChar +import java.io.File +import java.nio.charset.Charset +import java.util.* + +class ParsedBuildFile(val file: File, val context: KobaltContext) { + val plugins = arrayListOf() + val repos = arrayListOf() + val profileLines = arrayListOf() + + private val preBuildScript = arrayListOf("import com.beust.kobalt.*", + "import com.beust.kobalt.api.*") + val preBuildScriptCode : String get() = preBuildScript.joinToString("\n") + + private val buildScript = arrayListOf() + val buildScriptCode : String get() = buildScript.joinToString("\n") + + init { + parseBuildFile() + } + + private fun parseBuildFile() { + var parenCount = 0 + file.forEachLine(Charset.defaultCharset()) { line -> + var current: ArrayList? = null + var index = line.indexOf("plugins(") + if (index >= 0) { + current = plugins + } 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 { + if (line.contains("experimental")) { + println("DONOTCOMMIT") + } + 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) } + plugins.forEach { preBuildScript.add(it) } + } +} +