diff --git a/js/js.tests/test/org/jetbrains/kotlin/incremental/TestModel.kt b/js/js.tests/test/org/jetbrains/kotlin/incremental/TestModel.kt new file mode 100644 index 00000000000..e0e95aaf917 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/kotlin/incremental/TestModel.kt @@ -0,0 +1,225 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.incremental + +import java.io.File +import java.util.regex.Pattern + +class ProjectInfo(val name: String, val modules: List, val steps: List, val muted: Boolean) { + + class ProjectBuildStep(val id: Int, val order: List) +} + +class ModuleInfo(val moduleName: String) { + + sealed class Modification { + class Delete(private val fileName: String) : Modification() { + override fun execute(testDirectory: File, sourceDirectory: File) { + File(sourceDirectory, fileName).delete() + } + } + + class Update(private val fromFile: String, private val toFile: String) : Modification() { + override fun execute(testDirectory: File, sourceDirectory: File) { + val toFile = File(sourceDirectory, toFile) + if (toFile.exists()) { + toFile.delete() + } + + val fromFile = File(testDirectory, fromFile) + + fromFile.copyTo(toFile, overwrite = true) + } + } + + abstract fun execute(testDirectory: File, sourceDirectory: File) + } + + class ModuleStep(val id: Int, val dependencies: Collection, val dirtyFiles: Collection, val modifications: List) + + val steps = mutableListOf() +} + +const val MODULES_LIST = "MODULES" + +private val STEP_PATTERN = Pattern.compile("^\\s*STEP\\s+(\\d+)\\s*:?$") + +private val MODIFICATION_PATTERN = Pattern.compile("^([UD])\\s*:(.+)$") + +abstract class InfoParser(protected val infoFile: File) { + protected var lineCounter = 0 + protected val lines = infoFile.readLines() + + abstract fun parse(entryName: String): Info + + protected fun loop(lambda: (String) -> Boolean) { + while (lineCounter < lines.size) { + val line = lines[lineCounter] + if (line.isBlank()) { + ++lineCounter + continue + } + + if (lambda(line.trim())) { + break + } + } + } + + + protected fun diagnosticMessage(message: String, line: String): String { + return "$message in '$line' at ${infoFile.path}:${lineCounter - 1}" + } + + protected fun throwSyntaxError(line: String): Nothing { + throw AssertionError(diagnosticMessage("Syntax error", line)) + } + +} + +class ProjectInfoParser(infoFile: File) : InfoParser(infoFile) { + + + private fun parseStep(stepId: Int): ProjectInfo.ProjectBuildStep { + val order = mutableListOf() + + loop { line -> + val splitIndex = line.indexOf(':') + if (splitIndex < 0) throwSyntaxError(line) + + val splitted = line.split(":") + val op = splitted[0] + + if (op.matches(STEP_PATTERN.toRegex())) { + return@loop true // break the loop + } + + ++lineCounter + + when (op) { + "libs" -> { + val args = splitted[1] + args.split(",").filter { it.isNotBlank() }.forEach { order.add(it.trim()) } + } + else -> println(diagnosticMessage("Unknown op $op", line)) + } + + false + } + + return ProjectInfo.ProjectBuildStep(stepId, order) + } + + override fun parse(entryName: String): ProjectInfo { + val libraries = mutableListOf() + val steps = mutableListOf() + var muted = false + + loop { line -> + lineCounter++ + + if (line == "MUTED") { + muted = true + return@loop false + } + + val splitIndex = line.indexOf(':') + if (splitIndex < 0) throwSyntaxError(line) + + val splitted = line.split(":") + val op = splitted[0] + + when { + op == MODULES_LIST -> { + val arguments = splitted[1] + arguments.split(",").filter { it.isNotBlank() }.forEach { libraries.add(it.trim()) } + } + op.matches(STEP_PATTERN.toRegex()) -> { + val m = STEP_PATTERN.matcher(op) + if (!m.matches()) throwSyntaxError(line) + val stepId = Integer.parseInt(m.group(1)) + steps.add(parseStep(stepId)) + } + else -> println(diagnosticMessage("Unknown op $op", line)) + } + + false + } + + return ProjectInfo(entryName, libraries, steps, muted) + } +} + +class ModuleInfoParser(infoFile: File) : InfoParser(infoFile) { + + private fun parseModifications(): List { + val modifications = mutableListOf() + + loop { line -> + val matcher3 = MODIFICATION_PATTERN.matcher(line) + if (matcher3.matches()) { + lineCounter++ + val mop = matcher3.group(1) + val cmd = matcher3.group(2) + when (mop) { + "U" -> { + val (from, to) = cmd.split("->") + modifications.add(ModuleInfo.Modification.Update(from.trim(), to.trim())) + } + "D" -> modifications.add(ModuleInfo.Modification.Delete(cmd.trim())) + else -> error("Unknown modification $line") + } + false + } else { + true + } + } + + return modifications + } + + private fun parseStep(stepId: Int): ModuleInfo.ModuleStep { + val dependencies = mutableSetOf() + val dirtyFiles = mutableSetOf() + val modifications = mutableListOf() + + loop { line -> + if (line.matches(STEP_PATTERN.toRegex())) + return@loop true + lineCounter++ + + val opIndex = line.indexOf(':') + if (opIndex < 0) throwSyntaxError(line) + val op = line.substring(0, opIndex) + + when (op) { + "dependencies" -> line.substring(opIndex + 1).split(",").filter { it.isNotBlank() }.forEach { dependencies.add(it.trim()) } + "dirty" -> line.substring(opIndex + 1).split(",").filter { it.isNotBlank() }.forEach { dirtyFiles.add(it.trim()) } + "modifications" -> modifications.addAll(parseModifications()) + else -> println(diagnosticMessage("Unknown op $op", line)) + } + false + } + + return ModuleInfo.ModuleStep(stepId, dependencies, dirtyFiles, modifications) + } + + override fun parse(entryName: String): ModuleInfo { + val result = ModuleInfo(entryName) + + loop { line -> + lineCounter++ + val stepMatcher = STEP_PATTERN.matcher(line) + if (stepMatcher.matches()) { + val id = Integer.parseInt(stepMatcher.group(1)) + result.steps.add(parseStep(id)) + } + false + } + + return result + } +}