Merge branch 'master' of ../mainline into dui-editor

This commit is contained in:
Roberto Raggi
2009-05-07 09:09:28 +02:00
47 changed files with 670 additions and 426 deletions

View File

@@ -1,11 +1,9 @@
IDE_BUILD_TREE = $$OUT_PWD/../..
include(../../qtcreator.pri)
include(../shared/qtsingleapplication/qtsingleapplication.pri)
TEMPLATE = app
TARGET = $$IDE_APP_TARGET
DESTDIR = ../../bin
DESTDIR = $$IDE_APP_PATH
SOURCES += main.cpp

View File

@@ -0,0 +1,86 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
**
**************************************************************************/
#include "consoleprocess.h"
namespace Core {
namespace Utils {
QString ConsoleProcess::modeOption(Mode m)
{
switch (m) {
case Debug:
return QLatin1String("debug");
case Suspend:
return QLatin1String("suspend");
case Run:
break;
}
return QLatin1String("run");
}
QString ConsoleProcess::msgCommChannelFailed(const QString &error)
{
return tr("Cannot set up communication channel: %1").arg(error);
}
QString ConsoleProcess::msgPromptToClose()
{
//! Showed in a terminal which might have
//! a different character set on Windows.
return tr("Press <RETURN> to close this window...");
}
QString ConsoleProcess::msgCannotCreateTempFile(const QString &why)
{
return tr("Cannot create temporary file: %1").arg(why);
}
QString ConsoleProcess::msgCannotCreateTempDir(const QString & dir, const QString &why)
{
return tr("Cannot create temporary directory '%1': %2").arg(dir, why);
}
QString ConsoleProcess::msgUnexpectedOutput()
{
return tr("Unexpected output from helper program.");
}
QString ConsoleProcess::msgCannotChangeToWorkDir(const QString & dir, const QString &why)
{
return tr("Cannot change to working directory '%1': %2").arg(dir, why);
}
QString ConsoleProcess::msgCannotExecute(const QString & p, const QString &why)
{
return tr("Cannot execute '%1': %2").arg(p, why);
}
}
}

View File

@@ -59,14 +59,15 @@ class QWORKBENCH_UTILS_EXPORT ConsoleProcess : public QObject, public AbstractPr
Q_OBJECT
public:
enum Mode { Run, Debug, Suspend };
ConsoleProcess(QObject *parent = 0);
~ConsoleProcess();
bool start(const QString &program, const QStringList &args);
void stop();
void setDebug(bool on) { m_debug = on; }
bool isDebug() const { return m_debug; }
void setMode(Mode m) { m_mode = m; }
Mode mode() const { return m_mode; }
bool isRunning() const; // This reflects the state of the console+stub
qint64 applicationPID() const { return m_appPid; }
@@ -99,6 +100,15 @@ private slots:
#endif
private:
static QString modeOption(Mode m);
static QString msgCommChannelFailed(const QString &error);
static QString msgPromptToClose();
static QString msgCannotCreateTempFile(const QString &why);
static QString msgCannotCreateTempDir(const QString & dir, const QString &why);
static QString msgUnexpectedOutput();
static QString msgCannotChangeToWorkDir(const QString & dir, const QString &why);
static QString msgCannotExecute(const QString & p, const QString &why);
QString stubServerListen();
void stubServerShutdown();
#ifdef Q_OS_WIN
@@ -106,7 +116,7 @@ private:
void cleanupInferior();
#endif
bool m_debug;
Mode m_mode;
qint64 m_appPid;
int m_appCode;
QString m_executable;

View File

@@ -44,14 +44,13 @@
using namespace Core::Utils;
ConsoleProcess::ConsoleProcess(QObject *parent)
: QObject(parent)
ConsoleProcess::ConsoleProcess(QObject *parent) :
QObject(parent),
m_mode(Run),
m_appPid(0),
m_stubSocket(0),
m_settings(0)
{
m_debug = false;
m_appPid = 0;
m_stubSocket = 0;
m_settings = 0;
connect(&m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable()));
m_process.setProcessChannelMode(QProcess::ForwardedChannels);
@@ -69,9 +68,9 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
if (isRunning())
return false;
QString err = stubServerListen();
const QString err = stubServerListen();
if (!err.isEmpty()) {
emit processError(tr("Cannot set up communication channel: %1").arg(err));
emit processError(msgCommChannelFailed(err));
return false;
}
@@ -79,7 +78,7 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
m_tempFile = new QTemporaryFile();
if (!m_tempFile->open()) {
stubServerShutdown();
emit processError(tr("Cannot create temporary file: %1").arg(m_tempFile->errorString()));
emit processError(msgCannotCreateTempFile(m_tempFile->errorString()));
delete m_tempFile;
m_tempFile = 0;
return false;
@@ -94,13 +93,13 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
QStringList xtermArgs = terminalEmulator(m_settings).split(QLatin1Char(' ')); // FIXME: quoting
xtermArgs
#ifdef Q_OS_MAC
<< (QCoreApplication::applicationDirPath() + "/../Resources/qtcreator_process_stub")
<< (QCoreApplication::applicationDirPath() + QLatin1String("/../Resources/qtcreator_process_stub"))
#else
<< (QCoreApplication::applicationDirPath() + "/qtcreator_process_stub")
<< (QCoreApplication::applicationDirPath() + QLatin1String("/qtcreator_process_stub"))
#endif
<< (m_debug ? "debug" : "exec")
<< modeOption(m_mode)
<< m_stubServer.fullServerName()
<< tr("Press <RETURN> to close this window...")
<< msgPromptToClose()
<< workingDirectory()
<< (m_tempFile ? m_tempFile->fileName() : 0)
<< program << args;
@@ -145,7 +144,7 @@ QString ConsoleProcess::stubServerListen()
{
QTemporaryFile tf;
if (!tf.open())
return tr("Cannot create temporary file: %1").arg(tf.errorString());
return msgCannotCreateTempFile(tf.errorString());
stubFifoDir = QFile::encodeName(tf.fileName());
}
// By now the temp file was deleted again
@@ -153,9 +152,9 @@ QString ConsoleProcess::stubServerListen()
if (!::mkdir(m_stubServerDir.constData(), 0700))
break;
if (errno != EEXIST)
return tr("Cannot create temporary directory '%1': %2").arg(stubFifoDir, strerror(errno));
return msgCannotCreateTempDir(stubFifoDir, QString::fromLocal8Bit(strerror(errno)));
}
QString stubServer = stubFifoDir + "/stub-socket";
const QString stubServer = stubFifoDir + "/stub-socket";
if (!m_stubServer.listen(stubServer)) {
::rmdir(m_stubServerDir.constData());
return tr("Cannot create socket '%1': %2").arg(stubServer, m_stubServer.errorString());
@@ -190,11 +189,9 @@ void ConsoleProcess::readStubOutput()
QByteArray out = m_stubSocket->readLine();
out.chop(1); // \n
if (out.startsWith("err:chdir ")) {
emit processError(tr("Cannot change to working directory '%1': %2")
.arg(workingDirectory(), errorMsg(out.mid(10).toInt())));
emit processError(msgCannotChangeToWorkDir(workingDirectory(), errorMsg(out.mid(10).toInt())));
} else if (out.startsWith("err:exec ")) {
emit processError(tr("Cannot execute '%1': %2")
.arg(m_executable, errorMsg(out.mid(9).toInt())));
emit processError(msgCannotExecute(m_executable, errorMsg(out.mid(9).toInt())));
} else if (out.startsWith("pid ")) {
// Will not need it any more
delete m_tempFile;
@@ -213,7 +210,7 @@ void ConsoleProcess::readStubOutput()
m_appPid = 0;
emit processStopped();
} else {
emit processError(tr("Unexpected output from helper program."));
emit processError(msgUnexpectedOutput());
m_process.terminate();
break;
}
@@ -250,7 +247,7 @@ QString ConsoleProcess::defaultTerminalEmulator()
QString ConsoleProcess::terminalEmulator(const QSettings *settings)
{
QString dflt = defaultTerminalEmulator() + QLatin1String(" -e");
const QString dflt = defaultTerminalEmulator() + QLatin1String(" -e");
if (!settings)
return dflt;
return settings->value(QLatin1String("General/TerminalEmulator"), dflt).toString();

View File

@@ -42,18 +42,17 @@
using namespace Core::Utils;
ConsoleProcess::ConsoleProcess(QObject *parent)
: QObject(parent)
ConsoleProcess::ConsoleProcess(QObject *parent) :
QObject(parent),
m_mode(Run),
m_appPid(0),
m_pid(0),
m_hInferior(NULL),
m_tempFile(0),
m_stubSocket(0),
processFinishedNotifier(0),
inferiorFinishedNotifier(0)
{
m_debug = false;
m_appPid = 0;
m_pid = 0;
m_hInferior = NULL;
m_tempFile = 0;
m_stubSocket = 0;
processFinishedNotifier = 0;
inferiorFinishedNotifier = 0;
connect(&m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable()));
}
@@ -67,9 +66,9 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
if (isRunning())
return false;
QString err = stubServerListen();
const QString err = stubServerListen();
if (!err.isEmpty()) {
emit processError(tr("Cannot set up communication channel: %1").arg(err));
emit processError(msgCommChannelFailed(err));
return false;
}
@@ -77,7 +76,7 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
m_tempFile = new QTemporaryFile();
if (!m_tempFile->open()) {
stubServerShutdown();
emit processError(tr("Cannot create temporary file: %1").arg(m_tempFile->errorString()));
emit processError(msgCannotCreateTempFile(m_tempFile->errorString()));
delete m_tempFile;
m_tempFile = 0;
return false;
@@ -102,15 +101,15 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
workDir.append('\\');
QStringList stubArgs;
stubArgs << (m_debug ? "debug" : "exec")
stubArgs << modeOption(m_mode)
<< m_stubServer.fullServerName()
<< workDir
<< (m_tempFile ? m_tempFile->fileName() : 0)
<< createWinCommandline(program, args)
<< tr("Press <RETURN> to close this window...");
<< msgPromptToClose();
QString cmdLine = createWinCommandline(
QCoreApplication::applicationDirPath() + "/qtcreator_process_stub.exe", stubArgs);
const QString cmdLine = createWinCommandline(
QCoreApplication::applicationDirPath() + QLatin1String("/qtcreator_process_stub.exe"), stubArgs);
bool success = CreateProcessW(0, (WCHAR*)cmdLine.utf16(),
0, 0, FALSE, CREATE_NEW_CONSOLE,
@@ -180,13 +179,11 @@ void ConsoleProcess::readStubOutput()
QByteArray out = m_stubSocket->readLine();
out.chop(2); // \r\n
if (out.startsWith("err:chdir ")) {
emit processError(tr("Cannot change to working directory '%1': %2")
.arg(workingDirectory(), winErrorMessage(out.mid(10).toInt())));
emit processError(msgCannotChangeToWorkDir(workingDirectory(), winErrorMessage(out.mid(10).toInt())));
} else if (out.startsWith("err:exec ")) {
emit processError(tr("Cannot execute '%1': %2")
.arg(m_executable, winErrorMessage(out.mid(9).toInt())));
emit processError(msgCannotExecute(m_executable, winErrorMessage(out.mid(9).toInt())));
} else if (out.startsWith("pid ")) {
// Will not need it any more
// Wil not need it any more
delete m_tempFile;
m_tempFile = 0;
@@ -204,7 +201,7 @@ void ConsoleProcess::readStubOutput()
connect(inferiorFinishedNotifier, SIGNAL(activated(HANDLE)), SLOT(inferiorExited()));
emit processStarted();
} else {
emit processError(tr("Unexpected output from helper program."));
emit processError(msgUnexpectedOutput());
TerminateProcess(m_pid->hProcess, (unsigned)-1);
break;
}

View File

@@ -1,13 +1,8 @@
IDE_BUILD_TREE = $$OUT_PWD/../../..
include(../../../qtcreator.pri)
TEMPLATE = app
TARGET = qtcreator_process_stub
macx {
DESTDIR = $$IDE_BUILD_TREE/bin/$${IDE_APP_TARGET}.app/Contents/Resources
} else {
DESTDIR = ../../../bin
}
DESTDIR = $$IDE_LIBEXEC_PATH
CONFIG += warn_on console use_c_linker
CONFIG -= qt app_bundle
@@ -24,5 +19,5 @@ unix {
LIBS += -lshell32
}
target.path = /bin
target.path = /bin # FIXME: libexec, more or less
INSTALLS += target

View File

@@ -39,6 +39,8 @@
static FILE *qtcFd;
static wchar_t *sleepMsg;
enum RunMode { Run, Debug, Suspend };
/* Print some "press enter" message, wait for that, exit. */
static void doExit(int code)
{
@@ -112,6 +114,7 @@ int main()
STARTUPINFOW si;
PROCESS_INFORMATION pi;
DEBUG_EVENT dbev;
enum RunMode mode = Run;
argv = CommandLineToArgvW(GetCommandLine(), &argc);
@@ -158,8 +161,20 @@ int main()
si.cb = sizeof(si);
creationFlags = CREATE_UNICODE_ENVIRONMENT;
if (!wcscmp(argv[ArgAction], L"debug"))
if (!wcscmp(argv[ArgAction], L"debug")) {
mode = Debug;
} else if (!wcscmp(argv[ArgAction], L"suspend")) {
mode = Suspend;
}
switch (mode) {
case Debug:
creationFlags |= DEBUG_ONLY_THIS_PROCESS;
break;
case Suspend:
creationFlags |= CREATE_SUSPENDED;
break;
}
if (!CreateProcessW(0, argv[ArgCmdLine], 0, 0, FALSE, creationFlags, env, 0, &si, &pi)) {
/* Only expected error: no such file or direcotry, i.e. executable not found */
sendMsg("err:exec %d\n", GetLastError());
@@ -172,7 +187,7 @@ int main()
So instead we start a debugged process, eat all the initial
debug events, suspend the process and detach from it. If gdb
tries to attach *now*, everything goes smoothly. Yay. */
if (creationFlags & DEBUG_ONLY_THIS_PROCESS) {
if (mode == Debug) {
do {
if (!WaitForDebugEvent (&dbev, INFINITE))
systemError("Cannot fetch debug event, error %d\n");

View File

@@ -26,7 +26,8 @@ SOURCES += \
savedaction.cpp \
submiteditorwidget.cpp \
synchronousprocess.cpp \
submitfieldwidget.cpp
submitfieldwidget.cpp \
consoleprocess.cpp
win32 {
SOURCES += abstractprocess_win.cpp \

View File

@@ -153,7 +153,7 @@ using namespace Core::Internal;
*/
ActionContainerPrivate::ActionContainerPrivate(int id)
: m_data(CS_None), m_id(id)
: m_data(0), m_id(id)
{
}
@@ -168,16 +168,6 @@ bool ActionContainerPrivate::hasEmptyAction(EmptyAction ea) const
return (m_data & EA_Mask) == ea;
}
void ActionContainerPrivate::setState(ContainerState state)
{
m_data |= state;
}
bool ActionContainerPrivate::hasState(ContainerState state) const
{
return (m_data & state);
}
void ActionContainerPrivate::appendGroup(const QString &group)
{
int gid = UniqueIDManager::instance()->uniqueIdentifier(group);
@@ -198,26 +188,14 @@ void ActionContainerPrivate::addAction(Command *action, const QString &group)
return;
ActionManagerPrivate *am = ActionManagerPrivate::instance();
Action *a = static_cast<Action *>(action);
if (a->stateFlags() & CommandPrivate::CS_PreLocation) {
QList<CommandLocation> locs = a->locations();
for (int i=0; i<locs.size(); ++i) {
if (ActionContainer *aci = am->actionContainer(locs.at(i).m_container)) {
ActionContainerPrivate *ac = static_cast<ActionContainerPrivate *>(aci);
ac->addAction(action, locs.at(i).m_position, false);
}
}
a->setStateFlags(a->stateFlags() | CommandPrivate::CS_Initialized);
} else {
UniqueIDManager *idmanager = UniqueIDManager::instance();
int grpid = idmanager->uniqueIdentifier(Constants::G_DEFAULT_TWO);
if (!group.isEmpty())
grpid = idmanager->uniqueIdentifier(group);
if (!m_groups.contains(grpid) && !am->defaultGroups().contains(grpid))
qWarning() << "*** addAction(): Unknown group: " << group;
int pos = ((grpid << 16) | 0xFFFF);
addAction(action, pos, true);
}
UniqueIDManager *idmanager = UniqueIDManager::instance();
int grpid = idmanager->uniqueIdentifier(Constants::G_DEFAULT_TWO);
if (!group.isEmpty())
grpid = idmanager->uniqueIdentifier(group);
if (!m_groups.contains(grpid) && !am->defaultGroups().contains(grpid))
qWarning() << "*** addAction(): Unknown group: " << group;
int pos = ((grpid << 16) | 0xFFFF);
addAction(action, pos, true);
}
void ActionContainerPrivate::addMenu(ActionContainer *menu, const QString &group)
@@ -227,24 +205,14 @@ void ActionContainerPrivate::addMenu(ActionContainer *menu, const QString &group
return;
ActionManagerPrivate *am = ActionManagerPrivate::instance();
MenuActionContainer *mc = static_cast<MenuActionContainer *>(menu);
if (mc->hasState(ActionContainerPrivate::CS_PreLocation)) {
CommandLocation loc = mc->location();
if (ActionContainer *aci = am->actionContainer(loc.m_container)) {
ActionContainerPrivate *ac = static_cast<ActionContainerPrivate *>(aci);
ac->addMenu(menu, loc.m_position, false);
}
mc->setState(ActionContainerPrivate::CS_Initialized);
} else {
UniqueIDManager *idmanager = UniqueIDManager::instance();
int grpid = idmanager->uniqueIdentifier(Constants::G_DEFAULT_TWO);
if (!group.isEmpty())
grpid = idmanager->uniqueIdentifier(group);
if (!m_groups.contains(grpid) && !am->defaultGroups().contains(grpid))
qWarning() << "*** addMenu(): Unknown group: " << group;
int pos = ((grpid << 16) | 0xFFFF);
addMenu(menu, pos, true);
}
UniqueIDManager *idmanager = UniqueIDManager::instance();
int grpid = idmanager->uniqueIdentifier(Constants::G_DEFAULT_TWO);
if (!group.isEmpty())
grpid = idmanager->uniqueIdentifier(group);
if (!m_groups.contains(grpid) && !am->defaultGroups().contains(grpid))
qWarning() << "*** addMenu(): Unknown group: " << group;
int pos = ((grpid << 16) | 0xFFFF);
addMenu(menu, pos, true);
}
int ActionContainerPrivate::id() const
@@ -264,14 +232,7 @@ QMenuBar *ActionContainerPrivate::menuBar() const
bool ActionContainerPrivate::canAddAction(Command *action) const
{
if (action->type() != Command::CT_OverridableAction)
return false;
CommandPrivate *cmd = static_cast<CommandPrivate *>(action);
if (cmd->stateFlags() & CommandPrivate::CS_Initialized)
return false;
return true;
return (action->action() != 0);
}
void ActionContainerPrivate::addAction(Command *action, int pos, bool setpos)
@@ -444,9 +405,6 @@ bool MenuActionContainer::update()
bool MenuActionContainer::canBeAddedToMenu() const
{
if (hasState(ActionContainerPrivate::CS_Initialized))
return false;
return true;
}

View File

@@ -41,22 +41,12 @@ namespace Internal {
class ActionContainerPrivate : public Core::ActionContainer
{
public:
enum ContainerState {
CS_None = 0x000000,
CS_Initialized = 0x010000,
CS_PreLocation = 0x020000,
CS_UserDefined = 0x040000
};
ActionContainerPrivate(int id);
virtual ~ActionContainerPrivate() {}
void setEmptyAction(EmptyAction ea);
bool hasEmptyAction(EmptyAction ea) const;
void setState(ContainerState state);
bool hasState(ContainerState state) const;
QAction *insertLocation(const QString &group) const;
void appendGroup(const QString &group);
void addAction(Command *action, const QString &group = QString());

View File

@@ -335,13 +335,12 @@ Command *ActionManagerPrivate::registerOverridableAction(QAction *action, const
OverrideableAction *a = 0;
const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
if (c->type() != Command::CT_OverridableAction) {
a = qobject_cast<OverrideableAction *>(c);
if (!a) {
qWarning() << "registerAction: id" << id << "is registered with a different command type.";
return c;
}
a = static_cast<OverrideableAction *>(c);
}
if (!a) {
} else {
a = new OverrideableAction(uid);
m_idCmdMap.insert(uid, a);
}
@@ -381,11 +380,11 @@ Command *ActionManagerPrivate::registerShortcut(QShortcut *shortcut, const QStri
Shortcut *sc = 0;
int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
if (c->type() != Command::CT_Shortcut) {
sc = qobject_cast<Shortcut *>(c);
if (!sc) {
qWarning() << "registerShortcut: id" << id << "is registered with a different command type.";
return c;
}
sc = static_cast<Shortcut *>(c);
} else {
sc = new Shortcut(uid);
m_idCmdMap.insert(uid, sc);

View File

@@ -38,80 +38,165 @@
\mainclass
\brief The class Command represents an action like a menu item, tool button, or shortcut.
You don't create Command objects directly, instead use \l{ActionManager::registerAction()}
to register an action and retrieve a Command. The Command object represents the user visible
action and its properties. If multiple actions are registered with the same ID (but
different contexts) the returned Command is the shared one between these actions.
*/
A Command has two basic properties: A default shortcut and a default text. The default
shortcut is a key sequence that the user can use to trigger the active action that
the Command represents. The default text is e.g. used for representing the Command
in the keyboard shortcut preference pane. If the default text is empty, the text
of the visible action is used.
/*!
\enum Command::CommandType
The user visible action is updated to represent the state of the active action (if any).
For performance reasons only the enabled and visible state are considered by default though.
You can tell a Command to also update the actions icon and text by setting the
corresponding \l{Command::CommandAttribute}{attribute}.
If there is no active action, the default behavior of the visible action is to be disabled.
You can change that behavior to make the visible action hide instead via the Command's
\l{Command::CommandAttribute}{attributes}.
*/
/*!
\enum Command::CommandAttribute
Defines how the user visible action is updated when the active action changes.
The default is to update the enabled and visible state, and to disable the
user visible action when there is no active action.
\omitvalue CA_Mask
\value CA_UpdateText
Also update the actions text.
\value CA_UpdateIcon
Also update the actions icon.
\value CA_Hide
When there is no active action, hide the user "visible" action, instead of just
disabling it.
\value CA_NonConfigureable
Flag to indicate that the keyboard shortcut of this Command should not be
configurable by the user.
*/
/*!
\fn virtual void Command::setDefaultKeySequence(const QKeySequence &key)
\fn void Command::setDefaultKeySequence(const QKeySequence &key)
Set the default keyboard shortcut that can be used to activate this command to \a key.
This is used if the user didn't customize the shortcut, or resets the shortcut
to the default one.
*/
/*!
\fn virtual int Command::id() const
\fn void Command::defaultKeySequence() const
Returns the default keyboard shortcut that can be used to activate this command.
\sa setDefaultKeySequence()
*/
/*!
\fn virtual CommandType Command::type() const
\fn void Command::keySequenceChanged()
Sent when the keyboard shortcut assigned to this Command changes, e.g.
when the user sets it in the keyboard shortcut settings dialog.
*/
/*!
\fn virtual QAction *Command::action() const
\fn QKeySequence Command::keySequence() const
Returns the current keyboard shortcut assigned to this Command.
\sa defaultKeySequence()
*/
/*!
\fn virtual QShortcut *Command::shortcut() const
\fn void Command::setKeySequence(const QKeySequence &key)
\internal
*/
/*!
\fn virtual void Command::setAttribute(CommandAttribute attr)
\fn void Command::setDefaultText(const QString &text)
Set the \a text that is used to represent the Command in the
keyboard shortcut settings dialog. If you don't set this,
the current text from the user visible action is taken (which
is ok in many cases).
*/
/*!
\fn virtual void Command::removeAttribute(CommandAttribute attr)
\fn QString Command::defaultText() const
Returns the text that is used to present this Command to the user.
\sa setDefaultText()
*/
/*!
\fn virtual bool Command::hasAttribute(CommandAttribute attr) const
\fn int Command::id() const
\internal
*/
/*!
\fn virtual bool Command::isActive() const
\fn QString Command::stringWithAppendedShortcut(const QString &string) const
Returns the \a string with an appended representation of the keyboard shortcut
that is currently assigned to this Command.
*/
/*!
\fn virtual Command::~Command()
\fn QAction *Command::action() const
Returns the user visible action for this Command.
If the Command represents a shortcut, it returns null.
Use this action to put it on e.g. tool buttons. The action
automatically forwards trigger and toggle signals to the
action that is currently active for this Command.
It also shows the current keyboard shortcut in its
tool tip (in addition to the tool tip of the active action)
and gets disabled/hidden when there is
no active action for the current context.
*/
/*!
\fn QShortcut *Command::shortcut() const
Returns the shortcut for this Command.
If the Command represents an action, it returns null.
*/
/*!
\fn void Command::setAttribute(CommandAttribute attribute)
Add the \a attribute to the attributes of this Command.
\sa CommandAttribute
\sa removeAttribute()
\sa hasAttribute()
*/
/*!
\fn void Command::removeAttribute(CommandAttribute attribute)
Remove the \a attribute from the attributes of this Command.
\sa CommandAttribute
\sa setAttribute()
*/
/*!
\fn bool Command::hasAttribute(CommandAttribute attribute) const
Returns if the Command has the \a attribute set.
\sa CommandAttribute
\sa removeAttribute()
\sa setAttribute()
*/
/*!
\fn bool Command::isActive() const
Returns if the Command has an active action/shortcut for the current
context.
*/
/*!
\fn Command::~Command()
\internal
*/
using namespace Core::Internal;
/*!
\class CommandPrivate
\inheaderfile command_p.h
\internal
*/
CommandPrivate::CommandPrivate(CommandType type, int id)
: m_type(type), m_id(id)
CommandPrivate::CommandPrivate(int id)
: m_attributes(0), m_id(id)
{
}
void CommandPrivate::setStateFlags(int state)
{
m_type |= (state & CS_Mask);
}
int CommandPrivate::stateFlags() const
{
return (m_type & CS_Mask);
}
void CommandPrivate::setDefaultKeySequence(const QKeySequence &key)
{
m_defaultKey = key;
@@ -137,11 +222,6 @@ int CommandPrivate::id() const
return m_id;
}
CommandPrivate::CommandType CommandPrivate::type() const
{
return (CommandType)(m_type & CT_Mask);
}
QAction *CommandPrivate::action() const
{
return 0;
@@ -154,17 +234,17 @@ QShortcut *CommandPrivate::shortcut() const
void CommandPrivate::setAttribute(CommandAttribute attr)
{
m_type |= attr;
m_attributes |= attr;
}
void CommandPrivate::removeAttribute(CommandAttribute attr)
{
m_type &= ~attr;
m_attributes &= ~attr;
}
bool CommandPrivate::hasAttribute(CommandAttribute attr) const
{
return (m_type & attr);
return (m_attributes & attr);
}
QString CommandPrivate::stringWithAppendedShortcut(const QString &str) const
@@ -177,20 +257,15 @@ QString CommandPrivate::stringWithAppendedShortcut(const QString &str) const
/*!
\class Shortcut
\internal
*/
/*!
...
*/
Shortcut::Shortcut(int id)
: CommandPrivate(CT_Shortcut, id), m_shortcut(0)
: CommandPrivate(id), m_shortcut(0)
{
}
/*!
...
*/
QString Shortcut::name() const
{
if (!m_shortcut)
@@ -199,41 +274,26 @@ QString Shortcut::name() const
return m_shortcut->whatsThis();
}
/*!
...
*/
void Shortcut::setShortcut(QShortcut *shortcut)
{
m_shortcut = shortcut;
}
/*!
...
*/
QShortcut *Shortcut::shortcut() const
{
return m_shortcut;
}
/*!
...
*/
void Shortcut::setContext(const QList<int> &context)
{
m_context = context;
}
/*!
...
*/
QList<int> Shortcut::context() const
{
return m_context;
}
/*!
...
*/
void Shortcut::setDefaultKeySequence(const QKeySequence &key)
{
setKeySequence(key);
@@ -261,9 +321,6 @@ QString Shortcut::defaultText() const
return m_defaultText;
}
/*!
...
*/
bool Shortcut::setCurrentContext(const QList<int> &context)
{
foreach (int ctxt, m_context) {
@@ -276,9 +333,6 @@ bool Shortcut::setCurrentContext(const QList<int> &context)
return false;
}
/*!
...
*/
bool Shortcut::isActive() const
{
return m_shortcut->isEnabled();
@@ -287,21 +341,15 @@ bool Shortcut::isActive() const
// ---------- Action ------------
/*!
\class Action
\class Action
\internal
*/
/*!
...
*/
Action::Action(CommandType type, int id)
: CommandPrivate(type, id), m_action(0)
Action::Action(int id)
: CommandPrivate(id), m_action(0)
{
}
/*!
...
*/
QString Action::name() const
{
if (!m_action)
@@ -310,9 +358,6 @@ QString Action::name() const
return m_action->text();
}
/*!
...
*/
void Action::setAction(QAction *action)
{
m_action = action;
@@ -322,33 +367,21 @@ void Action::setAction(QAction *action)
}
}
/*!
...
*/
QAction *Action::action() const
{
return m_action;
}
/*!
...
*/
void Action::setLocations(const QList<CommandLocation> &locations)
{
m_locations = locations;
}
/*!
...
*/
QList<CommandLocation> Action::locations() const
{
return m_locations;
}
/*!
...
*/
void Action::setDefaultKeySequence(const QKeySequence &key)
{
setKeySequence(key);
@@ -379,28 +412,20 @@ QKeySequence Action::keySequence() const
/*!
\class OverrideableAction
\internal
*/
/*!
...
*/
OverrideableAction::OverrideableAction(int id)
: Action(CT_OverridableAction, id), m_currentAction(0), m_active(false),
: Action(id), m_currentAction(0), m_active(false),
m_contextInitialized(false)
{
}
/*!
...
*/
void OverrideableAction::setAction(QAction *action)
{
Action::setAction(action);
}
/*!
...
*/
bool OverrideableAction::setCurrentContext(const QList<int> &context)
{
m_context = context;
@@ -440,9 +465,6 @@ bool OverrideableAction::setCurrentContext(const QList<int> &context)
return false;
}
/*!
...
*/
void OverrideableAction::addOverrideAction(QAction *action, const QList<int> &context)
{
if (context.isEmpty()) {
@@ -457,9 +479,6 @@ void OverrideableAction::addOverrideAction(QAction *action, const QList<int> &co
}
}
/*!
...
*/
void OverrideableAction::actionChanged()
{
if (hasAttribute(CA_UpdateIcon)) {
@@ -482,9 +501,6 @@ void OverrideableAction::actionChanged()
m_action->setVisible(m_currentAction->isVisible());
}
/*!
...
*/
bool OverrideableAction::isActive() const
{
return m_active;

View File

@@ -42,12 +42,6 @@ class CORE_EXPORT Command : public QObject
{
Q_OBJECT
public:
enum CommandType {
CT_Shortcut = 0x0001,
CT_OverridableAction = 0x0002,
CT_Mask = 0x00FF
};
enum CommandAttribute {
CA_Hide = 0x0100,
CA_UpdateText = 0x0200,
@@ -57,14 +51,12 @@ public:
};
virtual void setDefaultKeySequence(const QKeySequence &key) = 0;
virtual void setKeySequence(const QKeySequence &key) = 0;
virtual QKeySequence defaultKeySequence() const = 0;
virtual QKeySequence keySequence() const = 0;
virtual void setDefaultText(const QString &text) = 0;
virtual QString defaultText() const = 0;
virtual int id() const = 0;
virtual CommandType type() const = 0;
virtual QAction *action() const = 0;
virtual QShortcut *shortcut() const = 0;
@@ -77,6 +69,8 @@ public:
virtual ~Command() {}
virtual void setKeySequence(const QKeySequence &key) = 0;
virtual QString stringWithAppendedShortcut(const QString &str) const = 0;
signals:

View File

@@ -45,19 +45,9 @@ class CommandPrivate : public Core::Command
{
Q_OBJECT
public:
enum CommandState {
CS_PreLocation = 0x020000,
CS_LocationChanged = 0x040000,
CS_Initialized = 0x080000,
CS_Mask = 0xFF0000
};
CommandPrivate(CommandType type, int id);
CommandPrivate(int id);
virtual ~CommandPrivate() {}
void setStateFlags(int state);
int stateFlags() const;
virtual QString name() const = 0;
void setDefaultKeySequence(const QKeySequence &key);
@@ -67,7 +57,6 @@ public:
QString defaultText() const;
int id() const;
CommandType type() const;
QAction *action() const;
QShortcut *shortcut() const;
@@ -82,7 +71,7 @@ public:
protected:
QString m_category;
int m_type;
int m_attributes;
int m_id;
QKeySequence m_defaultKey;
QString m_defaultText;
@@ -121,7 +110,7 @@ class Action : public CommandPrivate
{
Q_OBJECT
public:
Action(CommandType type, int id);
Action(int id);
QString name() const;

View File

@@ -33,7 +33,9 @@
#include <coreplugin/ifile.h>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <QtGui/QPushButton>
#include <QtGui/QTreeWidget>
#include <QtGui/QHeaderView>
@@ -69,7 +71,7 @@ SaveItemsDialog::SaveItemsDialog(QWidget *parent,
visibleName = info.fileName();
}
QTreeWidgetItem *item = new QTreeWidgetItem(m_ui.treeWidget, QStringList()
<< visibleName << directory);
<< visibleName << QDir::toNativeSeparators(directory));
item->setData(0, Qt::UserRole, qVariantFromValue(file));
}

View File

@@ -58,6 +58,7 @@ void CppHighlighter::highlightBlock(const QString &text)
braceDepth = previousState >> 8;
}
SimpleLexer tokenize;
tokenize.setQtMocRunEnabled(false);
@@ -210,6 +211,28 @@ void CppHighlighter::highlightBlock(const QString &text)
TextEditDocumentLayout::setParentheses(currentBlock(), parentheses);
// optimization: if only the brace depth changes, we adjust subsequent blocks
// to have QSyntaxHighlighter stop the rehighlighting
int currentState = currentBlockState();
if (currentState != -1) {
int oldState = currentState & 0xff;
int oldBraceDepth = currentState >> 8;
if (oldState == tokenize.state() && oldBraceDepth != braceDepth) {
int delta = braceDepth - oldBraceDepth;
QTextBlock block = currentBlock().next();
while (block.isValid()) {
currentState = block.userState();
if (currentState != -1) {
oldState = currentState & 0xff;
oldBraceDepth = currentState >> 8;
block.setUserState(qMax(0, (oldBraceDepth + delta) << 8 ) | oldState);
}
block = block.next();
}
}
}
setCurrentBlockState((braceDepth << 8) | tokenize.state());
}

View File

@@ -392,7 +392,7 @@ CdbDebugEngine::CdbDebugEngine(DebuggerManager *parent, const QSharedPointer<Cdb
IDebuggerEngine(parent),
m_d(new CdbDebugEnginePrivate(parent, options, this))
{
// m_d->m_consoleStubProc.setDebug(true);
m_d->m_consoleStubProc.setMode(Core::Utils::ConsoleProcess::Suspend);
connect(&m_d->m_consoleStubProc, SIGNAL(processError(QString)), this, SLOT(slotConsoleStubError(QString)));
connect(&m_d->m_consoleStubProc, SIGNAL(processStarted()), this, SLOT(slotConsoleStubStarted()));
connect(&m_d->m_consoleStubProc, SIGNAL(wrapperStopped()), this, SLOT(slotConsoleStubTerminated()));
@@ -469,10 +469,12 @@ bool CdbDebugEngine::startDebugger()
m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
QString errorMessage;
bool rc = false;
bool needWatchTimer = false;
m_d->clearForRun();
switch (mode) {
case AttachExternal:
rc = startAttachDebugger(m_d->m_debuggerManager->m_attachedPID, &errorMessage);
needWatchTimer = true;
break;
case StartInternal:
case StartExternal:
@@ -484,7 +486,9 @@ bool CdbDebugEngine::startDebugger()
rc = m_d->m_consoleStubProc.start(m_d->m_debuggerManager->m_executable, m_d->m_debuggerManager->m_processArgs);
if (!rc)
errorMessage = tr("The console stub process was unable to start '%1'.").arg(m_d->m_debuggerManager->m_executable);
// continues in slotConsoleStubStarted()...
} else {
needWatchTimer = true;
rc = startDebuggerWithExecutable(mode, &errorMessage);
}
break;
@@ -494,7 +498,8 @@ bool CdbDebugEngine::startDebugger()
}
if (rc) {
m_d->m_debuggerManager->showStatusMessage(tr("Debugger Running"), -1);
startWatchTimer();
if (needWatchTimer)
startWatchTimer();
} else {
qWarning("%s\n", qPrintable(errorMessage));
}
@@ -503,12 +508,12 @@ bool CdbDebugEngine::startDebugger()
bool CdbDebugEngine::startAttachDebugger(qint64 pid, QString *errorMessage)
{
// Need to aatrach invasively, otherwise, no notification signals
// Need to attrach invasively, otherwise, no notification signals
// for for CreateProcess/ExitProcess occur.
const HRESULT hr = m_d->m_cif.debugClient->AttachProcess(NULL, pid,
DEBUG_ATTACH_INVASIVE_RESUME_PROCESS);
const ULONG flags = DEBUG_ATTACH_INVASIVE_RESUME_PROCESS;
const HRESULT hr = m_d->m_cif.debugClient->AttachProcess(NULL, pid, flags);
if (debugCDB)
qDebug() << "Attaching to " << pid << " returns " << hr;
qDebug() << "Attaching to " << pid << " returns " << hr << executionStatusString(m_d->m_cif.debugControl);
if (FAILED(hr)) {
*errorMessage = tr("AttachProcess failed for pid %1: %2").arg(pid).arg(msgDebugEngineComResult(hr));
return false;
@@ -560,7 +565,6 @@ bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString *
m_d->m_mode = sm;
}
m_d->m_debuggerManagerAccess->notifyInferiorRunning();
return true;
}
@@ -1318,7 +1322,9 @@ void CdbDebugEngine::slotConsoleStubStarted()
QString errorMessage;
if (startAttachDebugger(appPid, &errorMessage)) {
m_d->m_debuggerManager->m_attachedPID = appPid;
startWatchTimer();
m_d->m_debuggerManagerAccess->notifyInferiorPidChanged(appPid);
m_d->m_debuggerManagerAccess->notifyInferiorRunning();
} else {
QMessageBox::critical(m_d->m_debuggerManager->mainWindow(), tr("Debugger Error"), errorMessage);
}
@@ -1343,7 +1349,8 @@ void CdbDebugEnginePrivate::notifyCrashed()
void CdbDebugEnginePrivate::handleDebugEvent()
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << m_hDebuggeeProcess;
qDebug() << Q_FUNC_INFO << '\n' << m_hDebuggeeProcess << m_breakEventMode
<< executionStatusString(m_cif.debugControl);
// restore mode and do special handling
const HandleBreakEventMode mode = m_breakEventMode;

View File

@@ -34,7 +34,7 @@
#include "breakhandler.h"
#include "cdbstacktracecontext.h"
enum { cppExceptionCode = 0xe06d7363 };
enum { cppExceptionCode = 0xe06d7363, startupCompleteTrap = 0x406d1388 };
#include <QtCore/QDebug>
#include <QtCore/QTextStream>
@@ -245,6 +245,9 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
case cppExceptionCode:
str << "C++ exception";
break;
case startupCompleteTrap:
str << "Startup complete";
break;
case EXCEPTION_ACCESS_VIOLATION: {
const bool writeOperation = e->ExceptionInformation[0];
str << (writeOperation ? "write" : "read")
@@ -341,7 +344,7 @@ static bool isFatalException(LONG code)
switch (code) {
case EXCEPTION_BREAKPOINT:
case EXCEPTION_SINGLE_STEP:
case 0x406d1388: // Mysterious exception at start of application
case startupCompleteTrap: // Mysterious exception at start of application
return false;
default:
break;
@@ -362,7 +365,7 @@ STDMETHODIMP CdbDebugEventCallback::Exception(
}
const bool fatal = isFatalException(Exception->ExceptionCode);
if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << fatal << msg;
qDebug() << Q_FUNC_INFO << "\nex=" << Exception->ExceptionCode << " fatal=" << fatal << msg;
m_pEngine->m_d->m_debuggerManagerAccess->showApplicationOutput(msg);
if (fatal)
m_pEngine->m_d->notifyCrashed();

View File

@@ -352,7 +352,7 @@ bool CdbDumperHelper::ensureInitialized(QString *errorMessage)
return false;
case CallLoadNoQtApp:
m_access->showDebuggerOutput(m_messagePrefix, QCoreApplication::translate("CdbDumperHelper", "The debuggee does not appear to be Qt application."));
disable();
m_state = Disabled; // No message here
return true;
}
break;
@@ -367,7 +367,7 @@ bool CdbDumperHelper::ensureInitialized(QString *errorMessage)
m_access->showDebuggerOutput(m_messagePrefix, m_helper.toString());
m_state = Initialized;
} else {
disable();
m_state = Disabled; // No message here
*errorMessage = QCoreApplication::translate("CdbDumperHelper", "The custom dumper library could not be initialized: %1").arg(*errorMessage);
m_access->showDebuggerOutput(m_messagePrefix, *errorMessage);
m_access->showQtDumperLibraryWarning(*errorMessage);

View File

@@ -34,7 +34,7 @@
#include <coreplugin/icore.h>
#include <QtCore/QCoreApplication>
const char * const CDB_SETTINGS_ID = QT_TRANSLATE_NOOP("Debugger::Internal::CdbOptionsPageWidget", "CDB");
const char * const CDB_SETTINGS_ID = QT_TRANSLATE_NOOP("Debugger::Internal::CdbOptionsPageWidget", "Cdb");
namespace Debugger {
namespace Internal {

View File

@@ -22,7 +22,7 @@
<string>These options take effect at the next start of Qt Creator.</string>
</property>
<property name="title">
<string>CDB</string>
<string>Cdb</string>
</property>
<property name="checkable">
<bool>true</bool>

View File

@@ -77,7 +77,6 @@ SOURCES += \
gdboptionspage.cpp
FORMS += attachexternaldialog.ui \
attachremotedialog.ui \
attachcoredialog.ui \
breakbyfunction.ui \
breakcondition.ui \
@@ -85,6 +84,7 @@ FORMS += attachexternaldialog.ui \
gdboptionspage.ui \
commonoptionspage.ui \
startexternaldialog.ui \
startremotedialog.ui \
RESOURCES += debugger.qrc

View File

@@ -31,8 +31,8 @@
#include "ui_attachcoredialog.h"
#include "ui_attachexternaldialog.h"
#include "ui_attachremotedialog.h"
#include "ui_startexternaldialog.h"
#include "ui_startremotedialog.h"
#ifdef Q_OS_WIN
# include "dbgwinutils.h"
@@ -302,37 +302,39 @@ void AttachExternalDialog::pidChanged(const QString &pid)
///////////////////////////////////////////////////////////////////////
//
// AttachRemoteDialog
// StartRemoteDialog
//
///////////////////////////////////////////////////////////////////////
AttachRemoteDialog::AttachRemoteDialog(QWidget *parent)
StartRemoteDialog::StartRemoteDialog(QWidget *parent)
: QDialog(parent),
m_ui(new Ui::AttachRemoteDialog)
m_ui(new Ui::StartRemoteDialog)
{
m_ui->setupUi(this);
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
m_ui->serverStartScript->setExpectedKind(Core::Utils::PathChooser::File);
m_ui->serverStartScript->setPromptDialogTitle(tr("Select Executable"));
connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
}
AttachRemoteDialog::~AttachRemoteDialog()
StartRemoteDialog::~StartRemoteDialog()
{
delete m_ui;
}
void AttachRemoteDialog::setRemoteChannel(const QString &channel)
void StartRemoteDialog::setRemoteChannel(const QString &channel)
{
m_ui->channelLineEdit->setText(channel);
}
QString AttachRemoteDialog::remoteChannel() const
QString StartRemoteDialog::remoteChannel() const
{
return m_ui->channelLineEdit->text();
}
void AttachRemoteDialog::setRemoteArchitectures(const QStringList &list)
void StartRemoteDialog::setRemoteArchitectures(const QStringList &list)
{
m_ui->architectureComboBox->clear();
if (!list.isEmpty()) {
@@ -341,19 +343,28 @@ void AttachRemoteDialog::setRemoteArchitectures(const QStringList &list)
}
}
void AttachRemoteDialog::setRemoteArchitecture(const QString &arch)
void StartRemoteDialog::setRemoteArchitecture(const QString &arch)
{
int index = m_ui->architectureComboBox->findText(arch);
if (index != -1)
m_ui->architectureComboBox->setCurrentIndex(index);
}
QString AttachRemoteDialog::remoteArchitecture() const
QString StartRemoteDialog::remoteArchitecture() const
{
int index = m_ui->architectureComboBox->currentIndex();
return m_ui->architectureComboBox->itemText(index);
}
void StartRemoteDialog::setServerStartScript(const QString &scriptName)
{
m_ui->serverStartScript->setPath(scriptName);
}
QString StartRemoteDialog::serverStartScript() const
{
return m_ui->serverStartScript->path();
}
///////////////////////////////////////////////////////////////////////
//

View File

@@ -40,8 +40,8 @@ class QPushButton;
namespace Ui {
class AttachCoreDialog;
class AttachExternalDialog;
class AttachRemoteDialog;
class StartExternalDialog;
class StartRemoteDialog;
} // namespace Ui
QT_END_NAMESPACE
@@ -101,22 +101,24 @@ private:
ProcessListFilterModel *m_model;
};
class AttachRemoteDialog : public QDialog
class StartRemoteDialog : public QDialog
{
Q_OBJECT
public:
explicit AttachRemoteDialog(QWidget *parent);
~AttachRemoteDialog();
explicit StartRemoteDialog(QWidget *parent);
~StartRemoteDialog();
void setRemoteChannel(const QString &host);
void setRemoteArchitecture(const QString &arch);
void setRemoteArchitectures(const QStringList &arches);
QString remoteChannel() const;
QString remoteArchitecture() const;
void setServerStartScript(const QString &scriptName);
QString serverStartScript() const;
private:
Ui::AttachRemoteDialog *m_ui;
Ui::StartRemoteDialog *m_ui;
};
class StartExternalDialog : public QDialog

View File

@@ -929,21 +929,24 @@ void DebuggerManager::startNewDebugger(DebuggerRunControl *runControl)
m_attachedPID = -1;
break;
}
case AttachRemote: {
AttachRemoteDialog dlg(mainWindow());
case StartRemote: {
StartRemoteDialog dlg(mainWindow());
QStringList arches;
arches.append(_("i386:x86-64:intel"));
dlg.setRemoteArchitectures(arches);
dlg.setRemoteChannel(configValue(_("LastRemoteChannel")).toString());
dlg.setRemoteArchitecture(configValue(_("LastRemoteArchtecture")).toString());
dlg.setServerStartScript(configValue(_("LastServerStartScript")).toString());
if (dlg.exec() != QDialog::Accepted) {
runControl->debuggingFinished();
return;
}
setConfigValue(_("LastRemoteChannel"), dlg.remoteChannel());
setConfigValue(_("LastRemoteArchitecture"), dlg.remoteArchitecture());
setConfigValue(_("LastServerStartScript"), dlg.serverStartScript());
m_remoteChannel = dlg.remoteChannel();
m_remoteArchitecture = dlg.remoteArchitecture();
m_serverStartScript = dlg.serverStartScript();
break;
}
}

View File

@@ -113,7 +113,7 @@ enum DebuggerStartMode
StartExternal, // Start binary found in file system
AttachExternal, // Attach to running process
AttachCore, // Attach to a core file
AttachRemote // Attach to a remote process
StartRemote // Start and attach to a remote process
};
class IDebuggerEngine;
@@ -359,8 +359,10 @@ public:
QString m_dumperLib;
int m_attachedPID;
bool m_useTerminal;
// for remote debugging
QString m_remoteChannel;
QString m_remoteArchitecture;
QString m_serverStartScript;
private:
void init();

View File

@@ -207,7 +207,7 @@ public slots:
DebuggerOutputWindow::DebuggerOutputWindow(QWidget *parent)
: QWidget(parent)
{
setWindowTitle(tr("Gdb"));
setWindowTitle(tr("Debugger"));
QSplitter *m_splitter = new QSplitter(Qt::Horizontal, this);
// mixed input/output

View File

@@ -480,10 +480,10 @@ bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMess
m_attachCoreAction->setText(tr("Attach to Core..."));
connect(m_attachCoreAction, SIGNAL(triggered()), this, SLOT(attachCore()));
m_attachRemoteAction = new QAction(this);
m_attachRemoteAction->setText(tr("Attach to Running Remote Application..."));
connect(m_attachRemoteAction, SIGNAL(triggered()),
this, SLOT(attachRemoteApplication()));
m_startRemoteAction = new QAction(this);
m_startRemoteAction->setText(tr("Start and Attach to Remote Application..."));
connect(m_startRemoteAction, SIGNAL(triggered()),
this, SLOT(startRemoteApplication()));
Core::ActionContainer *mdebug =
@@ -502,12 +502,9 @@ bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMess
Constants::ATTACHCORE, globalcontext);
mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
#if 1
// FIXME: not yet functional
cmd = am->registerAction(m_attachRemoteAction,
cmd = am->registerAction(m_startRemoteAction,
Constants::ATTACHREMOTE, globalcontext);
mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
#endif
cmd = am->registerAction(m_manager->m_continueAction,
ProjectExplorer::Constants::DEBUG, QList<int>() << m_gdbRunningContext);
@@ -1090,11 +1087,11 @@ void DebuggerPlugin::attachCore()
runControl->start();
}
void DebuggerPlugin::attachRemoteApplication()
void DebuggerPlugin::startRemoteApplication()
{
QSharedPointer<RunConfiguration> rc = activeRunConfiguration();
if (RunControl *runControl = m_debuggerRunner
->run(rc, ProjectExplorer::Constants::DEBUGMODE, AttachRemote))
->run(rc, ProjectExplorer::Constants::DEBUGMODE, StartRemote))
runControl->start();
}

View File

@@ -98,9 +98,9 @@ private slots:
void showSettingsDialog();
void startExternalApplication();
void startRemoteApplication();
void attachExternalApplication();
void attachCore();
void attachRemoteApplication();
private:
void readSettings();
@@ -122,9 +122,9 @@ private:
QAction *m_toggleLockedAction;
QAction *m_startExternalAction;
QAction *m_startRemoteAction;
QAction *m_attachExternalAction;
QAction *m_attachCoreAction;
QAction *m_attachRemoteAction;
};
} // namespace Internal

View File

@@ -1,9 +1,11 @@
# This is a compile check for the dumpers only. Don't install the library!
include(../../qworkbenchlibrary.pri)
TEMPLATE = lib
TARGET = DebuggingHelper
CONFIG += shared
DESTDIR = ../../../bin
include(../../qworkbenchlibrary.pri)
DESTDIR = $$IDE_LIBRARY_PATH # /tmp would be better in some respect ...
linux-* {
CONFIG -= release

View File

@@ -117,7 +117,7 @@ GdbEngine::GdbEngine(DebuggerManager *parent)
{
q = parent;
qq = parent->engineInterface();
m_stubProc.setDebug(true);
m_stubProc.setMode(Core::Utils::ConsoleProcess::Debug);
initializeVariables();
initializeConnections();
}
@@ -131,22 +131,28 @@ GdbEngine::~GdbEngine()
void GdbEngine::initializeConnections()
{
// Gdb Process interaction
connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)), this,
SLOT(gdbProcError(QProcess::ProcessError)));
connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()), this,
SLOT(readGdbStandardOutput()));
connect(&m_gdbProc, SIGNAL(readyReadStandardError()), this,
SLOT(readGdbStandardError()));
connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)), q,
SLOT(exitDebugger()));
connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
this, SLOT(gdbProcError(QProcess::ProcessError)));
connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()),
this, SLOT(readGdbStandardOutput()));
connect(&m_gdbProc, SIGNAL(readyReadStandardError()),
this, SLOT(readGdbStandardError()));
connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
q, SLOT(exitDebugger()));
connect(&m_stubProc, SIGNAL(processError(QString)), SLOT(stubError(QString)));
connect(&m_stubProc, SIGNAL(processStarted()), SLOT(stubStarted()));
connect(&m_stubProc, SIGNAL(wrapperStopped()), q, SLOT(exitDebugger()));
connect(&m_stubProc, SIGNAL(processError(QString)),
this, SLOT(stubError(QString)));
connect(&m_stubProc, SIGNAL(processStarted()),
this, SLOT(stubStarted()));
connect(&m_stubProc, SIGNAL(wrapperStopped()),
q, SLOT(exitDebugger()));
connect(&m_uploadProc, SIGNAL(error(QProcess::ProcessError)),
this, SLOT(uploadProcError(QProcess::ProcessError)));
// Output
connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
SLOT(readDebugeeOutput(QByteArray)));
this, SLOT(readDebugeeOutput(QByteArray)));
connect(this, SIGNAL(gdbOutputAvailable(QString,QString)),
q, SLOT(showDebuggerOutput(QString,QString)),
@@ -225,6 +231,43 @@ void GdbEngine::gdbProcError(QProcess::ProcessError error)
q->exitDebugger();
}
void GdbEngine::uploadProcError(QProcess::ProcessError error)
{
QString msg;
switch (error) {
case QProcess::FailedToStart:
msg = tr("The upload process failed to start. Either the "
"invoked script '%1' is missing, or you may have insufficient "
"permissions to invoke the program.")
.arg(theDebuggerStringSetting(GdbLocation));
break;
case QProcess::Crashed:
msg = tr("The upload process crashed some time after starting "
"successfully.");
break;
case QProcess::Timedout:
msg = tr("The last waitFor...() function timed out. "
"The state of QProcess is unchanged, and you can try calling "
"waitFor...() again.");
break;
case QProcess::WriteError:
msg = tr("An error occurred when attempting to write "
"to the upload process. For example, the process may not be running, "
"or it may have closed its input channel.");
break;
case QProcess::ReadError:
msg = tr("An error occurred when attempting to read from "
"the upload process. For example, the process may not be running.");
break;
default:
msg = tr("An unknown error in the upload process occurred. "
"This is the default return value of error().");
}
q->showStatusMessage(msg);
QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
}
#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
@@ -535,7 +578,7 @@ void GdbEngine::interruptInferior()
return;
}
if (q->startMode() == AttachRemote) {
if (q->startMode() == StartRemote) {
execCommand(_("-exec-interrupt"));
return;
}
@@ -613,7 +656,7 @@ void GdbEngine::flushCommand(GdbCommand &cmd)
++currentToken();
m_cookieForToken[currentToken()] = cmd;
cmd.command = QString::number(currentToken()) + cmd.command;
if (cmd.command.contains(__("%1")))
if (cmd.flags & EmbedToken)
cmd.command = cmd.command.arg(currentToken());
m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
@@ -907,6 +950,7 @@ void GdbEngine::handleAqcuiredInferior()
#endif
if (theDebuggerBoolSetting(ListSourceFiles))
reloadSourceFiles();
tryLoadDebuggingHelpers();
#ifndef Q_OS_MAC
@@ -1313,7 +1357,7 @@ void GdbEngine::exitDebugger()
qDebug() << "STATUS ON EXITDEBUGGER: " << q->status());
interruptInferior();
}
if (q->startMode() == AttachExternal || q->startMode() == AttachRemote)
if (q->startMode() == AttachExternal || q->startMode() == StartRemote)
execCommand(_("detach"));
else
execCommand(_("kill"));
@@ -1357,8 +1401,19 @@ bool GdbEngine::startDebugger()
if (q->startMode() == AttachCore || q->startMode() == AttachExternal) {
// nothing to do
} else if (q->startMode() == AttachRemote) {
// nothing to do
} else if (q->startMode() == StartRemote) {
// Start the remote server
if (q->m_serverStartScript.isEmpty()) {
q->showStatusMessage(_("No server start script given. "
"Assuming server runs already."));
} else {
if (!q->m_workingDir.isEmpty())
m_uploadProc.setWorkingDirectory(q->m_workingDir);
if (!q->m_environment.isEmpty())
m_uploadProc.setEnvironment(q->m_environment);
m_uploadProc.start(_("/bin/sh ") + q->m_serverStartScript);
m_uploadProc.waitForStarted();
}
} else if (q->m_useTerminal) {
m_stubProc.stop(); // We leave the console open, so recycle it now.
@@ -1495,7 +1550,7 @@ bool GdbEngine::startDebugger()
execCommand(_("-file-exec-and-symbols ") + fileName);
execCommand(_("target core ") + coreName, CB(handleTargetCore));
qq->breakHandler()->removeAllBreakpoints();
} else if (q->startMode() == AttachRemote) {
} else if (q->startMode() == StartRemote) {
execCommand(_("set architecture %1").arg(q->m_remoteArchitecture));
qq->breakHandler()->setAllPending();
//QFileInfo fi(q->m_executable);
@@ -2795,7 +2850,7 @@ bool GdbEngine::hasDebuggingHelperForType(const QString &type) const
if (!theDebuggerBoolSetting(UseDebuggingHelpers))
return false;
if (q->startMode() == AttachCore) {
if (!startModeAllowsDumpers()) {
// "call" is not possible in gdb when looking at core files
return type == __("QString") || type.endsWith(__("::QString"))
|| type == __("QStringList") || type.endsWith(__("::QStringList"));
@@ -2834,7 +2889,7 @@ void GdbEngine::runDirectDebuggingHelper(const WatchData &data, bool dumpChildre
void GdbEngine::runDebuggingHelper(const WatchData &data0, bool dumpChildren)
{
if (q->startMode() == AttachCore) {
if (!startModeAllowsDumpers()) {
runDirectDebuggingHelper(data0, dumpChildren);
return;
}
@@ -2867,7 +2922,7 @@ void GdbEngine::runDebuggingHelper(const WatchData &data0, bool dumpChildren)
QVariant var;
var.setValue(data);
execCommand(cmd, WatchUpdate, CB(handleDebuggingHelperValue1), var);
execCommand(cmd, WatchUpdate | EmbedToken, CB(handleDebuggingHelperValue1), var);
q->showStatusMessage(
tr("Retrieving data for watch view (%1 requests pending)...")
@@ -3832,6 +3887,9 @@ void GdbEngine::tryLoadDebuggingHelpers()
if (m_debuggingHelperState != DebuggingHelperUninitialized)
return;
if (!startModeAllowsDumpers())
return;
PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
m_debuggingHelperState = DebuggingHelperUnavailable;
if (!qq->qtDumperLibraryEnabled())
@@ -3875,15 +3933,24 @@ void GdbEngine::tryLoadDebuggingHelpers()
execCommand(_("sharedlibrary ") + dotEscape(lib));
#endif
// retreive list of dumpable classes
execCommand(_("call (void*)qDumpObjectData440(1,%1+1,0,0,0,0,0,0)"));
execCommand(_("call (void*)qDumpObjectData440(1,%1+1,0,0,0,0,0,0)"), EmbedToken);
execCommand(_("p (char*)&qDumpOutBuffer"), CB(handleQueryDebuggingHelper));
}
void GdbEngine::recheckDebuggingHelperAvailability()
{
// retreive list of dumpable classes
execCommand(_("call (void*)qDumpObjectData440(1,%1+1,0,0,0,0,0,0)"));
execCommand(_("p (char*)&qDumpOutBuffer"), CB(handleQueryDebuggingHelper));
if (startModeAllowsDumpers()) {
// retreive list of dumpable classes
execCommand(_("call (void*)qDumpObjectData440(1,%1+1,0,0,0,0,0,0)"), EmbedToken);
execCommand(_("p (char*)&qDumpOutBuffer"), CB(handleQueryDebuggingHelper));
}
}
bool GdbEngine::startModeAllowsDumpers() const
{
return q->startMode() == StartInternal
|| q->startMode() == StartExternal
|| q->startMode() == AttachExternal;
}
IDebuggerEngine *createGdbEngine(DebuggerManager *parent, QList<Core::IOptionsPage*> *opts)

View File

@@ -147,7 +147,8 @@ public: // otherwise the Qt flag macros are unhappy
NeedsStop = 1,
Discardable = 2,
RebuildModel = 4,
WatchUpdate = Discardable|RebuildModel
WatchUpdate = Discardable|RebuildModel,
EmbedToken = 8
};
Q_DECLARE_FLAGS(GdbCommandFlags, GdbCommandFlag)
private:
@@ -191,6 +192,7 @@ private slots:
void readDebugeeOutput(const QByteArray &data);
void stubStarted();
void stubError(const QString &msg);
void uploadProcError(QProcess::ProcessError error);
private:
int terminationIndex(const QByteArray &buffer, int &length);
@@ -226,6 +228,7 @@ private:
QByteArray m_inbuffer;
QProcess m_gdbProc;
QProcess m_uploadProc;
Core::Utils::ConsoleProcess m_stubProc;
@@ -354,6 +357,8 @@ private:
const WatchData &parent);
void setWatchDataType(WatchData &data, const GdbMi &mi);
void setLocals(const QList<GdbMi> &locals);
bool startModeAllowsDumpers() const;
QString m_editedData;
int m_pendingRequests;

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AttachRemoteDialog</class>
<widget class="QDialog" name="AttachRemoteDialog">
<class>StartRemoteDialog</class>
<widget class="QDialog" name="StartRemoteDialog">
<property name="geometry">
<rect>
<x>0</x>
@@ -49,6 +49,16 @@
<item row="1" column="1">
<widget class="QComboBox" name="architectureComboBox"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="serverStartScriptLabel">
<property name="text">
<string>Server start script</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Core::Utils::PathChooser" name="serverStartScript" native="true"/>
</item>
</layout>
</item>
<item>

View File

@@ -56,6 +56,12 @@
#include <QtCore/QDebug>
#ifdef __GNUC__ // MinGW does not have a complete windows.h
typedef DWORD (__stdcall *PTHREAD_START_ROUTINE) (LPVOID lpThreadParameter);
#endif
enum { debug = 0 };
static QString msgFuncFailed(const char *f, unsigned long error)
@@ -68,7 +74,7 @@ template <class SymbolType>
inline bool resolveSymbol(const char *libraryName, HMODULE libraryHandle, const char *symbolName, SymbolType *s, QString *errorMessage)
{
*s = 0;
void *vs = ::GetProcAddress(libraryHandle, symbolName);
FARPROC WINAPI vs = ::GetProcAddress(libraryHandle, symbolName);
if (vs == 0) {
*errorMessage = QString::fromLatin1("Unable to resolve '%2' in '%1'.").arg(QString::fromAscii(symbolName), QString::fromAscii(libraryName));
return false;
@@ -158,29 +164,29 @@ bool SharedLibraryInjector::hasLoaded(const QString &modulePath)
QString SharedLibraryInjector::findModule(const QString &moduleName)
{
const TCHAR *moduleNameC = moduleName.utf16();
const TCHAR *moduleNameC = reinterpret_cast<const TCHAR*>(moduleName.utf16());
if (GetFileAttributesW(moduleNameC) != INVALID_FILE_ATTRIBUTES)
return moduleName;
TCHAR testpathC[MAX_PATH];
// Check application path first
GetModuleFileNameW(NULL, testpathC, MAX_PATH);
QString testPath = QString::fromUtf16(testpathC);
QString testPath = QString::fromUtf16(reinterpret_cast<unsigned short*>(testpathC));
const int lastSlash = testPath.lastIndexOf(QLatin1Char('\\'));
if (lastSlash != -1)
testPath.truncate(lastSlash + 1);
testPath += moduleName;
if (GetFileAttributesW(testPath.utf16()) != INVALID_FILE_ATTRIBUTES)
if (GetFileAttributesW(reinterpret_cast<const TCHAR*>(testPath.utf16())) != INVALID_FILE_ATTRIBUTES)
return testPath;
// Path Search
if (SearchPathW(NULL, moduleName.utf16(), NULL, sizeof(testpathC)/2, testpathC, NULL))
return QString::fromUtf16(testpathC);
if (SearchPathW(NULL, reinterpret_cast<const TCHAR*>(moduleName.utf16()), NULL, sizeof(testpathC)/2, testpathC, NULL))
return QString::fromUtf16(reinterpret_cast<unsigned short*>(testpathC));
// Last chance, if the module has already been loaded in this process, then use that path
const HMODULE loadedModule = GetModuleHandleW(moduleName.utf16());
const HMODULE loadedModule = GetModuleHandleW(reinterpret_cast<const TCHAR*>(moduleName.utf16()));
if (loadedModule) {
GetModuleFileNameW(loadedModule, testpathC, sizeof(testpathC));
if (GetFileAttributes(testpathC) != INVALID_FILE_ATTRIBUTES)
return QString::fromUtf16(testpathC);
return QString::fromUtf16(reinterpret_cast<unsigned short*>(testpathC));
}
return QString();
}
@@ -188,13 +194,13 @@ QString SharedLibraryInjector::findModule(const QString &moduleName)
unsigned long SharedLibraryInjector::getModuleEntryPoint(const QString &moduleName)
{
// If file doesn't exist, just treat it like we cannot figure out the entry point
if (moduleName.isEmpty() || GetFileAttributesW(moduleName.utf16()) == INVALID_FILE_ATTRIBUTES)
if (moduleName.isEmpty() || GetFileAttributesW(reinterpret_cast<const TCHAR*>(moduleName.utf16())) == INVALID_FILE_ATTRIBUTES)
return 0;
// Read the first 1K of data from the file
unsigned char peData[1024];
unsigned long peDataSize = 0;
const HANDLE hFile = CreateFileW(moduleName.utf16(), FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
const HANDLE hFile = CreateFileW(reinterpret_cast<const WCHAR*>(moduleName.utf16()), FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile == INVALID_HANDLE_VALUE
|| !ReadFile(hFile, peData, sizeof(peData), &peDataSize, NULL))
return 0;
@@ -236,7 +242,7 @@ bool SharedLibraryInjector::escalatePrivileges(QString *errorMessage)
Debug_Privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // set to enable privilege
Debug_Privileges.PrivilegeCount = 1; // working with only 1
if (!OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
*errorMessage = msgFuncFailed("OpenProcessToken", GetLastError());
break;
}
@@ -289,8 +295,8 @@ bool SharedLibraryInjector::doStubInjection(unsigned long pid,
if (!escalatePrivileges(errorMessage))
return false;
#if (defined(WIN64) || defined(_WIN64) || defined(__WIN64__))
// MinGW lacks OpenThread() and the advapi.lib as of 6.5.2009
#if (defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) || defined(__GNUC__)
*errorMessage = QLatin1String("Not implemented for this architecture.");
return false;
#else
@@ -462,7 +468,7 @@ HMODULE SharedLibraryInjector::findModuleHandle(const QString &modulePath, QStri
for (unsigned i = 0; i < count; i++) {
TCHAR szModName[MAX_PATH];
if (m_pfnGetModuleFileNameExW(hProcess, hMods[i], szModName, sizeof(szModName))) {
if (QString::fromUtf16(szModName) == modulePath) {
if (QString::fromUtf16(reinterpret_cast<const unsigned short *>(szModName)) == modulePath) {
::FreeLibrary(m_hModPSAPI);
::CloseHandle(hProcess);
return hMods[i];

View File

@@ -7,5 +7,8 @@ HEADERS += $$PWD/peutils.h \
$$PWD/dbgwinutils.h \
$$PWD/sharedlibraryinjector.h
# For the Privilege manipulation functions in sharedlibraryinjector.cpp
LIBS += advapi32.lib
contains(QMAKE_CXX, cl) {
# For the Privilege manipulation functions in sharedlibraryinjector.cpp.
# Not required for MinGW.
LIBS += advapi32.lib
}

View File

@@ -136,7 +136,7 @@ void FindToolWindow::writeSettings()
{
QSettings *settings = Core::ICore::instance()->settings();
settings->beginGroup("Find");
settings->setValue("CurrentFilter", m_currentFilter ? 0 : m_currentFilter->id());
settings->setValue("CurrentFilter", m_currentFilter ? m_currentFilter->id() : 0);
foreach (IFindFilter *filter, m_filters)
filter->writeSettings(settings);
settings->endGroup();

View File

@@ -131,8 +131,6 @@ ProjectWindow::~ProjectWindow()
void ProjectWindow::restoreStatus()
{
m_panelsTabWidget->setFocus();
if (!m_treeWidget->currentItem() && m_treeWidget->topLevelItemCount()) {
m_treeWidget->setCurrentItem(m_treeWidget->topLevelItem(0), 0, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
}

View File

@@ -1,4 +1,3 @@
IDE_BUILD_TREE = $$OUT_PWD/../../..
include(../qtcreator.pri)
win32 {

View File

@@ -1,6 +1,3 @@
isEmpty(IDE_BUILD_TREE) {
IDE_BUILD_TREE = $$OUT_PWD/../../..
}
include(../qtcreator.pri)
isEmpty(PROVIDER) {
@@ -17,25 +14,17 @@ isEmpty(TARGET) {
error("qworkbenchplugin.pri: You must provide a TARGET")
}
# Copy the pluginspec file to the library directory.
# Note: On Windows/MinGW with some sh.exe in the path,
# QMAKE_COPY is some cp command that does not understand
# "\". Force the standard windows copy.
COPYDEST = $${DESTDIR}
COPYSRC = $${_PRO_FILE_PWD_}/$${TARGET}.pluginspec
PLUGINSPECS = $${_PRO_FILE_PWD_}/$${TARGET}.pluginspec
copy2build.input = PLUGINSPECS
copy2build.output = $$DESTDIR/${QMAKE_FUNC_FILE_IN_stripSrcDir}
isEmpty(vcproj):copy2build.variable_out = PRE_TARGETDEPS
copy2build.commands = $$QMAKE_COPY \"${QMAKE_FILE_IN}\" \"${QMAKE_FILE_OUT}\"
copy2build.name = COPY ${QMAKE_FILE_IN}
copy2build.CONFIG += no_link
QMAKE_EXTRA_COMPILERS += copy2build
TARGET = $$qtLibraryTarget($$TARGET)
win32 {
COPYDEST ~= s|/+|\|
COPYSRC ~= s|/+|\|
COPY_CMD=xcopy /y
} else {
COPY_CMD=$${QMAKE_COPY}
}
QMAKE_POST_LINK += $${COPY_CMD} $${COPYSRC} $${COPYDEST}
macx {
QMAKE_LFLAGS_SONAME = -Wl,-install_name,@executable_path/../PlugIns/$${PROVIDER}/
} else:linux-* {