Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

better build/sync output #234

Merged
merged 2 commits into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 83 additions & 54 deletions src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ import com.intellij.build.events.BuildEventsNls
import com.intellij.build.events.MessageEvent
import com.intellij.build.progress.BuildProgress
import com.intellij.build.progress.BuildProgressDescriptor
import com.intellij.execution.process.ProcessAdapter
import com.intellij.execution.process.ProcessEvent
import com.intellij.icons.AllIcons
import com.intellij.notification.NotificationType.ERROR
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionUpdateThread
Expand All @@ -30,12 +28,10 @@ import com.intellij.openapi.util.Key
import com.intellij.openapi.util.NlsContexts
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.concurrency.annotations.RequiresReadLock
import io.ktor.http.*
import org.move.cli.MoveProject.UpdateStatus
import org.move.cli.manifest.MoveToml
import org.move.cli.settings.getAptosCli
import org.move.cli.settings.moveSettings
import org.move.ide.notifications.showBalloon
import org.move.lang.toNioPathOrNull
import org.move.lang.toTomlFile
import org.move.openapiext.TaskResult
Expand Down Expand Up @@ -141,17 +137,27 @@ class MoveProjectsSyncTask(
runReadAction {
val tomlFile = moveTomlFile.toTomlFile(project)!!
val rootMoveToml = MoveToml.fromTomlFile(tomlFile)
// val rootMoveToml = MoveToml.fromTomlFile(tomlFile, projectRoot)
val rootPackage = MovePackage.fromMoveToml(rootMoveToml)
val rootProject = MoveProject(project, rootPackage, emptyList())
rootProject to rootMoveToml
}

val result = fetchDependencyPackages(context, projectRoot)
if (result is TaskResult.Err) {
moveProject =
moveProject.copy(fetchDepsStatus = UpdateStatus.UpdateFailed("Failed to fetch dependency packages"))
if (!project.moveSettings.fetchAptosDeps) {
context.syncProgress.info(
"Fetching dependencies (disabled)",
"Fetching dependencies disabled in the plugin settings",
)
} else {
context.runWithChildProgress("Fetching dependencies") { childContext ->
val result = fetchDependencyPackages(childContext, projectRoot)
if (result is TaskResult.Err) {
moveProject =
moveProject.copy(fetchDepsStatus = UpdateStatus.UpdateFailed("Failed to fetch dependency packages"))
}
result
}
}
context.checkCanceled()

val deps =
(context.runWithChildProgress("Loading dependencies") { childContext ->
Expand All @@ -166,7 +172,7 @@ class MoveProjectsSyncTask(
deps,
visitedDepIds,
true,
childContext.progress
childContext
)
TaskResult.Ok(deps)
}
Expand All @@ -175,30 +181,31 @@ class MoveProjectsSyncTask(
projects.add(moveProject.copy(dependencies = deps))
}

private fun fetchDependencyPackages(context: SyncContext, projectRoot: Path): TaskResult<Unit> =
context.runWithChildProgress("Synchronize dependencies") { childContext ->
val listener = SyncProcessAdapter(childContext)

val skipLatest = project.moveSettings.skipFetchLatestGitDeps
val aptos = project.getAptosCli(parentDisposable = this)
when {
aptos == null -> TaskResult.Err("Invalid Aptos CLI configuration")
else -> {
aptos.fetchPackageDependencies(
project,
projectRoot,
skipLatest,
processListener = listener
).unwrapOrElse {
return@runWithChildProgress TaskResult.Err(
"Failed to fetch / update dependencies",
it.message
)
}
TaskResult.Ok(Unit)
private fun fetchDependencyPackages(childContext: SyncContext, projectRoot: Path): TaskResult<Unit> {
val taskResult: TaskResult<Unit>? = null
val syncListener =
SyncProcessListener(childContext) { event ->
childContext.syncProgress.output(event.text, true)
}
val skipLatest = project.moveSettings.skipFetchLatestGitDeps
val aptos = project.getAptosCli(parentDisposable = this)
return when {
aptos == null -> TaskResult.Err("Invalid Aptos CLI configuration")
else -> {
aptos.fetchPackageDependencies(
projectRoot,
skipLatest,
processListener = syncListener
).unwrapOrElse {
return TaskResult.Err(
"Failed to fetch / update dependencies",
it.message
)
}
TaskResult.Ok(Unit)
}
}
}

private fun createSyncProgressDescriptor(progress: ProgressIndicator): BuildProgressDescriptor {
val buildContentDescriptor = BuildContentDescriptor(
Expand Down Expand Up @@ -243,6 +250,8 @@ class MoveProjectsSyncTask(

val id: Any get() = syncProgress.id

fun checkCanceled() = progress.checkCanceled()

fun <T> runWithChildProgress(
@NlsContexts.ProgressText title: String,
action: (SyncContext) -> TaskResult<T>
Expand Down Expand Up @@ -288,26 +297,26 @@ class MoveProjectsSyncTask(
deps: MutableList<Pair<MovePackage, RawAddressMap>>,
visitedIds: MutableSet<DepId>,
isRoot: Boolean,
progress: ProgressIndicator
syncContext: SyncContext,
) {
// checks for the cancel() of the whole SyncTask
progress.checkCanceled()
syncContext.checkCanceled()

var parsedDeps = rootMoveToml.deps
if (isRoot) {
parsedDeps = parsedDeps.withExtended(rootMoveToml.dev_deps)
}
for ((dep, addressMap) in parsedDeps) {
val depRoot = dep.rootDirectory()
if (depRoot == null) {
// root does not exist
project.showBalloon(
"Cannot resolve the ${dep.name.quote()} dependency. " +
"Root directory does not exist.",
ERROR
)
continue
}
.unwrapOrElse {
syncContext.syncProgress.message(
"Failed to load ${dep.name}.",
"Error when resolving dependency root, \n${it.message}",
MessageEvent.Kind.ERROR,
null
)
null
} ?: continue

val depId = DepId(depRoot.path)
if (depId in visitedIds) continue
Expand All @@ -321,19 +330,28 @@ class MoveProjectsSyncTask(

// parse all nested dependencies with their address maps
visitedIds.add(depId)
loadDependencies(project, depMoveToml, deps, visitedIds, false, progress)

loadDependencies(project, depMoveToml, deps, visitedIds, false, syncContext)
deps.add(Pair(depPackage, addressMap))

syncContext.syncProgress.output(
"${dep.name} dependency loaded successfully, " +
"\npackage directory: \n${depRoot.presentableUrl}",
true,
)
syncContext.syncProgress.newline()
syncContext.syncProgress.newline()
}
}
}
}

private class SyncProcessAdapter(
private val context: MoveProjectsSyncTask.SyncContext
): ProcessAdapter(),
ProcessProgressListener {
private class SyncProcessListener(
private val context: MoveProjectsSyncTask.SyncContext,
private val onTextAvailable: (ProcessEvent) -> Unit,
): ProcessProgressListener {
override fun onTextAvailable(event: ProcessEvent, outputType: Key<Any>) {
onTextAvailable(event)
// show progress bars for the long operations
val text = event.text.trim { it <= ' ' }
if (text.startsWith("FETCHING GIT DEPENDENCY")) {
val gitRepo = text.substring("FETCHING GIT DEPENDENCY".length)
Expand All @@ -345,8 +363,8 @@ private class SyncProcessAdapter(
}
}

override fun error(title: String, message: String) = context.error(title, message)
override fun warning(title: String, message: String) = context.warning(title, message)
override fun error(title: String, message: String) = context.syncProgress.error(title, message)
override fun warning(title: String, message: String) = context.syncProgress.warning(title, message)
}

private fun <T, R> BuildProgress<BuildProgressDescriptor>.runWithChildProgress(
Expand All @@ -371,18 +389,29 @@ private fun <T, R> BuildProgress<BuildProgressDescriptor>.runWithChildProgress(
}
}

private fun MoveProjectsSyncTask.SyncContext.error(
private fun BuildProgress<BuildProgressDescriptor>.error(
@BuildEventsNls.Title title: String,
@BuildEventsNls.Message message: String
) {
syncProgress.message(title, message, com.intellij.build.events.MessageEvent.Kind.ERROR, null)
message(title, message, MessageEvent.Kind.ERROR, null)
}

private fun MoveProjectsSyncTask.SyncContext.warning(
private fun BuildProgress<BuildProgressDescriptor>.warning(
@BuildEventsNls.Title title: String,
@BuildEventsNls.Message message: String
) {
syncProgress.message(title, message, com.intellij.build.events.MessageEvent.Kind.WARNING, null)
message(title, message, MessageEvent.Kind.WARNING, null)
}

private fun BuildProgress<BuildProgressDescriptor>.info(
@BuildEventsNls.Title title: String,
@BuildEventsNls.Message message: String
) {
message(title, message, MessageEvent.Kind.INFO, null)
}

private fun BuildProgress<BuildProgressDescriptor>.newline() {
output("\n", true)
}


40 changes: 31 additions & 9 deletions src/main/kotlin/org/move/cli/manifest/TomlDependency.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,66 @@ package org.move.cli.manifest
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.findDirectory
import com.intellij.util.SystemProperties
import com.intellij.util.concurrency.annotations.RequiresReadLock
import org.move.stdext.RsResult
import org.move.stdext.RsResult.Err
import org.move.stdext.RsResult.Ok
import org.move.stdext.exists
import org.move.stdext.toPath
import java.nio.file.Path

sealed class TomlDependency {
abstract val name: String

@RequiresReadLock
abstract fun rootDirectory(): VirtualFile?
abstract fun rootDirectory(): RsResult<VirtualFile, DependencyError>

data class Local(
override val name: String,
private val localPath: Path,
) : TomlDependency() {
): TomlDependency() {

@RequiresReadLock
override fun rootDirectory(): VirtualFile? = VfsUtil.findFile(localPath, true)
override fun rootDirectory(): RsResult<VirtualFile, DependencyError> {
val vFile = VfsUtil.findFile(localPath, true)
if (vFile == null) {
return Err(DependencyError("Cannot find dependency folder: $localPath"))
}
return Ok(vFile)
}
}

data class Git(
override val name: String,
private val repo: String,
private val rev: String,
private val subdir: String,
) : TomlDependency() {
): TomlDependency() {

@RequiresReadLock
override fun rootDirectory(): VirtualFile? {
val userHome = VfsUtil.getUserHomeDir() ?: return null
val sourceDirName = dirNameAptos(repo, rev)
return userHome.findDirectory(".move/$sourceDirName/$subdir")
override fun rootDirectory(): RsResult<VirtualFile, DependencyError> {
val moveHomePath = SystemProperties.getUserHome().toPath().resolve(".move")
if (!moveHomePath.exists()) {
return Err(DependencyError("$moveHomePath directory does not exist"))
}
val depDirName = dependencyDirName(repo, rev)
val depRoot = moveHomePath.resolve(depDirName).resolve(subdir)
val depRootFile = VfsUtil.findFile(depRoot, true)
if (depRootFile == null) {
return Err(DependencyError("cannot find folder: $depRoot"))
}
return Ok(depRootFile)
}

companion object {
fun dirNameAptos(repo: String, rev: String): String {
fun dependencyDirName(repo: String, rev: String): String {
val sanitizedRepoName = repo.replace(Regex("[/:.@]"), "_")
val aptosRevName = rev.replace("/", "_")
return "${sanitizedRepoName}_$aptosRevName"
}
}
}
}

data class DependencyError(val message: String)
26 changes: 11 additions & 15 deletions src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,25 +63,21 @@ data class Aptos(val cliLocation: Path, val parentDisposable: Disposable?): Disp
}

fun fetchPackageDependencies(
project: Project,
projectDir: Path,
skipLatest: Boolean,
processListener: ProcessListener
): RsProcessResult<Unit> {
if (project.moveSettings.fetchAptosDeps) {
val commandLine =
AptosCommandLine(
subCommand = "move compile",
arguments = listOfNotNull(
"--skip-fetch-latest-git-deps".takeIf { skipLatest }
),
workingDirectory = projectDir
)
// TODO: as Aptos does not yet support fetching dependencies without compiling, ignore errors here,
// TODO: still better than no call at all
executeCommandLine(commandLine, listener = processListener)
// .unwrapOrElse { return Err(it) }
}
val commandLine =
AptosCommandLine(
subCommand = "move compile",
arguments = listOfNotNull(
"--skip-fetch-latest-git-deps".takeIf { skipLatest }
),
workingDirectory = projectDir
)
commandLine
.toColoredCommandLine(this.cliLocation)
.execute(innerDisposable, listener = processListener)
return Ok(Unit)
}

Expand Down
18 changes: 0 additions & 18 deletions src/main/kotlin/org/move/ide/notifications/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,6 @@ fun Logger.logOrShowBalloon(@NotificationContent content: String, productionLeve
}
}

fun Project.showDebugBalloon(
@NotificationContent content: String,
type: NotificationType,
action: AnAction? = null
) {
if (isDebugModeEnabled()) {
showBalloon(content, type, action)
}
}

fun Project.showBalloon(
@NotificationContent content: String,
type: NotificationType,
action: AnAction? = null
) {
showBalloon("", content, type, action)
}

fun Project.showBalloon(
@NotificationTitle title: String,
@NotificationContent content: String,
Expand Down
5 changes: 5 additions & 0 deletions src/main/kotlin/org/move/stdext/RsResult.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ sealed class RsResult<out T, out E> {
}
}

fun unwrapErr(): E = when (this) {
is Err -> err
is Ok -> throw IllegalStateException("called `RsResult.unwrapErr()` on an `Ok` value")
}

fun unwrapOrNull(): T? = when (this) {
is Ok -> ok
is Err -> null
Expand Down
Loading