blob: 4ec2875fc0aa4293cf9d917e76ab669d1455489e [file] [log] [blame]
/*
* Copyright 2010-2020 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.generators.util
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.text.StringUtil
import java.io.File
import java.io.IOException
import kotlin.io.path.*
object GeneratorsFileUtil {
val isTeamCityBuild: Boolean = System.getenv("TEAMCITY_VERSION") != null
val GENERATED_MESSAGE = """
/*
* This file was generated automatically
* DO NOT MODIFY IT MANUALLY
*/
""".trimIndent()
@OptIn(ExperimentalPathApi::class)
@JvmStatic
@JvmOverloads
@Throws(IOException::class)
fun writeFileIfContentChanged(file: File, newText: String, logNotChanged: Boolean = true, forbidGenerationOnTeamcity: Boolean = true) {
val parentFile = file.parentFile
if (!parentFile.exists()) {
if (forbidGenerationOnTeamcity) {
if (failOnTeamCity("Create dir `${parentFile.path}`")) return
}
if (parentFile.mkdirs()) {
println("Directory created: " + parentFile.absolutePath)
} else {
throw IllegalStateException("Cannot create directory: $parentFile")
}
}
if (!isFileContentChangedIgnoringLineSeparators(file, newText)) {
if (logNotChanged) {
println("Not changed: " + file.absolutePath)
}
return
}
if (forbidGenerationOnTeamcity) {
if (failOnTeamCity("Write file `${file.toPath()}`")) return
}
val useTempFile = !SystemInfo.isWindows
val targetFile = file.toPath()
val tempFile =
if (useTempFile) createTempDirectory(targetFile.name) / "${targetFile.name}.tmp" else targetFile
tempFile.writeText(newText, Charsets.UTF_8)
println("File written: ${tempFile.toAbsolutePath()}")
if (useTempFile) {
tempFile.moveTo(targetFile, overwrite = true)
println("Renamed $tempFile to $targetFile")
}
println()
}
private fun failOnTeamCity(message: String): Boolean {
if (!isTeamCityBuild) return false
fun String.escapeForTC(): String = StringBuilder(length).apply {
for (char in this@escapeForTC) {
append(
when (char) {
'|' -> "||"
'\'' -> "|'"
'\n' -> "|n"
'\r' -> "|r"
'[' -> "|["
']' -> "|]"
else -> char
}
)
}
}.toString()
val fullMessage = "[Re-generation needed!] $message\n" +
"Run correspondent (check the log above) Gradle task locally and commit changes."
println("##teamcity[buildProblem description='${fullMessage.escapeForTC()}']")
return true
}
fun isFileContentChangedIgnoringLineSeparators(file: File, content: String): Boolean {
val currentContent: String = try {
StringUtil.convertLineSeparators(file.readText(Charsets.UTF_8))
} catch (ignored: Throwable) {
return true
}
return StringUtil.convertLineSeparators(content) != currentContent
}
fun collectPreviouslyGeneratedFiles(generationPath: File): List<File> {
return generationPath.walkTopDown().filter {
it.isFile && it.readText().contains(GENERATED_MESSAGE)
}.toList()
}
fun removeExtraFilesFromPreviousGeneration(previouslyGeneratedFiles: List<File>, generatedFiles: List<File>) {
val generatedFilesPath = generatedFiles.mapTo(mutableSetOf()) { it.absolutePath }
for (file in previouslyGeneratedFiles) {
if (file.absolutePath !in generatedFilesPath) {
if (failOnTeamCity("File delete `${file.absolutePath}`")) continue
println("Deleted: ${file.absolutePath}")
file.delete()
}
}
}
}