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

Multiple build file work.

This commit is contained in:
Cedric Beust 2017-03-29 15:41:48 -07:00
parent 5b2d4296dc
commit 4ad0222f0a
6 changed files with 310 additions and 230 deletions

View file

@ -1,8 +1,6 @@
package com.beust.kobalt.internal.build package com.beust.kobalt.internal.build
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.homeDir import com.beust.kobalt.homeDir
import com.beust.kobalt.misc.kobaltLog
import java.io.File import java.io.File
import java.nio.file.* import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes import java.nio.file.attribute.BasicFileAttributes
@ -19,30 +17,27 @@ class SingleFileBuildSources(val file: File) : IBuildSources {
override val root: File = file.parentFile.parentFile override val root: File = file.parentFile.parentFile
} }
class BuildSources(val file: File) : IBuildSources { class BuildSources(val file: File = File("")) : IBuildSources {
override val root = file override val root = file
override fun findSourceFiles() : List<File> { override fun findSourceFiles() : List<File> {
val result = arrayListOf("kobalt/src/Build.kt") return findBuildFiles(listOf(file))
if (Kobalt.buildSourceDirs.isNotEmpty()) result.addAll(findBuildFiles(Kobalt.buildSourceDirs))
return result.map(::File)
} }
override fun exists() = findSourceFiles().isNotEmpty() override fun exists() = findSourceFiles().isNotEmpty()
override fun toString() = "{BuildSources " + findSourceFiles().joinToString(", ") + "}" override fun toString() = "{BuildSources " + findSourceFiles().joinToString(", ") + "}"
fun findBuildFiles(roots: List<String>) : List<String> { fun findBuildFiles(roots: List<File>) : List<File> {
val result = arrayListOf<String>() val result = arrayListOf<File>()
roots.forEach { file -> roots.forEach { file ->
Files.walkFileTree(Paths.get(file), object : SimpleFileVisitor<Path>() { Files.walkFileTree(Paths.get(file.path), object : SimpleFileVisitor<Path>() {
override fun preVisitDirectory(dir: Path?, attrs: BasicFileAttributes?): FileVisitResult { override fun preVisitDirectory(dir: Path?, attrs: BasicFileAttributes?): FileVisitResult {
if (dir != null) { if (dir != null) {
val path = dir.toFile() val path = dir.toFile()
if (path.name == "src" && path.parentFile.name == "kobalt") { if (path.name == "src" && path.parentFile.name == "kobalt") {
val sources = path.listFiles().filter { it.name.endsWith(".kt") }.map { it.path } val sources = path.listFiles().filter { it.name.endsWith(".kt") }
result.addAll(sources) result.addAll(sources)
} }
} }

View file

@ -1,26 +1,29 @@
package com.beust.kobalt.misc package com.beust.kobalt.misc
import com.beust.kobalt.homeDir
import java.io.File import java.io.File
import java.util.regex.Pattern import java.util.regex.Pattern
fun main(argv: Array<String>) {
val lines = File(homeDir("kotlin/kobalt/kobalt/src/Build.kt")).readLines()
val result = BlockExtractor(Pattern.compile("val.*buildScript.*\\{"), '{', '}').extractBlock(lines)
// BlockExtractor("plugins", '(', ')').extractBlock(lines)
}
class Section(val start: Int, val end: Int) { class Section(val start: Int, val end: Int) {
override fun toString() = "$start-$end" override fun toString() = "$start-$end"
} }
class BuildScriptInfo(val content: String, val sections: List<Section>) { class IncludedBuildSourceDir(val line: Int, val dirs: List<String>)
class BuildScriptInfo(val file: File, val fullBuildFile: List<String>, val sections: List<Section>,
val imports: List<String>) {
fun isInSection(lineNumber: Int): Boolean { fun isInSection(lineNumber: Int): Boolean {
sections.forEach { sections.forEach {
if (lineNumber >= it.start && lineNumber <= it.end) return true if (lineNumber >= it.start && lineNumber <= it.end) return true
} }
return false return false
} }
val includedBuildSourceDirs = arrayListOf<IncludedBuildSourceDir>()
fun includedBuildSourceDirsForLine(line: Int): List<String> {
val result = includedBuildSourceDirs.find { it.line == line }?.dirs
return result ?: emptyList()
}
} }
/** /**
@ -28,7 +31,7 @@ class BuildScriptInfo(val content: String, val sections: List<Section>) {
* e.g. buildScript { ... }. * e.g. buildScript { ... }.
*/ */
class BlockExtractor(val regexp: Pattern, val opening: Char, val closing: Char) { class BlockExtractor(val regexp: Pattern, val opening: Char, val closing: Char) {
fun extractBlock(lines: List<String>): BuildScriptInfo? { fun extractBlock(file: File, lines: List<String>): BuildScriptInfo? {
var currentLineNumber = 0 var currentLineNumber = 0
// First line of the buildScript block // First line of the buildScript block
var startLine = 0 var startLine = 0
@ -60,12 +63,9 @@ class BlockExtractor(val regexp: Pattern, val opening: Char, val closing: Char)
if (currentLine.isNotEmpty() && foundKeyword) buildScript.add(currentLine.toString()) if (currentLine.isNotEmpty() && foundKeyword) buildScript.add(currentLine.toString())
} }
val allowedImports = listOf("com.beust", "java")
val disallowedImports = listOf("com.beust.kobalt.plugin")
val imports = arrayListOf<String>() val imports = arrayListOf<String>()
val sections = arrayListOf<Section>() val sections = arrayListOf<Section>()
lines.forEach { line -> lines.forEach { line ->
currentLineNumber++
val found = regexp.matcher(line).matches() val found = regexp.matcher(line).matches()
if (found) { if (found) {
startLine = currentLineNumber startLine = currentLineNumber
@ -75,7 +75,7 @@ class BlockExtractor(val regexp: Pattern, val opening: Char, val closing: Char)
topLines.add(line) topLines.add(line)
} else { } else {
if (line.startsWith("import")) { if (line.startsWith("import")) {
if (allowedImports.any { line.contains(it) } && !disallowedImports.any { line.contains(it) }) { if (isAllowedImport(line)) {
imports.add(line) imports.add(line)
} }
} else { } else {
@ -92,14 +92,25 @@ class BlockExtractor(val regexp: Pattern, val opening: Char, val closing: Char)
startLine = 0 startLine = 0
endLine = 0 endLine = 0
} }
currentLineNumber++
} }
if (sections.isNotEmpty()) { if (sections.isNotEmpty()) {
val result = (imports.distinct() + buildScript).joinToString("\n") + "\n" val result = (imports.distinct() + buildScript).joinToString("\n") + "\n"
return BuildScriptInfo(result, sections) return BuildScriptInfo(file, lines, sections, imports)
} else { } else {
return null return null
} }
} }
companion object {
private val allowedImports = listOf("com.beust", "java")
private val disallowedImports = listOf("com.beust.kobalt.plugin")
fun isAllowedImport(line: String) : Boolean {
return allowedImports.any { line.contains(it) } && !disallowedImports.any { line.contains(it) }
}
}
} }

View file

@ -3,7 +3,6 @@ package com.beust.kobalt.misc
import com.beust.kobalt.* import com.beust.kobalt.*
import com.beust.kobalt.api.Kobalt import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.api.Project import com.beust.kobalt.api.Project
import com.beust.kobalt.internal.build.IBuildSources
import com.beust.kobalt.maven.Md5 import com.beust.kobalt.maven.Md5
import java.io.* import java.io.*
import java.nio.file.Files import java.nio.file.Files
@ -261,9 +260,9 @@ class KFiles {
/** /**
* The build location for build scripts is .kobalt/build * The build location for build scripts is .kobalt/build
*/ */
fun findBuildScriptLocation(buildSources: IBuildSources, jarFile: String) : String { fun findBuildScriptDir(parent: String = ".") : File {
val result = joinDir(buildSources.root.path, KFiles.dotKobaltDir.path, KFiles.SCRIPT_BUILD_DIR, jarFile) val result = File(joinDir(parent, KFiles.dotKobaltDir.path, KFiles.SCRIPT_BUILD_DIR))
kobaltLog(2, " Script jar file: $result") kobaltLog(2, " Script jar files in: $result")
return result return result
} }

View file

@ -37,7 +37,7 @@ class BuildFileCompiler @Inject constructor(@Assisted("buildSources") val buildS
val executors: KobaltExecutors, val buildScriptUtil: BuildScriptUtil, val settings: KobaltSettings, val executors: KobaltExecutors, val buildScriptUtil: BuildScriptUtil, val settings: KobaltSettings,
val incrementalManagerFactory: IncrementalManager.IFactory, val args: Args, val incrementalManagerFactory: IncrementalManager.IFactory, val args: Args,
val resolver: KobaltMavenResolver, val pomGeneratorFactory: PomGenerator.IFactory, val resolver: KobaltMavenResolver, val pomGeneratorFactory: PomGenerator.IFactory,
val parallelLogger: ParallelLogger) { val parallelLogger: ParallelLogger, val buildFiles: BuildFiles) {
interface IFactory { interface IFactory {
fun create(@Assisted("buildSources") buildSources: IBuildSources, pluginInfo: PluginInfo) : BuildFileCompiler fun create(@Assisted("buildSources") buildSources: IBuildSources, pluginInfo: PluginInfo) : BuildFileCompiler
@ -76,22 +76,33 @@ class BuildFileCompiler @Inject constructor(@Assisted("buildSources") val buildS
return projectResult return projectResult
} }
val parsedBuildFiles = arrayListOf<ParsedBuildFile>() val parsedBuildFiles = arrayListOf<_ParsedBuildFile>()
class FindProjectResult(val context: KobaltContext, val projects: List<Project>, val pluginUrls: List<URL>, class FindProjectResult(val context: KobaltContext, val projects: List<Project>, val pluginUrls: List<URL>,
val taskResult: TaskResult) val taskResult: TaskResult)
// private fun findProjects(context: KobaltContext): FindProjectResult {
// buildFiles.run(File(".").absolutePath, context)
// val pluginUrls = Plugins.dynamicPlugins.map { it.jarFile.get().toURI().toURL()}
// val projects = listOf<Project>()
// val result = FindProjectResult(context, projects, pluginUrls, TaskResult())
// return result
// }
private fun findProjects(context: KobaltContext): FindProjectResult { private fun findProjects(context: KobaltContext): FindProjectResult {
val root = buildSources.root
var errorTaskResult: TaskResult? = null var errorTaskResult: TaskResult? = null
val projects = arrayListOf<Project>() val projects = arrayListOf<Project>()
run { run {
// buildFiles.forEach { buildFile -> // buildFiles.forEach { buildFile ->
// Parse kobalt/src/Build.kt // Parse kobalt/src/Build.kt
val parsedBuildFile = parseBuildFile(context, buildSources) // val parsedBuildFile = parseBuildFile(context, buildSources)
parsedBuildFiles.add(parsedBuildFile) // parsedBuildFiles.add(parsedBuildFile)
val pluginUrls = parsedBuildFile.pluginUrls // val pluginUrls = parsedBuildFile.pluginUrls
val buildScriptJarFile = File(KFiles.findBuildScriptLocation(buildSources, SCRIPT_JAR)) // val buildScriptJarFile = File(KFiles.findBuildScriptLocation(buildSources, SCRIPT_JAR))
val newBuildKt = buildFiles.run(root.absolutePath, context)
// //
// Save the current build script absolute directory // Save the current build script absolute directory
@ -100,32 +111,47 @@ class BuildFileCompiler @Inject constructor(@Assisted("buildSources") val buildS
// If the script jar files were generated by a different version, wipe them in case the API // If the script jar files were generated by a different version, wipe them in case the API
// changed in-between // changed in-between
buildScriptJarFile.parentFile.let { dir -> val buildScriptJarDir = KFiles.findBuildScriptDir(root.absolutePath)
buildScriptJarDir.let { dir ->
if (! VersionFile.isSameVersionFile(dir)) { if (! VersionFile.isSameVersionFile(dir)) {
kobaltLog(1, "Detected new installation, wiping $dir") kobaltLog(1, "Detected new installation, wiping $dir")
dir.listFiles().map(File::delete) dir.listFiles().map(File::delete)
} }
} }
val buildScriptJarFile = File(KFiles.findBuildScriptDir(root.absolutePath), SCRIPT_JAR)
//
// Compile the newly generated Build.kt file
//
val pluginUrls = Plugins.dynamicPlugins.map { it.jarFile.get().toURI().toURL() }
val containsProfiles = false
val taskResult = maybeCompileBuildFile(context, listOf(newBuildKt.absolutePath),
buildScriptJarFile, pluginUrls, context.internalContext.forceRecompile,
containsProfiles)
// //
// Now that Build.kt has been parsed, we might have additional build files (buildSources will // Now that Build.kt has been parsed, we might have additional build files (buildSources will
// return additional build files) so we parse again. // return additional build files) so we parse again.
// //
val newParsedBuildFile = parseBuildFile(context, buildSources) // Kobalt.buildSourceDirs.forEach { dir ->
// val additionalSourceFiles = BuildSources(File(dir))
// Write the modified Build.kt (e.g. maybe profiles were applied) to a temporary file, // val newParsedBuildFile = parseBuildFile(context, additionalSourceFiles)
// compile it, jar it in buildScript.jar and run it //
val modifiedBuildFile = KFiles.createTempBuildFileInTempDirectory(deleteOnExit = true) // // Write the modified Build.kt (e.g. maybe profiles were applied) to a temporary file,
KFiles.saveFile(modifiedBuildFile, newParsedBuildFile.nonBuildScriptCode) // // compile it, jar it in buildScript.jar and run it
val taskResult = maybeCompileBuildFile(context, listOf(modifiedBuildFile.path), // val modifiedBuildFile = KFiles.createTempBuildFileInTempDirectory(deleteOnExit = true)
buildScriptJarFile, pluginUrls, context.internalContext.forceRecompile, // KFiles.saveFile(modifiedBuildFile, newParsedBuildFile.nonBuildScriptCode)
newParsedBuildFile.containsProfiles) // val taskResult = maybeCompileBuildFile(context, listOf(modifiedBuildFile.path),
if (taskResult.success) { // buildScriptJarFile, pluginUrls, context.internalContext.forceRecompile,
projects.addAll(buildScriptUtil.runBuildScriptJarFile(buildScriptJarFile, pluginUrls, context)) // newParsedBuildFile.containsProfiles)
} else { if (taskResult.success) {
if (errorTaskResult == null) { projects.addAll(buildScriptUtil.runBuildScriptJarFile(buildScriptJarFile, pluginUrls, context))
errorTaskResult = taskResult } else {
} if (errorTaskResult == null) {
errorTaskResult = taskResult
}
// }
} }
// Clear the absolute dir // Clear the absolute dir
@ -142,7 +168,7 @@ class BuildFileCompiler @Inject constructor(@Assisted("buildSources") val buildS
= maybeCompileBuildFile(context, buildSources.findSourceFiles().map { it.path }, buildScriptJarFile, = maybeCompileBuildFile(context, buildSources.findSourceFiles().map { it.path }, buildScriptJarFile,
pluginUrls, forceRecompile, containsProfiles) pluginUrls, forceRecompile, containsProfiles)
private fun maybeCompileBuildFile(context: KobaltContext, sourceFiles: List<String>, buildScriptJarFile: File, fun maybeCompileBuildFile(context: KobaltContext, sourceFiles: List<String>, buildScriptJarFile: File,
pluginUrls: List<URL>, forceRecompile: Boolean, containsProfiles: Boolean) : TaskResult { pluginUrls: List<URL>, forceRecompile: Boolean, containsProfiles: Boolean) : TaskResult {
kobaltLog(2, "Compiling into $buildScriptJarFile") kobaltLog(2, "Compiling into $buildScriptJarFile")
@ -188,6 +214,6 @@ class BuildFileCompiler @Inject constructor(@Assisted("buildSources") val buildS
* - the source code for the modified Build.kt (after profiles are applied) * - the source code for the modified Build.kt (after profiles are applied)
* - the URL's of all the plug-ins that were found. * - the URL's of all the plug-ins that were found.
*/ */
private fun parseBuildFile(context: KobaltContext, buildSources: IBuildSources) = // private fun _parseBuildFile(context: KobaltContext, buildSources: IBuildSources) =
ParsedBuildFile(buildSources, context, buildScriptUtil, dependencyManager, files) // _ParsedBuildFile(buildSources, context, buildScriptUtil, dependencyManager, files)
} }

View file

@ -22,14 +22,13 @@ import java.util.regex.Pattern
* save the location where they appear (file, start/end). * save the location where they appear (file, start/end).
* Compile each of these buildScriptInfo separately, note which new build files they add * Compile each of these buildScriptInfo separately, note which new build files they add
* and at which location * and at which location.
* Go back over all the files from kobalt/src/ *kt, insert each new build file in it, * 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 * save it as a modified, concatenated big build file in .kobalt/build/Built.kt.
* Create buildScript.jar out of compiling all these modified build files
*/
* Create buildScript.jar out of compiling all these modified build files.
*/
fun main(argv: Array<String>) { fun main(argv: Array<String>) {
val args = Args().apply { val args = Args().apply {
noIncrementalKotlin = true noIncrementalKotlin = true
@ -74,8 +73,11 @@ class BuildFiles @Inject constructor(val factory: BuildFileCompiler.IFactory,
return result return result
} }
fun run(projectDir: String, context: KobaltContext) { /**
val sourceDirs = arrayListOf<String>().apply { add(projectDir + KOBALT_SRC) } * @return the new Build.kt
*/
fun run(projectDir: String, context: KobaltContext) : File {
val sourceDirs = arrayListOf<String>().apply { add(projectDir + File.separator + KOBALT_SRC) }
val map = hashMapOf<File, AnalyzedBuildFile>() val map = hashMapOf<File, AnalyzedBuildFile>()
val newSourceDirs = arrayListOf<IncludedBuildSourceDir>() val newSourceDirs = arrayListOf<IncludedBuildSourceDir>()
sourceDirs.forEach { sourceDirs.forEach {
@ -88,61 +90,80 @@ class BuildFiles @Inject constructor(val factory: BuildFileCompiler.IFactory,
val bsi = af.buildScriptInfo val bsi = af.buildScriptInfo
newSourceDirs.addAll(bsi.includedBuildSourceDirs) newSourceDirs.addAll(bsi.includedBuildSourceDirs)
} }
println("FOUND buildScriptInfos: " + filesWithBuildScript) log(2, " Found buildScriptInfos: " + filesWithBuildScript)
} else { } else {
println("No buildScriptInfos") log(2, " No buildScriptInfos")
} }
} }
// //
// Go through all the build files and insert the content of included directories wherever appropriate // Go through all the build files and insert the content of included directories wherever appropriate
// //
val bigFileContent = arrayListOf<String>() val imports = arrayListOf<String>()
val code = arrayListOf<String>()
sourceDirs.forEach { sourceDir -> sourceDirs.forEach { sourceDir ->
findFiles(File(sourceDir), { it.name.endsWith(".kt") }).forEach { file -> findFiles(File(sourceDir), { it.name.endsWith(".kt") }).forEach { file ->
println("Looking at " + file) code.add("\n// $file")
bigFileContent.add("// $file")
val analyzedFile = map[file] val analyzedFile = map[file]
val bsi = analyzedFile?.buildScriptInfo val bsi = analyzedFile?.buildScriptInfo
file.readLines().forEachIndexed { lineNumber, line -> file.readLines().forEachIndexed { lineNumber, line ->
if (bsi == null || ! bsi.isInSection(lineNumber)) { if (bsi == null || ! bsi.isInSection(lineNumber)) {
bigFileContent.add(correctProfileLine(context, line)) correctProfileLine(context, line).let { cpl ->
(if (cpl.startsWith("import")) imports else code).add(cpl)
}
} else { } else {
val isd = bsi.includedBuildSourceDirsForLine(lineNumber) val isd = bsi.includedBuildSourceDirsForLine(lineNumber)
println("Skipping line $lineNumber from file $file") log(2, " Skipping line $lineNumber from file $file")
if (isd.any()) { if (isd.any()) {
// If we found any new buildSourceDirs, all all the files found in these directories
// to the big Build.kt
val allBuildFiles = isd.flatMap { findBuildSourceFiles(projectDir + File.separator + it) } val allBuildFiles = isd.flatMap { findBuildSourceFiles(projectDir + File.separator + it) }
includeFileContent(context, allBuildFiles, bigFileContent) val sbf = includeFileContent(context, allBuildFiles)
imports.addAll(sbf.imports)
code.addAll(sbf.code)
} }
} }
} }
} }
} }
val bigFile = File(homeDir("t/Build.kt")) //
bigFile.writeText(bigFileContent.joinToString("\n")) // Create the big Build.kt out of the imports and code we've found so far
println("New included source dirs: " + newSourceDirs) //
val result = File(KFiles.findBuildScriptDir(), "Build.kt")
result.writeText(imports.joinToString("\n"))
result.appendText(code.joinToString("\n"))
return result
} }
private fun includeFileContent(context: KobaltContext, files: List<File>, out: ArrayList<String>) { class SplitBuildFile(val imports: List<String>, val code: List<String>)
private fun includeFileContent(context: KobaltContext, files: List<File>) : SplitBuildFile {
val imports = arrayListOf<String>()
val code = arrayListOf<String>()
files.forEach { files.forEach {
out.add("// $it") code.add("// $it")
out.addAll(applyProfiles(context, it.readLines())) val sbf = applyProfiles(context, it.readLines())
imports.addAll(sbf.imports)
code.addAll(sbf.code)
} }
return SplitBuildFile(imports, code)
} }
fun parseBuildScriptInfos(projectDir: String, context: KobaltContext) : List<AnalyzedBuildFile> { fun parseBuildScriptInfos(projectDir: String, context: KobaltContext) : List<AnalyzedBuildFile> {
val root = File(projectDir + KOBALT_SRC) val root = File(projectDir + File.separator + KOBALT_SRC)
val files = findBuildSourceFiles(projectDir) val files = findBuildSourceFiles(projectDir)
val toProcess = arrayListOf<File>().apply { addAll(files) } val toProcess = arrayListOf<File>().apply { addAll(files) }
// Parse each build file, associated it with a BuildScriptInfo if any found // Parse each build file, associated it with a BuildScriptInfo if any found
val analyzedFiles = arrayListOf<AnalyzedBuildFile>() val analyzedFiles = arrayListOf<AnalyzedBuildFile>()
toProcess.forEach { buildFile -> toProcess.forEach { buildFile ->
val lines = applyProfiles(context, buildFile.readLines()) val splitBuildFile = applyProfiles(context, buildFile.readLines())
val bsi = BlockExtractor(Pattern.compile("^val.*buildScript.*\\{"), '{', '}') val bsi = BlockExtractor(Pattern.compile("^val.*buildScript.*\\{"), '{', '}')
.extractBlock(buildFile, lines) .extractBlock(buildFile, (splitBuildFile.imports + splitBuildFile.code))
if (bsi != null) analyzedFiles.add(AnalyzedBuildFile(buildFile, bsi)) if (bsi != null) analyzedFiles.add(AnalyzedBuildFile(buildFile, bsi))
} }
@ -159,21 +180,23 @@ class BuildFiles @Inject constructor(val factory: BuildFileCompiler.IFactory,
val sourceFile = File(homeDir("t", "bf", "a.kt")).apply { val sourceFile = File(homeDir("t", "bf", "a.kt")).apply {
writeText(source) writeText(source)
} }
val buildScriptJarFile = File(homeDir("t", "preBuildScript-$counter.jar")).apply {
val buildScriptJarFile = File(KFiles.findBuildScriptDir(projectDir), "preBuildScript-$counter.jar").apply {
delete() delete()
} }
counter++ counter++
// //
// Compile it to preBuildScript-xxx.jar // Compile it to preBuildScript-xxx.jar
// //
kobaltLog(1, "Compiling $sourceFile to $buildScriptJarFile") kobaltLog(2, " Compiling buildScriptInfo $sourceFile to $buildScriptJarFile")
val taskResult = factory.create(BuildSources(root), context.pluginInfo).maybeCompileBuildFile(context, val taskResult = factory.create(BuildSources(root), context.pluginInfo).maybeCompileBuildFile(context,
listOf(sourceFile.path), listOf(sourceFile.path),
buildScriptJarFile, emptyList<URL>(), buildScriptJarFile, emptyList<URL>(),
context.internalContext.forceRecompile, context.internalContext.forceRecompile,
containsProfiles) containsProfiles)
println("Created $buildScriptJarFile") log(2, "Created $buildScriptJarFile")
// //
// Run preBuildScript.jar to initialize plugins and repos // Run preBuildScript.jar to initialize plugins and repos
@ -184,7 +207,6 @@ class BuildFiles @Inject constructor(val factory: BuildFileCompiler.IFactory,
newDirs.removeAll(currentDirs) newDirs.removeAll(currentDirs)
if (newDirs.any()) { if (newDirs.any()) {
af.buildScriptInfo.includedBuildSourceDirs.add(IncludedBuildSourceDir(section.start, newDirs)) af.buildScriptInfo.includedBuildSourceDirs.add(IncludedBuildSourceDir(section.start, newDirs))
println("*** ADDED DIRECTORIES " + newDirs)
} }
} }
@ -193,13 +215,16 @@ class BuildFiles @Inject constructor(val factory: BuildFileCompiler.IFactory,
return analyzedFiles return analyzedFiles
} }
private fun applyProfiles(context: KobaltContext, lines: List<String>): List<String> { private fun applyProfiles(context: KobaltContext, lines: List<String>): SplitBuildFile {
val result = arrayListOf<String>() val imports = arrayListOf<String>()
val code = arrayListOf<String>()
lines.forEach { line -> lines.forEach { line ->
result.add(correctProfileLine(context, line)) val isImport = line.startsWith("import")
val correctLine = correctProfileLine(context, line)
if (isImport) imports.add(correctLine)
else code.add(correctLine)
} }
return result return SplitBuildFile(imports, code)
} }
/** /**

View file

@ -1,28 +1,23 @@
package com.beust.kobalt.app package com.beust.kobalt.app
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.KobaltContext
import com.beust.kobalt.api.Project import com.beust.kobalt.api.Project
import com.beust.kobalt.internal.build.BuildFile
import com.beust.kobalt.internal.build.IBuildSources import com.beust.kobalt.internal.build.IBuildSources
import com.beust.kobalt.internal.build.VersionFile
import com.beust.kobalt.maven.DependencyManager import com.beust.kobalt.maven.DependencyManager
import com.beust.kobalt.misc.* import com.beust.kobalt.misc.BuildScriptInfo
import com.beust.kobalt.plugin.kotlin.kotlinCompilePrivate import com.beust.kobalt.misc.KFiles
import java.io.File import com.beust.kobalt.misc.kobaltLog
import com.beust.kobalt.misc.warn
import com.google.inject.Singleton
import java.net.URL import java.net.URL
import java.nio.charset.Charset
import java.util.regex.Pattern
class ParsedBuildFile(val buildSources: IBuildSources, val context: KobaltContext, val buildScriptUtil: BuildScriptUtil, @Singleton
class _ParsedBuildFile(val buildSources: IBuildSources, val context: KobaltContext, val buildScriptUtil:
BuildScriptUtil,
val dependencyManager: DependencyManager, val files: KFiles) { val dependencyManager: DependencyManager, val files: KFiles) {
private val profileLines = arrayListOf<String>() private val profileLines = arrayListOf<String>()
private val projects = arrayListOf<Project>() private val projects = arrayListOf<Project>()
private val activeProfiles = arrayListOf<String>() private val activeProfiles = arrayListOf<String>()
private val preBuildScriptJar = KFiles.findBuildScriptLocation(buildSources, "preBuildScript.jar")
private val preBuildScriptJarFile = File(preBuildScriptJar)
private val nonBuildScript = arrayListOf<String>() private val nonBuildScript = arrayListOf<String>()
var containsProfiles = false var containsProfiles = false
@ -30,7 +25,7 @@ class ParsedBuildFile(val buildSources: IBuildSources, val context: KobaltContex
/** /**
* Contains the addition of all the build files corrected with the active profiles and with * Contains the addition of all the build files corrected with the active profiles and with
* the buildScripts{} sections removed. * the buildScript{} sections removed.
*/ */
val nonBuildScriptCode : String get() = nonBuildScript.joinToString("\n") val nonBuildScriptCode : String get() = nonBuildScript.joinToString("\n")
@ -38,153 +33,182 @@ class ParsedBuildFile(val buildSources: IBuildSources, val context: KobaltContex
// Because profiles may have changed between two builds, we have to delete preBuildScript.jar file // Because profiles may have changed between two builds, we have to delete preBuildScript.jar file
// every time and then generate a new one (or skip that phase if no buildScript{} was found in the // every time and then generate a new one (or skip that phase if no buildScript{} was found in the
// buid files) // buid files)
preBuildScriptJarFile.delete() // preBuildScriptJarFile.delete()
val buildScriptInfo = parseBuildFile() // val buildScriptInfos = parseBuildFile()
// Only generate preBuildScript.jar if we found at least one buildScript{} // Only generate preBuildScript.jar if we found at least one buildScript{}
if (buildScriptInfo != null) { // if (buildScriptInfos.isNotEmpty()) {
parseBuildScriptInfo(buildScriptInfo) // parseBuildScriptInfo(buildScriptInfos)
} // }
// generateFinalBuildFile(buildScriptInfos)
} }
private fun parseBuildFile() : BuildScriptInfo? { // private fun generateFinalBuildFile(buildScriptInfos: List<BuildScriptInfo>) {
/** // //
* If the current line matches one of the profiles, turn the declaration into // // Write the build file to `nonBuildScript` excluding the buildScript{} directives since we already ran them
* val profile = true, otherwise return the same line // //
*/ // var lineNumber = 1
fun correctProfileLine(line: String): String { // buildSources.findSourceFiles().forEach { buildFile ->
(context.profiles as List<String>).forEach { profile -> // val buildScriptInfo = buildScriptInfos.find { it.file == buildFile }
val re = Regex(".*va[rl][ \\t]+([a-zA-Z0-9_]+)[ \\t]*.*profile\\(\\).*") // if (buildFile == buildScriptInfo?.file) {
val oldRe = Regex(".*va[rl][ \\t]+([a-zA-Z0-9_]+)[ \\t]*=[ \\t]*[tf][ra][ul][es].*") // println("Found file with buildScript in it: " + buildFile)
val matcher = re.matchEntire(line) // }
val oldMatcher = oldRe.matchEntire(line) // buildFile.forEachLine() { line ->
// if (buildScriptInfo == null || ! buildScriptInfo.isInSection(lineNumber)) {
// val cpl = correctProfileLine(line)
// if (cpl.startsWith("import")) nonBuildScript.add(0, cpl)
// else nonBuildScript.add(cpl)
// }
// lineNumber++
// }
// }
// }
fun profileMatch(matcher: MatchResult?) : Pair<Boolean, String?> { /**
val variable = if (matcher != null) matcher.groups[1]?.value else null * If the current line matches one of the profiles, turn the declaration into
return Pair(profile == variable, variable) * val profile = true, otherwise return the same line
} */
fun correctProfileLine(line: String): String {
(context.profiles as List<String>).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)
if ((matcher != null && matcher.groups.isNotEmpty()) fun profileMatch(matcher: MatchResult?) : Pair<Boolean, String?> {
|| (oldMatcher != null && oldMatcher.groups.isNotEmpty())) { val variable = if (matcher != null) matcher.groups[1]?.value else null
containsProfiles = true return Pair(profile == variable, variable)
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) { if ((matcher != null && matcher.groups.isNotEmpty())
warn("Old profile syntax detected for \"$line\"," + || (oldMatcher != null && oldMatcher.groups.isNotEmpty())) {
" please update to \"val $variable by profile()\"") 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
with("val $variable = true") { if (oldMatch.first) {
kobaltLog(2, "Activating profile $profile in build file") warn("Old profile syntax detected for \"$line\"," +
activeProfiles.add(profile) " please update to \"val $variable by profile()\"")
profileLines.add(this) }
return this
} with("val $variable = true") {
kobaltLog(2, "Activating profile $profile in build file")
activeProfiles.add(profile)
profileLines.add(this)
return this
} }
} }
} }
return line
} }
return line
fun applyProfiles(lines: List<String>): List<String> {
val result = arrayListOf<String>()
lines.forEach { line ->
result.add(correctProfileLine(line))
}
return result
}
//
// Take all the build files and adjust them with the active profiles
//
val buildWithCorrectProfiles = arrayListOf<String>()
buildSources.findSourceFiles().forEach {
buildWithCorrectProfiles.addAll(applyProfiles(it.readLines()))
}
//
// Now extract all the `buildScript{}` blocks from all these build files
//
val buildScriptInfo = BlockExtractor(Pattern.compile("^val.*buildScript.*\\{"), '{', '}')
.extractBlock(buildWithCorrectProfiles)
//
// Write the build file to `nonBuildScript` excluding the buildScript{} directives since we already ran them
//
var lineNumber = 1
buildSources.findSourceFiles().forEach { buildFile ->
buildFile.forEachLine() { line ->
if (buildScriptInfo == null || ! buildScriptInfo.isInSection(lineNumber)) {
val cpl = correctProfileLine(line)
if (cpl.startsWith("import")) nonBuildScript.add(0, cpl)
else nonBuildScript.add(cpl)
}
lineNumber++
}
}
return buildScriptInfo
} }
// private fun parseBuildFile() : List<BuildScriptInfo> {
// fun applyProfiles(lines: List<String>): List<String> {
// val result = arrayListOf<String>()
// lines.forEach { line ->
// result.add(correctProfileLine(line))
//
// }
// return result
// }
//
// //
// // Take all the build files and adjust them with the active profiles
// //
// val buildScriptInfos = arrayListOf<BuildScriptInfo>()
// val buildWithCorrectProfiles = arrayListOf<String>()
// val buildFiles = buildSources.findSourceFiles()
// buildFiles.forEach {
// buildWithCorrectProfiles.addAll(applyProfiles(it.readLines()))
//
// //
// // Now extract all the `buildScript{}` blocks from all these build files
// //
// val lsi = BlockExtractor(Pattern.compile("^val.*buildScript.*\\{"), '{', '}')
// .extractBlock(it, buildWithCorrectProfiles)
// if (lsi != null) buildScriptInfos.add(lsi)
// }
//
// return buildScriptInfos
// }
/** /**
* Generate preBuildScript.jar based on the buildScript{} found in the build files. * Generate preBuildScript.jar based on the buildScript{} found in the build files.
*/ */
private fun parseBuildScriptInfo(buildScriptInfo: BuildScriptInfo) { // private fun parseBuildScriptInfo(buildScriptInfos: List<BuildScriptInfo>) {
// // buildScriptInfos.forEach { buildScriptInfo ->
// Compile and run preBuildScriptCode, which contains all the plugins() calls extracted. This // buildScriptInfo.sections.forEach { section ->
// will add all the dynamic plugins found in this code to Plugins.dynamicPlugins // val buildScriptSection = (buildScriptInfo.imports +
// // buildScriptInfo.fullBuildFile.subList(section.start - 1, section.end))
val buildScriptSourceFile = KFiles.createTempBuildFileInTempDirectory(deleteOnExit = true) // .joinToString("\n")
buildScriptSourceFile.writeText(buildScriptInfo.content, Charset.defaultCharset()) // println("=== Compiling\n" + buildScriptSection + " for line " + (section.start - 1))
kobaltLog(2, "Saved " + KFiles.fixSlashes(buildScriptSourceFile.absolutePath)) //
// //
// // 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 buildScriptSourceFile = KFiles.createTempBuildFileInTempDirectory(deleteOnExit = true)
// buildScriptSourceFile.writeText(buildScriptSection, Charset.defaultCharset())
// kobaltLog(2, "Saved " + KFiles.fixSlashes(buildScriptSourceFile.absolutePath))
//
// //
// // Compile to preBuildScript.jar
// //
//
// val dir = preBuildScriptJarFile.parentFile
// dir.mkdirs()
// val bsJar = java.io.File(dir, "buildScript-" + section.start + ".jar")
// generateJarFile(context, listOf(buildScriptSourceFile.path), bsJar)
// VersionFile.generateVersionFile(preBuildScriptJarFile.parentFile)
// Kobalt.context!!.internalContext.buildFileOutOfDate = true
//
// //
// // Run preBuildScript.jar to initialize plugins and repos
// //
// val currentDirs = arrayListOf<String>().apply { addAll(Kobalt.buildSourceDirs) }
// projects.addAll(buildScriptUtil.runBuildScriptJarFile(bsJar, arrayListOf<URL>(), context))
// val newDirs = arrayListOf<String>().apply { addAll(Kobalt.buildSourceDirs) }
// newDirs.removeAll(currentDirs)
// buildScriptInfo.includedBuildSourceDirs.add(IncludedBuildSourceDir(section.start - 1, newDirs))
// println("*** ADDED DIRECTORIES " + newDirs)
// }
// }
//
// //
// // 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, sourceFiles: List<String>,
// Compile to preBuildScript.jar // buildScriptJarFile: File, originalFile: BuildFile? = null) {
// // //
preBuildScriptJarFile.parentFile.mkdirs() // // Compile the jar file
generateJarFile(context, listOf(buildScriptSourceFile.path), preBuildScriptJarFile) // //
VersionFile.generateVersionFile(preBuildScriptJarFile.parentFile) // val kotlinDeps = dependencyManager.calculateDependencies(null, context)
Kobalt.context!!.internalContext.buildFileOutOfDate = true // val deps: List<String> = kotlinDeps.map { it.jarFile.get().absolutePath }
// val outputJar = File(buildScriptJarFile.absolutePath)
// // val saved = context.internalContext.noIncrementalKotlin
// Run preBuildScript.jar to initialize plugins and repos // val result = kotlinCompilePrivate {
// // classpath(files.kobaltJar)
projects.addAll(buildScriptUtil.runBuildScriptJarFile(preBuildScriptJarFile, arrayListOf<URL>(), context)) // classpath(deps)
// sourceFiles(sourceFiles)
// // output = outputJar
// All the plug-ins are now in Plugins.dynamicPlugins, download them if they're not already // noIncrementalKotlin = true
// // }.compile(context = context)
Plugins.dynamicPlugins.forEach { // if (! result.success) {
pluginUrls.add(it.jarFile.get().toURI().toURL()) // val org = originalFile?.realPath ?: sourceFiles.joinToString(",")
} // throw KobaltException("Couldn't compile $org:\n" + result.errorMessage)
} // }
//
private fun generateJarFile(context: KobaltContext, sourceFiles: List<String>, // context.internalContext.noIncrementalKotlin = saved
buildScriptJarFile: File, originalFile: BuildFile? = null) { // }
//
// Compile the jar file
//
val kotlinDeps = dependencyManager.calculateDependencies(null, context)
val deps: List<String> = kotlinDeps.map { it.jarFile.get().absolutePath }
val outputJar = File(buildScriptJarFile.absolutePath)
val saved = context.internalContext.noIncrementalKotlin
val result = kotlinCompilePrivate {
classpath(files.kobaltJar)
classpath(deps)
sourceFiles(sourceFiles)
output = outputJar
noIncrementalKotlin = true
}.compile(context = context)
if (! result.success) {
val org = originalFile?.realPath ?: sourceFiles.joinToString(",")
throw KobaltException("Couldn't compile $org:\n" + result.errorMessage)
}
context.internalContext.noIncrementalKotlin = saved
}
} }