CLI: improve path relativization for compiler messages
On Windows, the absolute file for the root of the drive is `"C:\"`, which differs from the absolute file for any other directory, which doesn't end with `\`. This resulted in incorrect trimming of the first character of the path name in `descendantRelativeTo`. Also, do not use canonicalPath because there is no point in expanding symbolic links here. #KT-40979 Fixed
This commit is contained in:
committed by
teamcityserver
parent
f63dac26e6
commit
c898805ac5
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 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.integration
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.SystemInfo
|
||||||
|
import org.jetbrains.kotlin.utils.fileUtils.descendantRelativeTo
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.io.path.createSymbolicLinkPointingTo
|
||||||
|
|
||||||
|
class FilePathNormalizationTest : KotlinIntegrationTestBase() {
|
||||||
|
// This test checks that path normalization logic used in MessageRenderer.PLAIN_RELATIVE_PATHS works correctly.
|
||||||
|
// It compiles and runs a program in a separate process to be able to test how it works from different working directories.
|
||||||
|
// (It could be tested in the same process by changing the user.dir manually, but that could change behavior
|
||||||
|
// of other tests run in parallel.)
|
||||||
|
fun test() {
|
||||||
|
val descendantRelativeTo = File::descendantRelativeTo.name
|
||||||
|
val program = ProgramWithDependencyOnCompiler(
|
||||||
|
tmpdir, """
|
||||||
|
import org.jetbrains.kotlin.utils.fileUtils.$descendantRelativeTo
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
println(File(args[0]).$descendantRelativeTo(File(".").absoluteFile).path)
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
program.compile()
|
||||||
|
|
||||||
|
fun doTest(cwd: File, filePath: String, expectedWithForwardSlash: String) {
|
||||||
|
// We use "/" below for simplicity, but the actual paths in compiler messages use the system separator.
|
||||||
|
val expected = expectedWithForwardSlash.replace("/", File.separator)
|
||||||
|
val actual = program.run(cwd, filePath)
|
||||||
|
assertEquals("cwd: $cwd\nfilePath: $filePath\n", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
doTest(tmpdir, "a", "a")
|
||||||
|
doTest(tmpdir, "./a", "a")
|
||||||
|
doTest(tmpdir, "a/../b", "b")
|
||||||
|
doTest(tmpdir, "..", "..")
|
||||||
|
doTest(tmpdir, "../a", "../a")
|
||||||
|
doTest(tmpdir, tmpdir.path + "/a", "a")
|
||||||
|
doTest(tmpdir, tmpdir.path + "/./a", "a")
|
||||||
|
|
||||||
|
val root = File("/")
|
||||||
|
doTest(root, "test", "test")
|
||||||
|
|
||||||
|
doTest(root, tmpdir.path + "/a", (tmpdir.path + "/a").removePrefix(root.absolutePath))
|
||||||
|
|
||||||
|
doTest(
|
||||||
|
root, "./test",
|
||||||
|
if (SystemInfo.isWindows) "./test" else "test"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check symbolic links, but skip file systems which don't support them (e.g. Windows).
|
||||||
|
fun doSymbolicLinkTest(cwd: File, source: File, target: File, expected: String) {
|
||||||
|
val link = try {
|
||||||
|
source.toPath().createSymbolicLinkPointingTo(target.toPath()).toFile()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
if (link != null) {
|
||||||
|
doTest(cwd, link.path, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doSymbolicLinkTest(tmpdir, tmpdir / "a", tmpdir / "b", "a")
|
||||||
|
doSymbolicLinkTest(tmpdir / "unrelated", tmpdir / "a", tmpdir / "../b", tmpdir.path + "/a")
|
||||||
|
}
|
||||||
|
|
||||||
|
private operator fun File.div(x: String): File = File(this, x)
|
||||||
|
}
|
||||||
@@ -37,14 +37,9 @@ fun File.withReplacedExtensionOrNull(oldExt: String, newExt: String): File? {
|
|||||||
* If this file does not belong to the [base] directory, it is returned unchanged.
|
* If this file does not belong to the [base] directory, it is returned unchanged.
|
||||||
*/
|
*/
|
||||||
fun File.descendantRelativeTo(base: File): File {
|
fun File.descendantRelativeTo(base: File): File {
|
||||||
val prefix = base.canonicalPath
|
assert(base.isAbsolute) { "$base" }
|
||||||
val answer = this.canonicalPath
|
assert(base.isDirectory) { "$base" }
|
||||||
return if (answer.startsWith(prefix)) {
|
val cwd = base.normalize()
|
||||||
val prefixSize = prefix.length
|
val filePath = this.absoluteFile.normalize()
|
||||||
if (answer.length > prefixSize) {
|
return if (filePath.startsWith(cwd)) filePath.relativeTo(cwd) else this
|
||||||
File(answer.substring(prefixSize + 1))
|
}
|
||||||
} else File("")
|
|
||||||
} else {
|
|
||||||
this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user