[education/rkward] rkward: Move detection of R installation closer to the backend

Thomas Friedrichsmeier null at kde.org
Sat Jun 8 23:21:01 BST 2024


Git commit a2d710d221806ec16721c70ba8f49326867b982b by Thomas Friedrichsmeier.
Committed on 08/06/2024 at 22:10.
Pushed by tfry into branch 'master'.

Move detection of R installation closer to the backend

M  +0    -2    rkward/CMakeLists.txt
M  +1    -1    rkward/dialogs/rksetupwizard.cpp
M  +0    -61   rkward/main.cpp
M  +1    -0    rkward/rbackend/CMakeLists.txt
M  +59   -3    rkward/rbackend/rkfrontendtransmitter.cpp
M  +3    -0    rkward/rbackend/rkfrontendtransmitter.h
M  +0    -1    rkward/rbackend/rkrinterface.cpp
M  +1    -1    rkward/rbackend/rksessionvars.h

https://invent.kde.org/education/rkward/-/commit/a2d710d221806ec16721c70ba8f49326867b982b

diff --git a/rkward/CMakeLists.txt b/rkward/CMakeLists.txt
index 1ab4e48b8..331e4e0b7 100644
--- a/rkward/CMakeLists.txt
+++ b/rkward/CMakeLists.txt
@@ -37,12 +37,10 @@ SET(RKWard_Lib_Sources
 QT_ADD_RESOURCES(RKWard_Lib_Sources resources.qrc icons/icons.qrc)
 
 # somehow the detected R paths from FindR.cmake do not get passed down automatically
-GET_DIRECTORY_PROPERTY(R_EXECUTABLE DIRECTORY rbackend DEFINITION R_EXECUTABLE)
 GET_DIRECTORY_PROPERTY(R_SHAREDLIBDIR DIRECTORY rbackend LINK_DIRECTORIES)
 LINK_DIRECTORIES(${R_SHAREDLIBDIR})
 
 ADD_LIBRARY(rkward_lib STATIC ${RKWard_Lib_Sources})
-TARGET_COMPILE_DEFINITIONS(rkward_lib PUBLIC -DR_EXECUTABLE="${R_EXECUTABLE}")
 TARGET_LINK_LIBRARIES(rkward_lib windows ${RKWARD_ADDLIBS} agents dialogs plugin settings dataeditor core scriptbackends rbackend misc KDAB::kdsingleapplication KF6::WindowSystem Qt6::Widgets KF6::XmlGui)
 
 SET(RKWard_App_Sources
diff --git a/rkward/dialogs/rksetupwizard.cpp b/rkward/dialogs/rksetupwizard.cpp
index 88295fbdd..c1610073d 100644
--- a/rkward/dialogs/rksetupwizard.cpp
+++ b/rkward/dialogs/rksetupwizard.cpp
@@ -112,7 +112,7 @@ public:
 		h->addWidget(rstatus_icon);
 		h->addWidget(rstatus_label);
 		h->setStretch(1, 2);
-		detail_button = new QPushButton(i18n("Show error details"));
+		detail_button = new QPushButton(i18n("Show problem details"));
 		connect(detail_button, &QPushButton::clicked, this, [this]() {
 			if (!backend_error.details.isEmpty()) {
 				// WORKAROUND for silly KMessageBox behavior. (still needed in KF6 6.3.0)
diff --git a/rkward/main.cpp b/rkward/main.cpp
index 8071a01b4..1933b5e26 100644
--- a/rkward/main.cpp
+++ b/rkward/main.cpp
@@ -61,8 +61,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
 #include <QApplication>
 #include <QUrl>
 #include <QTime>
-#include <QSettings>
-#include <QStandardPaths>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -85,10 +83,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
 
 #include "version.h"
 
-#ifndef R_EXECUTABLE
-#error config problem
-#endif
-
 #ifdef Q_OS_WIN
 #	define PATH_VAR_SEP ';'
 #else
@@ -142,32 +136,6 @@ void RKDebug (int flags, int level, const char *fmt, ...) {
 	}
 }
 
-/** Check if the given path to R (or "auto") is executable, and fail with an appropriate message, otherwise. If "auto" is given as input, try to auto-locate an R installation at the standard
-installation path(s) for this platform. */
-QString resolveRSpecOrFail (QString input, const QString &message) {
-	if (input == QLatin1String ("auto")) {
-		QString ret = RKSessionVars::findRInstallations().value(0);
-
-		if (ret.isNull() || !QFileInfo (ret).isExecutable()) {
-			QMessageBox::critical(nullptr, i18n("Unable to detect R installation"), i18n("RKWard failed to detect an R installation on this system. Either R is not installed, or not at one of the standard installation locations. You can use the command line parameter '--r-executable <i>auto / PATH_TO_R</i>', or supply an rkward.ini file to specify a non-standard location."));
-			exit (1);
-		}
-
-		RK_DEBUG (APP, DL_DEBUG, "Using auto-detected R at %s", qPrintable (ret));
-		return ret;
-	} else {
-		if (QFileInfo (input).isExecutable ()) {
-			return input;
-		}
-
-		// TODO, while fixing krazy2 warnings: KMessageBox layout for static messages is quirky in that it has squeezed caption, and does not allow resize -> Submit a patch.
-		//KMessageBox::error (0, QString ("The R executable specified on the command line (%1) does not exist or is not executable.").arg (r_exe), "Specified R executable does not exist");
-		QMessageBox::critical(nullptr, i18n("Specified R executable does not exist"), message);
-		exit(1);
-	}
-	return QString(); // not reached
-}
-
 #include <QWebEngineUrlScheme>
 
 int main (int argc, char *argv[]) {
@@ -279,35 +247,6 @@ int main (int argc, char *argv[]) {
 		}
 	}
 
-	// Look for R:
-	//- command line parameter
-	//- Specified in cfg file next to rkward executable
-	//- compile-time default
-	QString r_exe = args[RKCommandLineArgs::RExecutable].toString();
-	if (!r_exe.isNull ()) {
-		r_exe = resolveRSpecOrFail (r_exe, i18n ("The R executable specified on the command line (%1) does not exist or is not executable.", r_exe));
-		RK_DEBUG (APP, DL_DEBUG, "Using R specified on command line");
-	} else {
-		QDir frontend_path = app.applicationDirPath ();
-		QFileInfo rkward_ini_file (frontend_path.absoluteFilePath ("rkward.ini"));
-		if (rkward_ini_file.isReadable ()) {
-			QSettings rkward_ini (rkward_ini_file.absoluteFilePath (), QSettings::IniFormat);
-			r_exe = rkward_ini.value ("R executable").toString ();
-			if (!r_exe.isNull ()) {
-				if (QDir::isRelativePath (r_exe) && r_exe != QStringLiteral ("auto")) {
-					r_exe = frontend_path.absoluteFilePath (r_exe);
-				}
-				r_exe = resolveRSpecOrFail (r_exe, i18n ("The R executable (%1) specified in the rkward.ini file (%2) does not exist or is not executable.", r_exe, rkward_ini_file.absoluteFilePath ()));
-			}
-			RK_DEBUG (APP, DL_DEBUG, "Using R as configured in config file %s", qPrintable (rkward_ini_file.absoluteFilePath ()));
-		}
-		if (r_exe.isNull ()) {
-			r_exe = resolveRSpecOrFail (R_EXECUTABLE, i18n ("The R executable specified at compile time (%1) does not exist or is not executable. Probably the installation of R has moved. You can use the command line parameter '--r-executable <i>auto / PATH_TO_R</i>', or supply an rkward.ini file to specify the new location.", QString (R_EXECUTABLE)));
-			RK_DEBUG (APP, DL_DEBUG, "Using R as configured at compile time");
-		}
-	}
-	RKSessionVars::r_binary = r_exe;
-
 	if (app.isSessionRestored ()) {
 		kRestoreMainWindows<RKWardMainWindow>();	// well, whatever this is supposed to do -> TODO
 	} else {
diff --git a/rkward/rbackend/CMakeLists.txt b/rkward/rbackend/CMakeLists.txt
index 2c6ff0480..be7004d2e 100644
--- a/rkward/rbackend/CMakeLists.txt
+++ b/rkward/rbackend/CMakeLists.txt
@@ -44,6 +44,7 @@ SET (
 )
 
 ADD_LIBRARY(rbackend STATIC ${rbackend_frontend_SRCS} ${rbackend_shared_SRCS})
+TARGET_COMPILE_DEFINITIONS(rbackend PUBLIC -DR_EXECUTABLE="${R_EXECUTABLE}")
 TARGET_LINK_LIBRARIES(rbackend rkgraphicsdevice.frontend Qt6::Widgets KF6::TextEditor KF6::WindowSystem)
 
 ADD_DEFINITIONS (-DRKWARD_BACKEND_PATH="${KDE_INSTALL_FULL_LIBEXECDIR}")
diff --git a/rkward/rbackend/rkfrontendtransmitter.cpp b/rkward/rbackend/rkfrontendtransmitter.cpp
index 9f6d2ce21..10a80d371 100644
--- a/rkward/rbackend/rkfrontendtransmitter.cpp
+++ b/rkward/rbackend/rkfrontendtransmitter.cpp
@@ -25,6 +25,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
 #include <QStandardPaths>
 #include <QElapsedTimer>
 #include <QTemporaryDir>
+#include <QSettings>
 
 #include "../version.h"
 #include "../debug.h"
@@ -107,10 +108,65 @@ QString localeDir () {
 	return QFileInfo (file.left (file.size() - relpath.size ())).absolutePath ();
 }
 
+/** Check if the given path to R (or "auto") is executable, and fail with an appropriate message, otherwise. If "auto" is given as input, try to auto-locate an R installation at the standard
+installation path(s) for this platform. */
+QString RKFrontendTransmitter::resolveRSpecOrFail(QString input) {
+	if (input == QLatin1String("auto")) {
+		QString ret = RKSessionVars::findRInstallations().value(0);
+
+		if (ret.isNull() || !QFileInfo(ret).isExecutable()) {
+			handleTransmissionError(i18n("RKWard failed to detect an R installation on this system. Either R is not installed, or not at one of the standard installation locations."));
+		}
+		RK_DEBUG (APP, DL_DEBUG, "Using auto-detected R at %s", qPrintable (ret));
+		return ret;
+	}
+
+	if (!QFileInfo(input).isExecutable()) {
+		handleTransmissionError(i18n("The configured R installation at <b>%1</b> does not exist or is not an executable.", input));
+	}
+	return input;
+}
+
+void RKFrontendTransmitter::detectAndCheckRBinary() {
+	if (!RKSessionVars::RBinary().isEmpty()) return;
+
+	// Look for R:
+	//- command line parameter
+	//- Specified in cfg file next to rkward executable
+	//- compile-time default
+	QString r_exe = RKCommandLineArgs::get(RKCommandLineArgs::RExecutable).toString();
+	if (!r_exe.isEmpty()) {
+		RK_DEBUG(APP, DL_DEBUG, "Using R as specified on command line");
+	} else {
+		QDir frontend_path = qApp->applicationDirPath();
+		QFileInfo rkward_ini_file(frontend_path.absoluteFilePath("rkward.ini"));
+		if (rkward_ini_file.isReadable()) {
+			QSettings rkward_ini(rkward_ini_file.absoluteFilePath(), QSettings::IniFormat);
+			r_exe = rkward_ini.value("R executable").toString ();
+			if (!r_exe.isNull()) {
+				if (QDir::isRelativePath(r_exe) && r_exe != QStringLiteral("auto")) {
+					r_exe = frontend_path.absoluteFilePath (r_exe);
+				}
+			}
+			RK_DEBUG(APP, DL_DEBUG, "Using R as configured in config file %s", qPrintable (rkward_ini_file.absoluteFilePath ()));
+		} else {
+			RK_DEBUG(APP, DL_DEBUG, "Using R as configured at compile time");
+			r_exe = R_EXECUTABLE;
+		}
+	}
+	if (r_exe.isEmpty()) {
+		RK_DEBUG(APP, DL_DEBUG, "Falling back to auto-detection of R binary");
+		r_exe = "auto";
+	}
+
+	RKSessionVars::r_binary = resolveRSpecOrFail(r_exe);
+}
+
 void RKFrontendTransmitter::run () {
 	RK_TRACE (RBACKEND);
 
 	quirkmode = RKCommandLineArgs::get(RKCommandLineArgs::QuirkMode).toBool();
+	detectAndCheckRBinary();
 
 	// start server
 	server = new QLocalServer (this);
@@ -320,9 +376,9 @@ void RKFrontendTransmitter::requestReceived (RBackendRequest* request) {
 void RKFrontendTransmitter::backendExit (int exitcode) {
 	RK_TRACE (RBACKEND);
 
-	if (!exitcode && token.isEmpty ()) handleTransmissionError (i18n ("The backend process could not be started. Please check your installation."));
-	else if (token.isEmpty ()) handleTransmissionError (i18n ("The backend process failed to start with exit code %1, message: '%2'.", exitcode, QString::fromLocal8Bit(backend->readAllStandardError())));
-	else handleTransmissionError (i18n ("Backend process has exited with code %1, message: '%2'.", exitcode, QString::fromLocal8Bit(backend->readAllStandardError())));
+	if (!exitcode && token.isEmpty()) handleTransmissionError(i18n("The backend process could not be started. Please check your installation."));
+	else if (token.isEmpty()) handleTransmissionError(i18n("The backend process failed to start with exit code %1, message: '%2'.", exitcode, QString::fromLocal8Bit(backend->readAllStandardError().replace('\n', "<br>"))));
+	else handleTransmissionError(i18n("Backend process has exited with code %1, message: '%2'.", exitcode, QString::fromLocal8Bit(backend->readAllStandardError().replace('\n', "<br>"))));
 }
 
 void RKFrontendTransmitter::writeRequest (RBackendRequest *request) {
diff --git a/rkward/rbackend/rkfrontendtransmitter.h b/rkward/rbackend/rkfrontendtransmitter.h
index eb0bd8b56..4c46d7fd7 100644
--- a/rkward/rbackend/rkfrontendtransmitter.h
+++ b/rkward/rbackend/rkfrontendtransmitter.h
@@ -44,6 +44,9 @@ private:
 	QLocalServer* server;
 	RKRBackendProtocolFrontend* frontend;
 	RKGraphicsDeviceFrontendTransmitter* rkd_transmitter;
+
+	QString resolveRSpecOrFail(QString input);
+	void detectAndCheckRBinary();
 };
 
 #endif
diff --git a/rkward/rbackend/rkrinterface.cpp b/rkward/rbackend/rkrinterface.cpp
index 67ad7607b..524a1112f 100644
--- a/rkward/rbackend/rkrinterface.cpp
+++ b/rkward/rbackend/rkrinterface.cpp
@@ -130,7 +130,6 @@ void RInterface::reportFatalError() {
 	RK_TRACE(RBACKEND);
 
 	RKErrorDialog::reportableErrorMessage(nullptr, backend_error.message, backend_error.details, backend_error.title, backend_error.id);
-	backend_error = BackendError();
 }
 
 bool RInterface::backendIsIdle () {
diff --git a/rkward/rbackend/rksessionvars.h b/rkward/rbackend/rksessionvars.h
index 332fc0e63..eac63ce7c 100644
--- a/rkward/rbackend/rksessionvars.h
+++ b/rkward/rbackend/rksessionvars.h
@@ -52,7 +52,7 @@ private:
 	static RKParsedVersion r_version;
 	static QString r_version_string;
 	static QString appimagedir;
-friend int main(int, char**);
+friend class RKFrontendTransmitter;
 friend class RKWardCoreTest;
 friend class RKSetupWizard;
 	static QString r_binary;



More information about the rkward-tracker mailing list