mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 05:21:29 +07:00
DS-4175: Workaround for symlinks on WSL
``/usr/bin/python`` is symlink in many distros and can't be checked via 9P (``\\wsl$``). Hence, we provide ``Unknown`` for such cases. See issue comment for more info (cherry picked from commit 529eec2203f21e91c1ebc4f3df5d9b22ce4fcf3b) IJ-MR-98852 (cherry picked from commit e4954edbc1d87709387ebaaf0a2cac0f150c5d05) GitOrigin-RevId: d6b4b955a14e38fe217a20f33e6260c050af0984
This commit is contained in:
committed by
intellij-monorepo-bot
parent
03adfecf20
commit
2e6c91c3ea
@@ -13,6 +13,8 @@ import com.intellij.execution.wsl.listWindowsRoots
|
||||
import com.intellij.openapi.components.BaseState
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.util.SystemInfoRt
|
||||
import com.sun.jna.platform.win32.Kernel32.*
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.pathString
|
||||
@@ -65,13 +67,21 @@ class WslTargetEnvironmentConfiguration() : TargetEnvironmentConfiguration(WslTa
|
||||
}
|
||||
|
||||
override fun getPathInfo(targetPath: String): PathInfo? {
|
||||
// TODO: 9P is unreliable and we must migrate to some tool running in WSL (like ijent)
|
||||
assert(SystemInfoRt.isWindows) { "WSL is for Windows only" }
|
||||
val distribution = distribution
|
||||
if (distribution == null) {
|
||||
thisLogger().warn("No distribution, cant check path")
|
||||
return null
|
||||
}
|
||||
val pathInfo = PathInfo.getPathInfoForLocalPath(Paths.get(distribution.getWindowsPath(targetPath)))
|
||||
// We can't check if file is executable or not (we could but it is too heavy), so we set this flag
|
||||
val winLocalPath = Paths.get(distribution.getWindowsPath(targetPath))
|
||||
val fileAttributes = INSTANCE.GetFileAttributes(winLocalPath.pathString)
|
||||
// Reparse point is probably symlink, but could be dir or file. See https://github.com/microsoft/WSL/issues/5118
|
||||
if (fileAttributes != INVALID_FILE_ATTRIBUTES && fileAttributes.and(FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
return PathInfo.Unknown
|
||||
}
|
||||
val pathInfo = PathInfo.getPathInfoForLocalPath(winLocalPath)
|
||||
// We can't check if file is executable or not (we could, but it is too heavy), so we set this flag
|
||||
return if (pathInfo is PathInfo.RegularFile) pathInfo.copy(executable = true) else pathInfo
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ import kotlin.io.path.*
|
||||
* Abstraction over target path because target paths (like ssh or wsl) can't always be represented as [Path].
|
||||
*/
|
||||
sealed class PathInfo {
|
||||
/**
|
||||
* File system object exists, but we do not know what is it
|
||||
*/
|
||||
object Unknown: PathInfo()
|
||||
data class Directory(val empty: Boolean) : PathInfo()
|
||||
data class RegularFile(val executable: Boolean) : PathInfo()
|
||||
companion object {
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.testFramework.impl" scope="TEST" />
|
||||
<orderEntry type="library" name="kotlin-reflect" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.ui" scope="TEST" />
|
||||
<orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="HdrHistogram" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.text.matching" scope="TEST" />
|
||||
</component>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.execution.wsl
|
||||
|
||||
import com.intellij.execution.target.TargetConfigurationWithLocalFsAccess
|
||||
import com.intellij.execution.target.readableFs.PathInfo.*
|
||||
import com.intellij.execution.wsl.target.WslTargetEnvironmentConfiguration
|
||||
import com.intellij.testFramework.fixtures.TestFixtureRule
|
||||
import org.hamcrest.CoreMatchers.`is`
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.Matchers.anyOf
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.RuleChain
|
||||
|
||||
/**
|
||||
* Test [TargetConfigurationWithLocalFsAccess] for WSL
|
||||
*/
|
||||
class WslPathInfoTest {
|
||||
companion object {
|
||||
private val appRule = TestFixtureRule()
|
||||
private val wslRule = WslRule()
|
||||
private val wslTempDirRule = WslTempDirRule(wslRule)
|
||||
|
||||
@ClassRule
|
||||
@JvmField
|
||||
val ruleChain: RuleChain = RuleChain.outerRule(appRule).around(wslRule).around(wslTempDirRule)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPathInfo() {
|
||||
val target = WslTargetEnvironmentConfiguration(wslRule.wsl)
|
||||
assertNull("Path doesn't exist", target.getPathInfo("/path/doesn/exist"))
|
||||
assertThat("Directory", target.getPathInfo("/bin"), anyOf(`is`(Unknown), `is`(Directory(false))))
|
||||
assertThat("Directory", target.getPathInfo("/usr/bin"), anyOf(`is`(Unknown), `is`(Directory(false))))
|
||||
assertThat("Executable file", target.getPathInfo("/bin/sh"), anyOf(`is`(Unknown), `is`(RegularFile(true))))
|
||||
assertThat("Not executable file", target.getPathInfo("/etc/resolv.conf"), anyOf(`is`(Unknown), `is`(RegularFile(false))))
|
||||
val emptyDir = wslTempDirRule.linuxPath
|
||||
assertEquals("Empty dir", Directory(true), target.getPathInfo(emptyDir))
|
||||
}
|
||||
}
|
||||
@@ -146,7 +146,7 @@ public abstract class PythonSdkFlavor<D extends PyFlavorData> {
|
||||
}
|
||||
|
||||
/**
|
||||
* False means file is not executable, but true means it is executable or we do not know.
|
||||
* False means file is not executable, but true means it is executable, or we do not know.
|
||||
*
|
||||
* @param fullPath full path on target
|
||||
*/
|
||||
@@ -157,6 +157,9 @@ public abstract class PythonSdkFlavor<D extends PyFlavorData> {
|
||||
}
|
||||
if (targetEnvConfig instanceof TargetConfigurationReadableFs) {
|
||||
var fileInfo = ((TargetConfigurationReadableFs)targetEnvConfig).getPathInfo(fullPath);
|
||||
if (fileInfo instanceof PathInfo.Unknown) {
|
||||
return true; // We can't be sure if file is executable or not
|
||||
}
|
||||
return (fileInfo instanceof PathInfo.RegularFile) && (((PathInfo.RegularFile)fileInfo).getExecutable());
|
||||
}
|
||||
// We can't be sure if file is executable or not
|
||||
|
||||
@@ -32,6 +32,7 @@ fun validateExecutableFile(
|
||||
request: ValidationRequest
|
||||
): ValidationInfo? = request.validate {
|
||||
when (it) {
|
||||
is PathInfo.Unknown -> null
|
||||
is PathInfo.RegularFile -> if (it.executable) null else PyBundle.message("python.sdk.cannot.execute", request.path)
|
||||
is PathInfo.Directory -> PyBundle.message("python.sdk.cannot.execute", request.path)
|
||||
else -> PyBundle.message("python.sdk.file.not.found", request.path)
|
||||
@@ -46,6 +47,7 @@ fun validateEmptyDir(request: ValidationRequest,
|
||||
@Nls directoryNotEmpty: String
|
||||
): ValidationInfo? = request.validate {
|
||||
when (it) {
|
||||
is PathInfo.Unknown -> null
|
||||
is PathInfo.Directory -> if (it.empty) null else directoryNotEmpty
|
||||
is PathInfo.RegularFile -> notADirectory
|
||||
else -> null
|
||||
|
||||
@@ -22,7 +22,7 @@ class PyCondaCommand(
|
||||
if (pathInfo == null) {
|
||||
return Result.failure(Exception("$fullCondaPathOnTarget does not exist"))
|
||||
}
|
||||
if ((pathInfo as? PathInfo.RegularFile)?.executable != true) {
|
||||
if (pathInfo != PathInfo.Unknown && (pathInfo as? PathInfo.RegularFile)?.executable != true) {
|
||||
return Result.failure(Exception("$fullCondaPathOnTarget is not executable file"))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user