Commit e6610f33 authored by Thomas Friedrichsmeier's avatar Thomas Friedrichsmeier
Browse files

EXPERIMENTAL: Remove the startup wrapper script, moving funcationality to the...

EXPERIMENTAL: Remove the startup wrapper script, moving funcationality to the main executable, as needed.

Should address a number of quirks (esp. on Windows and Mac), hopefully without adding  worse bugs in their place.
parent 86fb0a75
- Remove startup wrapper script (moving the still-needed functionality into the main executable)
TODO: This is still mostly EXPERIMENTAL, and only vaguely tested on Linux so far. It should help resolve a number of long standing quirks, though, such as command windows (on Windows), icons,
startup notification quirks, etc.
TODO: Clean up the Mac portions in CMakeLists.txt
- Function argument hinting is less easily fooled by braces inside quotes
- Preview status messages can now be closed
- Show the message accompanying rk.show.files() or rk.edit.files() inside the main window, instead of a separate dialog
......
......@@ -36,7 +36,6 @@
<group choice="opt"><option>--evaluate</option> <replaceable> Rcode</replaceable></group>
<group choice="opt"><option>--debug-level</option> <replaceable> level</replaceable></group>
<group choice="opt"><option>--debug-flags</option> <replaceable> flags</replaceable></group>
<arg><option>--debugger</option> <replaceable>debugger_command</replaceable><arg choice="opt">debugger_args<arg choice="opt"><option>--</option></arg></arg></arg>
<group choice="opt"><option>--backend-debugger</option> <replaceable> debugger_command</replaceable></group>
<group choice="opt"><option>--r-executable</option> <replaceable> path_to_executable</replaceable></group>
<group choice="opt"><option>--reuse</option></group>
......@@ -70,12 +69,6 @@
<listitem><para>Allows to configure, which sections of code to debug. Flags are given as a binary number. Refer to the source files for documentation, as this really is an internal option.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--debugger</option> <replaceable>command</replaceable> [<replaceable>arguments</replaceable> [<replaceable>--</replaceable>]]</term>
<listitem><para>Run &rkward; through the specified debugger command. All arguments following this will be passed to the debugger command. To end debugger arguments (and add arguments to pass to &rkward;), use "--". <emphasis>NOTE:</emphasis> Only the frontend process will be debugged, using this option.</para>
<para>Note that there are a number of pitfalls that may complicate setting up the debugger session as desired. Consider starting &rkward; with option \-\-debug-lebel 3, which will print the effective command line used to start the frontend (but not all relevant environment variables). As one hint, you will generally need to pass a separator argument with the debugger arguments, e.g. <command>rkward --debugger gdb --args</command>.
Under Windows, the debugger command will <emphasis>not</emphasis> be connected to stdin. For interactive debugging, consider using a graphical debugger.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--backend-debugger</option> <replaceable>command</replaceable></term>
<listitem><para>Run the &rkward; backend through the specified debugger command. To add command line options to the debugger command, enclose them in single quotes ('') together with the command. <emphasis>NOTE:</emphasis> Debugger arguments will be split by spaces. If this is not appropriate, you will have to write your own wrapper script for invoking the debugger. Also, make sure to redirect all debugger output and/or input as appropriate. See the examples.</para></listitem>
</varlistentry>
......@@ -115,8 +108,6 @@ rkward --reuse file_a.R file_b.R
# Run the rkward backend through valgrind
rkward --backend-debugger 'valgrind --log-file=valgrind.log'.
# Debug the frontend through gdb
rkward --debugger 'gdb --args'
</programlisting></para>
</refsect1>
......
......@@ -40,8 +40,18 @@ SET(RKWard_Sources
GET_DIRECTORY_PROPERTY(R_SHAREDLIBDIR DIRECTORY rbackend LINK_DIRECTORIES)
LINK_DIRECTORIES(${R_SHAREDLIBDIR})
add_executable(rkward.frontend ${RKWard_Sources})
SET_TARGET_PROPERTIES(rkward.frontend PROPERTIES
GET_DIRECTORY_PROPERTY(R_EXECUTABLE DIRECTORY rbackend DEFINITION R_EXECUTABLE)
ADD_CUSTOM_COMMAND (OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rkward.ico"
COMMAND cmake -E copy "${CMAKE_CURRENT_SOURCE_DIR}/icons/app-icon/rkward.ico"
"${CMAKE_CURRENT_BINARY_DIR}/rkward.ico")
add_executable(rkward ${RKWard_Sources} rkward_windows_icon.rc rkward.ico)
# NOTE: These definitions are needed for the wrapper, only.
# We should switch with to target_compile_definitions once we require CMAKE 2.6+
add_definitions (-DR_EXECUTABLE="${R_EXECUTABLE}")
add_definitions (-DINSTALL_PATH="${CMAKE_INSTALL_PREFIX}")
add_definitions (-DR_LIBS="${R_LIBDIR}")
SET_TARGET_PROPERTIES(rkward PROPERTIES
MACOSX_BUNDLE_BUNDLE_NAME "RKWard")
IF(Q_WS_MAC)
......@@ -53,31 +63,12 @@ IF(Q_WS_MAC)
@ONLY)
ENDIF(Q_WS_MAC)
TARGET_LINK_LIBRARIES(rkward.frontend windows ${RKWARD_ADDLIBS} agents dialogs plugin settings dataeditor core scriptbackends rbackend misc KF5::WindowSystem Qt5::Widgets KF5::XmlGui ${LibIntl_LIBRARIES})
TARGET_LINK_LIBRARIES(rkward windows ${RKWARD_ADDLIBS} agents dialogs plugin settings dataeditor core scriptbackends rbackend misc KF5::WindowSystem Qt5::Widgets KF5::XmlGui ${LibIntl_LIBRARIES})
IF(KF5Crash_FOUND)
TARGET_LINK_LIBRARIES(rkward.frontend KF5::Crash)
TARGET_LINK_LIBRARIES(rkward KF5::Crash)
SET_SOURCE_FILES_PROPERTIES(main.cpp PROPERTIES COMPILE_DEFINITIONS WITH_KCRASH=1)
ENDIF(KF5Crash_FOUND)
# wrapper executable
GET_DIRECTORY_PROPERTY(R_EXECUTABLE DIRECTORY rbackend DEFINITION R_EXECUTABLE)
IF (WIN32)
SET (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
ENDIF (WIN32)
ADD_CUSTOM_COMMAND (OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rkward.ico"
COMMAND cmake -E copy "${CMAKE_CURRENT_SOURCE_DIR}/icons/app-icon/rkward.ico"
"${CMAKE_CURRENT_BINARY_DIR}/rkward.ico")
ADD_EXECUTABLE (rkward rkward_startup_wrapper.cpp rkward_windows_icon.rc rkward.ico)
# NOTE: These definitions are needed for the wrapper, only.
# We should switch with to target_compile_definitions once we require CMAKE 2.6+
add_definitions (-DR_EXECUTABLE="${R_EXECUTABLE}")
add_definitions (-DINSTALL_PATH="${CMAKE_INSTALL_PREFIX}")
add_definitions (-DR_LIBS="${R_LIBDIR}")
add_definitions (-DRKWARD_FRONTEND_LOCATION="${CMAKE_INSTALL_PREFIX}/${RKWARD_FRONTEND_LOCATION}")
TARGET_LINK_LIBRARIES(rkward Qt5::Gui Qt5::Core Qt5::DBus Qt5::Widgets)
ADD_DEPENDENCIES(rkward.frontend rkward)
########### install files ###############
INSTALL(DIRECTORY plugins/ pages DESTINATION ${DATA_INSTALL_DIR}/rkward
......@@ -92,7 +83,6 @@ IF(Q_WS_MAC)
INSTALL(FILES ${CMAKE_SOURCE_DIR}/rkward/icons/app-icon/rkward.icns DESTINATION ${BUNDLE_INSTALL_DIR}/${CPACK_BUNDLE_NAME}.app/Contents/Resources)
INSTALL(FILES ${RKWARD_INFOPLIST_FILE} DESTINATION ${BUNDLE_INSTALL_DIR}/${CPACK_BUNDLE_NAME}.app/Contents)
ELSE(Q_WS_MAC)
INSTALL(TARGETS rkward.frontend DESTINATION ${LIBEXEC_INSTALL_DIR})
INSTALL(TARGETS rkward DESTINATION ${BIN_INSTALL_DIR})
ENDIF(Q_WS_MAC)
......
......@@ -66,6 +66,9 @@
#include <QApplication>
#include <QUrl>
#include <QCommandLineParser>
#include <QtDBus>
#include <QSettings>
#include <QMessageBox>
#include <stdio.h>
#include <stdlib.h>
......@@ -74,6 +77,8 @@
#include "rkglobals.h"
#include "settings/rksettingsmoduledebug.h"
#include "windows/rkdebugmessagewindow.h"
#include "misc/rkdbusapi.h"
#include "misc/rkcommonfunctions.h"
#ifdef Q_OS_WIN
// these are needed for the exit hack.
......@@ -84,6 +89,28 @@
#include "version.h"
#ifndef R_EXECUTABLE
# define R_EXECUTABLE ""
#endif
#ifdef Q_OS_WIN
# define PATH_VAR_SEP ';'
#else
# define PATH_VAR_SEP ':'
#endif
QString findExeAtPath (const QString appname, const QString &path) {
QDir dir (path);
dir.makeAbsolute ();
if (QFileInfo (dir.filePath (appname)).isExecutable ()) return dir.filePath (appname);
#ifdef Q_OS_WIN
if (QFileInfo (dir.filePath (appname + ".exe")).isExecutable ()) return dir.filePath (appname + ".exe");
if (QFileInfo (dir.filePath (appname + ".com")).isExecutable ()) return dir.filePath (appname + ".com");
if (QFileInfo (dir.filePath (appname + ".bat")).isExecutable ()) return dir.filePath (appname + ".bat");
#endif
return QString ();
}
int RK_Debug_Level = 0;
int RK_Debug_Flags = DEBUG_ALL;
int RK_Debug_CommandStep = 0;
......@@ -119,21 +146,12 @@ void RKDebug (int flags, int level, const char *fmt, ...) {
}
}
QString decodeArgument (const QString &input) {
return (QUrl::fromPercentEncoding (input.toUtf8()));
}
int main (int argc, char *argv[]) {
// before initializing the commandline args, remove the ".bin" from "rkward.bin".
// This is so it prints "Usage rkward..." instead of "Usage rkward.bin...", etc.
// it seems safest to keep a copy, since the shell still owns argv
char **argv_copy = new char*[argc];
argv_copy[0] = qstrdup (QString (argv[0]).remove (".frontend").replace (".exe", ".bat").toLocal8Bit ());
for (int i = 1; i < argc; ++i) {
argv_copy[i] = argv[i];
}
// TODO: This is a _temporary_ hack! --> Problems with output not updating on Windows Live Image. See https://mail.kde.org/pipermail/rkward-devel/2016-September/004660.html .
#warning Remove me!
qputenv ("KDIRWATCH_METHOD", QByteArray ("Stat"));
QApplication app (argc, argv_copy);
QApplication app (argc, argv);
#ifdef WITH_KCRASH
KCrash::setDrKonqiEnabled (true);
#endif
......@@ -174,7 +192,6 @@ int main (int argc, char *argv[]) {
parser.addOption (QCommandLineOption ("evaluate", i18n ("After starting (and after loading the specified workspace, if applicable), evaluate the given R code."), "Rcode", QString ()));
parser.addOption (QCommandLineOption ("debug-level", i18n ("Verbosity of debug messages (0-5)"), "level", "2"));
parser.addOption (QCommandLineOption ("debug-flags", i18n ("Mask for components to debug (see debug.h)"), "flags", QString::number (DEBUG_ALL)));
parser.addOption (QCommandLineOption ("debugger", i18n ("Debugger for the frontend. Specify last, or add '--' after all debugger arguments"), "command and arguments", QString ()));
parser.addOption (QCommandLineOption ("backend-debugger", i18n ("Debugger for the backend. (Enclose any debugger arguments in single quotes ('') together with the command. Make sure to re-direct stdout!)"), "command", QString ()));
parser.addOption (QCommandLineOption ("r-executable", i18n ("Use specified R installation, instead of the one configured at compile time (note: rkward R library must be installed to that installation of R)"), "command", QString ()));
parser.addOption (QCommandLineOption ("reuse", i18n ("Reuse a running RKWard instance (if available). If a running instance is reused, only the file arguments will be interpreted, all other options will be ignored.")));
......@@ -185,30 +202,124 @@ int main (int argc, char *argv[]) {
parser.process (app);
aboutData.processCommandLine (&parser);
// Set up debugging
RK_Debug_Level = DL_FATAL - QString (parser.value ("debug-level")).toInt ();
RK_Debug_Flags = QString (parser.value ("debug-flags")).toInt ();
if (!parser.value ("debugger").isEmpty ()) {
RK_DEBUG (DEBUG_ALL, DL_ERROR, "--debugger option should have been handled by wrapper script. Ignoring.");
RKSettingsModuleDebug::debug_file = new QTemporaryFile (QDir::tempPath () + "/rkward.frontend");
RKSettingsModuleDebug::debug_file->setAutoRemove (false);
if (RKSettingsModuleDebug::debug_file->open ()) {
RK_DEBUG (APP, DL_INFO, "Full debug output is at %s", qPrintable (RKSettingsModuleDebug::debug_file->fileName ()));
qInstallMessageHandler (RKDebugMessageOutput);
}
// handle positional (file) arguments, first
QStringList url_args = parser.positionalArguments ();
if (!url_args.isEmpty ()) {
for (int i = 0; i < url_args.size (); ++i) {
url_args[i] = decodeArgument (url_args[i]);
url_args[i] = QUrl::fromUserInput (url_args[i], QDir::currentPath (), QUrl::AssumeLocalFile).toString ();
}
RKGlobals::startup_options["initial_urls"] = url_args;
RKGlobals::startup_options["warn_external"] = !parser.isSet ("nowarn-external");
}
RKGlobals::startup_options["evaluate"] = decodeArgument (parser.value ("evaluate"));
RKGlobals::startup_options["backend-debugger"] = decodeArgument (parser.value ("backend-debugger"));
RKGlobals::startup_options["evaluate"] = parser.value ("evaluate");
RKGlobals::startup_options["backend-debugger"] = parser.value ("backend-debugger");
// install message handler *after* the componentData has been initialized
RKSettingsModuleDebug::debug_file = new QTemporaryFile (QDir::tempPath () + "/rkward.frontend");
RKSettingsModuleDebug::debug_file->setAutoRemove (false);
if (RKSettingsModuleDebug::debug_file->open ()) {
RK_DEBUG (APP, DL_INFO, "Full debug output is at %s", qPrintable (RKSettingsModuleDebug::debug_file->fileName ()));
qInstallMessageHandler (RKDebugMessageOutput);
// Handle --reuse option, by placing a dbus-call to existing RKWard process (if any) and exiting
if (parser.isSet ("reuse")) {
if (!QDBusConnection::sessionBus ().isConnected ()) {
RK_DEBUG (DEBUG_ALL, DL_WARNING, "Could not connect to session dbus");
} else {
QDBusInterface iface (RKDBUS_SERVICENAME, "/", "", QDBusConnection::sessionBus ());
if (iface.isValid ()) {
QDBusReply<void> reply = iface.call ("openAnyUrl", url_args, !parser.isSet ("nowarn-external"));
if (!reply.isValid ()) {
RK_DEBUG (DEBUG_ALL, DL_ERROR, "Error while placing dbus call: %s", qPrintable (reply.error ().message ()));
return 1;
}
return 0;
}
}
}
// MacOS may need some path adjustments, first
#ifdef Q_OS_MAC
QString oldpath = qgetenv ("PATH");
if (!oldpath.contains (INSTALL_PATH)) {
//ensure that PATH is set to include what we deliver with the bundle
qputenv ("PATH", QString ("%1/bin:%1/sbin:%2").arg (INSTALL_PATH).arg (oldpath).toLocal8Bit ());
if (debug_level > 3) qDebug ("Adjusting system path to %s", qPrintable (qgetenv ("PATH")));
}
// ensure that RKWard finds its own packages
qputenv ("R_LIBS", R_LIBS);
QProcess::execute ("launchctl", QStringList () << "load" << "-w" << INSTALL_PATH "/Library/LaunchAgents/org.freedesktop.dbus-session.plist");
#endif
// Locate KDE and RKWard installations
QString marker_exe_name ("qtpaths"); // Simply some file that should exist in the bin dir of a KDE installation on both Unix and Windows
QString marker_exe = findExeAtPath (marker_exe_name, QDir::currentPath ());
if (marker_exe.isNull ()) marker_exe = findExeAtPath (marker_exe_name, app.applicationDirPath ());
if (marker_exe.isNull ()) marker_exe = findExeAtPath (marker_exe_name, QDir (app.applicationDirPath ()).filePath ("KDE/bin"));
QStringList syspath = QString (qgetenv ("PATH")).split (PATH_VAR_SEP);
if (marker_exe.isNull ()) {
for (int i = 0; i < syspath.size (); ++i) {
marker_exe = findExeAtPath (marker_exe_name, syspath[i]);
if (!marker_exe.isNull ()) break;
}
}
if (marker_exe.isNull ()) {
QMessageBox::critical (0, "Could not find KDE installation", "The KDE installation could not be found (" + marker_exe_name + "). When moving / copying RKWard, make sure to copy the whole application folder, or create a shorcut / link, instead.");
exit (1);
}
QDir kde_dir (QFileInfo (marker_exe).absolutePath ());
kde_dir.makeAbsolute ();
QString kde_dir_safe_path = RKCommonFunctions::windowsShellScriptSafeCommand (kde_dir.path ());
if (syspath.indexOf (kde_dir.path ()) < 0) {
RK_DEBUG (DEBUG_ALL, DL_INFO, "Adding %s to the system path", qPrintable (kde_dir_safe_path));
qputenv ("PATH", QString (kde_dir_safe_path + PATH_VAR_SEP + qgetenv ("PATH")).toLocal8Bit ());
}
// Look for R:
//- command line parameter
//- Specified in cfg file next to rkward executable
//- compile-time default
QString r_exe = parser.value ("r-executable");
if (!r_exe.isNull ()) {
if (!QFileInfo (r_exe).isExecutable ()) {
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);
}
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 = 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);
}
}
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);
}
RK_DEBUG (APP, DL_DEBUG, "Using R as configured at compile time");
}
}
// TODO: Store somewhere else
qputenv ("R_BINARY", r_exe.toLocal8Bit ());
if (app.isSessionRestored ()) {
RESTORE(RKWardMainWindow); // well, whatever this is supposed to do -> TODO
......@@ -227,7 +338,6 @@ int main (int argc, char *argv[]) {
qInstallMessageHandler (0);
RKSettingsModuleDebug::debug_file->close ();
delete argv_copy;
return status;
}
......@@ -222,4 +222,22 @@ namespace RKCommonFunctions {
return (i18n ("<p><em>Note:</em> This setting does not take effect until you restart RKWard.</p>"));
}
#ifdef Q_OS_WIN
# include <windows.h>
# include <QTemporaryFile>
#endif
QString windowsShellScriptSafeCommand (const QString &orig) {
#ifdef Q_OS_WIN
// credits to http://erasmusjam.wordpress.com/2012/10/01/get-8-3-windows-short-path-names-in-a-qt-application/
QByteArray input (sizeof (wchar_t) * (orig.size()+1), '\0');
// wchar_t input[orig.size()+1]; -- No: MSVC (2013) does not support variable length arrays. Oh dear...
orig.toWCharArray ((wchar_t*) input.data ());
long length = GetShortPathName ((wchar_t*) input.data (), NULL, 0);
QByteArray output (sizeof (wchar_t) * (length), '\0');
GetShortPathName ((wchar_t*) input.data (), (wchar_t*) output.data (), length);
return QString::fromWCharArray ((wchar_t*) output.data (), length-1);
#else
return orig;
#endif
}
} // namespace
......@@ -59,6 +59,8 @@ namespace RKCommonFunctions {
/** simultaneously sets tool tips and what's this tips on up to three QWidgets */
void setTips (const QString tip, QWidget *first, QWidget *second=0, QWidget *third=0);
QString noteSettingsTakesEffectAfterRestart ();
/** Passing commands as part of arguments to windows shell scripts will fail miserably for paths with spaces or special characters. Transform to short path names for safety. No-op on sane platforms.*/
QString windowsShellScriptSafeCommand (const QString &orig);
};
#endif
......@@ -50,7 +50,7 @@ Comment[uk]=Графічний інтерфейс проекту R
Comment[x-test]=xxGUI for the R-projectxx
Icon=rkward
X-DBUS-ServiceName=org.kde.rkward
Exec=sh -c "STARTUP_ID_COPY=\\${DESKTOP_STARTUP_ID} rkward -qwindowtitle '%c'"
Exec=rkward -qwindowtitle '%c'
Terminal=false
Type=Application
X-DocPath=rkward/index.html
......
......@@ -77,11 +77,6 @@ int RInterface::na_int;
RInterface::RInterface () {
RK_TRACE (RBACKEND);
// If R_HOME is not set, most certainly the user called the binary without the wrapper script
if (!getenv ("R_HOME")) {
RK_DEBUG (RBACKEND, DL_ERROR, "No R_HOME environment variable set. RKWard will quit in a moment. Always start rkward in the default way unless you know what you're doing.");
}
new RCommandStackModel (this);
RCommandStack::regular_stack = new RCommandStack ();
startup_phase2_error = false;
......
......@@ -95,10 +95,11 @@ void RKFrontendTransmitter::run () {
QStringList args;
args.append ("--debug-level=" + QString::number (RK_Debug_Level));
args.append ("--server-name=" + server->fullServerName ());
args.append ("--rkd-server-name=" + rkd_transmitter->serverName ());
args.append ("--data-dir=" + RKSettingsModuleGeneral::filesPath ());
args.append ("--locale-dir=" + localeDir ());
// NOTE: QProcess quotes its arguments, *but* properly passing all spaces and quotes through the R CMD wrapper, seems near(?) impossible on Windows. Instead, we use percent encoding, internally.
args.append ("--server-name=" + server->fullServerName ().toUtf8 ().toPercentEncoding ());
args.append ("--rkd-server-name=" + rkd_transmitter->serverName ().toUtf8 ().toPercentEncoding ());
args.append ("--data-dir=" + RKSettingsModuleGeneral::filesPath ().toUtf8 ().toPercentEncoding ());
args.append ("--locale-dir=" + localeDir ().toUtf8 ().toPercentEncoding ());
connect (backend, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &RKFrontendTransmitter::backendExit);
QString backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath ());
if (backend_executable.isEmpty ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "/rbackend"); // for running directly from the build-dir
......@@ -107,14 +108,16 @@ void RKFrontendTransmitter::run () {
#endif
if (backend_executable.isEmpty ()) handleTransmissionError (i18n ("The backend executable could not be found. This is likely to be a problem with your installation."));
QString debugger = RKGlobals::startup_options["backend-debugger"].toString ();
args.prepend (RKCommonFunctions::windowsShellScriptSafeCommand (backend_executable));
args.prepend ("CMD");
if (!debugger.isEmpty ()) {
args.prepend (backend_executable);
QStringList l = debugger.split (' ');
args = l.mid (1) + args;
backend->start (l.first (), args, QIODevice::ReadOnly);
} else {
backend->start (backend_executable, args, QIODevice::ReadOnly);
args = debugger.split (' ') + args;
}
for (int i = 0; i < args.size (); ++i) {
qDebug ("%s", qPrintable (args[i]));
}
qDebug ("%s", qPrintable (qgetenv ("R_BINARY")));
backend->start (qgetenv ("R_BINARY"), args, QIODevice::ReadOnly);
if (!backend->waitForStarted ()) {
handleTransmissionError (i18n ("The backend executable could not be started. Error message was: %1", backend->errorString ()));
......
......@@ -33,6 +33,7 @@
#include <QUuid> // mis-used as a random-string generator
#include <QTemporaryFile>
#include <QDir>
#include <QUrl>
void RK_setupGettext (const char*);
int RK_Debug_Level = 2;
......@@ -83,16 +84,13 @@
if (args[i].startsWith ("--debug-level")) {
RK_Debug_Level = args[i].section ('=', 1).toInt ();
} else if (args[i].startsWith ("--server-name")) {
servername = args[i].section ('=', 1);
servername = QUrl::fromPercentEncoding (args[i].section ('=', 1).toUtf8 ());
} else if (args[i].startsWith ("--data-dir")) {
#ifdef __GNUC__
# warning What about paths with spaces?!
#endif
data_dir = args[i].section ('=', 1);
data_dir = QUrl::fromPercentEncoding (args[i].section ('=', 1).toUtf8 ());
} else if (args[i].startsWith ("--locale-dir")) {
locale_dir = args[i].section ('=', 1);
locale_dir = QUrl::fromPercentEncoding (args[i].section ('=', 1).toUtf8 ());
} else if (args[i].startsWith ("--rkd-server-name")) {
rkd_server_name = args[i].section ('=', 1);
rkd_server_name = QUrl::fromPercentEncoding (args[i].section ('=', 1).toUtf8 ());
} else {
printf ("unknown argument %s", qPrintable (args[i]));
}
......
/***************************************************************************
rkward_startup_wrapper - description
-------------------
begin : Sun Mar 10 2013
copyright : (C) 2013, 2014, 2015 by Thomas Friedrichsmeier
email : thomas.friedrichsmeier@kdemail.net
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/** This simple helper executable is responsible for reading basic configuration
* settings, checking for some services and starting RKWard. These things used
* to be done using a script, and there was nothing wrong with that, in principle.
* However, the binary allows more flexibility, and more consistency across
* platforms. */
#include <QApplication>
#include <QMessageBox>
#include <QDir>
#include <QProcess>
#include <QSettings>
#include <QUrl>
#include <QFile>
#include <QtDBus>
#include "misc/rkdbusapi.h"
#ifndef RKWARD_FRONTEND_LOCATION
# define RKWARD_FRONTEND_LOCATION ""
#endif
#ifndef R_EXECUTABLE
# define R_EXECUTABLE ""
#endif
QString findExeAtPath (const QString appname, const QString &path) {
QDir dir (path);
dir.makeAbsolute ();
if (QFileInfo (dir.filePath (appname)).isExecutable ()) return dir.filePath (appname);
#ifdef Q_OS_WIN
if (QFileInfo (dir.filePath (appname + ".exe")).isExecutable ()) return dir.filePath (appname + ".exe");
if (QFileInfo (dir.filePath (appname + ".com")).isExecutable ()) return dir.filePath (appname + ".com");
if (QFileInfo (dir.filePath (appname + ".bat")).isExecutable ()) return dir.filePath (appname + ".bat");
#endif
return QString ();
}
QString findRKWardAtPath (const QString &path) {
return findExeAtPath ("rkward.frontend", path);
}
#ifdef Q_OS_WIN
#include <windows.h>
#include <QTemporaryFile>
#endif
QString quoteCommand (const QString &orig) {
#ifdef Q_OS_WIN
// Get short path name as a safe way to pass all sort of commands on the Windows shell
// credits to http://erasmusjam.wordpress.com/2012/10/01/get-8-3-windows-short-path-names-in-a-qt-application/
QByteArray input (sizeof (wchar_t) * (orig.size()+1), '\0');
// wchar_t input[orig.size()+1]; -- No: MSVC (2013) does not support variable length arrays. Oh dear...
orig.toWCharArray ((wchar_t*) input.data ());
long length = GetShortPathName ((wchar_t*) input.data (), NULL, 0);
QByteArray output (sizeof (wchar_t) * (length), '\0');
GetShortPathName ((wchar_t*) input.data (), (wchar_t*) output.data (), length);
return QString::fromWCharArray ((wchar_t*) output.data (), length-1);
#else
return orig;
#endif
}
#ifndef Q_OS_WIN
// see http://blog.qt.digia.com/blog/2006/03/16/starting-interactive-processes-with-qprocess/
// Need an interactive process e.g. for running through gdb
# include <unistd.h>
class InteractiveProcess : public QProcess {
static int stdinClone;
public:
InteractiveProcess (QObject *parent = 0) : QProcess (parent) {
if (stdinClone == -1) stdinClone = ::dup (fileno(stdin));
}
protected:
void setupChildProcess () override {
::dup2 (stdinClone, fileno(stdin));
}
};
int InteractiveProcess::stdinClone = -1;
#else
// no easy solution for Windows. But ain't Windows the world of graphical debuggers, anyway...
# define InteractiveProcess QProcess
#endif
#ifdef Q_OS_WIN
# define PATH_VAR_SEP ';'
#else
# define PATH_VAR_SEP ':'
#endif
int main (int argc, char *argv[]) {
QApplication app (argc, argv);
QStringList args = app.arguments ();
if (!args.isEmpty ()) args.pop_front (); // The command itself
qputenv ("DESKTOP_STARTUP_ID", qgetenv ("STARTUP_ID_COPY")); // for startup notifications (set via org.kde.rkward.desktop)
qputenv ("STARTUP_ID_COPY", "");
// Parse arguments that need handling in the wrapper
bool usage = false;
QStringList debugger_args;
QStringList file_args;
bool reuse = false;
bool warn_external = true;
QString r_exe_arg;
int debug_level = 2;
for (int i=0; i < args.size (); ++i) {
if (args[i] == "--debugger") {
args.removeAt (i);
while (i < args.size ()) {
QString arg = args.takeAt (i);
if (arg == "--") break;
debugger_args.append (arg);
}
if (debugger_args.isEmpty ()) usage = true;
} else if (args[i] == "--r-executable") {
if ((i+1) < args.size ()) {
r_exe_arg = args.takeAt (i + 1);
} else usage = true;
args.removeAt (i);
--i;
} else if (args[i] == "--debug-level") {
if ((i+1) < args.size ()) {
debug_level = args[i+1].toInt ();
}
} else if (args[i] == "--reuse") {
reuse = true;
} else if (args[i] == "--nowarn-external") {
warn_external = false;
} else if (args[i].startsWith ("--")) {
// all RKWard and KDE options (other than --reuse) are of the for --option <value>. So skip over the <value>
i++;
} else {
QUrl url = QUrl::fromUserInput (args[i], QDir::currentPath (), QUrl::AssumeLocalFile);