Skip to content

Commit 32c29fe

Browse files
Undinyopox
authored andcommitted
CARGO: show build script errors in Sync view
1 parent 4574fc7 commit 32c29fe

File tree

9 files changed

+320
-96
lines changed

9 files changed

+320
-96
lines changed

src/main/kotlin/org/rust/cargo/project/model/impl/CargoSyncTask.kt

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.intellij.execution.process.ProcessEvent
2020
import com.intellij.icons.AllIcons
2121
import com.intellij.openapi.actionSystem.ActionManager
2222
import com.intellij.openapi.actionSystem.AnActionEvent
23+
import com.intellij.openapi.components.service
2324
import com.intellij.openapi.diagnostic.logger
2425
import com.intellij.openapi.progress.ProcessCanceledException
2526
import com.intellij.openapi.progress.ProgressIndicator
@@ -38,13 +39,12 @@ import org.rust.cargo.project.settings.toolchain
3839
import org.rust.cargo.project.workspace.CargoWorkspace
3940
import org.rust.cargo.project.workspace.PackageOrigin
4041
import org.rust.cargo.project.workspace.StandardLibrary
42+
import org.rust.cargo.runconfig.buildtool.CargoBuildAdapterBase
43+
import org.rust.cargo.runconfig.buildtool.CargoBuildContextBase
4144
import org.rust.cargo.runconfig.command.workingDirectory
4245
import org.rust.cargo.toolchain.RsToolchainBase
4346
import org.rust.cargo.toolchain.impl.RustcVersion
44-
import org.rust.cargo.toolchain.tools.Rustup
45-
import org.rust.cargo.toolchain.tools.cargoOrWrapper
46-
import org.rust.cargo.toolchain.tools.rustc
47-
import org.rust.cargo.toolchain.tools.rustup
47+
import org.rust.cargo.toolchain.tools.*
4848
import org.rust.cargo.util.DownloadResult
4949
import org.rust.openapiext.TaskResult
5050
import org.rust.stdext.mapNotNullToSet
@@ -112,7 +112,7 @@ class CargoSyncTask(
112112
val stdlibStatus = CargoProject.UpdateStatus.UpdateFailed("Project directory does not exist")
113113
CargoProjectWithStdlib(cargoProject.copy(stdlibStatus = stdlibStatus), null)
114114
} else {
115-
val context = SyncContext(project, cargoProject, toolchain, indicator, childProgress)
115+
val context = SyncContext(project, cargoProject, toolchain, indicator, syncProgress.id, childProgress)
116116
val rustcInfoResult = fetchRustcInfo(context)
117117
val rustcInfo = (rustcInfoResult as? TaskResult.Ok)?.value
118118
val cargoProjectWithRustcInfoAndWorkspace = cargoProject.withRustcInfo(rustcInfoResult)
@@ -137,7 +137,7 @@ class CargoSyncTask(
137137
buildContentDescriptor.isActivateToolWindowWhenAdded = false
138138
buildContentDescriptor.isNavigateToError = project.rustSettings.autoShowErrorsInEditor
139139
val refreshAction = ActionManager.getInstance().getAction("Cargo.RefreshCargoProject")
140-
val descriptor = DefaultBuildDescriptor("Cargo", "Cargo", project.basePath!!, System.currentTimeMillis())
140+
val descriptor = DefaultBuildDescriptor(Any(), "Cargo", project.basePath!!, System.currentTimeMillis())
141141
.withContentDescriptor { buildContentDescriptor }
142142
.withRestartAction(refreshAction)
143143
.withRestartAction(StopAction(progress))
@@ -168,8 +168,12 @@ class CargoSyncTask(
168168
val oldCargoProject: CargoProjectImpl,
169169
val toolchain: RsToolchainBase,
170170
val progress: ProgressIndicator,
171+
val buildId: Any,
171172
val syncProgress: BuildProgress<BuildProgressDescriptor>
172173
) {
174+
175+
val id: Any get() = syncProgress.id
176+
173177
fun <T> runWithChildProgress(
174178
title: String,
175179
action: (SyncContext) -> TaskResult<T>
@@ -315,12 +319,32 @@ private fun fetchCargoWorkspace(context: CargoSyncTask.SyncContext, rustcInfo: R
315319
val cargo = toolchain.cargoOrWrapper(projectDirectory)
316320
try {
317321
CargoEventService.getInstance(childContext.project).onMetadataCall(projectDirectory)
318-
val projectDescriptionData = cargo.fullProjectDescription(
322+
val (projectDescriptionData, status) = cargo.fullProjectDescription(
319323
childContext.project,
320324
projectDirectory,
321-
// TODO: collect build events and show them in structured way
322-
SyncProcessAdapter(childContext)
323-
)
325+
) {
326+
when (it) {
327+
CargoCallType.METADATA -> SyncProcessAdapter(childContext)
328+
CargoCallType.BUILD_SCRIPT_CHECK -> {
329+
val childProgress = childContext.syncProgress.startChildProgress("Build scripts evaluation")
330+
val syncContext = childContext.copy(syncProgress = childProgress)
331+
332+
val buildContext = SyncCargoBuildContext(
333+
childContext.oldCargoProject,
334+
buildId = syncContext.buildId,
335+
parentId = syncContext.id,
336+
progressIndicator = syncContext.progress
337+
)
338+
339+
SyncCargoBuildAdapter(syncContext, buildContext)
340+
}
341+
}
342+
}
343+
if (status == ProjectDescriptionStatus.BUILD_SCRIPT_EVALUATION_ERROR) {
344+
childContext.warning("Build scripts evaluation failed",
345+
"Build scripts evaluation failed. Features based on generated info by build scripts may not work in your IDE")
346+
}
347+
324348
val manifestPath = projectDirectory.resolve("Cargo.toml")
325349

326350
val cfgOptions = try {
@@ -435,6 +459,36 @@ private class SyncProcessAdapter(
435459
override fun warning(title: String, message: String) = context.warning(title, message)
436460
}
437461

462+
private class SyncCargoBuildContext(
463+
cargoProject: CargoProject,
464+
buildId: Any,
465+
parentId: Any,
466+
progressIndicator: ProgressIndicator
467+
) : CargoBuildContextBase(cargoProject, "Building...", false, buildId, parentId) {
468+
init {
469+
indicator = progressIndicator
470+
}
471+
}
472+
473+
private class SyncCargoBuildAdapter(
474+
private val context: CargoSyncTask.SyncContext,
475+
buildContext: CargoBuildContextBase
476+
) : CargoBuildAdapterBase(buildContext, context.project.service<SyncViewManager>()) {
477+
478+
override fun onBuildOutputReaderFinish(
479+
event: ProcessEvent,
480+
isSuccess: Boolean,
481+
isCanceled: Boolean,
482+
error: Throwable?
483+
) {
484+
when {
485+
isSuccess -> context.syncProgress.finish()
486+
isCanceled -> context.syncProgress.cancel()
487+
else -> context.syncProgress.fail()
488+
}
489+
}
490+
}
491+
438492
private fun CargoSyncTask.SyncContext.error(title: String, message: String) {
439493
syncProgress.message(title, message, MessageEvent.Kind.ERROR, null)
440494
}

src/main/kotlin/org/rust/cargo/runconfig/buildtool/CargoBuildAdapter.kt

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@ import com.intellij.build.BuildContentDescriptor
99
import com.intellij.build.BuildProgressListener
1010
import com.intellij.build.DefaultBuildDescriptor
1111
import com.intellij.build.events.impl.*
12-
import com.intellij.build.output.BuildOutputInstantReaderImpl
1312
import com.intellij.execution.ExecutorRegistry
1413
import com.intellij.execution.actions.StopProcessAction
1514
import com.intellij.execution.impl.ExecutionManagerImpl
16-
import com.intellij.execution.process.ProcessAdapter
1715
import com.intellij.execution.process.ProcessEvent
1816
import com.intellij.execution.process.ProcessHandler
1917
import com.intellij.execution.runners.ExecutionEnvironment
@@ -22,9 +20,7 @@ import com.intellij.icons.AllIcons
2220
import com.intellij.openapi.actionSystem.AnActionEvent
2321
import com.intellij.openapi.project.DumbAwareAction
2422
import com.intellij.openapi.project.DumbService
25-
import com.intellij.openapi.util.Key
2623
import com.intellij.openapi.util.text.StringUtil
27-
import com.intellij.openapi.util.text.StringUtil.convertLineSeparators
2824
import com.intellij.openapi.vfs.VfsUtil
2925
import org.rust.cargo.CargoConstants
3026
import org.rust.cargo.project.settings.rustSettings
@@ -34,14 +30,8 @@ import javax.swing.JComponent
3430
@Suppress("UnstableApiUsage")
3531
class CargoBuildAdapter(
3632
private val context: CargoBuildContext,
37-
private val buildProgressListener: BuildProgressListener
38-
) : ProcessAdapter() {
39-
private val instantReader = BuildOutputInstantReaderImpl(
40-
context.buildId,
41-
context.buildId,
42-
buildProgressListener,
43-
listOf(RsBuildEventsConverter(context))
44-
)
33+
buildProgressListener: BuildProgressListener
34+
) : CargoBuildAdapterBase(context, buildProgressListener) {
4535

4636
init {
4737
val processHandler = checkNotNull(context.processHandler) { "Process handler can't be null" }
@@ -63,45 +53,39 @@ class CargoBuildAdapter(
6353
buildProgressListener.onEvent(context.buildId, buildStarted)
6454
}
6555

66-
override fun processTerminated(event: ProcessEvent) {
67-
instantReader.closeAndGetFuture().whenComplete { _, error ->
68-
val isSuccess = event.exitCode == 0 && context.errors.get() == 0
69-
val isCanceled = context.indicator?.isCanceled ?: false
70-
71-
val (status, result) = when {
72-
isCanceled -> "canceled" to SkippedResultImpl()
73-
isSuccess -> "successful" to SuccessResultImpl()
74-
else -> "failed" to FailureResultImpl(error)
75-
}
76-
val buildFinished = FinishBuildEventImpl(
77-
context.buildId,
78-
null,
79-
System.currentTimeMillis(),
80-
"${context.taskName} $status",
81-
result
82-
)
83-
buildProgressListener.onEvent(context.buildId, buildFinished)
84-
context.finished(isSuccess)
85-
86-
context.environment.notifyProcessTerminated(event.processHandler, event.exitCode)
87-
88-
val targetPath = context.workingDirectory.resolve(CargoConstants.ProjectLayout.target)
89-
val targetDir = VfsUtil.findFile(targetPath, true) ?: return@whenComplete
90-
VfsUtil.markDirtyAndRefresh(true, true, true, targetDir)
56+
override fun onBuildOutputReaderFinish(
57+
event: ProcessEvent,
58+
isSuccess: Boolean,
59+
isCanceled: Boolean,
60+
error: Throwable?
61+
) {
62+
val (status, result) = when {
63+
isCanceled -> "canceled" to SkippedResultImpl()
64+
isSuccess -> "successful" to SuccessResultImpl()
65+
else -> "failed" to FailureResultImpl(error)
9166
}
67+
68+
val buildFinished = FinishBuildEventImpl(
69+
context.buildId,
70+
null,
71+
System.currentTimeMillis(),
72+
"${context.taskName} $status",
73+
result
74+
)
75+
buildProgressListener.onEvent(context.buildId, buildFinished)
76+
context.finished(isSuccess)
77+
78+
context.environment.notifyProcessTerminated(event.processHandler, event.exitCode)
79+
80+
val targetPath = context.workingDirectory.resolve(CargoConstants.ProjectLayout.target)
81+
val targetDir = VfsUtil.findFile(targetPath, true) ?: return
82+
VfsUtil.markDirtyAndRefresh(true, true, true, targetDir)
9283
}
9384

9485
override fun processWillTerminate(event: ProcessEvent, willBeDestroyed: Boolean) {
9586
context.environment.notifyProcessTerminating(event.processHandler)
9687
}
9788

98-
override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) {
99-
// Progress messages end with '\r' instead of '\n'. We want to replace '\r' with '\n'
100-
// so that `instantReader` sends progress messages to parsers separately from other messages.
101-
val text = convertLineSeparators(event.text)
102-
instantReader.append(text)
103-
}
104-
10589
companion object {
10690

10791
private fun createStopAction(processHandler: ProcessHandler): StopProcessAction =
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Use of this source code is governed by the MIT license that can be
3+
* found in the LICENSE file.
4+
*/
5+
6+
package org.rust.cargo.runconfig.buildtool
7+
8+
import com.intellij.build.BuildProgressListener
9+
import com.intellij.build.output.BuildOutputInstantReaderImpl
10+
import com.intellij.execution.process.ProcessAdapter
11+
import com.intellij.execution.process.ProcessEvent
12+
import com.intellij.openapi.util.Key
13+
import com.intellij.openapi.util.text.StringUtil.convertLineSeparators
14+
15+
@Suppress("UnstableApiUsage")
16+
abstract class CargoBuildAdapterBase(
17+
private val context: CargoBuildContextBase,
18+
protected val buildProgressListener: BuildProgressListener
19+
) : ProcessAdapter() {
20+
private val instantReader = BuildOutputInstantReaderImpl(
21+
context.buildId,
22+
context.parentId,
23+
buildProgressListener,
24+
listOf(RsBuildEventsConverter(context))
25+
)
26+
27+
override fun processTerminated(event: ProcessEvent) {
28+
instantReader.closeAndGetFuture().whenComplete { _, error ->
29+
val isSuccess = event.exitCode == 0 && context.errors.get() == 0
30+
val isCanceled = context.indicator?.isCanceled ?: false
31+
onBuildOutputReaderFinish(event, isSuccess = isSuccess, isCanceled = isCanceled, error)
32+
}
33+
}
34+
35+
open fun onBuildOutputReaderFinish(event: ProcessEvent, isSuccess: Boolean, isCanceled: Boolean, error: Throwable?) {}
36+
37+
override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) {
38+
// Progress messages end with '\r' instead of '\n'. We want to replace '\r' with '\n'
39+
// so that `instantReader` sends progress messages to parsers separately from other messages.
40+
val text = convertLineSeparators(event.text)
41+
instantReader.append(text)
42+
}
43+
}

src/main/kotlin/org/rust/cargo/runconfig/buildtool/CargoBuildContext.kt

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,39 +24,49 @@ import java.util.concurrent.Semaphore
2424
import java.util.concurrent.TimeUnit
2525
import java.util.concurrent.atomic.AtomicInteger
2626

27-
class CargoBuildContext(
27+
abstract class CargoBuildContextBase(
2828
val cargoProject: CargoProject,
29-
val environment: ExecutionEnvironment,
30-
val taskName: String,
3129
val progressTitle: String,
32-
val isTestBuild: Boolean
30+
val isTestBuild: Boolean,
31+
val buildId: Any,
32+
val parentId: Any
3333
) {
34-
val buildId: Any = Any()
35-
3634
val project: Project get() = cargoProject.project
3735
val workingDirectory: Path get() = cargoProject.workingDirectory
3836

39-
val result: FutureResult<CargoBuildResult> = FutureResult()
37+
@Volatile
38+
var indicator: ProgressIndicator? = null
4039

41-
private val buildSemaphore: Semaphore = project.getUserData(BUILD_SEMAPHORE_KEY)
42-
?: (project as UserDataHolderEx).putUserDataIfAbsent(BUILD_SEMAPHORE_KEY, Semaphore(1))
40+
val errors: AtomicInteger = AtomicInteger()
41+
val warnings: AtomicInteger = AtomicInteger()
4342

4443
@Volatile
45-
var indicator: ProgressIndicator? = null
44+
var artifact: CompilerArtifactMessage? = null
45+
}
46+
47+
class CargoBuildContext(
48+
cargoProject: CargoProject,
49+
val environment: ExecutionEnvironment,
50+
val taskName: String,
51+
progressTitle: String,
52+
isTestBuild: Boolean,
53+
buildId: Any,
54+
parentId: Any
55+
) : CargoBuildContextBase(cargoProject, progressTitle, isTestBuild, buildId, parentId) {
56+
4657
@Volatile
4758
var processHandler: ProcessHandler? = null
4859

60+
private val buildSemaphore: Semaphore = project.getUserData(BUILD_SEMAPHORE_KEY)
61+
?: (project as UserDataHolderEx).putUserDataIfAbsent(BUILD_SEMAPHORE_KEY, Semaphore(1))
62+
63+
val result: FutureResult<CargoBuildResult> = FutureResult()
64+
4965
val started: Long = System.currentTimeMillis()
5066
@Volatile
5167
var finished: Long = started
5268
private val duration: Long get() = finished - started
5369

54-
val errors: AtomicInteger = AtomicInteger()
55-
val warnings: AtomicInteger = AtomicInteger()
56-
57-
@Volatile
58-
var artifact: CompilerArtifactMessage? = null
59-
6070
fun waitAndStart(): Boolean {
6171
indicator?.pushState()
6272
try {

src/main/kotlin/org/rust/cargo/runconfig/buildtool/CargoBuildManager.kt

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,16 @@ object CargoBuildManager {
9999
lastBuildCommandLine = state.prepareCommandLine()
100100
}
101101

102-
return execute(
103-
CargoBuildContext(
104-
cargoProject = cargoProject,
105-
environment = environment,
106-
taskName = "Build",
107-
progressTitle = "Building...",
108-
isTestBuild = state.commandLine.command == "test"
109-
)
110-
) {
102+
val buildId = Any()
103+
return execute(CargoBuildContext(
104+
cargoProject = cargoProject,
105+
environment = environment,
106+
taskName = "Build",
107+
progressTitle = "Building...",
108+
isTestBuild = state.commandLine.command == "test",
109+
buildId = buildId,
110+
parentId = buildId
111+
)) {
111112
val buildProgressListener = ServiceManager.getService(project, BuildViewManager::class.java)
112113
if (!isHeadlessEnvironment) {
113114
@Suppress("UsePropertyAccessSyntax")

0 commit comments

Comments
 (0)