mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 06:50:54 +07:00
IJPL-174608 platform: report linux distributive info in 'About'
(cherry picked from commit 85835ee6ad77b4ff6c708857ba69761068d2d16f) GitOrigin-RevId: 0cb3f7b3451be0dc18bcac07e79ed10be0fd80b7
This commit is contained in:
committed by
intellij-monorepo-bot
parent
98ef6fded6
commit
d43fe4c2e4
@@ -2014,6 +2014,7 @@ c:com.intellij.help.impl.HelpManagerImpl
|
||||
- invokeHelp(java.lang.String):V
|
||||
com.intellij.ide.AboutPopupDescriptionProvider
|
||||
- a:getDescription():java.lang.String
|
||||
- getExtendedDescription():java.lang.String
|
||||
f:com.intellij.ide.ChangeProjectIconPalette
|
||||
- com.intellij.util.ui.ColorPalette
|
||||
- <init>(I):V
|
||||
|
||||
@@ -10,15 +10,12 @@ import com.intellij.internal.statistic.eventLog.events.EventFields.Version
|
||||
import com.intellij.internal.statistic.eventLog.events.EventPair
|
||||
import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsagesCollector
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.util.UnixUtil.getGlibcVersion
|
||||
import com.intellij.util.UnixUtil
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.time.OffsetDateTime
|
||||
import java.util.*
|
||||
import kotlin.io.path.name
|
||||
import kotlin.streams.asSequence
|
||||
|
||||
internal class OsDataCollector : ApplicationUsagesCollector() {
|
||||
private val OS_NAMES = listOf("Windows", "Mac", "Linux", "FreeBSD", "Solaris", "Other")
|
||||
@@ -64,12 +61,12 @@ internal class OsDataCollector : ApplicationUsagesCollector() {
|
||||
TIMEZONE.metric(tz))
|
||||
when {
|
||||
SystemInfo.isLinux -> {
|
||||
val (distro, release) = getReleaseData()
|
||||
val isUnderWsl = detectIsUnderWsl()
|
||||
val glibcVersion = getGlibcVersion()
|
||||
val linuxMetrics = mutableListOf<EventPair<*>>(DISTRO.with(distro), RELEASE.with(release), UNDER_WSL.with(isUnderWsl))
|
||||
if (glibcVersion != null) {
|
||||
linuxMetrics.add(GLIBC.with(glibcVersion.toString()))
|
||||
val distroInfo = UnixUtil.getOsInfo()
|
||||
val linuxMetrics = mutableListOf<EventPair<*>>(DISTRO.with(DISTROS.coerce(distroInfo.distro)),
|
||||
RELEASE.with(distroInfo.release),
|
||||
UNDER_WSL.with(distroInfo.isUnderWsl))
|
||||
if (distroInfo.glibcVersion != null) {
|
||||
linuxMetrics.add(GLIBC.with(distroInfo.glibcVersion.toString()))
|
||||
}
|
||||
metrics += LINUX.metric(*linuxMetrics.toTypedArray())
|
||||
}
|
||||
@@ -98,32 +95,6 @@ internal class OsDataCollector : ApplicationUsagesCollector() {
|
||||
if (SystemInfo.isWindows) null
|
||||
else SHELLS.coerce(runCatching { System.getenv("SHELL")?.let { Path.of(it).name } }.getOrNull())
|
||||
|
||||
// https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||
private fun getReleaseData(): Pair<String, String?> =
|
||||
try {
|
||||
Files.lines(Path.of("/etc/os-release")).use { lines ->
|
||||
val fields = setOf("ID", "VERSION_ID")
|
||||
val values = lines.asSequence()
|
||||
.map { it.split('=') }
|
||||
.filter { it.size == 2 && it[0] in fields }
|
||||
.associate { it[0] to it[1].trim('"') }
|
||||
val distro = DISTROS.coerce(values["ID"])
|
||||
distro to values["VERSION_ID"]
|
||||
}
|
||||
}
|
||||
catch (ignored: IOException) {
|
||||
"unknown" to null
|
||||
}
|
||||
|
||||
private fun detectIsUnderWsl(): Boolean =
|
||||
try {
|
||||
@Suppress("SpellCheckingInspection") val kernel = Files.readString(Path.of("/proc/sys/kernel/osrelease"))
|
||||
kernel.contains("-microsoft-")
|
||||
}
|
||||
catch(e: IOException) {
|
||||
false
|
||||
}
|
||||
|
||||
private fun List<String>.coerce(value: String?): String =
|
||||
when (value) {
|
||||
null -> "unknown"
|
||||
|
||||
@@ -13,4 +13,9 @@ interface AboutPopupDescriptionProvider {
|
||||
* Return additional info which should be shown in the "About" dialog.
|
||||
*/
|
||||
fun getDescription(): @DetailedDescription String?
|
||||
|
||||
/**
|
||||
* Return additional info which should be copied into clipboard in the "About" dialog.
|
||||
*/
|
||||
fun getExtendedDescription(): @DetailedDescription String? = getDescription()
|
||||
}
|
||||
|
||||
52
platform/platform-impl/src/com/intellij/ide/OsDataLogger.kt
Normal file
52
platform/platform-impl/src/com/intellij/ide/OsDataLogger.kt
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ide
|
||||
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.util.NlsContexts.DetailedDescription
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.util.UnixUtil
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Service(Service.Level.APP)
|
||||
private class OsDataLogger(val coroutineScope: CoroutineScope) {
|
||||
internal var osInfoAboutString: String? = null
|
||||
|
||||
fun reportLinuxDistro() {
|
||||
coroutineScope.launch {
|
||||
if (SystemInfo.isLinux || SystemInfo.isFreeBSD) {
|
||||
val osInfo = UnixUtil.getOsInfo()
|
||||
|
||||
val name = osInfo.prettyName
|
||||
?: (osInfo.distro ?: "Unknown Distro").appendNotBlank(" ", osInfo.release)
|
||||
val info = name
|
||||
.appendNotBlank(" ", if (osInfo.isUnderWsl) "(in WSL)" else null)
|
||||
.appendNotBlank("; glibc: ", osInfo.glibcVersion?.toString())
|
||||
logger<OsDataLogger>().info(info)
|
||||
|
||||
osInfoAboutString = info
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.appendNotBlank(delimiter: String?, value: String?): String {
|
||||
if (value.isNullOrBlank()) return this
|
||||
return this + delimiter + value
|
||||
}
|
||||
}
|
||||
|
||||
internal class OsDataLoggerApplicationInitializedListener : ApplicationInitializedListener {
|
||||
override suspend fun execute() {
|
||||
service<OsDataLogger>().reportLinuxDistro()
|
||||
}
|
||||
}
|
||||
|
||||
internal class OsDataLoggerAboutPopupDescriptionProvider : AboutPopupDescriptionProvider {
|
||||
override fun getDescription(): @DetailedDescription String? = null
|
||||
|
||||
override fun getExtendedDescription(): @DetailedDescription String? {
|
||||
return service<OsDataLogger>().osInfoAboutString
|
||||
}
|
||||
}
|
||||
@@ -194,8 +194,7 @@ public final class AboutDialog extends DialogWrapper {
|
||||
myInfo.add(MessageFormat.format("VM: {0} by {1}", vmVersion, vmVendor));
|
||||
|
||||
//Print extra information from plugins
|
||||
ExtensionPointName<AboutPopupDescriptionProvider> ep = new ExtensionPointName<>("com.intellij.aboutPopupDescriptionProvider");
|
||||
for (AboutPopupDescriptionProvider aboutInfoProvider : ep.getExtensions()) {
|
||||
for (AboutPopupDescriptionProvider aboutInfoProvider : EP_NAME.getExtensionList()) {
|
||||
String description = aboutInfoProvider.getDescription();
|
||||
if (description != null) {
|
||||
lines.add(description);
|
||||
@@ -287,7 +286,7 @@ public final class AboutDialog extends DialogWrapper {
|
||||
text.append(SystemInfo.getOsNameAndVersion()).append('\n');
|
||||
|
||||
for (var aboutInfoProvider : EP_NAME.getExtensionList()) {
|
||||
var description = aboutInfoProvider.getDescription();
|
||||
var description = aboutInfoProvider.getExtendedDescription();
|
||||
if (description != null) {
|
||||
text.append(description).append('\n');
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.util
|
||||
|
||||
import com.intellij.execution.ExecutionException
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.util.ExecUtil
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.streams.asSequence
|
||||
|
||||
private const val COMMAND_LINE_TIMEOUT = 1000
|
||||
private const val LIBC_LIBRARY_NAME = "LIBC"
|
||||
@@ -14,21 +19,75 @@ private const val LIBC_LIBRARY_NAME = "LIBC"
|
||||
object UnixUtil {
|
||||
private val LOG = Logger.getInstance(UnixUtil::class.java.name)
|
||||
|
||||
fun getOsInfo(): OsInfo {
|
||||
val osRelease = getReleaseData()
|
||||
val isUnderWsl = detectIsUnderWsl()
|
||||
val glibcVersion = getGlibcVersion()
|
||||
return OsInfo(osRelease.distro, osRelease.release, osRelease.prettyName,
|
||||
isUnderWsl, glibcVersion)
|
||||
}
|
||||
|
||||
private fun detectIsUnderWsl(): Boolean {
|
||||
try {
|
||||
@Suppress("SpellCheckingInspection") val kernel = Files.readString(Path.of("/proc/sys/kernel/osrelease"))
|
||||
return kernel.contains("-microsoft-")
|
||||
}
|
||||
catch (_: IOException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||
private fun getReleaseData(): OsRelease {
|
||||
try {
|
||||
Files.lines(Path.of("/etc/os-release")).use { lines ->
|
||||
val fields = setOf("ID", "VERSION_ID", "PRETTY_NAME")
|
||||
val values = lines.asSequence()
|
||||
.map { it.split('=') }
|
||||
.filter { it.size == 2 && it[0] in fields }
|
||||
.associate { it[0] to it[1].trim('"') }
|
||||
return OsRelease(values["ID"], values["VERSION_ID"], values["PRETTY_NAME"])
|
||||
}
|
||||
}
|
||||
catch (_: IOException) {
|
||||
return OsRelease(null, null, null)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getGlibcVersion(): Double? {
|
||||
check(SystemInfo.isLinux) { "glibc version is only supported on Linux" }
|
||||
val commandLine = GeneralCommandLine("ldd", "--version")
|
||||
val output = ExecUtil.execAndGetOutput(commandLine, COMMAND_LINE_TIMEOUT)
|
||||
if (output.exitCode != 0) {
|
||||
LOG.debug("Failed to execute ${commandLine.commandLineString} exit code ${output.exitCode}")
|
||||
try {
|
||||
check(SystemInfo.isLinux) { "glibc version is only supported on Linux" }
|
||||
val commandLine = GeneralCommandLine("ldd", "--version")
|
||||
val output = ExecUtil.execAndGetOutput(commandLine, COMMAND_LINE_TIMEOUT)
|
||||
if (output.exitCode != 0) {
|
||||
LOG.debug("Failed to execute ${commandLine.commandLineString} exit code ${output.exitCode}")
|
||||
return null
|
||||
}
|
||||
val outputStr = output.stdout.split("\n").firstOrNull { it.contains(LIBC_LIBRARY_NAME, true) }
|
||||
if (outputStr == null) {
|
||||
LOG.debug("Failed to find $LIBC_LIBRARY_NAME in ${output.stdout}")
|
||||
return null
|
||||
}
|
||||
val version = outputStr.split(" ").lastOrNull { it.isNotEmpty() } ?: return null
|
||||
return version.toDoubleOrNull()
|
||||
}
|
||||
catch (_: ExecutionException) {
|
||||
return null
|
||||
}
|
||||
val outputStr = output.stdout.split("\n").firstOrNull { it.contains(LIBC_LIBRARY_NAME, true) }
|
||||
if (outputStr == null) {
|
||||
LOG.debug("Failed to find $LIBC_LIBRARY_NAME in ${output.stdout}")
|
||||
return null
|
||||
}
|
||||
val version = outputStr.split(" ").lastOrNull { it.isNotEmpty() } ?: return null
|
||||
return version.toDoubleOrNull()
|
||||
}
|
||||
|
||||
data class OsInfo(
|
||||
val distro: String?,
|
||||
val release: String?,
|
||||
val prettyName: String?,
|
||||
val isUnderWsl: Boolean,
|
||||
val glibcVersion: Double?,
|
||||
)
|
||||
|
||||
private data class OsRelease(
|
||||
val distro: String?,
|
||||
val release: String?,
|
||||
val prettyName: String?,
|
||||
)
|
||||
}
|
||||
@@ -1035,6 +1035,9 @@
|
||||
<statistics.collectorExtension implementation="com.intellij.ide.actions.ToolwindowFusEventFields"/>
|
||||
<statistics.collectorExtension implementation="com.intellij.ide.actions.DragEditorTabsFusEventFields"/>
|
||||
|
||||
<applicationInitializedListener implementation="com.intellij.ide.OsDataLoggerApplicationInitializedListener"/>
|
||||
<aboutPopupDescriptionProvider implementation="com.intellij.ide.OsDataLoggerAboutPopupDescriptionProvider"/>
|
||||
|
||||
<statistics.applicationUsagesCollector implementation="com.intellij.featureStatistics.fusCollectors.EAPUsageCollector"
|
||||
allowOnStartup="true"/>
|
||||
<statistic.eventLog.eventLoggerProvider implementation="com.intellij.internal.statistic.eventLog.fus.FeatureUsageEventLoggerProvider"/>
|
||||
|
||||
Reference in New Issue
Block a user