#include #include #include #include #include #include #include #include #include #include // Resolve the spike root by walking up from the running binary // until we find a directory containing Caddyfile + bin/frankenphp. static QString resolveSpikeRoot() { QDir d(QCoreApplication::applicationDirPath()); for (int i = 0; i < 6; ++i) { if (QFileInfo(d.filePath("Caddyfile")).exists() && QFileInfo(d.filePath("bin/frankenphp")).exists()) { return d.absolutePath(); } if (!d.cdUp()) break; } qWarning() << "Could not locate spike root from" << QCoreApplication::applicationDirPath(); return QCoreApplication::applicationDirPath(); } // SIGTERM / SIGINT → graceful Qt quit so aboutToQuit cleanup runs. static void installSignalHandlers() { auto handler = +[](int) { QCoreApplication::quit(); }; std::signal(SIGTERM, handler); std::signal(SIGINT, handler); } int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); installSignalHandlers(); const QString spikeRoot = resolveSpikeRoot(); qInfo() << "spike root:" << spikeRoot; QProcess franken; franken.setProgram(spikeRoot + "/bin/frankenphp"); franken.setArguments({"run", "--config", spikeRoot + "/Caddyfile"}); franken.setWorkingDirectory(spikeRoot); franken.setProcessChannelMode(QProcess::ForwardedChannels); // Linux belt-and-suspenders: ask the kernel to SIGTERM the child // when this process dies, regardless of how (crash, kill -9, etc.). franken.setChildProcessModifier([] { prctl(PR_SET_PDEATHSIG, SIGTERM); }); franken.start(); QObject::connect(&app, &QCoreApplication::aboutToQuit, [&]() { if (franken.state() == QProcess::NotRunning) return; franken.terminate(); if (!franken.waitForFinished(2000)) franken.kill(); }); QQmlApplicationEngine engine; engine.loadFromModule("Spike", "Main"); if (engine.rootObjects().isEmpty()) return -1; return app.exec(); }