maemoruncontrol.cpp 15 KB
Newer Older
1
2
/****************************************************************************
**
hjk's avatar
hjk committed
3
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Creator.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "maemoruncontrol.h"
36
37

#include "maemopackagecreationstep.h"
38
#include "maemosshthread.h"
39
40
41
42
43
#include "maemorunconfiguration.h"

#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <debugger/debuggermanager.h>
44
45
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/toolchain.h>
46
47
48
49
50
#include <utils/qtcassert.h>

#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QFuture>
51
#include <QtCore/QProcess>
52
53
#include <QtCore/QStringBuilder>

ck's avatar
ck committed
54
55
#include <QtGui/QMessageBox>

56
57
58
namespace Qt4ProjectManager {
namespace Internal {

59
60
61
using ProjectExplorer::RunConfiguration;
using ProjectExplorer::ToolChain;

62
63
AbstractMaemoRunControl::AbstractMaemoRunControl(RunConfiguration *rc)
    : RunControl(rc)
ck's avatar
ck committed
64
65
    , m_runConfig(qobject_cast<MaemoRunConfiguration *>(rc))
    , m_devConfig(m_runConfig ? m_runConfig->deviceConfig() : MaemoDeviceConfig())
66
67
68
{
}

69
70
71
72
AbstractMaemoRunControl::~AbstractMaemoRunControl()
{
}

73
74
75
void AbstractMaemoRunControl::start()
{
    m_stoppedByUser = false;
76
77
78
79
80
81
    if (!m_devConfig.isValid()) {
        handleError(tr("No device configuration set for run configuration."));
    } else {
        emit started();
        startInitialCleanup();
    }
82
83
84
}

void AbstractMaemoRunControl::startInitialCleanup()
85
{   
86
87
88
89
    emit addToOutputWindow(this, tr("Cleaning up remote leftovers first ..."));
    const QStringList appsToKill
        = QStringList() << executableFileName() << QLatin1String("gdbserver");
    killRemoteProcesses(appsToKill, true);
90
91
92
93
94
}

void AbstractMaemoRunControl::stop()
{
    m_stoppedByUser = true;
95
96
97
98
    if (isCleaning())
        m_initialCleaner->stop();
    else if (isDeploying())
        m_sshDeployer->stop();
99
100
    else
        stopInternal();
101
102
}

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
void AbstractMaemoRunControl::handleInitialCleanupFinished()
{
    if (m_stoppedByUser) {
        emit addToOutputWindow(this, tr("Initial cleanup canceled by user."));
        emit finished();
    } else if (m_initialCleaner->hasError()) {
        handleError(tr("Error running initial cleanup: %1.")
                    .arg(m_initialCleaner->error()));
        emit finished();
    } else {
        emit addToOutputWindow(this, tr("Initial cleanup done."));
        startInternal();
    }
}

118
119
void AbstractMaemoRunControl::startDeployment(bool forDebugging)
{
ck's avatar
ck committed
120
    QTC_ASSERT(m_runConfig, return);
121

122
    if (m_stoppedByUser) {
123
124
        emit finished();
    } else {
125
        m_deployables.clear();
ck's avatar
ck committed
126
        if (m_runConfig->currentlyNeedsDeployment(m_devConfig.host)) {
127
            m_deployables.append(Deployable(packageFileName(),
128
129
                QFileInfo(executableOnHost()).canonicalPath(),
                &MaemoRunConfiguration::wasDeployed));
130
131
132
            m_needsInstall = true;
        } else {
            m_needsInstall = false;
133
        }
134
        if (forDebugging
ck's avatar
ck committed
135
136
            && m_runConfig->debuggingHelpersNeedDeployment(m_devConfig.host)) {
            const QFileInfo &info(m_runConfig->dumperLib());
137
            m_deployables.append(Deployable(info.fileName(), info.canonicalPath(),
138
139
140
141
142
143
144
145
146
                &MaemoRunConfiguration::debuggingHelpersDeployed));
        }

        deploy();
    }
}

void AbstractMaemoRunControl::deploy()
{
147
148
149
    Core::ICore::instance()->progressManager()
        ->addTask(m_progress.future(), tr("Deploying"),
                  QLatin1String("Maemo.Deploy"));
150
    if (!m_deployables.isEmpty()) {
151
        QList<SshDeploySpec> deploySpecs;
152
        QStringList files;
153
        foreach (const Deployable &deployable, m_deployables) {
154
155
            const QString srcFilePath
                = deployable.dir % QDir::separator() % deployable.fileName;
156
157
            const QString tgtFilePath
                = remoteDir() % QDir::separator() % deployable.fileName;
158
            files << srcFilePath;
159
            deploySpecs << SshDeploySpec(srcFilePath, tgtFilePath);
160
161
        }
        emit addToOutputWindow(this, tr("Files to deploy: %1.").arg(files.join(" ")));
ck's avatar
ck committed
162
        m_sshDeployer.reset(new MaemoSshDeployer(m_devConfig, deploySpecs));
163
        connect(m_sshDeployer.data(), SIGNAL(finished()),
164
                this, SLOT(handleDeployThreadFinished()));
165
        connect(m_sshDeployer.data(), SIGNAL(fileCopied(QString)),
166
                this, SLOT(handleFileCopied()));
167
        m_progress.setProgressRange(0, m_deployables.count());
168
169
        m_progress.setProgressValue(0);
        m_progress.reportStarted();
170
        m_sshDeployer->start();
171
    } else {
172
        m_progress.reportFinished();
173
        startExecution();
174
175
176
177
178
    }
}

void AbstractMaemoRunControl::handleFileCopied()
{
179
    Deployable deployable = m_deployables.takeFirst();
ck's avatar
ck committed
180
    (m_runConfig->*deployable.updateTimestamp)(m_devConfig.host);
181
182
183
    m_progress.setProgressValue(m_progress.progressValue() + 1);
}

184
bool AbstractMaemoRunControl::isDeploying() const
185
{
186
    return m_sshDeployer && m_sshDeployer->isRunning();
187
188
}

189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
QString AbstractMaemoRunControl::packageFileName() const
{
    return QFileInfo(packageFilePath()).fileName();
}

QString AbstractMaemoRunControl::packageFilePath() const
{
    return m_runConfig->packageStep()->packageFilePath();
}

QString AbstractMaemoRunControl::executableFilePathOnTarget() const
{
    return m_runConfig->packageStep()->executableFilePathOnTarget();
}

204
bool AbstractMaemoRunControl::isCleaning() const
205
{
206
    return m_initialCleaner && m_initialCleaner->isRunning();
207
208
}

209
void AbstractMaemoRunControl::startExecution()
210
{
211
212
213
214
215
216
    m_sshRunner.reset(new MaemoSshRunner(m_devConfig, remoteCall()));
    connect(m_sshRunner.data(), SIGNAL(finished()),
            this, SLOT(handleRunThreadFinished()));
    connect(m_sshRunner.data(), SIGNAL(remoteOutput(QString)),
            this, SLOT(handleRemoteOutput(QString)));
    emit addToOutputWindow(this, tr("Starting remote application."));
217
    m_sshRunner->start();
218
219
220
221
}

bool AbstractMaemoRunControl::isRunning() const
{
222
    return isDeploying() || (m_sshRunner && m_sshRunner->isRunning());
223
224
225
226
}

void AbstractMaemoRunControl::stopRunning(bool forDebugging)
{
227
228
    if (m_sshRunner && m_sshRunner->isRunning()) {
        m_sshRunner->stop();
229
230
231
        QStringList apps(executableFileName());
        if (forDebugging)
            apps << QLatin1String("gdbserver");
232
        killRemoteProcesses(apps, false);
233
234
235
    }
}

236
237
void AbstractMaemoRunControl::killRemoteProcesses(const QStringList &apps,
                                                  bool initialCleanup)
238
239
240
241
242
243
244
{
    QString niceKill;
    QString brutalKill;
    foreach (const QString &app, apps) {
        niceKill += QString::fromLocal8Bit("pkill -x %1;").arg(app);
        brutalKill += QString::fromLocal8Bit("pkill -x -9 %1;").arg(app);
    }
ck's avatar
ck committed
245
246
    QString remoteCall = niceKill + QLatin1String("sleep 1; ") + brutalKill;
    remoteCall.remove(remoteCall.count() - 1, 1); // Get rid of trailing semicolon.
247
248
249
250
251
252
253
    QScopedPointer<MaemoSshRunner> &runner
        = initialCleanup ? m_initialCleaner : m_sshStopper;
    runner.reset(new MaemoSshRunner(m_devConfig, remoteCall));
    if (initialCleanup)
        connect(runner.data(), SIGNAL(finished()),
                this, SLOT(handleInitialCleanupFinished()));
    runner->start();
254
255
}

256
void AbstractMaemoRunControl::handleDeployThreadFinished()
257
{
258
    bool cancel;
259
260
    if (m_stoppedByUser) {
        emit addToOutputWindow(this, tr("Deployment canceled by user."));
261
262
        cancel = true;
    } else if (m_sshDeployer->hasError()) {
263
        handleError(tr("Deployment failed: %1").arg(m_sshDeployer->error()));
264
265
266
267
268
269
270
        cancel = true;
    } else {
        emit addToOutputWindow(this, tr("Deployment finished."));
        cancel = false;
    }

    if (cancel) {
271
        m_progress.reportCanceled();
272
273
274
275
276
277
278
279
280
281
282
283
284
285
        m_progress.reportFinished();
        emit finished();
    } else {
        m_progress.reportFinished();
        startExecution();
    }
}

void AbstractMaemoRunControl::handleRunThreadFinished()
{
    if (m_stoppedByUser) {
        emit addToOutputWindow(this,
                 tr("Remote execution canceled due to user request."));
    } else if (m_sshRunner->hasError()) {
ck's avatar
ck committed
286
        emit addToOutputWindow(this, tr("Error running remote process: %1")
287
288
                                         .arg(m_sshRunner->error()));
    } else {
ck's avatar
ck committed
289
        emit addToOutputWindow(this, tr("Finished running remote process."));
290
    }
291
    emit finished();
292
293
294
295
}

const QString AbstractMaemoRunControl::executableOnHost() const
{
ck's avatar
ck committed
296
297
    qDebug("runconfig->executable: %s", qPrintable(m_runConfig->executable()));
    return m_runConfig->executable();
298
299
300
301
302
303
304
305
306
}

const QString AbstractMaemoRunControl::executableFileName() const
{
    return QFileInfo(executableOnHost()).fileName();
}

const QString AbstractMaemoRunControl::remoteDir() const
{
ck's avatar
ck committed
307
    return homeDirOnDevice(m_devConfig.uname);
308
309
}

310
QString AbstractMaemoRunControl::remoteSudo() const
311
{
312
    return QLatin1String("/usr/lib/mad-developer/devrootsh");
313
314
}

315
QString AbstractMaemoRunControl::remoteInstallCommand() const
316
{
317
318
    return QString::fromLocal8Bit("%1 dpkg -i %2").arg(remoteSudo())
        .arg(packageFileName());
319
320
321
322
}

const QString AbstractMaemoRunControl::targetCmdLinePrefix() const
{
323
324
325
326
327
    const QString &installPrefix = m_needsInstall
        ? remoteInstallCommand() + QLatin1String(" && ")
        : QString();
    return QString::fromLocal8Bit("%1%2 chmod u+x %3 && source /etc/profile && ")
        .arg(installPrefix).arg(remoteSudo()).arg(executableFilePathOnTarget());
328
329
330
331
332
}

QString AbstractMaemoRunControl::targetCmdLineSuffix() const
{
    return m_runConfig->arguments().join(" ");
333
334
}

ck's avatar
ck committed
335
336
337
338
339
340
void AbstractMaemoRunControl::handleError(const QString &errString)
{
    QMessageBox::critical(0, tr("Remote Execution Failure"), errString);
    emit error(this, errString);
}

341
342
343
344
345
346
347
348
349
350
351

MaemoRunControl::MaemoRunControl(RunConfiguration *runConfiguration)
    : AbstractMaemoRunControl(runConfiguration)
{
}

MaemoRunControl::~MaemoRunControl()
{
    stop();
}

352
void MaemoRunControl::startInternal()
353
354
355
356
{
    startDeployment(false);
}

357
QString MaemoRunControl::remoteCall() const
358
{
359
360
    return QString::fromLocal8Bit("%1 %2 %3").arg(targetCmdLinePrefix())
        .arg(executableFilePathOnTarget()).arg(targetCmdLineSuffix());
361
362
}

363
void MaemoRunControl::stopInternal()
364
{
365
    AbstractMaemoRunControl::stopRunning(false);
366
367
368
369
370
371
372
373
374
375
}

void MaemoRunControl::handleRemoteOutput(const QString &output)
{
    emit addToOutputWindowInline(this, output);
}


MaemoDebugRunControl::MaemoDebugRunControl(RunConfiguration *runConfiguration)
    : AbstractMaemoRunControl(runConfiguration)
376
    , m_debuggerManager(ExtensionSystem::PluginManager::instance()
377
                      ->getObject<Debugger::DebuggerManager>())
378
    , m_startParams(new Debugger::DebuggerStartParameters)
379
{
380
381
382
383
    QTC_ASSERT(m_debuggerManager != 0, return);
    m_startParams->startMode = Debugger::StartRemote;
    m_startParams->executable = executableOnHost();
    m_startParams->remoteChannel
ck's avatar
ck committed
384
        = m_devConfig.host % QLatin1Char(':') % gdbServerPort();
385
    m_startParams->remoteArchitecture = QLatin1String("arm");
ck's avatar
ck committed
386
    m_startParams->sysRoot = m_runConfig->sysRoot();
387
    m_startParams->toolChainType = ToolChain::GCC_MAEMO;
ck's avatar
ck committed
388
389
    m_startParams->debuggerCommand = m_runConfig->gdbCmd();
    m_startParams->dumperLibrary = m_runConfig->dumperLib();
390
    m_startParams->remoteDumperLib = QString::fromLocal8Bit("%1/%2")
ck's avatar
ck committed
391
        .arg(remoteDir()).arg(QFileInfo(m_runConfig->dumperLib()).fileName());
392

393
    connect(m_debuggerManager, SIGNAL(debuggingFinished()), this,
394
        SLOT(debuggingFinished()), Qt::QueuedConnection);
395
    connect(m_debuggerManager, SIGNAL(applicationOutputAvailable(QString)),
396
397
398
399
400
401
402
403
404
405
406
        this, SLOT(debuggerOutput(QString)), Qt::QueuedConnection);
}

MaemoDebugRunControl::~MaemoDebugRunControl()
{
    disconnect(SIGNAL(addToOutputWindow(RunControl*,QString)));
    disconnect(SIGNAL(addToOutputWindowInline(RunControl*,QString)));
    stop();
    debuggingFinished();
}

407
void MaemoDebugRunControl::startInternal()
408
{
409
    m_inferiorPid = -1;
410
    startDeployment(true);
411
412
}

413
QString MaemoDebugRunControl::remoteCall() const
414
{
415
416
    return QString::fromLocal8Bit("%1 gdbserver :%2 %3 %4")
        .arg(targetCmdLinePrefix()).arg(gdbServerPort())
417
        .arg(executableFilePathOnTarget()).arg(targetCmdLineSuffix());
418
419
}

420
void MaemoDebugRunControl::handleRemoteOutput(const QString &output)
421
422
{
    qDebug("gdbserver's stderr output: %s", output.toLatin1().data());
423
    if (m_inferiorPid != -1)
424
425
426
427
428
        return;
    const QString searchString("pid = ");
    const int searchStringLength = searchString.length();
    int pidStartPos = output.indexOf(searchString);
    const int pidEndPos = output.indexOf("\n", pidStartPos + searchStringLength);
429
430
    if (pidStartPos == -1 || pidEndPos == -1)
        return; // gdbserver has not started yet.
431
432
433
434
435
436
    pidStartPos += searchStringLength;
    QString pidString = output.mid(pidStartPos, pidEndPos - pidStartPos);
    qDebug("pidString = %s", pidString.toLatin1().data());
    bool ok;
    const int pid = pidString.toInt(&ok);
    if (!ok) {
437
438
439
440
441
        handleError(tr("Debugging failed: Could not parse gdbserver output."));
        m_debuggerManager->exitDebugger();
    } else {
        m_inferiorPid = pid;
        startDebugging();
442
443
444
445
446
    }
}

void MaemoDebugRunControl::startDebugging()
{
447
    m_debuggerManager->startNewDebugger(m_startParams);
448
449
}

450
void MaemoDebugRunControl::stopInternal()
451
{
452
    m_debuggerManager->exitDebugger();
453
454
455
456
}

bool MaemoDebugRunControl::isRunning() const
{
457
    return AbstractMaemoRunControl::isRunning()
458
        || m_debuggerManager->state() != Debugger::DebuggerNotReady;
459
460
461
462
}

void MaemoDebugRunControl::debuggingFinished()
{
463
    AbstractMaemoRunControl::stopRunning(true);
464
465
466
467
468
469
470
}

void MaemoDebugRunControl::debuggerOutput(const QString &output)
{
    emit addToOutputWindowInline(this, output);
}

ck's avatar
ck committed
471
472
QString MaemoDebugRunControl::gdbServerPort() const
{
ck's avatar
ck committed
473
474
475
    return m_devConfig.type == MaemoDeviceConfig::Physical
        ? QString::number(m_devConfig.gdbServerPort)
        : m_runConfig->simulatorGdbServerPort();
ck's avatar
ck committed
476
477
}

478
479
} // namespace Internal
} // namespace Qt4ProjectManager