diff --git a/platform/ide-core-impl/intellij.platform.ide.core.impl.iml b/platform/ide-core-impl/intellij.platform.ide.core.impl.iml
index 9278634c725f..7544bcabdd97 100644
--- a/platform/ide-core-impl/intellij.platform.ide.core.impl.iml
+++ b/platform/ide-core-impl/intellij.platform.ide.core.impl.iml
@@ -26,5 +26,6 @@
+
\ No newline at end of file
diff --git a/platform/lang-impl/api-dump-unreviewed.txt b/platform/lang-impl/api-dump-unreviewed.txt
index ca00927a7387..bf4f78f6d440 100644
--- a/platform/lang-impl/api-dump-unreviewed.txt
+++ b/platform/lang-impl/api-dump-unreviewed.txt
@@ -28883,8 +28883,6 @@ f:com.intellij.util.PatternValuesIndex
- ():V
- s:buildStringIndex(java.util.Collection):java.util.Set
- s:processStringValues(java.util.Collection,com.intellij.util.PairProcessor):Z
-f:com.intellij.util.ProjectConfigurationUtil
-- sf:awaitCompleteProjectConfiguration(com.intellij.openapi.project.Project,kotlin.jvm.functions.Function1,kotlin.coroutines.Continuation):java.lang.Object
a:com.intellij.util.TextFieldCompletionProvider
- com.intellij.openapi.project.PossiblyDumbAware
- com.intellij.util.textCompletion.TextCompletionProvider
diff --git a/platform/platform-api/api-dump-unreviewed.txt b/platform/platform-api/api-dump-unreviewed.txt
index 304d92600afc..5bfd8b36276d 100644
--- a/platform/platform-api/api-dump-unreviewed.txt
+++ b/platform/platform-api/api-dump-unreviewed.txt
@@ -1411,17 +1411,6 @@ com.intellij.ide.warmup.WarmupConfigurator
- a:runWarmup(com.intellij.openapi.project.Project,kotlin.coroutines.Continuation):java.lang.Object
f:com.intellij.ide.warmup.WarmupConfigurator$Companion
- f:getEP_NAME():com.intellij.openapi.extensions.ExtensionPointName
-com.intellij.ide.warmup.WarmupLogger
-- sf:Companion:com.intellij.ide.warmup.WarmupLogger$Companion
-- a:logError(java.lang.String,java.lang.Throwable):V
-- a:logFatalError(java.lang.String,java.lang.Throwable):V
-- a:logInfo(java.lang.String):V
-f:com.intellij.ide.warmup.WarmupLogger$Companion
-- f:error(java.lang.String,java.lang.Throwable):V
-- bs:error$default(com.intellij.ide.warmup.WarmupLogger$Companion,java.lang.String,java.lang.Throwable,I,java.lang.Object):V
-- f:fatalError(java.lang.String,java.lang.Throwable):V
-- bs:fatalError$default(com.intellij.ide.warmup.WarmupLogger$Companion,java.lang.String,java.lang.Throwable,I,java.lang.Object):V
-- f:message(java.lang.String):V
com.intellij.ide.warmup.WarmupStatus
- sf:Companion:com.intellij.ide.warmup.WarmupStatus$Companion
f:com.intellij.ide.warmup.WarmupStatus$Companion
@@ -3494,6 +3483,71 @@ a:com.intellij.openapi.project.ProjectReloadState
- s:getInstance(com.intellij.openapi.project.Project):com.intellij.openapi.project.ProjectReloadState
- a:isAfterAutomaticReload():Z
- a:onBeforeAutomaticProjectReload():V
+f:com.intellij.openapi.project.configuration.HeadlessLogging
+- sf:INSTANCE:com.intellij.openapi.project.configuration.HeadlessLogging
+- f:logFatalError(java.lang.String):V
+- f:logFatalError(java.lang.Throwable):V
+- f:logMessage(java.lang.String):V
+- f:logWarning(java.lang.String):V
+- f:logWarning(java.lang.Throwable):V
+- f:loggingFlow():kotlinx.coroutines.flow.SharedFlow
+com.intellij.openapi.project.configuration.HeadlessLogging$HeadlessLoggingService
+- sf:Companion:com.intellij.openapi.project.configuration.HeadlessLogging$HeadlessLoggingService$Companion
+- a:logEntry(com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry):V
+- a:loggingFlow():kotlinx.coroutines.flow.SharedFlow
+f:com.intellij.openapi.project.configuration.HeadlessLogging$HeadlessLoggingService$Companion
+- f:getInstance():com.intellij.openapi.project.configuration.HeadlessLogging$HeadlessLoggingService
+f:com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry
+- (com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind,com.intellij.openapi.project.configuration.HeadlessLogging$Message):V
+- f:component1():com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
+- f:component2():com.intellij.openapi.project.configuration.HeadlessLogging$Message
+- f:copy(com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind,com.intellij.openapi.project.configuration.HeadlessLogging$Message):com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry
+- bs:copy$default(com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry,com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind,com.intellij.openapi.project.configuration.HeadlessLogging$Message,I,java.lang.Object):com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry
+- equals(java.lang.Object):Z
+- f:getMessage():com.intellij.openapi.project.configuration.HeadlessLogging$Message
+- f:getSeverity():com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
+- hashCode():I
+- toString():java.lang.String
+com.intellij.openapi.project.configuration.HeadlessLogging$Message
+- a:representation():java.lang.String
+f:com.intellij.openapi.project.configuration.HeadlessLogging$Message$Exception
+- com.intellij.openapi.project.configuration.HeadlessLogging$Message
+- bsf:box-impl(java.lang.Throwable):com.intellij.openapi.project.configuration.HeadlessLogging$Message$Exception
+- s:constructor-impl(java.lang.Throwable):java.lang.Throwable
+- equals(java.lang.Object):Z
+- s:equals-impl(java.lang.Throwable,java.lang.Object):Z
+- sf:equals-impl0(java.lang.Throwable,java.lang.Throwable):Z
+- f:getException():java.lang.Throwable
+- hashCode():I
+- s:hashCode-impl(java.lang.Throwable):I
+- representation():java.lang.String
+- s:representation-impl(java.lang.Throwable):java.lang.String
+- toString():java.lang.String
+- s:toString-impl(java.lang.Throwable):java.lang.String
+- bf:unbox-impl():java.lang.Throwable
+f:com.intellij.openapi.project.configuration.HeadlessLogging$Message$Plain
+- com.intellij.openapi.project.configuration.HeadlessLogging$Message
+- bsf:box-impl(java.lang.String):com.intellij.openapi.project.configuration.HeadlessLogging$Message$Plain
+- s:constructor-impl(java.lang.String):java.lang.String
+- equals(java.lang.Object):Z
+- s:equals-impl(java.lang.String,java.lang.Object):Z
+- sf:equals-impl0(java.lang.String,java.lang.String):Z
+- f:getMessage():java.lang.String
+- hashCode():I
+- s:hashCode-impl(java.lang.String):I
+- representation():java.lang.String
+- s:representation-impl(java.lang.String):java.lang.String
+- toString():java.lang.String
+- s:toString-impl(java.lang.String):java.lang.String
+- bf:unbox-impl():java.lang.String
+e:com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
+- java.lang.Enum
+- sf:Fatal:com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
+- sf:Info:com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
+- sf:Warning:com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
+- s:getEntries():kotlin.enums.EnumEntries
+- s:valueOf(java.lang.String):com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
+- s:values():com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind[]
com.intellij.openapi.project.impl.ProjectLifecycleListener
- sf:TOPIC:com.intellij.util.messages.Topic
- afterProjectClosed(com.intellij.openapi.project.Project):V
diff --git a/platform/platform-api/src/com/intellij/ide/warmup/WarmupLogger.kt b/platform/platform-api/src/com/intellij/ide/warmup/WarmupLogger.kt
deleted file mode 100644
index b11a63d21230..000000000000
--- a/platform/platform-api/src/com/intellij/ide/warmup/WarmupLogger.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
-package com.intellij.ide.warmup
-
-import com.intellij.openapi.extensions.ExtensionPointName
-
-/**
- * This class is used when warmup decides to relay important information to the user.
- */
-interface WarmupLogger {
-
- /**
- * Allows reporting information messages.
- */
- fun logInfo(message: String)
-
- /**
- * Allows reporting error messages to the user.
- * Use this if there is an important error, which is still recoverable.
- */
- fun logError(message: String, throwable: Throwable?)
-
- /**
- * Allows aborting the process of warmup if something non-recoverable happens.
- */
- fun logFatalError(message: String, throwable: Throwable?)
-
- companion object {
- private val EP_NAME: ExtensionPointName = ExtensionPointName("com.intellij.warmupLogger")
-
- fun message(message: String) {
- for (warmupLogger in EP_NAME.extensionList) {
- warmupLogger.logInfo(message)
- }
- }
-
- fun error(message: String, throwable: Throwable? = null) {
- for (warmupLogger in EP_NAME.extensionList) {
- warmupLogger.logError(message, throwable)
- }
- }
-
- fun fatalError(message: String, throwable: Throwable? = null) {
- for (warmupLogger in EP_NAME.extensionList) {
- warmupLogger.logFatalError(message, throwable)
- }
- }
- }
-}
\ No newline at end of file
diff --git a/platform/platform-api/src/com/intellij/ide/warmup/WarmupStatus.kt b/platform/platform-api/src/com/intellij/ide/warmup/WarmupStatus.kt
index 3fc3ae7023c2..81f2e4d46893 100644
--- a/platform/platform-api/src/com/intellij/ide/warmup/WarmupStatus.kt
+++ b/platform/platform-api/src/com/intellij/ide/warmup/WarmupStatus.kt
@@ -1,7 +1,6 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.warmup
-import com.intellij.openapi.application.Application
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.util.Key
import org.jetbrains.annotations.ApiStatus.Internal
diff --git a/platform/platform-api/src/com/intellij/openapi/project/configuration/HeadlessLogging.kt b/platform/platform-api/src/com/intellij/openapi/project/configuration/HeadlessLogging.kt
new file mode 100644
index 000000000000..e0be22f0e2db
--- /dev/null
+++ b/platform/platform-api/src/com/intellij/openapi/project/configuration/HeadlessLogging.kt
@@ -0,0 +1,89 @@
+// 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.openapi.project.configuration
+
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.configuration.HeadlessLogging.Message.Plain
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharedFlow
+import javax.print.attribute.standard.Severity
+
+/**
+ * A sink for information that may be useful during command-line execution of the IDE.
+ *
+ * Each subsystem can use the methods in this class to send some information that may be of value to the end user.
+ *
+ * We do not provide a distinction between the severity levels here, as all the reported messages are of interest for the user.
+ * Severity differentiation makes sense for investigation of internal problems, and for this reason we have internal logging.
+ */
+object HeadlessLogging {
+
+ /**
+ * Reports an informational message about the IDE.
+ *
+ * This kind of messages is important to keep track of progress and to assure the user that the execution is not stuck.
+ */
+ fun logMessage(message: String) = HeadlessLoggingService.getInstance().logEntry(LogEntry(SeverityKind.Info, Plain(message)))
+
+ /**
+ * Reports a warning message.
+ *
+ * The collector of the message can decide to react differently to warnings, i.e., print them in different colors in the console,
+ * or collect and report them batched at the end of the configuration.
+ */
+ fun logWarning(message: String) = HeadlessLoggingService.getInstance().logEntry(LogEntry(SeverityKind.Warning, Plain(message)))
+
+ /**
+ * Reports a non-fatal exception. An exception is non-fatal if the IDE can recover from it.
+ *
+ * This kind of error does not stop the execution of the IDE.
+ */
+ fun logWarning(exception: Throwable) = HeadlessLoggingService.getInstance().logEntry(LogEntry(SeverityKind.Warning, Message.Exception(exception)))
+
+ /**
+ * Reports a fatal error during the execution. An error is fatal if the user's actions are required in order to fix the problem.
+ *
+ * This kind of errors **has influence on control flow**: once the IDE reports a fatal error,
+ * the headless execution may stop. An example of this error is a failure in the build system import.
+ * This
+ */
+ fun logFatalError(exception: Throwable) = HeadlessLoggingService.getInstance().logEntry(LogEntry(SeverityKind.Fatal, Message.Exception(exception)))
+ fun logFatalError(message: String) = HeadlessLoggingService.getInstance().logEntry(LogEntry(SeverityKind.Fatal, Plain(message)))
+
+ /**
+ * Retrieves a hot flow with messages about the IDE.
+ */
+ fun loggingFlow(): SharedFlow = HeadlessLoggingService.getInstance().loggingFlow()
+
+ enum class SeverityKind {
+ Info,
+ Warning,
+ Fatal
+ }
+
+ sealed interface Message {
+ fun representation(): String
+
+ @JvmInline
+ value class Plain(val message: String) : Message {
+ override fun representation(): String = message
+ }
+
+ @JvmInline
+ value class Exception(val exception: Throwable) : Message {
+ override fun representation(): String = exception.toString()
+ }
+ }
+
+ data class LogEntry(val severity: SeverityKind, val message: Message)
+
+ interface HeadlessLoggingService {
+ companion object {
+ fun getInstance(): HeadlessLoggingService {
+ return ApplicationManager.getApplication().getService(HeadlessLoggingService::class.java)
+ }
+ }
+
+ fun logEntry(logEntry: LogEntry)
+ fun loggingFlow(): SharedFlow
+ }
+}
\ No newline at end of file
diff --git a/platform/platform-impl/api-dump-unreviewed.txt b/platform/platform-impl/api-dump-unreviewed.txt
index 09c1470f6a76..834ac7864cbb 100644
--- a/platform/platform-impl/api-dump-unreviewed.txt
+++ b/platform/platform-impl/api-dump-unreviewed.txt
@@ -18065,6 +18065,29 @@ f:com.intellij.openapi.project.ScanningTracker
- awaitConfiguration(com.intellij.openapi.project.Project,kotlin.coroutines.Continuation):java.lang.Object
- getPresentableName():java.lang.String
- isInProgress(com.intellij.openapi.project.Project,kotlin.coroutines.Continuation):java.lang.Object
+f:com.intellij.openapi.project.configuration.ChannelingProgressIndicator
+- com.intellij.openapi.progress.util.ProgressIndicatorBase
+- (java.lang.String):V
+- setFraction(D):V
+- setIndeterminate(Z):V
+- setText(java.lang.String):V
+- setText2(java.lang.String):V
+f:com.intellij.openapi.project.configuration.EmptyLoggingService
+- com.intellij.openapi.project.configuration.HeadlessLogging$HeadlessLoggingService
+- ():V
+- logEntry(com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry):V
+- loggingFlow():kotlinx.coroutines.flow.SharedFlow
+f:com.intellij.openapi.project.configuration.HeadlessLoggingServiceImpl
+- com.intellij.openapi.project.configuration.HeadlessLogging$HeadlessLoggingService
+- ():V
+- logEntry(com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry):V
+- loggingFlow():kotlinx.coroutines.flow.SharedFlow
+f:com.intellij.openapi.project.configuration.HeadlessProgressListener
+- ():V
+- afterTaskFinished(com.intellij.openapi.progress.Task):V
+- beforeTaskStart(com.intellij.openapi.progress.Task,com.intellij.openapi.progress.ProgressIndicator):V
+f:com.intellij.openapi.project.configuration.ProjectConfigurationUtil
+- sf:awaitCompleteProjectConfiguration(com.intellij.openapi.project.Project,kotlin.jvm.functions.Function1,kotlin.coroutines.Continuation):java.lang.Object
a:com.intellij.openapi.project.impl.DefaultProjectTimed
- com.intellij.util.TimedReference
- dispose():V
diff --git a/platform/platform-impl/src/com/intellij/ide/CommandLineInspectionProjectConfigurator.java b/platform/platform-impl/src/com/intellij/ide/CommandLineInspectionProjectConfigurator.java
index eb98f03e8baf..129e8e88990c 100644
--- a/platform/platform-impl/src/com/intellij/ide/CommandLineInspectionProjectConfigurator.java
+++ b/platform/platform-impl/src/com/intellij/ide/CommandLineInspectionProjectConfigurator.java
@@ -7,6 +7,7 @@ import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -17,7 +18,9 @@ import java.util.function.Predicate;
/**
* Extension point that helps prepare project for opening in headless or automated environments.
* Implementation must be stateless.
+ * Consider using com.intellij.platform.backend.observation.ActivityTracker
*/
+@ApiStatus.Obsolete(since = "2024.1")
public interface CommandLineInspectionProjectConfigurator {
ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.commandLineInspectionProjectConfigurator");
diff --git a/platform/platform-impl/src/com/intellij/ide/environment/impl/HeadlessEnvironmentService.kt b/platform/platform-impl/src/com/intellij/ide/environment/impl/HeadlessEnvironmentService.kt
index a1789a4809a1..4bdfe6fcb665 100644
--- a/platform/platform-impl/src/com/intellij/ide/environment/impl/HeadlessEnvironmentService.kt
+++ b/platform/platform-impl/src/com/intellij/ide/environment/impl/HeadlessEnvironmentService.kt
@@ -6,9 +6,9 @@ import com.fasterxml.jackson.databind.type.CollectionType
import com.intellij.ide.environment.EnvironmentKey
import com.intellij.ide.environment.EnvironmentService
import com.intellij.ide.environment.description
-import com.intellij.ide.warmup.WarmupLogger
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.logger
+import com.intellij.openapi.project.configuration.HeadlessLogging
import kotlinx.coroutines.*
import java.io.IOException
@@ -22,7 +22,8 @@ class HeadlessEnvironmentService(scope: CoroutineScope) : BaseEnvironmentService
return getEnvironmentValueOrNull(key)
?: run {
val throwable = MissingEnvironmentKeyException(key)
- WarmupLogger.fatalError("Insufficient project configuration", MissingEnvironmentKeyException(key))
+ LOG.error(throwable)
+ HeadlessLogging.logFatalError(MissingEnvironmentKeyException(key))
throw throwable
}
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/project/configuration/ChannelingProgressIndicator.kt b/platform/platform-impl/src/com/intellij/openapi/project/configuration/ChannelingProgressIndicator.kt
new file mode 100644
index 000000000000..a0ce5996c2cf
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/project/configuration/ChannelingProgressIndicator.kt
@@ -0,0 +1,67 @@
+// 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.openapi.project.configuration
+
+import com.intellij.openapi.progress.util.ProgressIndicatorBase
+import java.util.concurrent.atomic.AtomicReference
+
+class ChannelingProgressIndicator(private val prefix: String) : ProgressIndicatorBase() {
+ override fun setIndeterminate(indeterminate: Boolean) {
+ super.setIndeterminate(indeterminate)
+ }
+
+ private val lastState = AtomicReference(0.0)
+
+ override fun setFraction(fraction: Double) {
+ super.setFraction(fraction)
+ val lastStateValue = lastState.get()
+ if (fraction - lastStateValue > 0.2) {
+ // we debounce the messages by 20%
+ if (lastState.compareAndSet(lastStateValue, fraction)) {
+ offerState()
+ }
+ }
+ }
+
+ override fun setText(text: String?) {
+ super.setText(text)
+ super.setText2("")
+ offerState()
+ }
+
+ override fun setText2(text: String?) {
+ super.setText2(text)
+ }
+
+ private fun trimProgressTextAndNullize(s: String?) = s?.trim()?.trimEnd('.', '\u2026', ' ')?.takeIf { it.isNotBlank() }
+
+ private fun progressStateText(fraction: Double?, text: String?, details: String?): String? {
+ val text = trimProgressTextAndNullize(text)
+ val text2 = trimProgressTextAndNullize(details)
+ if (text.isNullOrBlank() && text2.isNullOrBlank()) {
+ return null
+ }
+
+ val shortText = text ?: ""
+ val verboseText = shortText + (text2?.let { " ($it)" } ?: "")
+ if (shortText.isBlank() || fraction == null) {
+ return verboseText
+ }
+
+ val v = (100.0 * fraction).toInt()
+ val total = 18
+ val completed = (total * fraction).toInt().coerceAtLeast(0)
+ val d = ".".repeat(completed).padEnd(total, ' ')
+ val verboseReport = verboseText.take(100).padEnd(105) + "$d $v%"
+ return verboseReport
+ }
+
+ private fun offerState() {
+ val progressState = progressStateText(
+ fraction = if (isIndeterminate) null else fraction,
+ text = text,
+ details = text2,
+ ) ?: return
+ val actualPrefix = if (prefix.isEmpty()) "" else "[$prefix]: "
+ HeadlessLogging.logMessage(actualPrefix + progressState)
+ }
+}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/openapi/project/configuration/EmptyLoggingService.kt b/platform/platform-impl/src/com/intellij/openapi/project/configuration/EmptyLoggingService.kt
new file mode 100644
index 000000000000..43f1bb90b275
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/project/configuration/EmptyLoggingService.kt
@@ -0,0 +1,15 @@
+// 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.openapi.project.configuration
+
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+
+class EmptyLoggingService : HeadlessLogging.HeadlessLoggingService {
+ private val emptyFlow = MutableSharedFlow()
+ override fun logEntry(exception: HeadlessLogging.LogEntry) {
+ }
+
+ override fun loggingFlow(): SharedFlow {
+ return emptyFlow
+ }
+}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/openapi/project/configuration/HeadlessLoggingServiceImpl.kt b/platform/platform-impl/src/com/intellij/openapi/project/configuration/HeadlessLoggingServiceImpl.kt
new file mode 100644
index 000000000000..57b28b6ad682
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/project/configuration/HeadlessLoggingServiceImpl.kt
@@ -0,0 +1,36 @@
+// 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.openapi.project.configuration
+
+import com.intellij.openapi.diagnostic.thisLogger
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+
+class HeadlessLoggingServiceImpl : HeadlessLogging.HeadlessLoggingService {
+
+ private val flow: MutableSharedFlow = MutableSharedFlow(replay = 0, extraBufferCapacity = 1024, onBufferOverflow = BufferOverflow.SUSPEND)
+
+
+ override fun logEntry(logEntry: HeadlessLogging.LogEntry) {
+ emitLogEntry(logEntry)
+ }
+
+ private fun emitLogEntry(entry: HeadlessLogging.LogEntry) {
+ var internalLoggingPerformed = false
+ while (!flow.tryEmit(entry)) {
+ // we have some slow collectors
+ // there is nothing the platform can do, other than report this incident
+ if (!internalLoggingPerformed) {
+ thisLogger().warn("Cannot log message: ${entry}. \n" +
+ "Headless logger has exhausted the buffer of messages. Please speed up the listeners of the Headless logger.")
+ internalLoggingPerformed = true
+ }
+ // Nevertheless, it is important to deliver complete information to the user.
+ Thread.sleep(100)
+ }
+ }
+
+ override fun loggingFlow(): SharedFlow {
+ return flow
+ }
+}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/openapi/project/configuration/HeadlessProgressListener.kt b/platform/platform-impl/src/com/intellij/openapi/project/configuration/HeadlessProgressListener.kt
new file mode 100644
index 000000000000..accc5fc47ed9
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/project/configuration/HeadlessProgressListener.kt
@@ -0,0 +1,32 @@
+// 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.openapi.project.configuration
+
+import com.intellij.openapi.progress.ProgressIndicator
+import com.intellij.openapi.progress.ProgressManagerListener
+import com.intellij.openapi.progress.Task
+import com.intellij.openapi.progress.util.ProgressIndicatorBase
+import com.intellij.openapi.util.text.Formats
+import com.intellij.openapi.wm.ex.ProgressIndicatorEx
+import java.util.concurrent.ConcurrentHashMap
+
+class HeadlessProgressListener : ProgressManagerListener {
+
+ private val taskDurationMap = ConcurrentHashMap()
+
+ override fun beforeTaskStart(task: Task, indicator: ProgressIndicator) {
+ if (indicator !is ProgressIndicatorEx) {
+ return
+ }
+ HeadlessLogging.logMessage("[IDE]: Task '${task.title}' started")
+ taskDurationMap[System.identityHashCode(task)] = System.currentTimeMillis()
+ indicator.addStateDelegate(ChannelingProgressIndicator("IDE"))
+ super.beforeTaskStart(task, indicator)
+ }
+
+ override fun afterTaskFinished(task: Task) {
+ val currentTime = System.currentTimeMillis()
+ val startTime = taskDurationMap.remove(System.identityHashCode(task))
+ val elapsedTimeSuffix = if (startTime == null) "" else " in ${Formats.formatDuration(currentTime - startTime)}"
+ HeadlessLogging.logMessage("[IDE]: Task '${task.title}' ended" + elapsedTimeSuffix)
+ }
+}
\ No newline at end of file
diff --git a/platform/lang-impl/src/com/intellij/util/ProjectConfiguration.kt b/platform/platform-impl/src/com/intellij/openapi/project/configuration/ProjectConfiguration.kt
similarity index 92%
rename from platform/lang-impl/src/com/intellij/util/ProjectConfiguration.kt
rename to platform/platform-impl/src/com/intellij/openapi/project/configuration/ProjectConfiguration.kt
index 9b72000b9017..f8f9dd604bfa 100644
--- a/platform/lang-impl/src/com/intellij/util/ProjectConfiguration.kt
+++ b/platform/platform-impl/src/com/intellij/openapi/project/configuration/ProjectConfiguration.kt
@@ -1,9 +1,10 @@
-@file:JvmName("ProjectConfigurationUtil")
// 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
+@file:JvmName("ProjectConfigurationUtil")
+package com.intellij.openapi.project.configuration
+
+import com.intellij.configurationStore.StoreUtil.saveSettings
import com.intellij.configurationStore.saveProjectsAndApp
-import com.intellij.configurationStore.saveSettings
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.platform.backend.observation.Observation
diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml
index e1d092747e44..c7b6a158e41e 100644
--- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml
+++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml
@@ -511,7 +511,6 @@
-
diff --git a/platform/platform-resources/src/META-INF/PlatformLangComponents.xml b/platform/platform-resources/src/META-INF/PlatformLangComponents.xml
index 35c327831520..cc6ba85d13d4 100644
--- a/platform/platform-resources/src/META-INF/PlatformLangComponents.xml
+++ b/platform/platform-resources/src/META-INF/PlatformLangComponents.xml
@@ -109,6 +109,10 @@
+
+
+
+
diff --git a/platform/warmup/api-dump-unreviewed.txt b/platform/warmup/api-dump-unreviewed.txt
index 44e08c5033fe..af26052ecade 100644
--- a/platform/warmup/api-dump-unreviewed.txt
+++ b/platform/warmup/api-dump-unreviewed.txt
@@ -25,12 +25,6 @@ f:com.intellij.warmup.util.ConsoleLog
- bs:error$default(com.intellij.warmup.util.ConsoleLog,java.lang.String,java.lang.Throwable,I,java.lang.Object):V
- f:info(java.lang.String):V
- f:warn(java.lang.String):V
-f:com.intellij.warmup.util.DefaultWarmupLogger
-- com.intellij.ide.warmup.WarmupLogger
-- ():V
-- logError(java.lang.String,java.lang.Throwable):V
-- logFatalError(java.lang.String,java.lang.Throwable):V
-- logInfo(java.lang.String):V
com.intellij.warmup.util.HeadlessConfigurableArgs
- a:getPathToConfigurationFile():java.nio.file.Path
c:com.intellij.warmup.util.HeadlessConfigurableArgsImpl
@@ -74,10 +68,6 @@ f:com.intellij.warmup.util.WarmupLogger
- f:logError(java.lang.String,java.lang.Throwable):V
- bs:logError$default(com.intellij.warmup.util.WarmupLogger,java.lang.String,java.lang.Throwable,I,java.lang.Object):V
- f:logInfo(java.lang.String):V
-f:com.intellij.warmup.util.WarmupProgressListener
-- ():V
-- afterTaskFinished(com.intellij.openapi.progress.Task):V
-- beforeTaskStart(com.intellij.openapi.progress.Task,com.intellij.openapi.progress.ProgressIndicator):V
com.intellij.warmup.util.WarmupProjectArgs
- com.intellij.warmup.util.OpenProjectArgs
- a:getBuild():Z
diff --git a/platform/warmup/resources/META-INF/PlatformWarmup.xml b/platform/warmup/resources/META-INF/PlatformWarmup.xml
index 978a74de02a0..fc867bfa0a11 100644
--- a/platform/warmup/resources/META-INF/PlatformWarmup.xml
+++ b/platform/warmup/resources/META-INF/PlatformWarmup.xml
@@ -6,7 +6,6 @@
-
\ No newline at end of file
diff --git a/platform/warmup/src/com/intellij/warmup/ProjectCachesWarmup.kt b/platform/warmup/src/com/intellij/warmup/ProjectCachesWarmup.kt
index 46a868e87671..075be8489ffc 100644
--- a/platform/warmup/src/com/intellij/warmup/ProjectCachesWarmup.kt
+++ b/platform/warmup/src/com/intellij/warmup/ProjectCachesWarmup.kt
@@ -58,7 +58,7 @@ internal class ProjectCachesWarmup : ModernApplicationStarter() {
configureVcsIndexing(commandArgs)
runWarmupActivity {
- initLogger(args)
+ val loggingJob = initLogger(args)
waitIndexInitialization()
val project = try {
importOrOpenProjectAsync(commandArgs)
@@ -79,6 +79,7 @@ internal class ProjectCachesWarmup : ModernApplicationStarter() {
waitForRefreshQueue()
}
ProjectManagerEx.getInstanceEx().forceCloseProjectAsync(project, save = true)
+ loggingJob.cancel()
}
exitApplication()
@@ -225,7 +226,7 @@ private suspend fun buildProject(project: Project, commandArgs: WarmupProjectArg
}
}
-private suspend fun runWarmupActivity(action: suspend () -> Unit) {
+private suspend fun runWarmupActivity(action: suspend CoroutineScope.() -> Unit) {
val indexedFiles = installStatisticsCollector()
WarmupStatus.statusChanged(WarmupStatus.InProgress)
try {
diff --git a/platform/warmup/src/com/intellij/warmup/util/ActivityBasedWarmup.kt b/platform/warmup/src/com/intellij/warmup/util/ActivityBasedWarmup.kt
index d0e875217549..c6ccb25c28f3 100644
--- a/platform/warmup/src/com/intellij/warmup/util/ActivityBasedWarmup.kt
+++ b/platform/warmup/src/com/intellij/warmup/util/ActivityBasedWarmup.kt
@@ -4,14 +4,12 @@ package com.intellij.warmup.util
import com.intellij.ide.impl.OpenProjectTask
import com.intellij.ide.impl.ProjectUtil
import com.intellij.openapi.project.Project
-import com.intellij.util.awaitCompleteProjectConfiguration
+import com.intellij.openapi.project.configuration.HeadlessLogging
+import com.intellij.openapi.project.configuration.awaitCompleteProjectConfiguration
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.job
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.selects.select
suspend fun configureProjectByActivities(args: OpenProjectArgs) : Project {
@@ -39,24 +37,15 @@ suspend fun configureProjectByActivities(args: OpenProjectArgs) : Project {
return project
}
-
-internal val abortFlow : MutableStateFlow = MutableStateFlow(null)
-
private fun CoroutineScope.getFailureDeferred() : Deferred {
return async {
- while (coroutineContext.job.isActive) {
- val message = abortFlow.value
- if (message != null) {
- return@async message
- }
- delay(500)
- }
- error("unreachable")
+ val firstFatal = HeadlessLogging.loggingFlow().first { (level, _) -> level == HeadlessLogging.SeverityKind.Fatal }
+ firstFatal.message.representation()
}
}
private fun CoroutineScope.getConfigurationDeferred(project : Project) : Deferred {
- return async(start = CoroutineStart.UNDISPATCHED) {
+ return async {
withLoggingProgressReporter {
project.awaitCompleteProjectConfiguration(WarmupLogger::logInfo)
}
diff --git a/platform/warmup/src/com/intellij/warmup/util/DefaultWarmupLogger.kt b/platform/warmup/src/com/intellij/warmup/util/DefaultWarmupLogger.kt
deleted file mode 100644
index 847de564c9f3..000000000000
--- a/platform/warmup/src/com/intellij/warmup/util/DefaultWarmupLogger.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
-package com.intellij.warmup.util
-
-import com.intellij.ide.warmup.WarmupLogger
-
-class DefaultWarmupLogger : WarmupLogger {
- override fun logInfo(message: String) {
- com.intellij.warmup.util.WarmupLogger.logInfo(message)
- }
-
- override fun logError(message: String, throwable: Throwable?) {
- com.intellij.warmup.util.WarmupLogger.logError(message, throwable)
- }
-
- override fun logFatalError(message: String, throwable: Throwable?) {
- logError(message, throwable)
- abortFlow.tryEmit(message)
- }
-}
\ No newline at end of file
diff --git a/platform/warmup/src/com/intellij/warmup/util/logging.kt b/platform/warmup/src/com/intellij/warmup/util/logging.kt
index 64b95ba29613..b91a692ff499 100644
--- a/platform/warmup/src/com/intellij/warmup/util/logging.kt
+++ b/platform/warmup/src/com/intellij/warmup/util/logging.kt
@@ -2,26 +2,19 @@
package com.intellij.warmup.util
import com.intellij.diagnostic.ThreadDumper
-import com.intellij.ide.warmup.WarmupStatus
import com.intellij.openapi.application.ApplicationInfo
-import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ApplicationNamesInfo
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.components.Service
-import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.JulLogger
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.RollingFileHandler
import com.intellij.openapi.progress.ProgressIndicator
-import com.intellij.openapi.progress.ProgressManagerListener
-import com.intellij.openapi.progress.Task
-import com.intellij.openapi.progress.util.ProgressIndicatorBase
+import com.intellij.openapi.project.configuration.ChannelingProgressIndicator
+import com.intellij.openapi.project.configuration.HeadlessLogging
import com.intellij.openapi.util.io.findOrCreateFile
-import com.intellij.openapi.util.text.Formats
-import com.intellij.openapi.wm.ex.ProgressIndicatorEx
import com.intellij.platform.ide.bootstrap.logEssentialInfoAboutIde
import com.intellij.platform.util.progress.createProgressPipe
-import com.intellij.util.application
import com.intellij.util.lazyPub
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.BufferOverflow
@@ -42,28 +35,33 @@ import kotlin.time.Duration.Companion.milliseconds
object WarmupLogger {
fun logInfo(message: String) {
ConsoleLog.info(message)
- warmupLogger?.info(message)
}
fun logError(message: String, t: Throwable? = null) {
ConsoleLog.error(message, t)
- warmupLogger?.error(message, t)
}
internal fun logStructured(message: StructuredMessage) {
ConsoleLog.info(message.fullMessage)
- warmupLogger?.info(message.contractedMessage)
}
}
-internal fun initLogger(args: List) {
- val logger = warmupLogger ?: return
+internal fun CoroutineScope.initLogger(args: List): Job {
+ val logger = warmupLogger ?: return Job()
val info = ApplicationInfo.getInstance()
val buildDate = SimpleDateFormat("dd MMM yyyy HH:mm", Locale.US).format(info.buildDate.time)
logEssentialInfoAboutIde(log = logger, appInfo = info, args = args)
- val connection = ApplicationManager.getApplication().messageBus.connect()
- connection.subscribe(ProgressManagerListener.TOPIC, WarmupProgressListener())
logger.info("IDE: ${ApplicationNamesInfo.getInstance().fullProductName} (build #${info.build.asString()}, ${buildDate})")
+ return launch {
+ HeadlessLogging.loggingFlow().collect { (level, message) ->
+ val messageRepresentation = message.representation()
+ when (level) {
+ HeadlessLogging.SeverityKind.Info -> ConsoleLog.info(messageRepresentation)
+ HeadlessLogging.SeverityKind.Warning -> ConsoleLog.warn(messageRepresentation)
+ HeadlessLogging.SeverityKind.Fatal -> ConsoleLog.error(messageRepresentation)
+ }
+ }
+ }
}
@@ -99,42 +97,7 @@ internal fun progressStateText(fraction: Double?, text: String?, details: String
return StructuredMessage(verboseReport, shortReport)
}
-private class ChannelingProgressIndicator(private val prefix: String) : ProgressIndicatorBase() {
- override fun setIndeterminate(indeterminate: Boolean) {
- super.setIndeterminate(indeterminate)
- offerState()
- }
- override fun setFraction(fraction: Double) {
- super.setFraction(fraction)
- offerState()
- }
-
- override fun setText(text: String?) {
- super.setText(text)
- super.setText2("")
- offerState()
- }
-
- override fun setText2(text: String?) {
- super.setText2(text)
- offerState()
- }
-
- private fun offerState() {
- val messages = ApplicationManager.getApplication().service().messages
- val progressState = progressStateText(
- fraction = if (isIndeterminate) null else fraction,
- text = text,
- details = text2,
- ) ?: return
- val actualPrefix = if (prefix.isEmpty()) "" else "[$prefix]: "
- messages.tryEmit(progressState.copy(
- contractedMessage = actualPrefix + progressState.contractedMessage,
- fullMessage = actualPrefix + progressState.fullMessage,
- ))
- }
-}
/**
* Installs a progress reporter that sends the information about progress to the stdout instead of UI.
@@ -191,13 +154,8 @@ private val loggerFactory: WarmupLoggerFactory? by lazyPub {
}
private val warmupLogger: Logger? by lazyPub {
- if (WarmupStatus.currentStatus() != WarmupStatus.InProgress) {
- null
- }
- else {
- val instance = loggerFactory?.getLoggerInstance("Warmup") ?: return@lazyPub null
- instance
- }
+ val instance = loggerFactory?.getLoggerInstance("Warmup") ?: return@lazyPub null
+ instance
}
internal data class StructuredMessage(
@@ -222,28 +180,6 @@ private class WarmupLoggingService(scope: CoroutineScope) {
}
}
-class WarmupProgressListener : ProgressManagerListener {
-
- private val taskDurationMap = ConcurrentHashMap()
-
-
- override fun beforeTaskStart(task: Task, indicator: ProgressIndicator) {
- if (indicator !is ProgressIndicatorEx) {
- return
- }
- WarmupLogger.logInfo("[IDE]: Task '${task.title}' started")
- taskDurationMap[System.identityHashCode(task)] = System.currentTimeMillis()
- indicator.addStateDelegate(ChannelingProgressIndicator("IDE"))
- super.beforeTaskStart(task, indicator)
- }
-
- override fun afterTaskFinished(task: Task) {
- val currentTime = System.currentTimeMillis()
- val startTime = taskDurationMap.remove(System.identityHashCode(task))
- val elapsedTimeSuffix = if (startTime == null) "" else " in ${Formats.formatDuration(currentTime - startTime)}"
- WarmupLogger.logInfo("[IDE]: Task '${task.title}' ended" + elapsedTimeSuffix)
- }
-}
internal fun dumpThreadsAfterConfiguration() {
val dump = ThreadDumper.getThreadDumpInfo(ThreadDumper.getThreadInfos(), false)
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleWarmupConfigurator.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleWarmupConfigurator.kt
index 48123d419a60..2cd042dda40f 100644
--- a/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleWarmupConfigurator.kt
+++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleWarmupConfigurator.kt
@@ -6,26 +6,23 @@ import com.intellij.ide.CommandLineProgressReporterElement
import com.intellij.ide.environment.EnvironmentService
import com.intellij.ide.impl.ProjectOpenKeyProvider
import com.intellij.ide.warmup.WarmupConfigurator
-import com.intellij.ide.warmup.WarmupLogger
-import com.intellij.ide.warmup.WarmupStatus
-import com.intellij.openapi.application.PathManager
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.externalSystem.autoimport.AutoImportProjectTracker
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener
-import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType
import com.intellij.openapi.externalSystem.service.notification.ExternalSystemProgressNotificationManager
import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataImportListener
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.progress.blockingContextScope
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
-import com.intellij.util.io.createParentDirectories
import kotlinx.coroutines.CoroutineScope
-import org.jetbrains.plugins.gradle.service.notification.ExternalAnnotationsProgressNotificationListener
import org.jetbrains.plugins.gradle.service.notification.ExternalAnnotationsProgressNotificationManager
-import org.jetbrains.plugins.gradle.service.notification.ExternalAnnotationsTaskId
+import org.jetbrains.plugins.gradle.service.project.GradleHeadlessLoggingProjectActivity.LoggingNotificationListener
+import org.jetbrains.plugins.gradle.service.project.GradleHeadlessLoggingProjectActivity.StateExternalAnnotationNotificationListener
+import org.jetbrains.plugins.gradle.service.project.GradleHeadlessLoggingProjectActivity.StateNotificationListener
+import org.jetbrains.plugins.gradle.service.project.isGradleProjectResolveTask
import org.jetbrains.plugins.gradle.service.project.open.createLinkSettings
import org.jetbrains.plugins.gradle.settings.GradleImportHintService
import org.jetbrains.plugins.gradle.settings.GradleSettings
@@ -37,14 +34,10 @@ import java.nio.file.Paths
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import kotlin.coroutines.coroutineContext
-import kotlin.io.path.appendText
-import kotlin.io.path.createFile
-import kotlin.io.path.div
private val LOG = logger()
-private val gradleLogWriterPath = Path.of(PathManager.getLogPath()) / "gradle-import.log"
private const val DISABLE_GRADLE_AUTO_IMPORT = "external.system.auto.import.disabled"
private const val DISABLE_GRADLE_JDK_FIX = "gradle.auto.auto.jdk.fix.disabled"
@@ -156,66 +149,9 @@ class GradleWarmupConfigurator : WarmupConfigurator {
return false
}
- class StateExternalAnnotationNotificationListener : ExternalAnnotationsProgressNotificationListener {
- override fun onStartResolve(id: ExternalAnnotationsTaskId) {
- LOG.info("Gradle resolving external annotations started ${id.projectId}")
- }
- override fun onFinishResolve(id: ExternalAnnotationsTaskId) {
- LOG.info("Gradle resolving external annotations completed ${id.projectId}")
- }
- }
- class StateNotificationListener(
- private val project: Project, private val scope: CoroutineScope
- ) : ExternalSystemTaskNotificationListener {
-
- override fun onSuccess(id: ExternalSystemTaskId) {
- if (!id.isGradleProjectResolveTask()) return
- LOG.info("Gradle resolve stage finished with success: ${id.ideProjectId}")
-
- project.messageBus.connect(scope)
- .subscribe(ProjectDataImportListener.TOPIC, object : ProjectDataImportListener {
- override fun onImportStarted(projectPath: String?) {
- LOG.info("Gradle data import stage started: ${id.ideProjectId}")
- }
-
- override fun onImportFinished(projectPath: String?) {
- LOG.info("Gradle data import stage finished with success: ${id.ideProjectId}")
- }
-
- override fun onFinalTasksFinished(projectPath: String?) {
- LOG.info("Gradle data import(final tasks) stage finished: ${id.ideProjectId}")
- }
-
- override fun onFinalTasksStarted(projectPath: String?) {
- LOG.info("Gradle data import(final tasks) stage started: ${id.ideProjectId}")
- }
-
- override fun onImportFailed(projectPath: String?, t: Throwable) {
- WarmupLogger.fatalError(t.message ?: "Gradle import finished with error", t)
- LOG.info("Gradle data import stage finished with failure: ${id.ideProjectId}")
- }
- })
- }
-
- override fun onFailure(id: ExternalSystemTaskId, e: Exception) {
- if (!id.isGradleProjectResolveTask()) return
- WarmupLogger.fatalError(e.message ?: "Gradle import finished with error", e)
- LOG.error("Gradle resolve stage finished with failure ${id.ideProjectId}", e)
- }
-
- override fun onCancel(id: ExternalSystemTaskId) {
- if (!id.isGradleProjectResolveTask()) return
- LOG.error("Gradle resolve stage canceled ${id.ideProjectId}")
- }
-
- override fun onStart(id: ExternalSystemTaskId, workingDir: String) {
- if (!id.isGradleProjectResolveTask()) return
- LOG.info("Gradle resolve stage started ${id.ideProjectId}, working dir: $workingDir")
- }
- }
class ImportErrorListener(
@@ -246,39 +182,6 @@ class GradleWarmupConfigurator : WarmupConfigurator {
}
}
- class LoggingNotificationListener(val logger: CommandLineInspectionProgressReporter?) : ExternalSystemTaskNotificationListener {
-
- private val logPath = try {
- gradleLogWriterPath.createParentDirectories().createFile()
- }
- catch (e: java.nio.file.FileAlreadyExistsException) {
- gradleLogWriterPath
- }
-
- override fun onTaskOutput(id: ExternalSystemTaskId, text: String, stdOut: Boolean) {
- val gradleText = (if (stdOut) "" else "STDERR: ") + text
- logPath.appendText(gradleText)
- val croppedMessage = processMessage(gradleText)
- if (croppedMessage != null) {
- logger?.reportMessage(1, croppedMessage)
- }
- }
-
- private fun processMessage(gradleText: String): String? {
- if (WarmupStatus.currentStatus() != WarmupStatus.InProgress) {
- return gradleText
- }
- val cropped = gradleText.trimStart('\r').trimEnd('\n')
- if (cropped.startsWith("Download")) {
- // we don't want to be flooded by a ton of download messages, so we'll print only final message
- if (cropped.contains(" took ")) {
- return cropped
- }
- return null
- }
- return cropped
- }
- }
}
internal fun prepareGradleConfiguratorEnvironment(logger: CommandLineInspectionProgressReporter?) {
@@ -288,11 +191,9 @@ internal fun prepareGradleConfiguratorEnvironment(logger: CommandLineInspectionP
System.setProperty(DISABLE_UPDATE_ANDROID_SDK_LOCAL_PROPERTIES, true.toString())
val progressManager = ExternalSystemProgressNotificationManager.getInstance()
if (logger != null) {
- progressManager.addNotificationListener(GradleWarmupConfigurator.LoggingNotificationListener(logger))
+ progressManager.addNotificationListener(LoggingNotificationListener())
}
Unit
}
-private fun ExternalSystemTaskId.isGradleProjectResolveTask() = this.projectSystemId == GradleConstants.SYSTEM_ID &&
- this.type == ExternalSystemTaskType.RESOLVE_PROJECT
\ No newline at end of file
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleHeadlessLoggingProjectActivity.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleHeadlessLoggingProjectActivity.kt
index 6cf6d9aed6dd..f9041087dba3 100644
--- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleHeadlessLoggingProjectActivity.kt
+++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleHeadlessLoggingProjectActivity.kt
@@ -1,22 +1,32 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.plugins.gradle.service.project
-import com.intellij.ide.CommandLineInspectionProgressReporter
-import com.intellij.ide.warmup.WarmupLogger
-import com.intellij.ide.warmup.WarmupStatus
-import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.application.PathManager
+import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId
+import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener
+import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType
import com.intellij.openapi.externalSystem.service.notification.ExternalSystemProgressNotificationManager
+import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataImportListener
import com.intellij.openapi.project.Project
+import com.intellij.openapi.project.configuration.HeadlessLogging
import com.intellij.openapi.startup.ProjectActivity
-import com.intellij.openapi.util.registry.Registry
+import com.intellij.util.application
import com.intellij.util.awaitCancellationAndInvoke
+import com.intellij.util.io.createParentDirectories
import kotlinx.coroutines.CoroutineScope
-import org.jetbrains.plugins.gradle.GradleWarmupConfigurator
+import kotlinx.coroutines.launch
+import org.jetbrains.plugins.gradle.service.notification.ExternalAnnotationsProgressNotificationListener
import org.jetbrains.plugins.gradle.service.notification.ExternalAnnotationsProgressNotificationManager
+import org.jetbrains.plugins.gradle.service.notification.ExternalAnnotationsTaskId
+import org.jetbrains.plugins.gradle.util.GradleConstants
+import java.nio.file.Path
+import kotlin.io.path.appendText
+import kotlin.io.path.createFile
+import kotlin.io.path.div
class GradleHeadlessLoggingProjectActivity(val scope: CoroutineScope) : ProjectActivity {
override suspend fun execute(project: Project) {
- if (WarmupStatus.currentStatus() != WarmupStatus.InProgress || !Registry.`is`("ide.warmup.use.predicates")) {
+ if (!application.isHeadlessEnvironment || application.isUnitTestMode) {
return
}
val progressManager = ExternalSystemProgressNotificationManager.getInstance()
@@ -26,42 +36,132 @@ class GradleHeadlessLoggingProjectActivity(val scope: CoroutineScope) : ProjectA
}
private fun addTaskNotificationListener(progressManager: ExternalSystemProgressNotificationManager) {
- val listener = GradleWarmupConfigurator.LoggingNotificationListener(object : CommandLineInspectionProgressReporter {
- override fun reportError(message: String?) {
- if (message == null) {
- return
- }
- WarmupLogger.fatalError(message, null)
- }
-
- override fun reportMessage(minVerboseLevel: Int, message: String?) {
- if (message == null) {
- return
- }
- WarmupLogger.message(message)
- }
- })
+ val listener = LoggingNotificationListener()
progressManager.addNotificationListener(listener)
- scope.awaitCancellationAndInvoke {
- progressManager.removeNotificationListener(listener)
+ scope.launch {
+ awaitCancellationAndInvoke {
+ progressManager.removeNotificationListener(listener)
+ }
}
}
private fun addStateNotificationListener(project: Project, progressManager: ExternalSystemProgressNotificationManager) {
- val notificationListener = GradleWarmupConfigurator.StateNotificationListener(project, scope)
+ val notificationListener = StateNotificationListener(project, scope)
progressManager.addNotificationListener(notificationListener)
- scope.awaitCancellationAndInvoke {
- progressManager.removeNotificationListener(notificationListener)
+ scope.launch {
+ awaitCancellationAndInvoke {
+ progressManager.removeNotificationListener(notificationListener)
+ }
}
}
private fun addAnnotationListener() {
val externalAnnotationsNotificationManager = ExternalAnnotationsProgressNotificationManager.getInstance()
- val externalAnnotationsProgressListener = GradleWarmupConfigurator.StateExternalAnnotationNotificationListener()
+ val externalAnnotationsProgressListener = StateExternalAnnotationNotificationListener()
externalAnnotationsNotificationManager.addNotificationListener(externalAnnotationsProgressListener)
- scope.awaitCancellationAndInvoke {
- externalAnnotationsNotificationManager.removeNotificationListener(externalAnnotationsProgressListener)
+ scope.launch {
+ awaitCancellationAndInvoke {
+ externalAnnotationsNotificationManager.removeNotificationListener(externalAnnotationsProgressListener)
+ }
}
}
-}
\ No newline at end of file
+
+ class StateExternalAnnotationNotificationListener : ExternalAnnotationsProgressNotificationListener {
+
+ override fun onStartResolve(id: ExternalAnnotationsTaskId) {
+ HeadlessLogging.logMessage(gradlePrefix + "Gradle resolving external annotations started ${id.projectId}")
+ }
+
+ override fun onFinishResolve(id: ExternalAnnotationsTaskId) {
+ HeadlessLogging.logMessage(gradlePrefix + "Gradle resolving external annotations finished ${id.projectId}")
+ }
+ }
+
+ class StateNotificationListener(
+ private val project: Project, private val scope: CoroutineScope
+ ) : ExternalSystemTaskNotificationListener {
+
+ override fun onSuccess(id: ExternalSystemTaskId) {
+ if (!id.isGradleProjectResolveTask()) return
+ HeadlessLogging.logMessage(gradlePrefix + "Gradle resolve stage finished with success: ${id.ideProjectId}")
+
+ project.messageBus.connect(scope)
+ .subscribe(ProjectDataImportListener.TOPIC, object : ProjectDataImportListener {
+ override fun onImportStarted(projectPath: String?) {
+ HeadlessLogging.logMessage(gradlePrefix + "Gradle data import stage started: ${id.ideProjectId}")
+ }
+
+ override fun onImportFinished(projectPath: String?) {
+ HeadlessLogging.logMessage(gradlePrefix + "Gradle data import stage finished with success: ${id.ideProjectId}")
+ }
+
+ override fun onFinalTasksFinished(projectPath: String?) {
+ HeadlessLogging.logMessage(gradlePrefix + "Gradle data import(final tasks) stage finished: ${id.ideProjectId}")
+ }
+
+ override fun onFinalTasksStarted(projectPath: String?) {
+ HeadlessLogging.logMessage(gradlePrefix + "Gradle data import(final tasks) stage started: ${id.ideProjectId}")
+ }
+
+ override fun onImportFailed(projectPath: String?, t: Throwable) {
+ HeadlessLogging.logFatalError(t)
+ }
+ })
+ }
+
+ override fun onFailure(id: ExternalSystemTaskId, e: Exception) {
+ if (!id.isGradleProjectResolveTask()) return
+ HeadlessLogging.logFatalError(e)
+ }
+
+ override fun onCancel(id: ExternalSystemTaskId) {
+ if (!id.isGradleProjectResolveTask()) return
+ HeadlessLogging.logWarning(gradlePrefix + "Gradle resolve stage canceled ${id.ideProjectId}")
+ }
+
+ override fun onStart(id: ExternalSystemTaskId, workingDir: String) {
+ if (!id.isGradleProjectResolveTask()) return
+ HeadlessLogging.logMessage(gradlePrefix + "Gradle resolve stage started ${id.ideProjectId}, working dir: $workingDir")
+ }
+ }
+
+ class LoggingNotificationListener : ExternalSystemTaskNotificationListener {
+
+ private val logPath = try {
+ gradleLogWriterPath.createParentDirectories().createFile()
+ }
+ catch (e: java.nio.file.FileAlreadyExistsException) {
+ gradleLogWriterPath
+ }
+
+ override fun onTaskOutput(id: ExternalSystemTaskId, text: String, stdOut: Boolean) {
+ val gradleText = (if (stdOut) "" else "STDERR: ") + text
+ logPath.appendText(gradleText)
+ val croppedMessage = processMessage(gradleText)
+ if (croppedMessage != null) {
+ HeadlessLogging.logMessage(gradlePrefix + croppedMessage)
+ }
+ }
+
+ private fun processMessage(gradleText: String): String? {
+ val cropped = gradleText.trimStart('\r').trimEnd('\n')
+ if (cropped.startsWith("Download")) {
+ // we don't want to be flooded by a ton of download messages, so we'll print only final message
+ if (cropped.contains(" took ")) {
+ return cropped
+ }
+ return null
+ }
+ return cropped
+ }
+ }
+
+}
+
+internal fun ExternalSystemTaskId.isGradleProjectResolveTask() = this.projectSystemId == GradleConstants.SYSTEM_ID &&
+ this.type == ExternalSystemTaskType.RESOLVE_PROJECT
+
+private val gradleLogWriterPath = Path.of(PathManager.getLogPath()) / "gradle-import.log"
+
+private val gradlePrefix = "[Gradle]: "
\ No newline at end of file
diff --git a/plugins/performanceTesting/core/src/com/jetbrains/performancePlugin/commands/AwaitCompleteProjectConfigurationCommand.kt b/plugins/performanceTesting/core/src/com/jetbrains/performancePlugin/commands/AwaitCompleteProjectConfigurationCommand.kt
index b8929facaef8..7aa642f6a79a 100644
--- a/plugins/performanceTesting/core/src/com/jetbrains/performancePlugin/commands/AwaitCompleteProjectConfigurationCommand.kt
+++ b/plugins/performanceTesting/core/src/com/jetbrains/performancePlugin/commands/AwaitCompleteProjectConfigurationCommand.kt
@@ -2,8 +2,8 @@ package com.jetbrains.performancePlugin.commands
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.logger
+import com.intellij.openapi.project.configuration.awaitCompleteProjectConfiguration
import com.intellij.openapi.ui.playback.PlaybackContext
-import com.intellij.util.awaitCompleteProjectConfiguration
private val LOG: Logger