move WinAppxTools to sdk to make it usable by sdk validation, add test

GitOrigin-RevId: 01db8da280be654313cb78aa8d4b8ee9d52b7fa5
This commit is contained in:
Ilya.Kazakevich
2023-07-29 19:50:26 +00:00
committed by intellij-monorepo-bot
parent 09d3f7e6ee
commit 45dd3dd86e
4 changed files with 45 additions and 19 deletions
@@ -5,6 +5,7 @@
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
@@ -25,5 +26,7 @@
<orderEntry type="module" module-name="intellij.platform.execution" />
<orderEntry type="module" module-name="intellij.platform.remote.core" />
<orderEntry type="library" name="commons-collections" level="project" />
<orderEntry type="library" name="jna" level="project" />
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
</component>
</module>
@@ -1,16 +1,17 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.sdk.flavors
package com.jetbrains.python.sdk
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.SystemInfo
import com.intellij.util.io.isAncestor
import com.sun.jna.platform.win32.Kernel32
import com.sun.jna.platform.win32.Kernel32.*
import com.sun.jna.platform.win32.Ntifs
import com.sun.jna.platform.win32.WinioctlUtil
import com.sun.jna.ptr.IntByReference
import java.io.File
import java.io.FilenameFilter
import java.nio.ByteBuffer
import java.nio.file.Path
import kotlin.io.path.*
/**
* AppX packages installed to AppX volume (see ``Get-AppxDefaultVolume``, ``Get-AppxPackage``).
@@ -26,6 +27,8 @@ import java.nio.ByteBuffer
* There is no Java API to see reparse point destination, so we use JNA.
* This tool returns AppX name (either ``PythonSoftwareFoundation...`` or ``DesktopAppInstaller..``).
* We use it to check if ``python.exe`` is real python or WindowsStore mock.
*
* See [WinAppxTest]
*/
/**
@@ -39,22 +42,24 @@ private const val storeMarker = "DesktopAppInstaller"
* There may be several files linked to this product, we need only first.
* And for 3.7 there could be ``PythonSoftwareFoundation.Python.3.7_(SOME_OTHER_UID)``.
*/
fun getAppxFiles(expectedProduct: String, filePattern: Regex): Collection<File> =
userAppxFolder?.listFiles(FilenameFilter { _, name -> filePattern.matches(name) })
fun getAppxFiles(expectedProduct: String?, filePattern: Regex): Collection<Path> =
userAppxFolder?.listDirectoryEntries()
?.filter { filePattern.matches(it.name) }
?.sortedBy { it.nameWithoutExtension }
?.mapNotNull { file -> file.appxProduct?.let { product -> Pair(product, file) } }
?.toMap()
?.filterKeys { expectedProduct in it }
?.filterKeys { expectedProduct == null || expectedProduct in it }
?.values ?: emptyList()
/**
* If file is AppX reparse point link -- return its product name
*/
val File.appxProduct: String?
val Path.appxProduct: String?
get() {
if (parentFile?.equals(userAppxFolder) != true) return null
return getAppxTag(absolutePath)?.let {
val userAppxFolder = userAppxFolder ?: return null
if (!userAppxFolder.isAncestor(this)) return null
return getAppxTag(this)?.let {
if (storeMarker !in it) it else null
}
}
@@ -63,13 +68,13 @@ val File.appxProduct: String?
/**
* Path to ``%LOCALAPPDATA%\Microsoft\WindowsApps``
*/
private val userAppxFolder =
private val userAppxFolder: Path? =
if (!SystemInfo.isWin10OrNewer) {
null
}
else {
System.getenv("LOCALAPPDATA")?.let { localappdata ->
val appsPath = File(localappdata, "Microsoft//WindowsApps")
val appsPath = Path.of(localappdata, "Microsoft//WindowsApps")
if (appsPath.exists()) appsPath else null
}
}
@@ -99,11 +104,11 @@ See https://youtrack.jetbrains.com/issue/PY-43082
Output is unicode 16-LE
*/
private fun getAppxTag(path: String): String? {
private fun getAppxTag(path: Path): String? {
if (!SystemInfo.isWin10OrNewer) return null
val kernel = INSTANCE
val logger = Logger.getInstance(Kernel32::class.java)
val file = kernel.CreateFile(path, GENERIC_READ, FILE_SHARE_READ, null, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, null)
val file = kernel.CreateFile(path.pathString, GENERIC_READ, FILE_SHARE_READ, null, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, null)
if (file == INVALID_HANDLE_VALUE) {
logger.warn("Invalid handle for $path")
return null
@@ -0,0 +1,20 @@
package com.jetbrains.python.tests
import com.intellij.openapi.util.SystemInfoRt
import com.jetbrains.python.sdk.appxProduct
import com.jetbrains.python.sdk.getAppxFiles
import org.junit.Assert
import org.junit.Assume
import org.junit.Test
class WinAppxTest {
@Test
fun testAppx() {
Assume.assumeTrue("Win only", SystemInfoRt.isWindows)
val appxFiles = getAppxFiles(null, Regex(".+"))
Assume.assumeTrue("No appx apps found", appxFiles.isNotEmpty())
for (appxFile in appxFiles) {
Assert.assertNotNull("No appx product product for $appxFile", appxFile.appxProduct)
}
}
}
@@ -26,8 +26,8 @@ import java.io.File;
import java.nio.file.Path;
import java.util.*;
import static com.jetbrains.python.sdk.flavors.WinAppxToolsKt.getAppxFiles;
import static com.jetbrains.python.sdk.flavors.WinAppxToolsKt.getAppxProduct;
import static com.jetbrains.python.sdk.WinAppxToolsKt.getAppxFiles;
import static com.jetbrains.python.sdk.WinAppxToolsKt.getAppxProduct;
/**
* This class knows how to find python in Windows Registry according to
@@ -100,7 +100,6 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor<PyFlavorData.Empty> {
}
var path = sdk.getHomePath();
return path != null && isLocalPathValidPython(Path.of(path));
}
@Override
@@ -117,8 +116,7 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor<PyFlavorData.Empty> {
return true;
}
final File file = path.toFile();
return StringUtils.contains(getAppxProduct(file), APPX_PRODUCT) && isValidSdkPath(file);
return StringUtils.contains(getAppxProduct(path), APPX_PRODUCT) && isValidSdkPath(path.toFile());
}
@Override
@@ -160,7 +158,7 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor<PyFlavorData.Empty> {
@NotNull
private static Set<String> getPythonsFromStore() {
return ContainerUtil.map2Set(getAppxFiles(APPX_PRODUCT, PYTHON_EXE), file -> file.getAbsolutePath());
return ContainerUtil.map2Set(getAppxFiles(APPX_PRODUCT, PYTHON_EXE), file -> file.toAbsolutePath().toString());
}
@NotNull