debuggerruncontrol.cpp 23.7 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
hjk's avatar
hjk committed
25

26
#include "debuggerruncontrol.h"
con's avatar
con committed
27

28
#include "analyzer/analyzermanager.h"
29
#include "debuggeractions.h"
30
#include "debuggercore.h"
31 32
#include "debuggerengine.h"
#include "debuggerkitinformation.h"
33
#include "debuggerplugin.h"
34
#include "debuggerrunconfigurationaspect.h"
Friedemann Kleint's avatar
Friedemann Kleint committed
35
#include "debuggerstartparameters.h"
36 37
#include "debuggerstringutils.h"
#include "breakhandler.h"
hjk's avatar
hjk committed
38
#include "shared/peutils.h"
Friedemann Kleint's avatar
Friedemann Kleint committed
39

hjk's avatar
hjk committed
40
#include <projectexplorer/buildconfiguration.h>
41 42 43
#include <projectexplorer/devicesupport/deviceprocessesdialog.h>
#include <projectexplorer/devicesupport/deviceprocesslist.h>
#include <projectexplorer/environmentaspect.h> // For the environment
con's avatar
con committed
44
#include <projectexplorer/project.h>
hjk's avatar
hjk committed
45
#include <projectexplorer/projectexplorer.h>
46
#include <projectexplorer/projectexplorericons.h>
47
#include <projectexplorer/runnables.h>
Tobias Hunger's avatar
Tobias Hunger committed
48
#include <projectexplorer/target.h>
49
#include <projectexplorer/taskhub.h>
50
#include <projectexplorer/toolchain.h>
con's avatar
con committed
51

Kai Koehne's avatar
Kai Koehne committed
52
#include <utils/checkablemessagebox.h>
Orgad Shaneh's avatar
Orgad Shaneh committed
53
#include <utils/fileutils.h>
hjk's avatar
hjk committed
54
#include <utils/qtcassert.h>
55
#include <utils/qtcprocess.h>
56
#include <coreplugin/icore.h>
57
#include <coreplugin/coreconstants.h>
58
#include <qmldebug/qmldebugcommandlinearguments.h>
hjk's avatar
hjk committed
59

60 61
#include <qtsupport/qtkitinformation.h>

62
#include <QTcpServer>
con's avatar
con committed
63

64
using namespace Debugger::Internal;
hjk's avatar
hjk committed
65 66
using namespace ProjectExplorer;
using namespace Utils;
67

68 69
enum { debug = 0 };

70
namespace Debugger {
71
namespace Internal {
72

73
DebuggerEngine *createCdbEngine(const DebuggerRunParameters &rp, QStringList *error);
74 75 76
const auto *DebugRunMode = ProjectExplorer::Constants::DEBUG_RUN_MODE;
const auto *DebugRunModeWithBreakOnMain = ProjectExplorer::Constants::DEBUG_RUN_MODE_WITH_BREAK_ON_MAIN;

77 78 79
DebuggerEngine *createGdbEngine(const DebuggerRunParameters &rp);
DebuggerEngine *createPdbEngine(const DebuggerRunParameters &rp);
DebuggerEngine *createQmlEngine(const DebuggerRunParameters &rp);
80
DebuggerEngine *createQmlCppEngine(const DebuggerRunParameters &rp, QStringList *error);
81
DebuggerEngine *createLldbEngine(const DebuggerRunParameters &rp);
82

83 84
} // namespace Internal

85

86 87 88 89 90 91
static const char *engineTypeName(DebuggerEngineType et)
{
    switch (et) {
    case Debugger::NoEngineType:
        break;
    case Debugger::GdbEngineType:
92
        return "Gdb engine";
93 94 95
    case Debugger::CdbEngineType:
        return "Cdb engine";
    case Debugger::PdbEngineType:
96
        return "Pdb engine";
97 98 99 100 101
    case Debugger::QmlEngineType:
        return "QML engine";
    case Debugger::QmlCppEngineType:
        return "QML C++ engine";
    case Debugger::LldbEngineType:
102
        return "LLDB command line engine";
103 104 105 106 107 108
    case Debugger::AllEngineTypes:
        break;
    }
    return "No engine";
}

109 110
DebuggerRunControl::DebuggerRunControl(RunConfiguration *runConfig, DebuggerEngine *engine)
    : RunControl(runConfig, DebugRunMode),
111 112
      m_engine(engine),
      m_running(false)
113
{
114
    setIcon(ProjectExplorer::Icons::DEBUG_START_SMALL_TOOLBAR);
115
    connect(this, &RunControl::finished, this, &DebuggerRunControl::handleFinished);
hjk's avatar
hjk committed
116 117 118 119 120 121 122

    connect(engine, &DebuggerEngine::requestRemoteSetup,
            this, &DebuggerRunControl::requestRemoteSetup);
    connect(engine, &DebuggerEngine::stateChanged,
            this, &DebuggerRunControl::stateChanged);
    connect(engine, &DebuggerEngine::aboutToNotifyInferiorSetupOk,
            this, &DebuggerRunControl::aboutToNotifyInferiorSetupOk);
123 124
}

hjk's avatar
hjk committed
125 126 127
DebuggerRunControl::~DebuggerRunControl()
{
    disconnect();
128 129 130
    if (m_engine) {
        DebuggerEngine *engine = m_engine;
        m_engine = 0;
hjk's avatar
hjk committed
131 132 133 134 135
        engine->disconnect();
        delete engine;
    }
}

136
QString DebuggerRunControl::displayName() const
137
{
138
    QTC_ASSERT(m_engine, return QString());
139
    return m_engine->runParameters().displayName;
140 141
}

142 143 144 145 146 147
bool DebuggerRunControl::supportsReRunning() const
{
    // QML and/or mixed are not prepared for it.
    return m_engine && !(m_engine->runParameters().languages & QmlLanguage);
}

148
void DebuggerRunControl::start()
149
{
150
    Debugger::Internal::saveModeToRestore();
151
    Debugger::selectPerspective(Debugger::Constants::CppPerspectiveId);
152 153 154
    TaskHub::clearTasks(Debugger::Constants::TASK_CATEGORY_DEBUGGER_DEBUGINFO);
    TaskHub::clearTasks(Debugger::Constants::TASK_CATEGORY_DEBUGGER_RUNTIME);

155
    QTC_ASSERT(m_engine, return);
156
    // User canceled input dialog asking for executable when working on library project.
157
    if (m_engine->runParameters().startMode == StartInternal
158
            && m_engine->runParameters().inferior.executable.isEmpty()
hjk's avatar
hjk committed
159
            && m_engine->runParameters().interpreter.isEmpty()) {
160
        appendMessage(tr("No executable specified.") + QLatin1Char('\n'), ErrorMessageFormat);
161 162 163 164 165
        emit started();
        emit finished();
        return;
    }

166
    if (m_engine->runParameters().startMode == StartInternal) {
167
        QStringList unhandledIds;
168 169 170
        foreach (Breakpoint bp, breakHandler()->allBreakpoints()) {
            if (bp.isEnabled() && !m_engine->acceptsBreakpoint(bp))
                unhandledIds.append(bp.id().toString());
171 172 173 174 175 176 177
        }
        if (!unhandledIds.isEmpty()) {
            QString warningMessage =
                    DebuggerPlugin::tr("Some breakpoints cannot be handled by the debugger "
                                       "languages currently active, and will be ignored.\n"
                                       "Affected are breakpoints %1")
                    .arg(unhandledIds.join(QLatin1String(", ")));
178

179
            Internal::showMessage(warningMessage, LogWarning);
180

181 182
            static bool checked = true;
            if (checked)
183
                CheckableMessageBox::information(Core::ICore::mainWindow(),
Kai Koehne's avatar
Kai Koehne committed
184 185 186 187
                                                 tr("Debugger"),
                                                 warningMessage,
                                                 tr("&Show this message again."),
                                                 &checked, QDialogButtonBox::Ok);
188 189 190
        }
    }

191
    Internal::runControlStarted(m_engine);
192

193 194
    // We might get a synchronous startFailed() notification on Windows,
    // when launching the process fails. Emit a proper finished() sequence.
195
    emit started();
196
    m_running = true;
197

198
    m_engine->startDebugger(this);
199

200
    if (m_running)
201
        appendMessage(tr("Debugging starts") + QLatin1Char('\n'), NormalMessageFormat);
202 203
}

204
void DebuggerRunControl::startFailed()
205
{
206
    appendMessage(tr("Debugging has failed") + QLatin1Char('\n'), NormalMessageFormat);
207
    m_running = false;
208
    emit finished();
209
    m_engine->handleStartFailed();
210 211
}

hjk's avatar
hjk committed
212 213 214 215 216 217 218 219 220 221
void DebuggerRunControl::notifyEngineRemoteServerRunning(const QByteArray &msg, int pid)
{
    m_engine->notifyEngineRemoteServerRunning(msg, pid);
}

void DebuggerRunControl::notifyEngineRemoteSetupFinished(const RemoteSetupResult &result)
{
    m_engine->notifyEngineRemoteSetupFinished(result);
}

222
void DebuggerRunControl::handleFinished()
223
{
224
    appendMessage(tr("Debugging has finished") + QLatin1Char('\n'), NormalMessageFormat);
225 226
    if (m_engine)
        m_engine->handleFinished();
227
    Internal::runControlFinished(m_engine);
228 229
}

230
bool DebuggerRunControl::promptToStop(bool *optionalPrompt) const
231
{
232
    QTC_ASSERT(isRunning(), return true);
233

234 235 236
    if (optionalPrompt && !*optionalPrompt)
        return true;

Jarek Kobus's avatar
Jarek Kobus committed
237
    const QString question = tr("A debugging session is still in progress. "
238 239 240
            "Terminating the session in the current"
            " state can leave the target in an inconsistent state."
            " Would you still like to terminate it?");
241 242 243 244 245
    bool result = showPromptToStopDialog(tr("Close Debugging Session"), question,
                                         QString(), QString(), optionalPrompt);
    if (result)
        disconnect(this);
    return result;
246 247 248 249
}

RunControl::StopResult DebuggerRunControl::stop()
{
250 251
    QTC_ASSERT(m_engine, return StoppedSynchronously);
    m_engine->quitDebugger();
252
    return AsynchronousStop;
253 254
}

255
void DebuggerRunControl::debuggingFinished()
256
{
257
    m_running = false;
258
    emit finished();
259 260
}

hjk's avatar
hjk committed
261 262 263 264 265
void DebuggerRunControl::showMessage(const QString &msg, int channel)
{
    m_engine->showMessage(msg, channel);
}

266
bool DebuggerRunControl::isRunning() const
267
{
268
    return m_running;
269 270
}

hjk's avatar
hjk committed
271 272
DebuggerStartParameters &DebuggerRunControl::startParameters()
{
273
    return m_engine->runParameters();
hjk's avatar
hjk committed
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
}

void DebuggerRunControl::notifyInferiorIll()
{
    m_engine->notifyInferiorIll();
}

void DebuggerRunControl::notifyInferiorExited()
{
    m_engine->notifyInferiorExited();
}

void DebuggerRunControl::quitDebugger()
{
    m_engine->quitDebugger();
}

void DebuggerRunControl::abortDebugger()
292
{
hjk's avatar
hjk committed
293
    m_engine->abortDebugger();
294 295
}

296
///////////////////////////////////////////////////////////////////////
297
//
298
// DebuggerRunControlCreator
299
//
300 301
///////////////////////////////////////////////////////////////////////

302

303 304
namespace Internal {

305
class DebuggerRunControlCreator
306 307
{
public:
308 309 310 311 312 313
    DebuggerRunControlCreator() {}

    // Life cycle: Initialize from StartParameters, enrich by automatically
    // detectable pieces, construct an Engine and a RunControl.
    void initialize(const DebuggerStartParameters &sp);
    void enrich(const RunConfiguration *runConfig, const Kit *kit);
314
    void createRunControl(Core::Id runMode = DebugRunMode);
315 316 317 318 319 320
    QString fullError() const { return m_errors.join(QLatin1Char('\n')); }

    // Result.
    DebuggerRunParameters m_rp;
    QStringList m_errors;
    DebuggerRunControl *m_runControl = 0;
321 322

    const RunConfiguration *m_runConfig = 0;
323 324
};

325
void DebuggerRunControlCreator::initialize(const DebuggerStartParameters &sp)
326
{
327
    m_rp.DebuggerStartParameters::operator=(sp);
328 329
}

330
void DebuggerRunControlCreator::enrich(const RunConfiguration *runConfig, const Kit *kit)
hjk's avatar
hjk committed
331
{
hjk's avatar
hjk committed
332
    m_runConfig = runConfig;
333
    QTC_ASSERT(kit, return);
hjk's avatar
hjk committed
334

335 336 337
    Target *target = 0;
    Project *project = 0;

hjk's avatar
hjk committed
338 339
    // Find a Kit and Target. Either could be missing.
    if (m_runConfig)
340
        target = m_runConfig->target();
hjk's avatar
hjk committed
341

342
    // Extract as much as possible from available RunConfiguration.
hjk's avatar
hjk committed
343
    if (m_runConfig && m_runConfig->runnable().is<StandardRunnable>()) {
344 345
        m_rp.inferior = m_runConfig->runnable().as<StandardRunnable>();
        m_rp.useTerminal = m_rp.inferior.runMode == ApplicationLauncher::Console;
346
        // Normalize to work around QTBUG-17529 (QtDeclarative fails with 'File name case mismatch'...)
347
        m_rp.inferior.workingDirectory = FileUtils::normalizePathName(m_rp.inferior.workingDirectory);
348 349
    }

350
    // We might get an executable from a local PID.
351
    if (m_rp.inferior.executable.isEmpty() && m_rp.attachPID != InvalidPid) {
352 353
        foreach (const DeviceProcessItem &p, DeviceProcessList::localProcesses())
            if (p.pid == m_rp.attachPID)
354
                m_rp.inferior.executable = p.exe;
355
    }
356

357 358 359
    if (m_rp.symbolFile.isEmpty())
        m_rp.symbolFile = m_rp.inferior.executable;

360
    if (m_runConfig) {
361
        if (auto envAspect = m_runConfig->extraAspect<EnvironmentAspect>()) {
362 363 364
            m_rp.inferior.environment = envAspect->environment(); // Correct.
            m_rp.stubEnvironment = m_rp.inferior.environment; // FIXME: Wrong, but contains DYLD_IMAGE_SUFFIX
            m_rp.debuggerEnvironment = m_rp.inferior.environment; // FIXME: Wrong, but contains DYLD_IMAGE_SUFFIX
365
        }
366 367
    }

368
    if (ToolChain *tc = ToolChainKitInformation::toolChain(kit))
369
        m_rp.toolChainAbi = tc->targetAbi();
370

371 372
    if (target)
        project = target->project();
373

374 375
    if (project && m_rp.projectSourceDirectory.isEmpty())
        m_rp.projectSourceDirectory = project->projectDirectory().toString();
376

377 378
    if (project && m_rp.projectSourceFiles.isEmpty())
        m_rp.projectSourceFiles = project->files(Project::SourceFiles);
379

380 381
    if (project && m_rp.projectSourceFiles.isEmpty())
        m_rp.projectSourceFiles = project->files(Project::SourceFiles);
382

383
    if (false && project) {
384
        const QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(kit);
385 386 387 388 389 390 391 392
        m_rp.nativeMixedEnabled = version && version->qtVersion() >= QtSupport::QtVersionNumber(5, 7, 0);
    }

    bool ok = false;
    int nativeMixedOverride = qgetenv("QTC_DEBUGGER_NATIVE_MIXED").toInt(&ok);
    if (ok)
        m_rp.nativeMixedEnabled = bool(nativeMixedOverride);

393 394 395 396
    m_rp.cppEngineType = DebuggerKitInformation::engineType(kit);
    m_rp.sysRoot = SysRootKitInformation::sysRoot(kit).toString();
    m_rp.debuggerCommand = DebuggerKitInformation::debuggerCommand(kit).toString();
    m_rp.device = DeviceKitInformation::device(kit);
397

398 399 400
    if (project) {
        m_rp.projectSourceDirectory = project->projectDirectory().toString();
        m_rp.projectSourceFiles = project->files(Project::SourceFiles);
401 402
    }

hjk's avatar
hjk committed
403 404 405
    if (m_rp.displayName.isEmpty() && m_runConfig)
        m_rp.displayName = m_runConfig->displayName();

406 407 408
    if (m_runConfig && m_runConfig->property("supportsDebugger").toBool()) {
        QString mainScript = m_runConfig->property("mainScript").toString();
        QString interpreter = m_runConfig->property("interpreter").toString();
hjk's avatar
hjk committed
409 410 411
        if (!interpreter.isEmpty() && mainScript.endsWith(_(".py"))) {
            m_rp.mainScript = mainScript;
            m_rp.interpreter = interpreter;
412
            QString args = m_runConfig->property("arguments").toString();
413
            if (!args.isEmpty()) {
414 415 416
                if (!m_rp.inferior.commandLineArguments.isEmpty())
                    m_rp.inferior.commandLineArguments.append(QLatin1Char(' '));
                m_rp.inferior.commandLineArguments.append(args);
417
            }
hjk's avatar
hjk committed
418 419 420 421
            m_rp.masterEngineType = PdbEngineType;
        }
    }

422 423 424 425 426 427
    DebuggerRunConfigurationAspect *debuggerAspect = 0;
    if (m_runConfig)
        debuggerAspect = m_runConfig->extraAspect<DebuggerRunConfigurationAspect>();

    if (debuggerAspect)
        m_rp.multiProcess = debuggerAspect->useMultiProcess();
428

429
    if (debuggerAspect) {
430
        m_rp.languages = NoLanguage;
431
        if (debuggerAspect->useCppDebugger())
432
            m_rp.languages |= CppLanguage;
433
        if (debuggerAspect->useQmlDebugger())
434
            m_rp.languages |= QmlLanguage;
435 436 437 438 439 440 441 442
    }

    // This can happen e.g. when started from the command line.
    if (m_rp.languages == NoLanguage)
        m_rp.languages = CppLanguage;

    // validate debugger if C++ debugging is enabled
    if (m_rp.languages & CppLanguage) {
443
        const QList<Task> tasks = DebuggerKitInformation::validateDebugger(kit);
444 445 446 447 448 449 450 451 452
        if (!tasks.isEmpty()) {
            foreach (const Task &t, tasks)
                m_errors.append(t.description);
            return;
        }
    }

    if (m_rp.languages & QmlLanguage) {
        if (m_rp.device && m_rp.device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
453 454 455 456 457 458 459 460 461 462 463
            if (m_rp.qmlServerAddress.isEmpty() || m_rp.qmlServerPort == InvalidPort) {
                QTcpServer server;
                const bool canListen = server.listen(QHostAddress::LocalHost)
                        || server.listen(QHostAddress::LocalHostIPv6);
                if (!canListen) {
                    m_errors.append(DebuggerPlugin::tr("Not enough free ports for QML debugging.")
                                    + QLatin1Char(' '));
                    return;
                }
                m_rp.qmlServerAddress = server.serverAddress().toString();
                m_rp.qmlServerPort = server.serverPort();
464
            }
465 466 467 468 469 470

            // Makes sure that all bindings go through the JavaScript engine, so that
            // breakpoints are actually hit!
            const QString optimizerKey = _("QML_DISABLE_OPTIMIZER");
            if (!m_rp.inferior.environment.hasKey(optimizerKey))
                m_rp.inferior.environment.set(optimizerKey, _("1"));
471 472 473
        }
    }

474 475 476 477 478 479 480 481 482 483 484 485
    if (!boolSetting(AutoEnrichParameters)) {
        const QString sysroot = m_rp.sysRoot;
        if (m_rp.debugInfoLocation.isEmpty())
            m_rp.debugInfoLocation = sysroot + QLatin1String("/usr/lib/debug");
        if (m_rp.debugSourceLocation.isEmpty()) {
            QString base = sysroot + QLatin1String("/usr/src/debug/");
            m_rp.debugSourceLocation.append(base + QLatin1String("qt5base/src/corelib"));
            m_rp.debugSourceLocation.append(base + QLatin1String("qt5base/src/gui"));
            m_rp.debugSourceLocation.append(base + QLatin1String("qt5base/src/network"));
        }
    }

486 487
    if (m_rp.masterEngineType == NoEngineType) {
        if (m_rp.languages & QmlLanguage) {
488
            QmlDebug::QmlDebugServicesPreset service;
489
            if (m_rp.languages & CppLanguage) {
490
                if (m_rp.nativeMixedEnabled) {
491
                    service = QmlDebug::QmlNativeDebuggerServices;
492 493
                } else {
                    m_rp.masterEngineType = QmlCppEngineType;
494
                    service = QmlDebug::QmlDebuggerServices;
495 496
                }
            } else {
497
                m_rp.masterEngineType = QmlEngineType;
498
                service = QmlDebug::QmlDebuggerServices;
499
            }
500
            if (m_rp.startMode != AttachExternal && m_rp.startMode != AttachCrashedExternal) {
501
                QtcProcess::addArg(&m_rp.inferior.commandLineArguments,
502
                                   (m_rp.languages & CppLanguage) && m_rp.nativeMixedEnabled ?
503 504
                                       QmlDebug::qmlDebugNativeArguments(service, false) :
                                       QmlDebug::qmlDebugTcpArguments(service, m_rp.qmlServerPort));
505
            }
506 507 508 509 510 511 512 513 514 515 516 517
        }
    }

    if (m_rp.masterEngineType == NoEngineType)
        m_rp.masterEngineType = m_rp.cppEngineType;

    if (m_rp.device && m_rp.connParams.port == 0)
        m_rp.connParams = m_rp.device->sshParameters();

    // Could have been set from command line.
    if (m_rp.remoteChannel.isEmpty())
        m_rp.remoteChannel = m_rp.connParams.host + QLatin1Char(':') + QString::number(m_rp.connParams.port);
518

519 520
    if (m_rp.startMode == NoStartMode)
        m_rp.startMode = StartInternal;
521 522
}

523 524
// Re-used for Combined C++/QML engine.
DebuggerEngine *createEngine(DebuggerEngineType et, const DebuggerRunParameters &rp, QStringList *errors)
525 526 527
{
    switch (et) {
    case GdbEngineType:
528
        return createGdbEngine(rp);
529
    case CdbEngineType:
530
        return createCdbEngine(rp, errors);
531
    case PdbEngineType:
532
        return createPdbEngine(rp);
533
    case QmlEngineType:
534
        return createQmlEngine(rp);
535
    case LldbEngineType:
536
        return createLldbEngine(rp);
hjk's avatar
hjk committed
537
    case QmlCppEngineType:
538
        return createQmlCppEngine(rp, errors);
539
    default:
540 541
        if (errors)
            errors->append(DebuggerPlugin::tr("Unknown debugger type \"%1\"").arg(_(engineTypeName(et))));
542
    }
543 544 545
    return 0;
}

546
void DebuggerRunControlCreator::createRunControl(Core::Id runMode)
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
{
    if (runMode == DebugRunModeWithBreakOnMain)
        m_rp.breakOnMain = true;

    DebuggerEngine *engine = createEngine(m_rp.masterEngineType, m_rp, &m_errors);
    if (!engine) {
        m_errors.append(DebuggerPlugin::tr("Unable to create a debugger engine of the type \"%1\"").
                        arg(_(engineTypeName(m_rp.masterEngineType))));
        m_rp.startMode = NoStartMode;
        return;
    }

    m_runControl = new DebuggerRunControl(const_cast<RunConfiguration *>(m_runConfig), engine);

    if (!m_runControl)
        m_rp.startMode = NoStartMode;
}

////////////////////////////////////////////////////////////////////////
//
// DebuggerRunControlFactory
//
////////////////////////////////////////////////////////////////////////

hjk's avatar
hjk committed
571 572 573 574 575 576
static bool isDebuggableScript(RunConfiguration *runConfig)
{
    QString mainScript = runConfig->property("mainScript").toString();
    return mainScript.endsWith(_(".py")); // Only Python for now.
}

577 578 579 580 581 582 583 584
class DebuggerRunControlFactory : public IRunControlFactory
{
public:
    explicit DebuggerRunControlFactory(QObject *parent)
        : IRunControlFactory(parent)
    {}

    RunControl *create(RunConfiguration *runConfig,
585
                       Core::Id mode, QString *errorMessage) override
586 587 588 589 590 591 592
    {
        QTC_ASSERT(runConfig, return 0);
        QTC_ASSERT(mode == DebugRunMode || mode == DebugRunModeWithBreakOnMain, return 0);

        // We cover only local setup here. Remote setups are handled by the
        // RunControl factories in the target specific plugins.
        DebuggerRunControlCreator creator;
593
        creator.enrich(runConfig, runConfig->target()->kit());
594 595 596 597 598 599
        creator.createRunControl(mode);
        if (errorMessage)
            *errorMessage = creator.fullError();
        return creator.m_runControl;
    }

600
    bool canRun(RunConfiguration *runConfig, Core::Id mode) const override
601
    {
602 603
        if (!(mode == DebugRunMode || mode == DebugRunModeWithBreakOnMain))
            return false;
604 605 606 607 608 609 610 611

        Runnable runnable = runConfig->runnable();
        if (runnable.is<StandardRunnable>()) {
            IDevice::ConstPtr device = runnable.as<StandardRunnable>().device;
            if (device && device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)
                return true;
        }

612 613 614
        return DeviceTypeKitInformation::deviceTypeId(runConfig->target()->kit())
                    == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE
                 || isDebuggableScript(runConfig);
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
    }

    IRunConfigurationAspect *createRunConfigurationAspect(RunConfiguration *rc) override
    {
        return new DebuggerRunConfigurationAspect(rc);
    }
};

QObject *createDebuggerRunControlFactory(QObject *parent)
{
    return new DebuggerRunControlFactory(parent);
}

////////////////////////////////////////////////////////////////////////
//
// Externally visible helper.
//
////////////////////////////////////////////////////////////////////////

/**
 * Used for direct "special" starts from actions in the debugger plugin.
 */
DebuggerRunControl *createAndScheduleRun(const DebuggerRunParameters &rp, const Kit *kit)
{
639
    QTC_ASSERT(kit, return 0); // Caller needs to look for a suitable kit.
640 641 642
    DebuggerRunControlCreator creator;
    creator.m_rp = rp;
    creator.enrich(0, kit);
643
    creator.createRunControl(DebugRunMode);
644 645 646 647 648 649 650 651 652
    if (!creator.m_runControl) {
        ProjectExplorerPlugin::showRunErrorMessage(creator.fullError());
        return 0;
    }
    Internal::showMessage(rp.startMessage, 0);
    ProjectExplorerPlugin::startRunControl(creator.m_runControl, DebugRunMode);
    return creator.m_runControl; // Only used for tests.
}

653
} // namespace Internal
654

655 656 657
/**
 * Main entry point for target plugins.
 */
658 659 660
DebuggerRunControl *createDebuggerRunControl(const DebuggerStartParameters &sp,
                                             RunConfiguration *runConfig,
                                             QString *errorMessage,
661
                                             Core::Id runMode)
662
{
663
    QTC_ASSERT(runConfig, return 0);
664 665
    DebuggerRunControlCreator creator;
    creator.initialize(sp);
666
    creator.enrich(runConfig, runConfig->target()->kit());
667
    creator.createRunControl(runMode);
668 669 670 671 672 673 674 675 676
    if (errorMessage)
        *errorMessage = creator.fullError();
    if (!creator.m_runControl) {
        Core::ICore::showWarningWithOptions(DebuggerRunControl::tr("Debugger"), creator.fullError());
        return 0;
    }
    return creator.m_runControl;
}

hjk's avatar
hjk committed
677
} // namespace Debugger