aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-04-15 16:57:40 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-04-15 16:57:40 -0500
commit7a865a8b757dc8aa5b521fce2d610fa52a86a9f1 (patch)
tree0fd1e2e7c438b22b154b4d235137c86d86ea5155 /src
parent4cd6aa34160ec97180b41b9658f2ab31264ce1ae (diff)
Abort setup on prefix-init failure, flag broken Proton installs
When stepProtonInit failed, the runner kept marching through every downstream step against a half-initialized prefix, producing a cascade of misleading "wine client error: version mismatch" failures that masked the real problem. Seen in the wild with a GE-Proton10-34 install that was missing concrt140.dll from its bundled default_pfx template: setup_prefix crashes with FileNotFoundError, then seven unrelated steps are reported as broken. Break out of the step loop as soon as proton_init fails, and inspect the captured Proton output for the "FileNotFoundError" + "default_pfx" hint so the error message points at the broken Proton distribution instead of a generic "exit code 1". runProcess gains an optional QByteArray* out param so the caller can pattern-match on the merged output without changing any existing call sites. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'src')
-rw-r--r--src/src/prefixsetuprunner.cpp52
-rw-r--r--src/src/prefixsetuprunner.h8
2 files changed, 51 insertions, 9 deletions
diff --git a/src/src/prefixsetuprunner.cpp b/src/src/prefixsetuprunner.cpp
index 74d59bc..bc1b9ce 100644
--- a/src/src/prefixsetuprunner.cpp
+++ b/src/src/prefixsetuprunner.cpp
@@ -484,8 +484,20 @@ void PrefixSetupRunner::start()
if (m_steps[i].status == SetupStep::Succeeded)
continue;
- allOk = runStep(i) && allOk;
+ const bool stepOk = runStep(i);
+ allOk = stepOk && allOk;
emit progressChanged(static_cast<float>(i + 1) / total);
+
+ // The prefix-init step is a hard prerequisite for everything that
+ // follows. If it fails (e.g. broken Proton install), running downstream
+ // steps just produces a cascade of misleading "version mismatch" errors
+ // against a half-initialized prefix. Surface the real error and stop.
+ if (!stepOk && m_steps[i].id == "proton_init") {
+ emit logMessage(
+ "Prefix initialization failed — skipping remaining setup steps. "
+ "Fix the Proton installation and retry.");
+ break;
+ }
}
emit finished(allOk);
@@ -625,7 +637,8 @@ QProcess* PrefixSetupRunner::buildWrappedProcess(
int PrefixSetupRunner::runProcess(const QString& exe,
const QStringList& args,
const QMap<QString, QString>& extraEnv,
- int timeoutMs)
+ int timeoutMs,
+ QByteArray* captured)
{
QProcess* proc = buildWrappedProcess(exe, extraEnv);
@@ -643,8 +656,10 @@ int PrefixSetupRunner::runProcess(const QString& exe,
if (proc->canReadLine() || proc->bytesAvailable() > 0) {
const QByteArray data = proc->readAll();
- if (!data.isEmpty())
+ if (!data.isEmpty()) {
+ if (captured) captured->append(data);
emitFilteredOutput(this, data);
+ }
}
if (isCancelled()) {
@@ -656,8 +671,10 @@ int PrefixSetupRunner::runProcess(const QString& exe,
}
const QByteArray remaining = proc->readAll();
- if (!remaining.isEmpty())
+ if (!remaining.isEmpty()) {
+ if (captured) captured->append(remaining);
emitFilteredOutput(this, remaining);
+ }
const int exitCode = proc->exitCode();
proc->deleteLater();
@@ -785,12 +802,33 @@ bool PrefixSetupRunner::stepProtonInit()
emit logMessage("Initializing Wine prefix with Proton...");
+ QByteArray protonOutput;
const int rc = runProcess(protonScript,
{"run", "wineboot", "-u"},
- env);
+ env, -1, &protonOutput);
if (rc != 0) {
- m_steps.last().errorMessage =
- QStringLiteral("proton wineboot failed (exit code %1)").arg(rc);
+ // Detect a broken Proton install: setup_prefix copies DLLs from Proton's
+ // bundled default_pfx template, so a FileNotFoundError there means the
+ // Proton distribution itself is missing files — not a Fluorine issue.
+ const QByteArray out = protonOutput;
+ if (out.contains("FileNotFoundError") &&
+ out.contains("default_pfx/drive_c")) {
+ int start = out.indexOf("default_pfx/drive_c");
+ int end = out.indexOf('\'', start);
+ if (end < 0) end = out.indexOf('"', start);
+ const QString missing =
+ (end > start) ? QString::fromUtf8(out.mid(start, end - start))
+ : QStringLiteral("default_pfx/drive_c/...");
+ m_steps.last().errorMessage =
+ QStringLiteral(
+ "Proton install is incomplete: missing '%1'. "
+ "Reinstall or verify your Proton build (e.g. GE-Proton) — "
+ "this is not a Fluorine bug.")
+ .arg(missing);
+ } else {
+ m_steps.last().errorMessage =
+ QStringLiteral("proton wineboot failed (exit code %1)").arg(rc);
+ }
return false;
}
diff --git a/src/src/prefixsetuprunner.h b/src/src/prefixsetuprunner.h
index ed8f12c..5ac71c8 100644
--- a/src/src/prefixsetuprunner.h
+++ b/src/src/prefixsetuprunner.h
@@ -2,6 +2,7 @@
#define PREFIXSETUPRUNNER_H
#include <QAtomicInt>
+#include <QByteArray>
#include <QObject>
#include <QProcess>
#include <QString>
@@ -63,11 +64,14 @@ private:
bool isCancelled() const { return m_cancelled.loadAcquire() != 0; }
/// Run an external process with SLR wrapping and log its output.
- /// Returns exit code (0 = success).
+ /// Returns exit code (0 = success). If captured is non-null, a copy of
+ /// the merged stdout/stderr output is appended to it for post-mortem
+ /// pattern matching.
int runProcess(const QString& exe,
const QStringList& args,
const QMap<QString, QString>& extraEnv,
- int timeoutMs = -1);
+ int timeoutMs = -1,
+ QByteArray* captured = nullptr);
/// Run a plain host command (NO SLR wrapping).
/// Used for host utilities like curl, unzip that must not run in the container.