From 8dadbd391a7d06b14e09080fdcaf4e9c4591402d Mon Sep 17 00:00:00 2001 From: Cedric Beust Date: Wed, 29 Mar 2017 11:08:20 -0700 Subject: [PATCH] Rewriting build file parsing logic. --- .../kotlin/com/beust/kobalt/app/BuildFiles.kt | 188 +++++++++++++++++- 1 file changed, 180 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/com/beust/kobalt/app/BuildFiles.kt b/src/main/kotlin/com/beust/kobalt/app/BuildFiles.kt index e9f39cae..32393c90 100644 --- a/src/main/kotlin/com/beust/kobalt/app/BuildFiles.kt +++ b/src/main/kotlin/com/beust/kobalt/app/BuildFiles.kt @@ -1,14 +1,186 @@ package com.beust.kobalt.app -class BuildFiles { - // Parse all the files found in kobalt/src/*kt, extract their buildScriptInfo blocks, - // save the location where they appear (file, start/end). +import com.beust.kobalt.Args +import com.beust.kobalt.api.Kobalt +import com.beust.kobalt.api.KobaltContext +import com.beust.kobalt.api.Project +import com.beust.kobalt.homeDir +import com.beust.kobalt.internal.KobaltPluginXml +import com.beust.kobalt.internal.KobaltSettings +import com.beust.kobalt.internal.PluginInfo +import com.beust.kobalt.internal.build.BuildSources +import com.beust.kobalt.misc.* +import com.google.inject.Inject +import java.io.File +import java.net.URL +import java.nio.file.* +import java.nio.file.attribute.BasicFileAttributes +import java.util.regex.Pattern - // Compile each of these buildScriptInfo separately, note which new build files they add - // and at which location +/** + * Parse all the files found in kobalt/src/ *kt, extract their buildScriptInfo blocks, + * save the location where they appear (file, start/end). - // Go back over all the files from kobalt/src/*kt, insert each new build file in it, - // save it as a modified, concatenated build file + * Compile each of these buildScriptInfo separately, note which new build files they add + * and at which location - // Create buildScript.jar out of compiling all these modified build files + * Go back over all the files from kobalt/src/ *kt, insert each new build file in it, + * save it as a modified, concatenated build file + + * Create buildScript.jar out of compiling all these modified build files +*/ + +fun main(argv: Array) { + val args = Args().apply { + noIncrementalKotlin = true + } + val context = KobaltContext(args) + KobaltLogger.LOG_LEVEL = 3 + context.pluginInfo = PluginInfo(KobaltPluginXml(), null, null) + Kobalt.init(MainModule(args, KobaltSettings.readSettingsXml())) + val bf = Kobalt.INJECTOR.getInstance(BuildFiles::class.java) + bf.run(homeDir("kotlin/klaxon"), context) +} + +class BuildFiles @Inject constructor(val factory: BuildFileCompiler.IFactory, + val buildScriptUtil: BuildScriptUtil) { + private val profileLines = arrayListOf() + private val activeProfiles = arrayListOf() + private val ROOT = File("kobalt/src") + + var containsProfiles = false + val projects = arrayListOf() + + private fun findFiles(file: File, accept: (File) -> Boolean) : List { + val result = arrayListOf() + Files.walkFileTree(Paths.get(file.path), object : SimpleFileVisitor() { + override fun visitFile(file: Path, attrs: BasicFileAttributes?): FileVisitResult { + if (accept(file.toFile())) result.add(file.toFile()) + return FileVisitResult.CONTINUE + } + }) + return result + } + + class AnalyzedBuildFile(val file: File, val buildScriptInfo: BuildScriptInfo) + + fun run(projectDir: String, context: KobaltContext) { + val analyzedFiles = parseBuildScriptInfos(projectDir, context) + if (analyzedFiles.any()) { + println("FOUND buildScriptInfos: " + analyzedFiles) + } else { + println("No buildScriptInfos") + } + } + + fun parseBuildScriptInfos(projectDir: String, context: KobaltContext) : List { + val root = File(projectDir, ROOT.path) + val files = findFiles(root, { f: File -> f.name.endsWith(".kt")}) + val toProcess = arrayListOf().apply { addAll(files) } + + // Parse each build file, associated it with a BuildScriptInfo if any found + val analyzedFiles = arrayListOf() + toProcess.forEach { buildFile -> + val lines = applyProfiles(context, buildFile.readLines()) + val bsi = BlockExtractor(Pattern.compile("^val.*buildScript.*\\{"), '{', '}') + .extractBlock(buildFile, lines) + if (bsi != null) analyzedFiles.add(AnalyzedBuildFile(buildFile, bsi)) + } + + // Run every buildScriptInfo section in its own source file + var counter = 0 + analyzedFiles.forEach { af -> + af.buildScriptInfo.sections.forEach { section -> + + // + // Create a source file with just this buildScriptInfo{} + // + val bs = af.file.readLines().subList(section.start - 1, section.end) + val source = (af.buildScriptInfo.imports + bs).joinToString("\n") + val sourceFile = File(homeDir("t", "bf", "a.kt")).apply { + writeText(source) + } + val buildScriptJarFile = File(homeDir("t", "preBuildScript-$counter.jar")).apply { + delete() + } + counter++ + + // + // Compile it to preBuildScript-xxx.jar + // + kobaltLog(1, "Compiling $sourceFile to $buildScriptJarFile") + val taskResult = factory.create(BuildSources(root), context.pluginInfo).maybeCompileBuildFile(context, + listOf(sourceFile.path), + buildScriptJarFile, emptyList(), + context.internalContext.forceRecompile, + containsProfiles) + println("Created $buildScriptJarFile") + + // + // Run preBuildScript.jar to initialize plugins and repos + // + val currentDirs = arrayListOf().apply { addAll(Kobalt.buildSourceDirs) } + buildScriptUtil.runBuildScriptJarFile(buildScriptJarFile, listOf(), context) + val newDirs = arrayListOf().apply { addAll(Kobalt.buildSourceDirs) } + newDirs.removeAll(currentDirs) + if (newDirs.any()) { + af.buildScriptInfo.includedBuildSourceDirs.add(IncludedBuildSourceDir(section.start - 1, newDirs)) + println("*** ADDED DIRECTORIES " + newDirs) + } + } + + } + + return analyzedFiles + } + + private fun applyProfiles(context: KobaltContext, lines: List): List { + val result = arrayListOf() + lines.forEach { line -> + result.add(correctProfileLine(context, line)) + + } + return result + } + + /** + * If the current line matches one of the profiles, turn the declaration into + * val profile = true, otherwise return the same line + */ + fun correctProfileLine(context: KobaltContext, line: String): String { + (context.profiles as List).forEach { profile -> + val re = Regex(".*va[rl][ \\t]+([a-zA-Z0-9_]+)[ \\t]*.*profile\\(\\).*") + val oldRe = Regex(".*va[rl][ \\t]+([a-zA-Z0-9_]+)[ \\t]*=[ \\t]*[tf][ra][ul][es].*") + val matcher = re.matchEntire(line) + val oldMatcher = oldRe.matchEntire(line) + + fun profileMatch(matcher: MatchResult?) : Pair { + val variable = if (matcher != null) matcher.groups[1]?.value else null + return Pair(profile == variable, variable) + } + + if ((matcher != null && matcher.groups.isNotEmpty()) + || (oldMatcher != null && oldMatcher.groups.isNotEmpty())) { + containsProfiles = true + val match = profileMatch(matcher) + val oldMatch = profileMatch(oldMatcher) + if (match.first || oldMatch.first) { + val variable = if (match.first) match.second else oldMatch.second + + if (oldMatch.first) { + warn("Old profile syntax detected for \"$line\"," + + " please update to \"val $variable by profile()\"") + } + + with("val $variable = true") { + kobaltLog(2, "Activating profile $profile in build file") + activeProfiles.add(profile) + profileLines.add(this) + return this + } + } + } + } + return line + } }