Commit 322a8481 authored by Oswald Buddenhagen's avatar Oswald Buddenhagen
Browse files

unify application launcher between platforms

purge abstractprocess and winguiprocess. they serve no purpose.

applications can have stdio on windows as well, in addition to the
windows debug channel.

Task-number: QTCREATORBUG-4822
parent 9a2814d6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#ifndef ABSTRACTPROCESS_H
#define ABSTRACTPROCESS_H
#include "utils_global.h"
#include "environment.h"
#include <QtCore/QStringList>
namespace Utils {
class QTCREATOR_UTILS_EXPORT AbstractProcess
{
public:
AbstractProcess();
virtual ~AbstractProcess();
QString workingDirectory() const;
void setWorkingDirectory(const QString &dir);
void setEnvironment(const Environment &env);
Environment environment() const;
virtual bool start(const QString &program, const QString &args) = 0;
virtual void stop() = 0;
virtual bool isRunning() const = 0;
virtual qint64 applicationPID() const = 0;
virtual int exitCode() const = 0;
static QString msgWinCannotRetrieveDebuggingOutput();
//signals:
virtual void processMessage(const QString &error, bool isError) = 0;
#ifdef Q_OS_WIN
// Add PATH and SystemRoot environment variables in case they are missing
static QStringList fixWinEnvironment(const QStringList &env);
// Quote a Windows command line correctly for the "CreateProcess" API
static QString createWinCommandline(const QString &program, const QStringList &args);
static QString createWinCommandline(const QString &program, const QString &args);
// Create a bytearray suitable to be passed on as environment
// to the "CreateProcess" API (0-terminated UTF 16 strings).
static QByteArray createWinEnvironment(const QStringList &env);
#endif
protected:
QString m_workingDir;
Environment m_environment;
};
} //namespace Utils
#endif // ABSTRACTPROCESS_H
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#include "abstractprocess.h"
#include <windows.h>
namespace Utils {
QStringList AbstractProcess::fixWinEnvironment(const QStringList &env)
{
QStringList envStrings = env;
// add PATH if necessary (for DLL loading)
if (envStrings.filter(QRegExp(QLatin1String("^PATH="),Qt::CaseInsensitive)).isEmpty()) {
QByteArray path = qgetenv("PATH");
if (!path.isEmpty())
envStrings.prepend(QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path)));
}
// add systemroot if needed
if (envStrings.filter(QRegExp(QLatin1String("^SystemRoot="),Qt::CaseInsensitive)).isEmpty()) {
QByteArray systemRoot = qgetenv("SystemRoot");
if (!systemRoot.isEmpty())
envStrings.prepend(QString(QLatin1String("SystemRoot=%1")).arg(QString::fromLocal8Bit(systemRoot)));
}
return envStrings;
}
static QString quoteWinCommand(const QString &program)
{
const QChar doubleQuote = QLatin1Char('"');
// add the program as the first arg ... it works better
QString programName = program;
programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
if (!programName.startsWith(doubleQuote) && !programName.endsWith(doubleQuote)
&& programName.contains(QLatin1Char(' '))) {
programName.prepend(doubleQuote);
programName.append(doubleQuote);
}
return programName;
}
static QString quoteWinArgument(const QString &arg)
{
if (!arg.length())
return QString::fromLatin1("\"\"");
QString ret(arg);
// Quotes are escaped and their preceding backslashes are doubled.
ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\""));
if (ret.contains(QRegExp(QLatin1String("\\s")))) {
// The argument must not end with a \ since this would be interpreted
// as escaping the quote -- rather put the \ behind the quote: e.g.
// rather use "foo"\ than "foo\"
int i = ret.length();
while (i > 0 && ret.at(i - 1) == QLatin1Char('\\'))
--i;
ret.insert(i, QLatin1Char('"'));
ret.prepend(QLatin1Char('"'));
}
return ret;
}
QString AbstractProcess::createWinCommandline(const QString &program, const QStringList &args)
{
QString programName = quoteWinCommand(program);
foreach (const QString &arg, args) {
programName += QLatin1Char(' ');
programName += quoteWinArgument(arg);
}
return programName;
}
QString AbstractProcess::createWinCommandline(const QString &program, const QString &args)
{
QString programName = quoteWinCommand(program);
if (!args.isEmpty()) {
programName += QLatin1Char(' ');
programName += args;
}
return programName;
}
QByteArray AbstractProcess::createWinEnvironment(const QStringList &env)
{
QByteArray envlist;
int pos = 0;
foreach (const QString &tmp, env) {
const uint tmpSize = sizeof(TCHAR) * (tmp.length() + 1);
envlist.resize(envlist.size() + tmpSize);
memcpy(envlist.data() + pos, tmp.utf16(), tmpSize);
pos += tmpSize;
}
envlist.resize(envlist.size() + 2);
envlist[pos++] = 0;
envlist[pos++] = 0;
return envlist;
}
} //namespace Utils
......@@ -30,10 +30,25 @@
**
**************************************************************************/
#include "consoleprocess.h"
#include "consoleprocess_p.h"
namespace Utils {
ConsoleProcess::~ConsoleProcess()
{
stop();
}
void ConsoleProcess::setMode(Mode m)
{
d->m_mode = m;
}
ConsoleProcess::Mode ConsoleProcess::mode() const
{
return d->m_mode;
}
QString ConsoleProcess::modeOption(Mode m)
{
switch (m) {
......@@ -47,6 +62,41 @@ QString ConsoleProcess::modeOption(Mode m)
return QLatin1String("run");
}
qint64 ConsoleProcess::applicationPID() const
{
return d->m_appPid;
}
int ConsoleProcess::exitCode() const
{
return d->m_appCode;
} // This will be the signal number if exitStatus == CrashExit
QProcess::ExitStatus ConsoleProcess::exitStatus() const
{
return d->m_appStatus;
}
void ConsoleProcess::setWorkingDirectory(const QString &dir)
{
d->m_workingDir = dir;
}
QString ConsoleProcess::workingDirectory() const
{
return d->m_workingDir;
}
void ConsoleProcess::setEnvironment(const Environment &env)
{
d->m_environment = env;
}
Environment ConsoleProcess::environment() const
{
return d->m_environment;
}
QString ConsoleProcess::msgCommChannelFailed(const QString &error)
{
return tr("Cannot set up communication channel: %1").arg(error);
......
......@@ -33,7 +33,9 @@
#ifndef CONSOLEPROCESS_H
#define CONSOLEPROCESS_H
#include "abstractprocess.h"
#include "utils_global.h"
#include "environment.h"
#include <QtCore/QObject>
#include <QtCore/QString>
......@@ -48,7 +50,7 @@ QT_END_NAMESPACE
namespace Utils {
struct ConsoleProcessPrivate;
class QTCREATOR_UTILS_EXPORT ConsoleProcess : public QObject, public AbstractProcess
class QTCREATOR_UTILS_EXPORT ConsoleProcess : public QObject
{
Q_OBJECT
......@@ -57,6 +59,12 @@ public:
ConsoleProcess(QObject *parent = 0);
~ConsoleProcess();
void setWorkingDirectory(const QString &dir);
QString workingDirectory() const;
void setEnvironment(const Environment &env);
Environment environment() const;
bool start(const QString &program, const QString &args);
void stop();
......@@ -73,7 +81,13 @@ public:
int exitCode() const;
QProcess::ExitStatus exitStatus() const;
#ifdef Q_OS_UNIX
#ifdef Q_OS_WIN
// Add PATH and SystemRoot environment variables in case they are missing
static QStringList fixWinEnvironment(const QStringList &env);
// Quote a Windows command line correctly for the "CreateProcess" API
static QString createWinCommandline(const QString &program, const QStringList &args);
static QString createWinCommandline(const QString &program, const QString &args);
#else
void setSettings(QSettings *settings);
static QString defaultTerminalEmulator();
static QString terminalEmulator(const QSettings *settings);
......
......@@ -30,45 +30,51 @@
**
**************************************************************************/
#include "abstractprocess.h"
#ifndef CONSOLEPROCESS_P_H
#define CONSOLEPROCESS_P_H
#include <QtCore/QCoreApplication>
#include "consoleprocess.h"
namespace Utils {
AbstractProcess::AbstractProcess()
{
}
#include <QtCore/QTemporaryFile>
AbstractProcess::~AbstractProcess()
{
}
#include <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>
QString AbstractProcess::workingDirectory() const
{
return m_workingDir;
}
#ifdef Q_OS_WIN
#include <QtCore/private/qwineventnotifier_p.h>
void AbstractProcess::setWorkingDirectory(const QString &dir)
{
m_workingDir = dir;
}
#include <windows.h>
#endif
void AbstractProcess::setEnvironment(const Environment &env)
{
m_environment = env;
}
namespace Utils {
Environment AbstractProcess::environment() const
{
return m_environment;
}
struct ConsoleProcessPrivate {
ConsoleProcessPrivate();
QString AbstractProcess::msgWinCannotRetrieveDebuggingOutput()
{
return QCoreApplication::translate("Utils::AbstractProcess", "Cannot retrieve debugging output.\n");
}
ConsoleProcess::Mode m_mode;
QString m_workingDir;
Environment m_environment;
qint64 m_appPid;
int m_appCode;
QString m_executable;
QProcess::ExitStatus m_appStatus;
QLocalServer m_stubServer;
QLocalSocket *m_stubSocket;
QTemporaryFile *m_tempFile;
} // namespace Utils
#ifdef Q_OS_UNIX
QProcess m_process;
QByteArray m_stubServerDir;
QSettings *m_settings;
#else
qint64 m_appMainThreadId;
PROCESS_INFORMATION *m_pid;
HANDLE m_hInferior;
QWinEventNotifier *inferiorFinishedNotifier;
QWinEventNotifier *processFinishedNotifier;
#endif
};
} //namespace Utils
#endif
......@@ -30,7 +30,7 @@
**
**************************************************************************/
#include "consoleprocess.h"
#include "consoleprocess_p.h"
#include "environment.h"
#include "qtcprocess.h"
......@@ -38,10 +38,6 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QSettings>
#include <QtCore/QTemporaryFile>
#include <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>
#include <sys/stat.h>
#include <sys/types.h>
......@@ -50,23 +46,6 @@
#include <unistd.h>
namespace Utils {
struct ConsoleProcessPrivate {
ConsoleProcessPrivate();
ConsoleProcess::Mode m_mode;
qint64 m_appPid;
qint64 m_appMainThreadId;
int m_appCode;
QString m_executable;
QProcess::ExitStatus m_appStatus;
QLocalServer m_stubServer;
QLocalSocket *m_stubSocket;
QTemporaryFile *m_tempFile;
QProcess m_process;
QByteArray m_stubServerDir;
QSettings *m_settings;
};
ConsoleProcessPrivate::ConsoleProcessPrivate() :
m_mode(ConsoleProcess::Run),
......@@ -76,6 +55,7 @@ ConsoleProcessPrivate::ConsoleProcessPrivate() :
m_settings(0)
{
}
ConsoleProcess::ConsoleProcess(QObject *parent) :
QObject(parent), d(new ConsoleProcessPrivate)
{
......@@ -86,36 +66,6 @@ ConsoleProcess::ConsoleProcess(QObject *parent) :
SLOT(stubExited()));
}
ConsoleProcess::~ConsoleProcess()
{
stop();
}
void ConsoleProcess::setMode(Mode m)
{
d->m_mode = m;
}
ConsoleProcess::Mode ConsoleProcess::mode() const
{
return d->m_mode;
}
qint64 ConsoleProcess::applicationPID() const
{
return d->m_appPid;
}
int ConsoleProcess::exitCode() const
{
return d->m_appCode;
} // This will be the signal number if exitStatus == CrashExit
QProcess::ExitStatus ConsoleProcess::exitStatus() const
{
return d->m_appStatus;
}
void ConsoleProcess::setSettings(QSettings *settings)
{
d->m_settings = settings;
......@@ -127,7 +77,7 @@ bool ConsoleProcess::start(const QString &program, const QString &args)
return false;
QtcProcess::SplitError perr;
QStringList pargs = QtcProcess::prepareArgs(args, &perr, &m_environment, &m_workingDir);
QStringList pargs = QtcProcess::prepareArgs(args, &perr, &d->m_environment, &d->m_workingDir);
QString pcmd;
if (perr == QtcProcess::SplitOk) {
pcmd = program;
......@@ -148,7 +98,7 @@ bool ConsoleProcess::start(const QString &program, const QString &args)
QtcProcess::SplitError qerr;
QStringList xtermArgs = QtcProcess::prepareArgs(terminalEmulator(d->m_settings), &qerr,
&m_environment, &m_workingDir);
&d->m_environment, &d->m_workingDir);
if (qerr != QtcProcess::SplitOk) {
emit processMessage(qerr == QtcProcess::BadQuoting
? tr("Quoting error in terminal command.")
......@@ -162,7 +112,7 @@ bool ConsoleProcess::start(const QString &program, const QString &args)
return false;
}
QStringList env = m_environment.toStringList();
QStringList env = d->m_environment.toStringList();
if (!env.isEmpty()) {
d->m_tempFile = new QTemporaryFile();
if (!d->m_tempFile->open()) {
......
......@@ -30,42 +30,18 @@
**
**************************************************************************/
#include "consoleprocess.h"
#include "consoleprocess_p.h"
#include "environment.h"
#include "qtcprocess.h"
#include "winutils.h"
#include <windows.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QTemporaryFile>
#include <QtCore/QAbstractEventDispatcher>
#include <QtCore/private/qwineventnotifier_p.h>
#include <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>
#include <stdlib.h>
namespace Utils {
struct ConsoleProcessPrivate {
ConsoleProcessPrivate();
ConsoleProcess::Mode m_mode;
qint64 m_appPid;
qint64 m_appMainThreadId;
int m_appCode;
QString m_executable;
QProcess::ExitStatus m_appStatus;
QLocalServer m_stubServer;
QLocalSocket *m_stubSocket;
QTemporaryFile *m_tempFile;
PROCESS_INFORMATION *m_pid;
HANDLE m_hInferior;
QWinEventNotifier *inferiorFinishedNotifier;
QWinEventNotifier *processFinishedNotifier;
};
ConsoleProcessPrivate::ConsoleProcessPrivate() :
m_mode(ConsoleProcess::Run),
......@@ -85,41 +61,11 @@ ConsoleProcess::ConsoleProcess(QObject *parent) :
connect(&d->m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable()));
}
ConsoleProcess::~ConsoleProcess()
{
stop();
}
void ConsoleProcess::setMode(Mode m)
{
d->m_mode = m;
}
ConsoleProcess::Mode ConsoleProcess::mode() const
{
return d->m_mode;
}
qint64 ConsoleProcess::applicationPID() const
{
return d->m_appPid;
}
qint64 ConsoleProcess::applicationMainThreadID() const
{
return d->m_appMainThreadId;
}
int ConsoleProcess::exitCode() const
{
return d->m_appCode;
} // This will be the signal number if exitStatus == CrashExit
QProcess::ExitStatus ConsoleProcess::exitStatus() const
{
return d->m_appStatus;
}
bool ConsoleProcess::start(const QString &program, const QString &args)
{
if (isRunning())
......@@ -131,7 +77,7 @@ bool ConsoleProcess::start(const QString &program, const QString &args)
pcmd = program;
pargs = args;