mirror of
https://github.com/ethauvin/kobalt.git
synced 2025-04-27 00:38:11 -07:00
Extract some logic into Profiles.
This commit is contained in:
parent
6694426eef
commit
8e46cdb83b
2 changed files with 146 additions and 107 deletions
|
@ -1,6 +1,7 @@
|
||||||
package com.beust.kobalt.app
|
package com.beust.kobalt.app
|
||||||
|
|
||||||
import com.beust.kobalt.Args
|
import com.beust.kobalt.Args
|
||||||
|
import com.beust.kobalt.KobaltException
|
||||||
import com.beust.kobalt.api.Kobalt
|
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
|
||||||
|
@ -15,6 +16,7 @@ import java.io.File
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.*
|
import java.nio.file.*
|
||||||
import java.nio.file.attribute.BasicFileAttributes
|
import java.nio.file.attribute.BasicFileAttributes
|
||||||
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,62 +31,31 @@ import java.util.regex.Pattern
|
||||||
|
|
||||||
* 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>) {
|
|
||||||
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.parseBuildFiles(homeDir("kotlin/klaxon/"), context)
|
|
||||||
}
|
|
||||||
|
|
||||||
class BuildFiles @Inject constructor(val factory: BuildFileCompiler.IFactory,
|
class BuildFiles @Inject constructor(val factory: BuildFileCompiler.IFactory,
|
||||||
val buildScriptUtil: BuildScriptUtil) {
|
val buildScriptUtil: BuildScriptUtil) {
|
||||||
private val profileLines = arrayListOf<String>()
|
|
||||||
private val activeProfiles = arrayListOf<String>()
|
|
||||||
var containsProfiles = false
|
var containsProfiles = false
|
||||||
val projects = arrayListOf<Project>()
|
val projects = arrayListOf<Project>()
|
||||||
|
|
||||||
private fun sourceDir(root: String) = File(KFiles.joinDir(root, "kobalt", "src"))
|
class BuildFileWithBuildScript(val file: File, val buildScriptInfo: BuildScriptInfo)
|
||||||
|
|
||||||
private fun findFiles(file: File, accept: (File) -> Boolean) : List<File> {
|
|
||||||
val result = arrayListOf<File>()
|
|
||||||
|
|
||||||
// It's possible for no build file to be present (e.g. testing)
|
|
||||||
if (file.exists()) {
|
|
||||||
Files.walkFileTree(Paths.get(file.path), object : SimpleFileVisitor<Path>() {
|
|
||||||
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)
|
|
||||||
|
|
||||||
private fun findBuildSourceFiles(root: String) : List<File> {
|
|
||||||
val result = arrayListOf<File>()
|
|
||||||
|
|
||||||
result.addAll(findFiles(sourceDir(root), { it.name.endsWith(".kt") }))
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the new Build.kt
|
* @return the new Build.kt
|
||||||
*/
|
*/
|
||||||
fun parseBuildFiles(projectDir: String, context: KobaltContext) : File {
|
fun parseBuildFiles(projectDir: String, context: KobaltContext) : File {
|
||||||
val map = hashMapOf<File, AnalyzedBuildFile>()
|
val profiles = Profiles(context)
|
||||||
|
val bsiMap = hashMapOf<File, BuildFileWithBuildScript>()
|
||||||
val newSourceDirs = arrayListOf<IncludedBuildSourceDir>()
|
val newSourceDirs = arrayListOf<IncludedBuildSourceDir>()
|
||||||
val filesWithBuildScript = parseBuildScriptInfos(projectDir, context)
|
//
|
||||||
|
// Create a map of File -> FileWithBuildScript
|
||||||
|
//
|
||||||
|
val filesWithBuildScript = parseBuildScriptInfos(projectDir, context, profiles)
|
||||||
filesWithBuildScript.forEach {
|
filesWithBuildScript.forEach {
|
||||||
map.put(it.file, it)
|
bsiMap.put(it.file, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Add any source directory we found
|
||||||
|
//
|
||||||
if (filesWithBuildScript.any()) {
|
if (filesWithBuildScript.any()) {
|
||||||
filesWithBuildScript.forEach { af ->
|
filesWithBuildScript.forEach { af ->
|
||||||
val bsi = af.buildScriptInfo
|
val bsi = af.buildScriptInfo
|
||||||
|
@ -103,22 +74,31 @@ class BuildFiles @Inject constructor(val factory: BuildFileCompiler.IFactory,
|
||||||
val sourceDir = sourceDir(projectDir)
|
val sourceDir = sourceDir(projectDir)
|
||||||
findFiles(sourceDir, { it.name.endsWith(".kt") }).forEach { file ->
|
findFiles(sourceDir, { it.name.endsWith(".kt") }).forEach { file ->
|
||||||
code.add("\n// $file")
|
code.add("\n// $file")
|
||||||
val analyzedFile = map[file]
|
val analyzedFile = bsiMap[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)) {
|
||||||
correctProfileLine(context, line).let { cpl ->
|
//
|
||||||
|
// Not a buildScriptInfo section, just copy the line as is
|
||||||
|
//
|
||||||
|
profiles.correctProfileLine(line).let { pair ->
|
||||||
|
val cpl = pair.first
|
||||||
|
containsProfiles = containsProfiles or pair.second
|
||||||
(if (cpl.startsWith("import")) imports else code).add(cpl)
|
(if (cpl.startsWith("import")) imports else code).add(cpl)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
//
|
||||||
|
// We're inside a buildScriptInfo section, see if it includes any buildSourceDirs
|
||||||
|
// and if it does, include these build files here
|
||||||
|
//
|
||||||
val isd = bsi.includedBuildSourceDirsForLine(lineNumber)
|
val isd = bsi.includedBuildSourceDirsForLine(lineNumber)
|
||||||
log(2, " 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
|
// If we found any new buildSourceDirs, all all the files found in these directories
|
||||||
// to the big Build.kt
|
// to the big Build.kt
|
||||||
val allBuildFiles = isd.flatMap { findBuildSourceFiles(projectDir + File.separator + it) }
|
val allBuildFiles = isd.flatMap { findBuildSourceFiles(projectDir + File.separator + it) }
|
||||||
val sbf = includeFileContent(context, allBuildFiles)
|
val sbf = includeFileContent(context, allBuildFiles, profiles)
|
||||||
imports.addAll(sbf.imports)
|
imports.addAll(sbf.imports)
|
||||||
code.addAll(sbf.code)
|
code.addAll(sbf.code)
|
||||||
}
|
}
|
||||||
|
@ -129,41 +109,49 @@ class BuildFiles @Inject constructor(val factory: BuildFileCompiler.IFactory,
|
||||||
//
|
//
|
||||||
// Create the big Build.kt out of the imports and code we've found so far
|
// Create the big Build.kt out of the imports and code we've found so far
|
||||||
//
|
//
|
||||||
val result = File(KFiles.findBuildScriptDir(projectDir), "Build.kt")
|
with(File(KFiles.findBuildScriptDir(projectDir), "Build.kt")) {
|
||||||
result.parentFile.mkdirs()
|
parentFile.mkdirs()
|
||||||
result.writeText(imports.joinToString("\n"))
|
val imp = arrayListOf<String>().apply {
|
||||||
result.appendText(code.joinToString("\n"))
|
addAll(imports)
|
||||||
|
}.distinct()
|
||||||
|
Collections.sort(imp)
|
||||||
|
writeText(imp.joinToString("\n"))
|
||||||
|
appendText(code.joinToString("\n"))
|
||||||
|
|
||||||
return result
|
return this
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SplitBuildFile(val imports: List<String>, val code: List<String>)
|
class SplitBuildFile(val imports: List<String>, val code: List<String>, val containsProfiles: Boolean)
|
||||||
|
|
||||||
private fun includeFileContent(context: KobaltContext, files: List<File>) : SplitBuildFile {
|
private fun includeFileContent(context: KobaltContext, files: List<File>, profiles: Profiles) : SplitBuildFile {
|
||||||
val imports = arrayListOf<String>()
|
val imports = arrayListOf<String>()
|
||||||
val code = arrayListOf<String>()
|
val code = arrayListOf<String>()
|
||||||
|
|
||||||
files.forEach {
|
files.forEach {
|
||||||
code.add("// $it")
|
code.add("// $it")
|
||||||
val sbf = applyProfiles(context, it.readLines())
|
val sbf = profiles.applyProfiles(it.readLines())
|
||||||
|
containsProfiles = containsProfiles or sbf.containsProfiles
|
||||||
imports.addAll(sbf.imports)
|
imports.addAll(sbf.imports)
|
||||||
code.addAll(sbf.code)
|
code.addAll(sbf.code)
|
||||||
}
|
}
|
||||||
return SplitBuildFile(imports, code)
|
return SplitBuildFile(imports, code, containsProfiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parseBuildScriptInfos(projectDir: String, context: KobaltContext) : List<AnalyzedBuildFile> {
|
fun parseBuildScriptInfos(projectDir: String, context: KobaltContext, profiles: Profiles)
|
||||||
|
: List<BuildFileWithBuildScript> {
|
||||||
val root = sourceDir(projectDir)
|
val root = sourceDir(projectDir)
|
||||||
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 and associate it with a BuildScriptInfo if any buildScript{} is found
|
||||||
val analyzedFiles = arrayListOf<AnalyzedBuildFile>()
|
val analyzedFiles = arrayListOf<BuildFileWithBuildScript>()
|
||||||
toProcess.forEach { buildFile ->
|
toProcess.forEach { buildFile ->
|
||||||
val splitBuildFile = applyProfiles(context, buildFile.readLines())
|
val splitBuildFile = profiles.applyProfiles(buildFile.readLines())
|
||||||
|
containsProfiles = containsProfiles or splitBuildFile.containsProfiles
|
||||||
val bsi = BlockExtractor(Pattern.compile("^val.*buildScript.*\\{"), '{', '}')
|
val bsi = BlockExtractor(Pattern.compile("^val.*buildScript.*\\{"), '{', '}')
|
||||||
.extractBlock(buildFile, (splitBuildFile.imports + splitBuildFile.code))
|
.extractBlock(buildFile, (splitBuildFile.imports + splitBuildFile.code))
|
||||||
if (bsi != null) analyzedFiles.add(AnalyzedBuildFile(buildFile, bsi))
|
if (bsi != null) analyzedFiles.add(BuildFileWithBuildScript(buildFile, bsi))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run every buildScriptInfo section in its own source file
|
// Run every buildScriptInfo section in its own source file
|
||||||
|
@ -195,6 +183,10 @@ class BuildFiles @Inject constructor(val factory: BuildFileCompiler.IFactory,
|
||||||
buildScriptJarFile, emptyList<URL>(),
|
buildScriptJarFile, emptyList<URL>(),
|
||||||
context.internalContext.forceRecompile,
|
context.internalContext.forceRecompile,
|
||||||
containsProfiles)
|
containsProfiles)
|
||||||
|
if (! taskResult.success) {
|
||||||
|
throw KobaltException("Couldn't compile $sourceFile: ${taskResult.errorMessage}")
|
||||||
|
}
|
||||||
|
|
||||||
log(2, "Created $buildScriptJarFile")
|
log(2, "Created $buildScriptJarFile")
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -214,56 +206,40 @@ class BuildFiles @Inject constructor(val factory: BuildFileCompiler.IFactory,
|
||||||
return analyzedFiles
|
return analyzedFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyProfiles(context: KobaltContext, lines: List<String>): SplitBuildFile {
|
private fun sourceDir(root: String) = File(KFiles.joinDir(root, "kobalt", "src"))
|
||||||
val imports = arrayListOf<String>()
|
|
||||||
val code = arrayListOf<String>()
|
|
||||||
lines.forEach { line ->
|
|
||||||
val isImport = line.startsWith("import")
|
|
||||||
val correctLine = correctProfileLine(context, line)
|
|
||||||
if (isImport) imports.add(correctLine)
|
|
||||||
else code.add(correctLine)
|
|
||||||
}
|
|
||||||
return SplitBuildFile(imports, code)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private fun findFiles(file: File, accept: (File) -> Boolean) : List<File> {
|
||||||
* If the current line matches one of the profiles, turn the declaration into
|
val result = arrayListOf<File>()
|
||||||
* val profile = true, otherwise return the same line
|
|
||||||
*/
|
|
||||||
fun correctProfileLine(context: KobaltContext, 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)
|
|
||||||
|
|
||||||
fun profileMatch(matcher: MatchResult?) : Pair<Boolean, String?> {
|
// It's possible for no build file to be present (e.g. testing)
|
||||||
val variable = if (matcher != null) matcher.groups[1]?.value else null
|
if (file.exists()) {
|
||||||
return Pair(profile == variable, variable)
|
Files.walkFileTree(Paths.get(file.path), object : SimpleFileVisitor<Path>() {
|
||||||
}
|
override fun visitFile(file: Path, attrs: BasicFileAttributes?): FileVisitResult {
|
||||||
|
if (accept(file.toFile())) result.add(file.toFile())
|
||||||
if ((matcher != null && matcher.groups.isNotEmpty())
|
return FileVisitResult.CONTINUE
|
||||||
|| (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
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
private fun findBuildSourceFiles(root: String) : List<File> {
|
||||||
|
val result = arrayListOf<File>()
|
||||||
|
|
||||||
|
result.addAll(findFiles(sourceDir(root), { it.name.endsWith(".kt") }))
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun main(argv: Array<String>) {
|
||||||
|
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.parseBuildFiles(homeDir("kotlin/klaxon/"), context)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
63
src/main/kotlin/com/beust/kobalt/app/Profiles.kt
Normal file
63
src/main/kotlin/com/beust/kobalt/app/Profiles.kt
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package com.beust.kobalt.app
|
||||||
|
|
||||||
|
import com.beust.kobalt.api.KobaltContext
|
||||||
|
import com.beust.kobalt.misc.kobaltLog
|
||||||
|
import com.beust.kobalt.misc.warn
|
||||||
|
|
||||||
|
class Profiles(val context: KobaltContext) {
|
||||||
|
fun applyProfiles(lines: List<String>): BuildFiles.SplitBuildFile {
|
||||||
|
val imports = arrayListOf<String>()
|
||||||
|
val code = arrayListOf<String>()
|
||||||
|
var containsProfiles = false
|
||||||
|
lines.forEach { line ->
|
||||||
|
val isImport = line.startsWith("import")
|
||||||
|
val correctLine = correctProfileLine(line)
|
||||||
|
containsProfiles = containsProfiles or correctLine.second
|
||||||
|
if (isImport) imports.add(correctLine.first)
|
||||||
|
else code.add(correctLine.first)
|
||||||
|
}
|
||||||
|
return BuildFiles.SplitBuildFile(imports, code, containsProfiles)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the current line matches one of the profiles, turn the declaration into
|
||||||
|
* val profile = true, otherwise return the same line.
|
||||||
|
*
|
||||||
|
* @return the line adjusted with the profile and a boolean indicating if a profile was detected in that line.
|
||||||
|
*/
|
||||||
|
fun correctProfileLine(line: String): Pair<String, Boolean> {
|
||||||
|
var containsProfiles = false
|
||||||
|
(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)
|
||||||
|
|
||||||
|
fun profileMatch(matcher: MatchResult?) : Pair<Boolean, String?> {
|
||||||
|
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")
|
||||||
|
return Pair(this, containsProfiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Pair(line, containsProfiles)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue