-
-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3727 from tristankretzer/issue/3653
Fix `kpsewhich` timeout on Windows using Tex Live Full
- Loading branch information
Showing
7 changed files
with
149 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
package nl.hannahsten.texifyidea.util | ||
|
||
import com.intellij.execution.ExecutionException | ||
import com.intellij.execution.configurations.GeneralCommandLine | ||
import com.intellij.util.io.awaitExit | ||
import kotlinx.coroutines.* | ||
import java.io.File | ||
import java.io.IOException | ||
import javax.swing.SwingUtilities | ||
|
||
data class CommandResult( | ||
val exitCode: Int, | ||
val standardOutput: String?, | ||
val errorOutput: String? | ||
) { | ||
|
||
val output: String? | ||
get() = if (standardOutput != null || errorOutput != null) (standardOutput ?: "") + (errorOutput ?: "") else null | ||
} | ||
|
||
/** | ||
* Run a command in the terminal in a non-blocking way. | ||
* | ||
* @param workingDirectory If provided, the process' working directory. | ||
* @param input If provided, this will be written to the process' input pipe. | ||
* @param discardOutput Whether to discard all command outputs (stdout, stderr) and only return its exit code. | ||
* @param returnExceptionMessageAsErrorOutput Whether to return exception messages as error output if exceptions are thrown. | ||
* @param timeout The timeout for execution. Does not stop reading the process' output as long as it is available. | ||
*/ | ||
suspend fun runCommandNonBlocking( | ||
vararg commands: String, | ||
workingDirectory: File? = null, | ||
input: String? = null, | ||
discardOutput: Boolean = false, | ||
returnExceptionMessageAsErrorOutput: Boolean = false, | ||
timeout: Long = 3 | ||
): CommandResult = withContext(Dispatchers.IO) { | ||
try { | ||
Log.debug("isEDT=${SwingUtilities.isEventDispatchThread()} Executing in ${workingDirectory ?: "current working directory"} ${GeneralCommandLine(*commands).commandLineString}") | ||
|
||
val processBuilder = GeneralCommandLine(*commands) | ||
.withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE) | ||
.withWorkDirectory(workingDirectory) | ||
.toProcessBuilder() | ||
|
||
if (discardOutput) { | ||
processBuilder.redirectOutput(ProcessBuilder.Redirect.DISCARD) | ||
processBuilder.redirectError(ProcessBuilder.Redirect.DISCARD) | ||
} | ||
|
||
val process = processBuilder.start() | ||
|
||
process.outputWriter().use { if (input != null) it.write(input) } | ||
val output = if (!discardOutput) async { process.inputReader().use { it.readText() } } else null | ||
val error = if (!discardOutput) async { process.errorReader().use { it.readText() } } else null | ||
|
||
withTimeoutOrNull(1_000 * timeout) { | ||
process.awaitExit() | ||
} ?: run { | ||
process.destroy() | ||
Log.debug("${commands.firstOrNull()} destroyed after timeout $timeout seconds") | ||
} | ||
|
||
val result = CommandResult(process.awaitExit(), output?.await()?.trim(), error?.await()?.trim()) | ||
Log.debug("${commands.firstOrNull()} exited with ${result.exitCode} ${result.standardOutput?.take(100)} ${result.errorOutput?.take(100)}") | ||
|
||
return@withContext result | ||
} | ||
catch (e: IOException) { | ||
Log.debug(e.message ?: "Unknown IOException occurred") | ||
|
||
return@withContext CommandResult( | ||
-1, | ||
null, | ||
if (returnExceptionMessageAsErrorOutput) e.message else null | ||
) | ||
} | ||
catch (e: ExecutionException) { | ||
Log.debug(e.message ?: "Unknown ExecutionException occurred") | ||
|
||
return@withContext CommandResult( | ||
-1, | ||
null, | ||
if (returnExceptionMessageAsErrorOutput) e.message else null | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* Run a command in the terminal. | ||
* | ||
* @return The output of the command or null if an exception was thrown. | ||
*/ | ||
fun runCommand(vararg commands: String, workingDirectory: File? = null, timeout: Long = 3): String? = | ||
runBlocking { | ||
runCommandNonBlocking(*commands, workingDirectory = workingDirectory, timeout = timeout).output | ||
} | ||
|
||
/** | ||
* See [runCommandNonBlocking]. | ||
* | ||
* @param returnExceptionMessage Whether to return exception messages as output if exceptions are thrown. | ||
* @param inputString If provided, this will be written to the process' input pipe. | ||
* @return Pair of output (stdout + stderr) to exit code. | ||
*/ | ||
fun runCommandWithExitCode( | ||
vararg commands: String, | ||
workingDirectory: File? = null, | ||
timeout: Long = 3, | ||
returnExceptionMessage: Boolean = false, | ||
discardOutput: Boolean = false, | ||
inputString: String = "" | ||
): Pair<String?, Int> = | ||
runBlocking { | ||
with( | ||
runCommandNonBlocking( | ||
*commands, | ||
workingDirectory = workingDirectory, | ||
timeout = timeout, | ||
returnExceptionMessageAsErrorOutput = returnExceptionMessage, | ||
discardOutput = discardOutput, | ||
input = inputString | ||
) | ||
) { | ||
Pair(output, exitCode) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters