[rkward] rkward: Add support for auto-detection of R installation (on demand)

Thomas Friedrichsmeier null at kde.org
Mon Dec 30 10:52:31 GMT 2019


Git commit e7234ed17e0436e6d1600a538b7d93eeff2bb648 by Thomas Friedrichsmeier.
Committed on 30/12/2019 at 10:50.
Pushed by tfry into branch 'master'.

Add support for auto-detection of R installation (on demand)

TODO:
- Test on Windows and Mac
- Add documentation
- Add auto-installation of rkward R package to make the feature actually useful

M  +72   -18   rkward/main.cpp

https://commits.kde.org/rkward/e7234ed17e0436e6d1600a538b7d93eeff2bb648

diff --git a/rkward/main.cpp b/rkward/main.cpp
index 90403bb7..d23e1d8c 100644
--- a/rkward/main.cpp
+++ b/rkward/main.cpp
@@ -2,7 +2,7 @@
                           main.cpp  -  description
                              -------------------
     begin                : Tue Oct 29 20:06:08 CET 2002
-    copyright            : (C) 2002-2018 by Thomas Friedrichsmeier
+    copyright            : (C) 2002-2019 by Thomas Friedrichsmeier
     email                : thomas.friedrichsmeier at kdemail.net
  ***************************************************************************/
 
@@ -72,6 +72,8 @@
 #include <QDBusReply>
 #include <QTime>
 #include <QSettings>
+#include <QStandardPaths>
+#include <QVersionNumber>
 
 #ifdef Q_OS_MACOS
 	// Needed to allow execution of launchctl
@@ -166,6 +168,70 @@ 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, QString message) {
+	if (input == QLatin1String ("auto")) {
+		QString ret;
+#ifdef Q_OS_MACOS
+		QString instroot ("/Library/Frameworks/R.framework/Versions");
+		if (QFileInfo (instroot).isReadable ()) {
+			QDir dir (instroot);
+			QStringList candidates = dir.entryList ();
+			QVersionNumber highest (0, 0, 0);
+			for (int i = candidates.count (); i >= 0; --i) {
+				QString found = findExeAtPath ("Resoures/bin/R", dir.absoluteFilePath (candidates[i]));
+				if (!found.isNull()) {
+					QVersionNumber version = QVersionNumber::fromString (candidates[i]);
+					if (version > highest) {
+						ret = found;
+					}
+				}
+			}
+		}
+#endif
+#ifdef Q_OS_WIN
+		QString instroot = QString (getenv ("PROGRAMFILES")) + "/R";
+		if (!QFileInfo (instroot).isReadable ()) instroot = QString (getenv ("PROGRAMFILES(x86)")) + "/R";
+		if (QFileInfo (instroot).isReadable ()) {
+			QDir dir (instroot);
+			QStringList candidates = dir.entryList (QStringList ("R-*"), QDir::Dirs);
+			QVersionNumber highest (0, 0, 0);
+			for (int i = candidates.count (); i >= 0; --i) {
+				QString found = findExeAtPath ("bin/R", dir.absoluteFilePath (candidates[i]));
+				if (!found.isNull()) {
+					QVersionNumber version = QVersionNumber::fromString (candidates[i].mid (2));
+					if (version > highest) {
+						ret = found;
+					}
+				}
+			}
+		}
+#endif
+		// On Unix, but also, if R was not found in the default locations on Windows or Mac,
+		// try to find R in the system path.
+		if (ret.isNull ()) ret = QStandardPaths::findExecutable ("R");
+
+		if (ret.isNull() || !QFileInfo (ret).isExecutable()) {
+			QMessageBox::critical (0, 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 (0, i18n ("Specified R executable does not exist"), message);
+		exit(1);
+	}
+	return QString(); // not reached
+}
+
 int main (int argc, char *argv[]) {
 	RK_Debug::RK_Debug_Level = DL_WARNING;
 	QApplication app (argc, argv);
@@ -178,7 +244,7 @@ int main (int argc, char *argv[]) {
 	KUrlAuthorized::allowUrlAction ("redirect", QUrl("rkward://"), QUrl ("help:"));
 
 	KLocalizedString::setApplicationDomain ("rkward");
-	KAboutData aboutData ("rkward", i18n ("RKWard"), RKWARD_VERSION, i18n ("Frontend to the R statistics language"), KAboutLicense::GPL, i18n ("(c) 2002, 2004 - 2018"), QString (), "http://rkward.kde.org");
+	KAboutData aboutData ("rkward", i18n ("RKWard"), RKWARD_VERSION, i18n ("Frontend to the R statistics language"), KAboutLicense::GPL, i18n ("(c) 2002, 2004 - 2019"), QString (), "http://rkward.kde.org");
 	aboutData.addAuthor (i18n ("Thomas Friedrichsmeier"), i18n ("Project leader / main developer"));
 	aboutData.addAuthor (i18n ("Pierre Ecochard"), i18n ("C++ developer between 2004 and 2007"));
 	aboutData.addAuthor (i18n ("Prasenjit Kapat"), i18n ("Many plugins, suggestions, plot history feature"));
@@ -290,12 +356,7 @@ int main (int argc, char *argv[]) {
 	//- compile-time default
 	QString r_exe = parser.value ("r-executable");
 	if (!r_exe.isNull ()) {
-		if (!QFileInfo (r_exe).isExecutable ()) {
-			// 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 (0, "Specified R executable does not exist", QString ("The R executable specified on the command line (%1) does not exist or is not executable.").arg (r_exe));
-			exit (1);
-		}
+		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 ();
@@ -304,22 +365,15 @@ int main (int argc, char *argv[]) {
 			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)) {
+				if (QDir::isRelativePath (r_exe) && r_exe != QStringLiteral ("auto")) {
 					r_exe = frontend_path.absoluteFilePath (r_exe);
 				}
-				if (!QFileInfo (r_exe).isExecutable ()) {
-					QMessageBox::critical (0, "Specified R executable does not exist", QString ("The R executable specified in the rkward.ini file (%1) does not exist or is not executable.").arg (rkward_ini_file.absoluteFilePath ()));
-					exit (1);
-				}
+				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 = R_EXECUTABLE;
-			if (!QFileInfo (r_exe).isExecutable ()) {
-				QMessageBox::critical (0, "Specified R executable does not exist", QString ("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>PATH_TO_R</i>', or supply an rkward.ini file to specify the new location.").arg (r_exe));
-				exit (1);
-			}
+			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");
 		}
 	}



More information about the rkward-tracker mailing list