From 706771829e886d52b8aebdb78f800b9af59e3d6f Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Fri, 31 Jul 2015 16:44:50 +0300 Subject: [PATCH] Processing sources for multiple platforms in one pass. --- .../Conditional.kt | 4 +- .../Preprocessor.kt | 184 ++++++++++++++---- .../SplitArguments.kt | 1 - 3 files changed, 149 insertions(+), 40 deletions(-) diff --git a/compiler/conditional-preprocessor/src/org.jetbrains.kotlin.preprocessor/Conditional.kt b/compiler/conditional-preprocessor/src/org.jetbrains.kotlin.preprocessor/Conditional.kt index ba0bb38e624..d0af9a3bd9e 100644 --- a/compiler/conditional-preprocessor/src/org.jetbrains.kotlin.preprocessor/Conditional.kt +++ b/compiler/conditional-preprocessor/src/org.jetbrains.kotlin.preprocessor/Conditional.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.preprocessor import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.getChildOfType interface Conditional { @@ -59,3 +58,6 @@ fun JetAnnotated.parseConditionalAnnotations(): List = }.filterNotNull() +val JetAnnotationEntry.typeReferenceName: String? get() = + (typeReference?.typeElement as? JetUserType)?.referencedName + diff --git a/compiler/conditional-preprocessor/src/org.jetbrains.kotlin.preprocessor/Preprocessor.kt b/compiler/conditional-preprocessor/src/org.jetbrains.kotlin.preprocessor/Preprocessor.kt index f7d6809913f..09196532756 100644 --- a/compiler/conditional-preprocessor/src/org.jetbrains.kotlin.preprocessor/Preprocessor.kt +++ b/compiler/conditional-preprocessor/src/org.jetbrains.kotlin.preprocessor/Preprocessor.kt @@ -18,12 +18,14 @@ package org.jetbrains.kotlin.preprocessor import com.intellij.openapi.Disposable import com.intellij.openapi.util.TextRange +import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.text.StringUtil import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.idea.JetFileType import org.jetbrains.kotlin.psi.* import java.io.File +import java.io.IOException fun main(args: Array) { @@ -31,6 +33,8 @@ fun main(args: Array) { val sourcePath = File(args.first()) + processRecursive(sourcePath, File("libraries/stdlib/target/jvm6")) + val configuration = CompilerConfiguration() val environment = KotlinCoreEnvironment.createForProduction(Disposable { }, configuration, emptyList()) @@ -38,77 +42,181 @@ fun main(args: Array) { val jetPsiFactory = JetPsiFactory(project) val fileType = JetFileType.INSTANCE - val evaluator = JvmPlatformEvaluator(version = 7) - //val evaluator = JsPlatformEvaluator() + val evaluators = listOf(JvmPlatformEvaluator(version = 7), JsPlatformEvaluator()) - println("Using condition evaluator: $evaluator") - (FileTreeWalk(sourcePath) as Sequence) + println("Using condition evaluator: $evaluators") + + (sourcePath.walk() as Sequence) .filter { it.isFile && it.extension == fileType.defaultExtension } .forEach { sourceFile -> val sourceText = sourceFile.readText().convertLineSeparators() val psiFile = jetPsiFactory.createFile(sourceFile.name, sourceText) println("$psiFile") - val visitor = EvaluatorVisitor(evaluator) + val visitor = CollectModificationsVisitor(evaluators) psiFile.accept(visitor) + val modifications = visitor.elementModifications - var prevIndex = 0 - val resultText = StringBuilder() - for ((range, selector) in visitor.elementModifications) { - resultText.append(sourceText, prevIndex, range.startOffset) - val rangeText = range.substring(sourceText) - val newValue = selector(rangeText) - if (newValue.isEmpty()) { - resultText.append("/* Not available on ${visitor.evaluator} */") - repeat(StringUtil.getLineBreakCount(rangeText)) { - resultText.append("\n") - } + for ((evaluator, list) in modifications) { + if (list.isNotEmpty()) { + val resultText = applyModifications(list, sourceText, evaluator) + println("Version of $sourceFile for $evaluator") + println(resultText) } - else { - resultText.append(newValue) - } - prevIndex = range.endOffset } - resultText.append(sourceText, prevIndex, sourceText.length()) - - println(resultText.toString()) - //processDeclaration("/", psiFile, evaluator) } } +private fun applyModifications(modifications: List, sourceText: String, evaluator: Evaluator): String { + var prevIndex = 0 + val result = StringBuilder() + for ((range, selector) in modifications) { + result.append(sourceText, prevIndex, range.startOffset) + val rangeText = range.substring(sourceText) + val newValue = selector(rangeText) + if (newValue.isEmpty()) { + result.append("/* Not available on $evaluator */") + repeat(StringUtil.getLineBreakCount(rangeText)) { + result.append("\n") + } + } + else { + result.append(newValue) + } + prevIndex = range.endOffset + } + result.append(sourceText, prevIndex, sourceText.length()) + return result.toString() +} -class EvaluatorVisitor(val evaluator: Evaluator) : JetTreeVisitorVoid() { - val elementModifications: MutableList String>> = arrayListOf() +data class Modification(val range: TextRange, val selector: (String) -> String) +class CollectModificationsVisitor(evaluators: List) : JetTreeVisitorVoid() { + + val elementModifications: Map> = + evaluators.toMap(selector = { it }, transform = { arrayListOf() }) override fun visitDeclaration(declaration: JetDeclaration) { super.visitDeclaration(declaration) val annotations = declaration.parseConditionalAnnotations() val name = (declaration as? JetNamedDeclaration)?.nameAsSafeName ?: declaration.name - val conditionalResult = evaluator(annotations) - println("declaration: ${declaration.javaClass.simpleName} $name, annotations: ${annotations.joinToString { it.toString() }}, evaluation result: $conditionalResult") - if (!conditionalResult) - elementModifications.add(declaration.textRange to {it -> ""}) - else { - val targetName = annotations.filterIsInstance().singleOrNull() - if (targetName != null) { - val placeholderName = (declaration as JetNamedDeclaration).nameAsName!!.asString() - val realName = targetName.name - elementModifications.add(declaration.textRange to { it -> it.replace(placeholderName, realName) }) + + val declResults = arrayListOf>() + for ((evaluator, modifications) in elementModifications) { + val conditionalResult = evaluator(annotations) + declResults.add(evaluator to conditionalResult) + + if (!conditionalResult) + modifications.add(Modification(declaration.textRange) {""}) + else { + val targetName = annotations.filterIsInstance().singleOrNull() + if (targetName != null) { + val placeholderName = (declaration as JetNamedDeclaration).nameAsName!!.asString() + val realName = targetName.name + modifications.add(Modification(declaration.textRange) { it.replace(placeholderName, realName) }) + } } + } + println("declaration: ${declaration.javaClass.simpleName} $name${if (annotations.isNotEmpty()) ", annotations: ${annotations.joinToString { it.toString() }}, evaluation result: $declResults" else ""}") } } -val JetAnnotationEntry.typeReferenceName: String? get() = - (typeReference?.typeElement as? JetUserType)?.referencedName +data class Profile(val name: String, val evaluator: Evaluator, val targetRoot: File) + + +public class Preprocessor { + + val fileType = JetFileType.INSTANCE + + + sealed class FileProcessingResult { + object Skip : FileProcessingResult() + object Copy : FileProcessingResult() + + class Modify(val resultText: String) : FileProcessingResult() + } + +// private fun processFile(sourceFile: File, evaluator: Evaluator): FileProcessingResult { +// if (sourceFile.extension != fileType.defaultExtension) +// return FileProcessingResult.Copy +// +// val sourceText = sourceFile.readText().convertLineSeparators() +// val psiFile = jetPsiFactory.createFile(sourceFile.name, sourceText) +// println("$psiFile") +// +// +// } +} + +private fun processDirectory(sourceRoot: File, targetRelativeRoot: File, profiles: List) { + + val (sourceFiles, sourceDirectories) = sourceRoot.listFiles().partition { !it.isDirectory } + + // TODO: keep processed file list for each profile +} + +private fun processRecursive(sourceRoot: File, targetRoot: File) { + val (sourceFiles, sourceDirectories) = sourceRoot.listFiles().partition { !it.isDirectory } + + val processedFiles = hashSetOf() + for (sourceFile in sourceFiles) + { + // TODO: only if .kt + val resultText = processFileText(sourceFile) + + // if (keepFile) + + val destFile = sourceFile.makeRelativeTo(sourceRoot, targetRoot) + // if no modifications — copy + + processedFiles += destFile + + if (destFile.exists()) { + if (destFile.isDirectory) + destFile.deleteRecursively() + else + if (destFile.isTextEqualTo(resultText)) + continue + } + destFile.writeText(resultText) + } + + for (sourceDir in sourceDirectories) { + val destDir = sourceDir.makeRelativeTo(sourceRoot, targetRoot) + if (!destDir.exists()) { + destDir.mkdirsOrFail() + } + else if (!destDir.isDirectory) { + destDir.delete() + } + processRecursive(sourceDir, destDir) + processedFiles += destDir + } + + targetRoot.listFiles().forEach { targetFile -> + if (!processedFiles.remove(processedFiles.find { FileUtil.filesEqual(it, targetFile) })) { + targetFile.deleteRecursively() + } + } +} + +private fun processFileText(sourceFile: File): String = sourceFile.readText() fun String.convertLineSeparators(): String = StringUtil.convertLineSeparators(this) +fun File.isTextEqualTo(content: String): Boolean = readText().lines() == content.lines() +fun File.makeRelativeTo(from: File, to: File) = File(to, relativeTo(from)) + +fun File.mkdirsOrFail() { + if (!mkdirs() && !exists()) { + throw IOException("Failed to create directory $this.") + } +} \ No newline at end of file diff --git a/compiler/conditional-preprocessor/src/org.jetbrains.kotlin.preprocessor/SplitArguments.kt b/compiler/conditional-preprocessor/src/org.jetbrains.kotlin.preprocessor/SplitArguments.kt index 031df9f3339..f6ad2614b49 100644 --- a/compiler/conditional-preprocessor/src/org.jetbrains.kotlin.preprocessor/SplitArguments.kt +++ b/compiler/conditional-preprocessor/src/org.jetbrains.kotlin.preprocessor/SplitArguments.kt @@ -18,7 +18,6 @@ package org.jetbrains.kotlin.preprocessor import org.jetbrains.kotlin.psi.JetStringTemplateEntry import org.jetbrains.kotlin.psi.ValueArgument -import org.jetbrains.kotlin.psi.psiUtil.getChildOfType import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType data class SplitArguments(val positional: List, val named: List)