4#include <QCoreApplication>
6#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
9#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
10#include <QStringDecoder>
24 static_cast<void (QProcess::*)(
int, QProcess::ExitStatus)
>(
27 static_cast<void (
Executor::*)(
int, QProcess::ExitStatus)
>(
28 &Executor::finished));
38void Executor::startProcess(
const QString &app,
const QStringList &args) {
39 if (app.startsWith(
"wsl ")) {
40 QStringList wslArgs = args;
41 QString actualApp = app;
42 wslArgs.prepend(actualApp.remove(0, 4));
43 m_process.start(
"wsl", wslArgs);
45 m_process.start(app, args);
56void Executor::startProcessBlocking(QProcess &internal,
const QString &app,
57 const QStringList &args) {
58 if (app.startsWith(
"wsl ")) {
59 QStringList wslArgs = args;
60 QString actualApp = app;
61 wslArgs.prepend(actualApp.remove(0, 4));
62 internal.start(
"wsl", wslArgs);
64 internal.start(app, args);
71void Executor::executeNext() {
73 if (!m_execQueue.isEmpty()) {
74 const execQueueItem &i = m_execQueue.head();
76 if (!i.workingDir.isEmpty()) {
77 m_process.setWorkingDirectory(i.workingDir);
79 startProcess(i.app, i.args);
80 if (!i.input.isEmpty()) {
81 if (!m_process.waitForStarted(-1)) {
83 dbg() <<
"Process failed to start:" << i.id <<
" " << i.app;
85 m_process.closeWriteChannel();
87 m_execQueue.dequeue();
91 QByteArray data = i.input.toUtf8();
92 if (m_process.write(data) != data.length()) {
94 dbg() <<
"Not all data written to process:" << i.id <<
" " << i.app;
98 m_process.closeWriteChannel();
112 bool readStdout,
bool readStderr) {
113 execute(
id, QString(), app, args, QString(), readStdout, readStderr);
126 const QStringList &args,
bool readStdout,
128 execute(
id, workDir, app, args, QString(), readStdout, readStderr);
141 QString input,
bool readStdout,
bool readStderr) {
142 execute(
id, QString(), app, args, std::move(input), readStdout, readStderr);
157 const QStringList &args, QString input,
bool readStdout,
164 dbg() <<
"Trying to execute nothing...";
168 QString appPath = app;
169 if (!appPath.startsWith(
"wsl ")) {
171 QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(app);
173 m_execQueue.push_back(
174 {id, appPath, args, std::move(input), readStdout, readStderr, workDir});
188static auto decodeAssumingUtf8(
const QByteArray &in) -> QString {
189#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
190 QTextCodec *codec = QTextCodec::codecForName(
"UTF-8");
191 QTextCodec::ConverterState state;
192 QString out = codec->toUnicode(in.constData(), in.size(), &state);
193 if (state.invalidChars == 0) {
196 codec = QTextCodec::codecForUtfText(in);
197 return codec->toUnicode(in);
199 auto converter = QStringDecoder(QStringDecoder::Utf8);
200 QString out = converter(in);
201 if (!converter.hasError()) {
205 auto fallback = QStringDecoder(QStringDecoder::System);
224 const QString &input, QString *process_out,
225 QString *process_err) ->
int {
227 startProcessBlocking(internal, app, args);
228 if (!internal.waitForStarted(-1)) {
230 dbg() <<
"Process failed to start:" << app;
234 if (!input.isEmpty()) {
235 QByteArray data = input.toUtf8();
236 if (internal.write(data) != data.length()) {
238 dbg() <<
"Not all input written:" << app;
241 internal.closeWriteChannel();
243 internal.waitForFinished(-1);
244 if (internal.exitStatus() == QProcess::NormalExit) {
245 QString pout = decodeAssumingUtf8(internal.readAllStandardOutput());
246 QString perr = decodeAssumingUtf8(internal.readAllStandardError());
247 if (process_out !=
nullptr) {
250 if (process_err !=
nullptr) {
253 return internal.exitCode();
269 QString *process_out, QString *process_err)
285 const QStringList &args, QString *process_out,
286 QString *process_err) ->
int {
288 QProcessEnvironment penv;
289 for (
const QString &var : env) {
290 int idx = var.indexOf(
'=');
292 penv.insert(var.left(idx), var.mid(idx + 1));
295 process.setProcessEnvironment(penv);
296 startProcessBlocking(process, app, args);
297 if (!process.waitForStarted(-1)) {
299 dbg() <<
"Process failed to start:" << app;
303 process.waitForFinished(-1);
304 if (process.exitStatus() != QProcess::NormalExit) {
308 *process_out = decodeAssumingUtf8(process.readAllStandardOutput());
311 *process_err = decodeAssumingUtf8(process.readAllStandardError());
313 return process.exitCode();
322 m_process.setEnvironment(env);
332 if (running || m_execQueue.isEmpty()) {
336 return m_execQueue.dequeue().id;
344void Executor::finished(
int exitCode, QProcess::ExitStatus exitStatus) {
345 execQueueItem i = m_execQueue.dequeue();
347 if (exitStatus == QProcess::NormalExit) {
351 output = decodeAssumingUtf8(m_process.readAllStandardOutput());
353 if (i.readStderr || exitCode != 0) {
354 err = decodeAssumingUtf8(m_process.readAllStandardError());
357 dbg() << exitCode << err;
361 emit finished(i.id, exitCode, output, err);
static auto executeBlocking(QString app, const QStringList &args, const QString &input=QString(), QString *process_out=nullptr, QString *process_err=nullptr) -> int
Executor::executeBlocking blocking version of the executor, takes input and presents it as stdin.
void execute(int id, const QString &app, const QStringList &args, bool readStdout, bool readStderr=true)
Executor::execute execute an app.
Executor(QObject *parent=nullptr)
Executor::Executor executes external applications.
void setEnvironment(const QStringList &env)
Executor::setEnvironment set environment variables for executor processes.
void starting()
starting signal that is emited when process starts
auto cancelNext() -> int
Executor::cancelNext cancels execution of first process in queue if it's not already running.
Debug utilities for QtPass.
#define dbg()
Simple debug macro that includes file and line number.