* @brief Spawns a helper process; returns its id, or -1 with @p error set on failure. */
| 74 | * @brief Spawns a helper process; returns its id, or -1 with @p error set on failure. |
| 75 | */ |
| 76 | int API::ProcessLauncher::launch(const QString& program, |
| 77 | const QStringList& arguments, |
| 78 | const QString& workingDirectory, |
| 79 | QString& error) |
| 80 | { |
| 81 | if (program.isEmpty()) { |
| 82 | error = tr("No program specified"); |
| 83 | return -1; |
| 84 | } |
| 85 | |
| 86 | const QString resolved = resolveExecutable(program); |
| 87 | if (resolved.isEmpty()) { |
| 88 | error = tr("Program \"%1\" not found in PATH").arg(program); |
| 89 | return -1; |
| 90 | } |
| 91 | |
| 92 | auto* process = new QProcess(this); |
| 93 | process->setProcessChannelMode(QProcess::MergedChannels); |
| 94 | if (!workingDirectory.isEmpty()) |
| 95 | process->setWorkingDirectory(workingDirectory); |
| 96 | |
| 97 | auto env = QProcessEnvironment::systemEnvironment(); |
| 98 | const QStringList extra = extraSearchPaths(); |
| 99 | if (!extra.isEmpty()) { |
| 100 | #ifdef Q_OS_WIN |
| 101 | const QChar sep = QLatin1Char(';'); |
| 102 | #else |
| 103 | const QChar sep = QLatin1Char(':'); |
| 104 | #endif |
| 105 | const QString current = env.value(QStringLiteral("PATH")); |
| 106 | env.insert(QStringLiteral("PATH"), extra.join(sep) + sep + current); |
| 107 | process->setProcessEnvironment(env); |
| 108 | } |
| 109 | |
| 110 | const int id = m_nextId++; |
| 111 | const QString logName = QFileInfo(program).fileName(); |
| 112 | |
| 113 | connect(process, &QProcess::readyReadStandardOutput, this, [this, id, logName, process]() { |
| 114 | logProcessOutput(id, logName, process->readAllStandardOutput()); |
| 115 | }); |
| 116 | connect( |
| 117 | process, &QProcess::finished, this, [this, id, logName](int exitCode, QProcess::ExitStatus) { |
| 118 | auto* proc = m_processes.take(id); |
| 119 | if (proc) |
| 120 | proc->deleteLater(); |
| 121 | |
| 122 | logLine(id, logName, QStringLiteral("exited with code %1").arg(exitCode)); |
| 123 | Q_EMIT processFinished(id, exitCode); |
| 124 | }); |
| 125 | |
| 126 | process->start(resolved, arguments); |
| 127 | if (!process->waitForStarted(kTerminateGraceMs)) { |
| 128 | error = process->errorString(); |
| 129 | process->deleteLater(); |
| 130 | return -1; |
| 131 | } |
| 132 | |
| 133 | m_processes.insert(id, process); |