qt4maemotarget.cpp 38.3 KB
Newer Older
1
2
3
4
5
6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
8
9
10
11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
18
**
Tobias Hunger's avatar
Tobias Hunger committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
Tobias Hunger's avatar
Tobias Hunger committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
Tobias Hunger's avatar
Tobias Hunger committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
30
31
32
**
**************************************************************************/

33
#include "qt4maemotarget.h"
34

35
36
#include "maemoglobal.h"
#include "maemopackagecreationstep.h"
37
#include "maemorunconfiguration.h"
38
#include "maemotoolchain.h"
39
40
#include "qt4maemodeployconfiguration.h"

Tobias Hunger's avatar
Tobias Hunger committed
41
#include <coreplugin/filemanager.h>
42
43
44
#include <coreplugin/icore.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>
45
#include <projectexplorer/abi.h>
46
#include <projectexplorer/customexecutablerunconfiguration.h>
47
48
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectnodes.h>
49
#include <projectexplorer/toolchain.h>
50
#include <qt4projectmanager/qt4project.h>
51
#include <utils/fileutils.h>
52
#include <utils/filesystemwatcher.h>
53
#include <qtsupport/baseqtversion.h>
dt's avatar
dt committed
54

55
#include <QtGui/QApplication>
56
#include <QtGui/QMainWindow>
57
#include <QtCore/QBuffer>
Christian Kandeler's avatar
Christian Kandeler committed
58
#include <QtCore/QRegExp>
59
60
61
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
Christian Kandeler's avatar
Christian Kandeler committed
62
63
#include <QtCore/QProcess>
#include <QtCore/QStringList>
64
#include <QtGui/QIcon>
65
#include <QtGui/QMessageBox>
66

Friedemann Kleint's avatar
Friedemann Kleint committed
67
68
#include <cctype>

69
using namespace Qt4ProjectManager;
70
71
72

namespace RemoteLinux {
namespace Internal {
73

74
namespace {
Christian Kandeler's avatar
Christian Kandeler committed
75
const QByteArray NameFieldName("Package");
76
77
const QByteArray IconFieldName("XB-Maemo-Icon-26");
const QByteArray ShortDescriptionFieldName("Description");
Christian Kandeler's avatar
Christian Kandeler committed
78
const QByteArray PackageFieldName("Package");
79
const QLatin1String PackagingDirName("qtc_packaging");
80
81
82
const QByteArray NameTag("Name");
const QByteArray SummaryTag("Summary");
const QByteArray VersionTag("Version");
Christian Kandeler's avatar
Christian Kandeler committed
83
const QByteArray ReleaseTag("Release");
84
85
86
87
88

bool adaptTagValue(QByteArray &document, const QByteArray &fieldName,
    const QByteArray &newFieldValue, bool caseSensitive)
{
    QByteArray adaptedLine = fieldName + ": " + newFieldValue;
Friedemann Kleint's avatar
Friedemann Kleint committed
89
    const QByteArray completeTag = fieldName + ':';
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
    const int lineOffset = caseSensitive ? document.indexOf(completeTag)
        : document.toLower().indexOf(completeTag.toLower());
    if (lineOffset == -1) {
        document.append(adaptedLine).append('\n');
        return true;
    }

    int newlineOffset = document.indexOf('\n', lineOffset);
    bool updated = false;
    if (newlineOffset == -1) {
        newlineOffset = document.length();
        adaptedLine += '\n';
        updated = true;
    }
    const int replaceCount = newlineOffset - lineOffset;
    if (!updated && document.mid(lineOffset, replaceCount) != adaptedLine)
        updated = true;
    if (updated)
        document.replace(lineOffset, replaceCount, adaptedLine);
    return updated;
}


113
114
115
} // anonymous namespace


116
AbstractQt4MaemoTarget::AbstractQt4MaemoTarget(Qt4Project *parent, const QString &id) :
117
    Qt4BaseTarget(parent, id),
118
    m_filesWatcher(new Utils::FileSystemWatcher(this)),
119
    m_buildConfigurationFactory(new Qt4BuildConfigurationFactory(this)),
120
    m_isInitialized(false)
121
{
122
    m_filesWatcher->setObjectName(QLatin1String("Qt4MaemoTarget"));
123
    setIcon(QIcon(":/projectexplorer/images/MaemoDevice.png"));
124
    connect(parent, SIGNAL(addedTarget(ProjectExplorer::Target*)),
125
126
127
        this, SLOT(handleTargetAdded(ProjectExplorer::Target*)));
    connect(parent, SIGNAL(fromMapFinished()),
        this, SLOT(handleFromMapFinished()));
128
129
}

130
AbstractQt4MaemoTarget::~AbstractQt4MaemoTarget()
131
132
133
134
135
136
137
138
139
140
141
142
143
{ }

QList<ProjectExplorer::ToolChain *> AbstractQt4MaemoTarget::possibleToolChains(ProjectExplorer::BuildConfiguration *bc) const
{
    QList<ProjectExplorer::ToolChain *> result;

    Qt4BuildConfiguration *qt4Bc = qobject_cast<Qt4BuildConfiguration *>(bc);
    if (!qt4Bc)
        return result;

    QList<ProjectExplorer::ToolChain *> candidates = Qt4BaseTarget::possibleToolChains(bc);
    foreach (ProjectExplorer::ToolChain *i, candidates) {
        MaemoToolChain *tc = dynamic_cast<MaemoToolChain *>(i);
dt's avatar
dt committed
144
        if (!tc || !qt4Bc->qtVersion())
145
146
147
148
149
150
            continue;
        if (tc->qtVersionId() == qt4Bc->qtVersion()->uniqueId())
            result.append(tc);
    }

    return result;
151
152
}

153

154
Qt4BuildConfigurationFactory *AbstractQt4MaemoTarget::buildConfigurationFactory() const
155
156
157
158
{
    return m_buildConfigurationFactory;
}

159
void AbstractQt4MaemoTarget::createApplicationProFiles()
160
161
162
163
164
165
166
167
168
{
    removeUnconfiguredCustomExectutableRunConfigurations();

    QList<Qt4ProFileNode *> profiles = qt4Project()->applicationProFiles();
    QSet<QString> paths;
    foreach (Qt4ProFileNode *pro, profiles)
        paths << pro->path();

    foreach (ProjectExplorer::RunConfiguration *rc, runConfigurations())
169
        if (MaemoRunConfiguration *qt4rc = qobject_cast<MaemoRunConfiguration *>(rc))
170
171
172
173
            paths.remove(qt4rc->proFilePath());

    // Only add new runconfigurations if there are none.
    foreach (const QString &path, paths)
174
        addRunConfiguration(new MaemoRunConfiguration(this, path));
175
176
177
178
179
180

    // Oh still none? Add a custom executable runconfiguration
    if (runConfigurations().isEmpty()) {
        addRunConfiguration(new ProjectExplorer::CustomExecutableRunConfiguration(this));
    }
}
181

dt's avatar
dt committed
182
183
184
185
QList<ProjectExplorer::RunConfiguration *> AbstractQt4MaemoTarget::runConfigurationsForNode(ProjectExplorer::Node *n)
{
    QList<ProjectExplorer::RunConfiguration *> result;
    foreach (ProjectExplorer::RunConfiguration *rc, runConfigurations())
186
        if (MaemoRunConfiguration *mrc = qobject_cast<MaemoRunConfiguration *>(rc))
dt's avatar
dt committed
187
188
189
190
191
            if (mrc->proFilePath() == n->path())
                result << rc;
    return result;
}

192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
bool AbstractQt4MaemoTarget::setProjectVersion(const QString &version,
    QString *error)
{
    bool success = true;
    foreach (Target * const target, project()->targets()) {
        AbstractQt4MaemoTarget * const maemoTarget
            = qobject_cast<AbstractQt4MaemoTarget *>(target);
        if (maemoTarget) {
            if (!maemoTarget->setProjectVersionInternal(version, error))
                success = false;
        }
    }
    return success;
}

Christian Kandeler's avatar
Christian Kandeler committed
207
bool AbstractQt4MaemoTarget::setPackageName(const QString &name)
208
209
210
211
212
213
{
    bool success = true;
    foreach (Target * const target, project()->targets()) {
        AbstractQt4MaemoTarget * const maemoTarget
            = qobject_cast<AbstractQt4MaemoTarget *>(target);
        if (maemoTarget) {
Christian Kandeler's avatar
Christian Kandeler committed
214
            if (!maemoTarget->setPackageNameInternal(name))
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
                success = false;
        }
    }
    return success;
}

bool AbstractQt4MaemoTarget::setShortDescription(const QString &description)
{
    bool success = true;
    foreach (Target * const target, project()->targets()) {
        AbstractQt4MaemoTarget * const maemoTarget
            = qobject_cast<AbstractQt4MaemoTarget *>(target);
        if (maemoTarget) {
            if (!maemoTarget->setShortDescriptionInternal(description))
                success = false;
        }
    }
    return success;
}

QSharedPointer<QFile> AbstractQt4MaemoTarget::openFile(const QString &filePath,
    QIODevice::OpenMode mode, QString *error) const
{
    const QString nativePath = QDir::toNativeSeparators(filePath);
    QSharedPointer<QFile> file(new QFile(filePath));
240
    if (!file->open(mode)) {
241
242
243
244
245
246
247
248
249
        if (error) {
            *error = tr("Cannot open file '%1': %2")
                .arg(nativePath, file->errorString());
        }
        file.clear();
    }
    return file;
}

250
251
252
253
254
void AbstractQt4MaemoTarget::handleFromMapFinished()
{
    handleTargetAdded(this);
}

255
256
257
258
259
void AbstractQt4MaemoTarget::handleTargetAdded(ProjectExplorer::Target *target)
{
    if (target != this)
        return;

260
261
262
263
264
265
266
267
    if (!project()->rootProjectNode()) {
        // Project is not fully setup yet, happens on new project
        // we wait for the fromMapFinished that comes afterwards
        return;
    }

    disconnect(project(), SIGNAL(fromMapFinished()),
        this, SLOT(handleFromMapFinished()));
268
269
270
271
    disconnect(project(), SIGNAL(addedTarget(ProjectExplorer::Target*)),
        this, SLOT(handleTargetAdded(ProjectExplorer::Target*)));
    connect(project(), SIGNAL(aboutToRemoveTarget(ProjectExplorer::Target*)),
        SLOT(handleTargetToBeRemoved(ProjectExplorer::Target*)));
272
273
    const ActionStatus status = createTemplates();
    if (status == ActionFailed)
274
        return;
275
276
    if (status == ActionSuccessful) // Don't do this when the packaging data already exists.
        initPackagingSettingsFromOtherTarget();
277
    handleTargetAddedSpecial();
278
    m_isInitialized = true;
279
280
281
282
283
284
285
286
}

void AbstractQt4MaemoTarget::handleTargetToBeRemoved(ProjectExplorer::Target *target)
{
    if (target != this)
        return;
    if (!targetCanBeRemoved())
        return;
287
288
289
290

    Core::ICore * const core = Core::ICore::instance();
    const int answer = QMessageBox::warning(core->mainWindow(),
        tr("Qt Creator"), tr("Do you want to remove the packaging file(s) "
291
292
293
294
           "associated with the target '%1'?").arg(displayName()),
        QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
    if (answer == QMessageBox::No)
        return;
295
    const QStringList pkgFilePaths = packagingFilePaths();
296
297
298
299
300
301
302
303
304
    if (!pkgFilePaths.isEmpty()) {
        project()->rootProjectNode()->removeFiles(ProjectExplorer::UnknownFileType,
            pkgFilePaths);
        Core::IVersionControl * const vcs = core->vcsManager()
            ->findVersionControlForDirectory(QFileInfo(pkgFilePaths.first()).dir().path());
        if (vcs && vcs->supportsOperation(Core::IVersionControl::DeleteOperation)) {
            foreach (const QString &filePath, pkgFilePaths)
                vcs->vcsDelete(filePath);
        }
305
    }
306
    delete m_filesWatcher;
307
308
309
310
311
312
313
    removeTarget();
    QString error;
    const QString packagingPath = project()->projectDirectory()
        + QLatin1Char('/') + PackagingDirName;
    const QStringList otherContents = QDir(packagingPath).entryList(QDir::Dirs
        | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot);
    if (otherContents.isEmpty()) {
314
        if (!Utils::FileUtils::removeRecursively(packagingPath, &error))
315
316
317
318
            qDebug("%s", qPrintable(error));
    }
}

319
AbstractQt4MaemoTarget::ActionStatus AbstractQt4MaemoTarget::createTemplates()
320
321
322
323
{
    QDir projectDir(project()->projectDirectory());
    if (!projectDir.exists(PackagingDirName)
            && !projectDir.mkdir(PackagingDirName)) {
324
        raiseError(tr("Error creating packaging directory '%1'.")
325
            .arg(PackagingDirName));
326
        return ActionFailed;
327
328
    }

329
330
331
332
333
334
335
336
337
338
339
340
341
    const ActionStatus actionStatus = createSpecialTemplates();
    if (actionStatus == ActionFailed)
        return ActionFailed;
    if (actionStatus == ActionSuccessful) {
        const QStringList &files = packagingFilePaths();
        QMessageBox::StandardButton button
            = QMessageBox::question(Core::ICore::instance()->mainWindow(),
                  tr("Add Packaging Files to Project"),
                  tr("Qt Creator has set up the following files to enable "
                     "packaging:\n   %1\nDo you want to add them to the project?")
                      .arg(files.join(QLatin1String("\n   "))),
                  QMessageBox::Yes | QMessageBox::No);
        if (button == QMessageBox::Yes)
342
            ProjectExplorer::ProjectExplorerPlugin::instance()->addExistingFiles(project()->rootProjectNode(), files);
343
344
    }
    return actionStatus;
345
346
347
348
349
350
351
352
}

bool AbstractQt4MaemoTarget::initPackagingSettingsFromOtherTarget()
{
    bool success = true;
    foreach (const Target * const target, project()->targets()) {
        const AbstractQt4MaemoTarget * const maemoTarget
            = qobject_cast<const AbstractQt4MaemoTarget *>(target);
353
        if (maemoTarget && maemoTarget != this && maemoTarget->m_isInitialized) {
354
355
            if (!setProjectVersionInternal(maemoTarget->projectVersion()))
                success = false;
Christian Kandeler's avatar
Christian Kandeler committed
356
            if (!setPackageNameInternal(maemoTarget->packageName()))
357
358
359
360
361
362
                success = false;
            if (!setShortDescriptionInternal(maemoTarget->shortDescription()))
                success = false;
            break;
        }
    }
363
    return initAdditionalPackagingSettingsFromOtherTarget() && success;
364
365
366
367
}

void AbstractQt4MaemoTarget::raiseError(const QString &reason)
{
368
    QMessageBox::critical(0, tr("Error creating MeeGo templates"), reason);
369
370
371
372
373
374
375
376
377
378
}

AbstractDebBasedQt4MaemoTarget::AbstractDebBasedQt4MaemoTarget(Qt4Project *parent,
    const QString &id) : AbstractQt4MaemoTarget(parent, id)
{
}

AbstractDebBasedQt4MaemoTarget::~AbstractDebBasedQt4MaemoTarget() {}

QString AbstractDebBasedQt4MaemoTarget::projectVersion(QString *error) const
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
{
    QSharedPointer<QFile> changeLog = openFile(changeLogFilePath(),
        QIODevice::ReadOnly, error);
    if (!changeLog)
        return QString();
    const QByteArray &firstLine = changeLog->readLine();
    const int openParenPos = firstLine.indexOf('(');
    if (openParenPos == -1) {
        if (error) {
            *error = tr("Debian changelog file '%1' has unexpected format.")
                .arg(QDir::toNativeSeparators(changeLog->fileName()));
        }
        return QString();
    }
    const int closeParenPos = firstLine.indexOf(')', openParenPos);
    if (closeParenPos == -1) {
        if (error) {
            *error = tr("Debian changelog file '%1' has unexpected format.")
                .arg(QDir::toNativeSeparators(changeLog->fileName()));
        }
        return QString();
    }
    return QString::fromUtf8(firstLine.mid(openParenPos + 1,
        closeParenPos - openParenPos - 1).data());
}

405
bool AbstractDebBasedQt4MaemoTarget::setProjectVersionInternal(const QString &version,
406
    QString *error)
407
408
{
    const QString filePath = changeLogFilePath();
409
410
    Utils::FileReader reader;
    if (!reader.fetch(filePath, error))
411
        return false;
412
    QString content = QString::fromUtf8(reader.data());
413
414
    content.replace(QRegExp(QLatin1String("\\([a-zA-Z0-9_\\.]+\\)")),
        QLatin1Char('(') + version + QLatin1Char(')'));
Tobias Hunger's avatar
Tobias Hunger committed
415
    Core::FileChangeBlocker update(filePath);
416
417
418
    Utils::FileSaver saver(filePath);
    saver.write(content.toUtf8());
    return saver.finalize(error);
419
420
}

421
QIcon AbstractDebBasedQt4MaemoTarget::packageManagerIcon(QString *error) const
422
423
424
425
426
427
428
429
430
431
432
433
434
{
    const QByteArray &base64Icon = controlFileFieldValue(IconFieldName, true);
    if (base64Icon.isEmpty())
        return QIcon();
    QPixmap pixmap;
    if (!pixmap.loadFromData(QByteArray::fromBase64(base64Icon))) {
        if (error)
            *error = tr("Invalid icon data in Debian control file.");
        return QIcon();
    }
    return QIcon(pixmap);
}

435
bool AbstractDebBasedQt4MaemoTarget::setPackageManagerIconInternal(const QString &iconFilePath,
436
    QString *error)
437
438
{
    const QString filePath = controlFilePath();
439
440
    Utils::FileReader reader;
    if (!reader.fetch(filePath, error))
441
442
443
        return false;
    const QPixmap pixmap(iconFilePath);
    if (pixmap.isNull()) {
444
445
        if (error)
            *error = tr("Could not read image file '%1'.").arg(iconFilePath);
446
447
448
449
450
451
452
453
        return false;
    }

    QByteArray iconAsBase64;
    QBuffer buffer(&iconAsBase64);
    buffer.open(QIODevice::WriteOnly);
    if (!pixmap.scaled(48, 48).save(&buffer,
        QFileInfo(iconFilePath).suffix().toAscii())) {
454
455
        if (error)
            *error = tr("Could not export image file '%1'.").arg(iconFilePath);
456
457
458
459
        return false;
    }
    buffer.close();
    iconAsBase64 = iconAsBase64.toBase64();
460
    QByteArray contents = reader.data();
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
    const QByteArray iconFieldNameWithColon = IconFieldName + ':';
    const int iconFieldPos = contents.startsWith(iconFieldNameWithColon)
        ? 0 : contents.indexOf('\n' + iconFieldNameWithColon);
    if (iconFieldPos == -1) {
        if (!contents.endsWith('\n'))
            contents += '\n';
        contents.append(iconFieldNameWithColon).append(' ').append(iconAsBase64)
            .append('\n');
    } else {
        const int oldIconStartPos = (iconFieldPos != 0) + iconFieldPos
            + iconFieldNameWithColon.length();
        int nextEolPos = contents.indexOf('\n', oldIconStartPos);
        while (nextEolPos != -1 && nextEolPos != contents.length() - 1
            && contents.at(nextEolPos + 1) != '\n'
            && (contents.at(nextEolPos + 1) == '#'
                || std::isspace(contents.at(nextEolPos + 1))))
            nextEolPos = contents.indexOf('\n', nextEolPos + 1);
        if (nextEolPos == -1)
            nextEolPos = contents.length();
        contents.replace(oldIconStartPos, nextEolPos - oldIconStartPos,
            ' ' + iconAsBase64);
    }
Tobias Hunger's avatar
Tobias Hunger committed
483
    Core::FileChangeBlocker update(filePath);
484
485
486
    Utils::FileSaver saver(filePath);
    saver.write(contents);
    return saver.finalize(error);
487
488
}

Christian Kandeler's avatar
Christian Kandeler committed
489
QString AbstractDebBasedQt4MaemoTarget::packageName() const
490
491
492
493
{
    return QString::fromUtf8(controlFileFieldValue(NameFieldName, false));
}

Christian Kandeler's avatar
Christian Kandeler committed
494
495
bool AbstractDebBasedQt4MaemoTarget::setPackageNameInternal(const QString &packageName)
{
496
497
    const QString oldPackageName = this->packageName();

Christian Kandeler's avatar
Christian Kandeler committed
498
499
500
501
    if (!setControlFieldValue(NameFieldName, packageName.toUtf8()))
        return false;
    if (!setControlFieldValue("Source", packageName.toUtf8()))
        return false;
502

503
504
    Utils::FileReader reader;
    if (!reader.fetch(changeLogFilePath()))
Christian Kandeler's avatar
Christian Kandeler committed
505
        return false;
506
    QString changelogContents = QString::fromUtf8(reader.data());
Christian Kandeler's avatar
Christian Kandeler committed
507
    QRegExp pattern(QLatin1String("[^\\s]+( \\(\\d\\.\\d\\.\\d\\))"));
508
    changelogContents.replace(pattern, packageName + QLatin1String("\\1"));
509
510
511
    Utils::FileSaver saver(changeLogFilePath());
    saver.write(changelogContents.toUtf8());
    if (!saver.finalize())
512
513
        return false;

514
    if (!reader.fetch(rulesFilePath()))
515
        return false;
516
    QByteArray rulesContents = reader.data();
517
518
519
    const QString oldString = QLatin1String("debian/") + oldPackageName;
    const QString newString = QLatin1String("debian/") + packageName;
    rulesContents.replace(oldString.toUtf8(), newString.toUtf8());
520
    Utils::FileSaver rulesSaver(rulesFilePath());
521
522
    rulesSaver.write(rulesContents);
    return rulesSaver.finalize();
Christian Kandeler's avatar
Christian Kandeler committed
523
524
525
526
}

QString AbstractDebBasedQt4MaemoTarget::packageManagerName() const
{
527
    return QString::fromUtf8(controlFileFieldValue(packageManagerNameFieldName(), false));
Christian Kandeler's avatar
Christian Kandeler committed
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
}

bool AbstractDebBasedQt4MaemoTarget::setPackageManagerName(const QString &name,
    QString *error)
{
    bool success = true;
    foreach (Target * const t, project()->targets()) {
        AbstractDebBasedQt4MaemoTarget * const target
            = qobject_cast<AbstractDebBasedQt4MaemoTarget *>(t);
        if (target) {
            if (!target->setPackageManagerNameInternal(name, error))
                success = false;
        }
    }
    return success;
}

bool AbstractDebBasedQt4MaemoTarget::setPackageManagerNameInternal(const QString &name,
    QString *error)
547
{
Christian Kandeler's avatar
Christian Kandeler committed
548
    Q_UNUSED(error);
549
    return setControlFieldValue(packageManagerNameFieldName(), name.toUtf8());
550
551
}

552
QString AbstractDebBasedQt4MaemoTarget::shortDescription() const
553
554
555
556
{
    return QString::fromUtf8(controlFileFieldValue(ShortDescriptionFieldName, false));
}

Christian Kandeler's avatar
Christian Kandeler committed
557
558
559
560
561
562
QString AbstractDebBasedQt4MaemoTarget::packageFileName() const
{
    return QString::fromUtf8(controlFileFieldValue(PackageFieldName, false))
        + QLatin1Char('_') + projectVersion() + QLatin1String("_armel.deb");
}

563
bool AbstractDebBasedQt4MaemoTarget::setShortDescriptionInternal(const QString &description)
564
565
566
567
{
    return setControlFieldValue(ShortDescriptionFieldName, description.toUtf8());
}

568
QString AbstractDebBasedQt4MaemoTarget::debianDirPath() const
569
570
{
    return project()->projectDirectory() + QLatin1Char('/') + PackagingDirName
571
        + QLatin1Char('/') + debianDirName();
572
573
}

574
QStringList AbstractDebBasedQt4MaemoTarget::debianFiles() const
575
576
577
578
579
{
    return QDir(debianDirPath())
        .entryList(QDir::Files, QDir::Name | QDir::IgnoreCase);
}

580
QString AbstractDebBasedQt4MaemoTarget::changeLogFilePath() const
581
582
583
584
{
    return debianDirPath() + QLatin1String("/changelog");
}

585
QString AbstractDebBasedQt4MaemoTarget::controlFilePath() const
586
587
588
589
{
    return debianDirPath() + QLatin1String("/control");
}

590
591
592
593
594
QString AbstractDebBasedQt4MaemoTarget::rulesFilePath() const
{
    return debianDirPath() + QLatin1String("/rules");
}

595
QByteArray AbstractDebBasedQt4MaemoTarget::controlFileFieldValue(const QString &key,
596
597
598
    bool multiLine) const
{
    QByteArray value;
599
600
    Utils::FileReader reader;
    if (!reader.fetch(controlFilePath()))
601
        return value;
602
    const QByteArray &contents = reader.data();
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
    const int keyPos = contents.indexOf(key.toUtf8() + ':');
    if (keyPos == -1)
        return value;
    int valueStartPos = keyPos + key.length() + 1;
    int valueEndPos = contents.indexOf('\n', keyPos);
    if (valueEndPos == -1)
        valueEndPos = contents.count();
    value = contents.mid(valueStartPos, valueEndPos - valueStartPos).trimmed();
    if (multiLine) {
        Q_FOREVER {
            valueStartPos = valueEndPos + 1;
            if (valueStartPos >= contents.count())
                break;
            const char firstChar = contents.at(valueStartPos);
            if (firstChar == '#' || isspace(firstChar)) {
                valueEndPos = contents.indexOf('\n', valueStartPos);
                if (valueEndPos == -1)
                    valueEndPos = contents.count();
                if (firstChar != '#') {
                    value += contents.mid(valueStartPos,
                        valueEndPos - valueStartPos).trimmed();
                }
            } else {
                break;
            }
        }
    }
    return value;
}

633
bool AbstractDebBasedQt4MaemoTarget::setControlFieldValue(const QByteArray &fieldName,
634
635
    const QByteArray &fieldValue)
{
636
637
    Utils::FileReader reader;
    if (!reader.fetch(controlFilePath()))
638
        return false;
639
    QByteArray contents = reader.data();
640
    if (adaptControlFileField(contents, fieldName, fieldValue)) {
Tobias Hunger's avatar
Tobias Hunger committed
641
        Core::FileChangeBlocker update(controlFilePath());
642
        Utils::FileSaver saver(controlFilePath());
643
644
        saver.write(contents);
        return saver.finalize();
645
646
647
648
    }
    return true;
}

649
bool AbstractDebBasedQt4MaemoTarget::adaptControlFileField(QByteArray &document,
650
651
    const QByteArray &fieldName, const QByteArray &newFieldValue)
{
652
    return adaptTagValue(document, fieldName, newFieldValue, true);
653
654
}

655
void AbstractDebBasedQt4MaemoTarget::handleTargetAddedSpecial()
656
{
657
658
659
660
661
662
663
664
    if (controlFileFieldValue(IconFieldName, true).isEmpty()) {
        // Such a file is created by the mobile wizards.
        const QString iconPath = project()->projectDirectory()
            + QLatin1Char('/') + project()->displayName()
            + QLatin1String(".png");
        if (QFileInfo(iconPath).exists())
            setPackageManagerIcon(iconPath);
    }
665
    m_filesWatcher->addDirectory(debianDirPath(), Utils::FileSystemWatcher::WatchAllChanges);
666
667
668
669
670
671
    m_controlFile = new WatchableFile(controlFilePath(), this);
    connect(m_controlFile, SIGNAL(modified()), SIGNAL(controlChanged()));
    m_changeLogFile = new WatchableFile(changeLogFilePath(), this);
    connect(m_changeLogFile, SIGNAL(modified()), SIGNAL(changeLogChanged()));
    Core::FileManager::instance()->addFiles(QList<Core::IFile *>()
        << m_controlFile << m_changeLogFile);
672
    connect(m_filesWatcher, SIGNAL(directoryChanged(QString)), this,
673
674
        SLOT(handleDebianDirContentsChanged()));
    handleDebianDirContentsChanged();
675
676
    emit controlChanged();
    emit changeLogChanged();
677
678
}

679
680
681
682
683
684
bool AbstractDebBasedQt4MaemoTarget::targetCanBeRemoved() const
{
    return QFileInfo(debianDirPath()).exists();
}

void AbstractDebBasedQt4MaemoTarget::removeTarget()
685
686
{
    QString error;
687
    if (!Utils::FileUtils::removeRecursively(debianDirPath(), &error))
dt's avatar
dt committed
688
        qDebug("%s", qPrintable(error));
689
690
}

691
692
693
694
695
void AbstractDebBasedQt4MaemoTarget::handleDebianDirContentsChanged()
{
    emit debianDirContentsChanged();
}

696
AbstractQt4MaemoTarget::ActionStatus AbstractDebBasedQt4MaemoTarget::createSpecialTemplates()
697
698
{
    if (QFileInfo(debianDirPath()).exists())
699
        return NoActionRequired;
700
701
702
703
    QDir projectDir(project()->projectDirectory());
    QProcess dh_makeProc;
    QString error;
    const Qt4BuildConfiguration * const bc = activeBuildConfiguration();
704
    AbstractMaemoPackageCreationStep::preparePackagingProcess(&dh_makeProc, bc,
705
        projectDir.path() + QLatin1Char('/') + PackagingDirName);
706
707
    const QString dhMakeDebianDir = projectDir.path() + QLatin1Char('/')
        + PackagingDirName + QLatin1String("/debian");
708
    Utils::FileUtils::removeRecursively(dhMakeDebianDir, &error);
709
710
711
    const QStringList dh_makeArgs = QStringList() << QLatin1String("dh_make")
        << QLatin1String("-s") << QLatin1String("-n") << QLatin1String("-p")
        << (defaultPackageFileName() + QLatin1Char('_')
712
            + AbstractMaemoPackageCreationStep::DefaultVersionNumber);
713
    QtSupport::BaseQtVersion *lqt = activeBuildConfiguration()->qtVersion();
dt's avatar
dt committed
714
    if (!lqt) {
715
        raiseError(tr("Unable to create Debian templates: No Qt version set"));
dt's avatar
dt committed
716
717
718
        return ActionFailed;
    }
    if (!MaemoGlobal::callMad(dh_makeProc, dh_makeArgs, lqt->qmakeCommand(), true)
719
            || !dh_makeProc.waitForStarted()) {
720
721
        raiseError(tr("Unable to create Debian templates: dh_make failed (%1)")
            .arg(dh_makeProc.errorString()));
722
        return ActionFailed;
723
724
725
726
727
728
729
    }
    dh_makeProc.write("\n"); // Needs user input.
    dh_makeProc.waitForFinished(-1);
    if (dh_makeProc.error() != QProcess::UnknownError
        || dh_makeProc.exitCode() != 0) {
        raiseError(tr("Unable to create debian templates: dh_make failed (%1)")
            .arg(dh_makeProc.errorString()));
730
        return ActionFailed;
731
732
733
734
735
    }

    if (!QFile::rename(dhMakeDebianDir, debianDirPath())) {
        raiseError(tr("Unable to move new debian directory to '%1'.")
            .arg(QDir::toNativeSeparators(debianDirPath())));
736
        Utils::FileUtils::removeRecursively(dhMakeDebianDir, &error);
737
        return ActionFailed;
738
739
740
741
742
743
744
745
746
747
748
749
750
    }

    QDir debianDir(debianDirPath());
    const QStringList &files = debianDir.entryList(QDir::Files);
    foreach (const QString &fileName, files) {
        if (fileName.endsWith(QLatin1String(".ex"), Qt::CaseInsensitive)
            || fileName.compare(QLatin1String("README.debian"), Qt::CaseInsensitive) == 0
            || fileName.compare(QLatin1String("dirs"), Qt::CaseInsensitive) == 0
            || fileName.compare(QLatin1String("docs"), Qt::CaseInsensitive) == 0) {
            debianDir.remove(fileName);
        }
    }

751
752
    return adaptRulesFile() && adaptControlFile()
        ? ActionSuccessful : ActionFailed;
753
754
}

755
bool AbstractDebBasedQt4MaemoTarget::adaptRulesFile()
756
{
757
758
759
    Utils::FileReader reader;
    if (!reader.fetch(rulesFilePath())) {
        raiseError(reader.errorString());
760
761
        return false;
    }
762
    QByteArray rulesContents = reader.data();
763
    const QByteArray comment("# Uncomment this line for use without Qt Creator");
764
    rulesContents.replace("DESTDIR", "INSTALL_ROOT");
765
766
767
768
    rulesContents.replace("dh_shlibdeps", "# dh_shlibdeps " + comment);
    rulesContents.replace("# Add here commands to configure the package.",
        "# qmake PREFIX=/usr" + comment);
    rulesContents.replace("$(MAKE)\n", "# $(MAKE) " + comment + '\n');
769
770
771
772
773

    // Would be the right solution, but does not work (on Windows),
    // because dpkg-genchanges doesn't know about it (and can't be told).
    // rulesContents.replace("dh_builddeb", "dh_builddeb --destdir=.");

774
775
776
777
    Utils::FileSaver saver(rulesFilePath());
    saver.write(rulesContents);
    if (!saver.finalize()) {
        raiseError(saver.errorString());
778
779
780
781
782
        return false;
    }
    return true;
}

783
bool AbstractDebBasedQt4MaemoTarget::adaptControlFile()
784
{
785
786
787
    Utils::FileReader reader;
    if (!reader.fetch(controlFilePath())) {
        raiseError(reader.errorString());
788
789
        return false;
    }
790
    QByteArray controlContents = reader.data();
791

792
    adaptControlFileField(controlContents, "Section", defaultSection());
793
    adaptControlFileField(controlContents, "Priority", "optional");
794
    adaptControlFileField(controlContents, packageManagerNameFieldName(),
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
        project()->displayName().toUtf8());
    const int buildDependsOffset = controlContents.indexOf("Build-Depends:");
    if (buildDependsOffset == -1) {
        qDebug("Unexpected: no Build-Depends field in debian control file.");
    } else {
        int buildDependsNewlineOffset
            = controlContents.indexOf('\n', buildDependsOffset);
        if (buildDependsNewlineOffset == -1) {
            controlContents += '\n';
            buildDependsNewlineOffset = controlContents.length() - 1;
        }
        controlContents.insert(buildDependsNewlineOffset,
            ", libqt4-dev");
    }

810
    addAdditionalControlFileFields(controlContents);
811
812
813
814
    Utils::FileSaver saver(controlFilePath());
    saver.write(controlContents);
    if (!saver.finalize()) {
        raiseError(saver.errorString());
815
816
817
818
819
        return false;
    }
    return true;
}

820
821
822
823
824
bool AbstractDebBasedQt4MaemoTarget::initAdditionalPackagingSettingsFromOtherTarget()
{
    foreach (const Target * const t, project()->targets()) {
        const AbstractDebBasedQt4MaemoTarget *target
            = qobject_cast<const AbstractDebBasedQt4MaemoTarget *>(t);
825
        if (target && target != this) {
826
827
828
829
830
831
832
            return setControlFieldValue(IconFieldName,
                target->controlFileFieldValue(IconFieldName, true));
        }
    }
    return true;
}

833
834
835
836
837
838
839
840
841
QStringList AbstractDebBasedQt4MaemoTarget::packagingFilePaths() const
{
    QStringList filePaths;
    const QString parentDir = debianDirPath();
    foreach (const QString &fileName, debianFiles())
        filePaths << parentDir + QLatin1Char('/') + fileName;
    return filePaths;
}

Christian Kandeler's avatar
Christian Kandeler committed
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
QString AbstractDebBasedQt4MaemoTarget::defaultPackageFileName() const
{
    QString packageName = project()->displayName().toLower();

    // We also replace dots, because OVI store chokes on them.
    const QRegExp legalLetter(QLatin1String("[a-z0-9+-]"), Qt::CaseSensitive,
        QRegExp::WildcardUnix);

    for (int i = 0; i < packageName.length(); ++i) {
        if (!legalLetter.exactMatch(packageName.mid(i, 1)))
            packageName[i] = QLatin1Char('-');
    }
    return packageName;
}

857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
bool AbstractDebBasedQt4MaemoTarget::setPackageManagerIcon(const QString &iconFilePath,
    QString *error)
{
    bool success = true;
    foreach (Target * const target, project()->targets()) {
        AbstractDebBasedQt4MaemoTarget* const maemoTarget
            = qobject_cast<AbstractDebBasedQt4MaemoTarget*>(target);
        if (maemoTarget) {
            if (!maemoTarget->setPackageManagerIconInternal(iconFilePath, error))
                success = false;
        }
    }
    return success;
}


AbstractRpmBasedQt4MaemoTarget::AbstractRpmBasedQt4MaemoTarget(Qt4Project *parent,
    const QString &id) : AbstractQt4MaemoTarget(parent, id)
{
}

AbstractRpmBasedQt4MaemoTarget::~AbstractRpmBasedQt4MaemoTarget()
{
}

QString AbstractRpmBasedQt4MaemoTarget::specFilePath() const
{
    const QLatin1Char sep('/');
    return project()->projectDirectory() + sep + PackagingDirName + sep
        + specFileName();
}

QString AbstractRpmBasedQt4MaemoTarget::projectVersion(QString *error) const
{
    return QString::fromUtf8(getValueForTag(VersionTag, error));
}

bool AbstractRpmBasedQt4MaemoTarget::setProjectVersionInternal(const QString &version,
    QString *error)
{
    return setValueForTag(VersionTag, version.toUtf8(), error);
}

Christian Kandeler's avatar
Christian Kandeler committed
900
QString AbstractRpmBasedQt4MaemoTarget::packageName() const
901
{
Christian Kandeler's avatar
Christian Kandeler committed
902
    return QString::fromUtf8(getValueForTag(NameTag, 0));
903
904
}

Christian Kandeler's avatar
Christian Kandeler committed
905
bool AbstractRpmBasedQt4MaemoTarget::setPackageNameInternal(const QString &name)
906
907
908
909
910
911
{
    return setValueForTag(NameTag, name.toUtf8(), 0);
}

QString AbstractRpmBasedQt4MaemoTarget::shortDescription() const
{
Christian Kandeler's avatar
Christian Kandeler committed
912
913
914
915
916
    return QString::fromUtf8(getValueForTag(SummaryTag, 0));
}

QString AbstractRpmBasedQt4MaemoTarget::packageFileName() const
{
917
    QtSupport::BaseQtVersion *lqt = activeBuildConfiguration()->qtVersion();
dt's avatar
dt committed
918
919
    if (!lqt)
        return QString();
Christian Kandeler's avatar
Christian Kandeler committed
920
921
    return packageName() + QLatin1Char('-') + projectVersion() + QLatin1Char('-')
        + QString::fromUtf8(getValueForTag(ReleaseTag, 0)) + QLatin1Char('.')
dt's avatar
dt committed
922
        + MaemoGlobal::architecture(lqt->qmakeCommand())
Christian Kandeler's avatar
Christian Kandeler committed
923
        + QLatin1String(".rpm");
924
925
926
927
928
929
930
}

bool AbstractRpmBasedQt4MaemoTarget::setShortDescriptionInternal(const QString &description)
{
    return setValueForTag(SummaryTag, description.toUtf8(), 0);
}

931
AbstractQt4MaemoTarget::ActionStatus AbstractRpmBasedQt4MaemoTarget::createSpecialTemplates()
932
933
{
    if (QFileInfo(specFilePath()).exists())
934
        return NoActionRequired;
935
936
937
938
939
    QByteArray initialContent(
        "Name: %%name%%\n"
        "Summary: <insert short description here>\n"
        "Version: 0.0.1\n"
        "Release: 1\n"
Christian Kandeler's avatar
Christian Kandeler committed
940
        "License: <Enter your application's license here>\n"
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
        "Group: <Set your application's group here>\n"
        "%description\n"
        "<Insert longer, multi-line description\n"
        "here.>\n"
        "\n"
        "%prep\n"
        "%setup -q\n"
        "\n"
        "%build\n"
        "# You can leave this empty for use with Qt Creator."
        "\n"
        "%install\n"
        "rm -rf %{buildroot}\n"
        "make INSTALL_ROOT=%{buildroot} install\n"
        "\n"
        "%clean\n"
        "rm -rf %{buildroot}\n"
        "\n"
        "BuildRequires: \n"
Christian Kandeler's avatar
Christian Kandeler committed
960
961
        "# %define _unpackaged_files_terminate_build 0\n"
        "%files\n"
962
963
        "%defattr(-,root,root,-)"
        "/usr\n"
964
        "/opt\n"
965
        "# Add additional files to be included in the package here.\n"
Christian Kandeler's avatar
Christian Kandeler committed
966
967
        "%pre\n"
        "# Add pre-install scripts here."
968
969
        "%post\n"
        "/sbin/ldconfig # For shared libraries\n"
Christian Kandeler's avatar
Christian Kandeler committed
970
971
972
973
        "%preun\n"
        "# Add pre-uninstall scripts here."
        "%postun\n"
        "# Add post-uninstall scripts here."
974
975
        );
    initialContent.replace("%%name%%", project()->displayName().toUtf8());
976
977
978
    Utils::FileSaver saver(specFilePath());
    saver.write(initialContent);
    return saver.finalize() ? ActionSuccessful : ActionFailed;
979
980
981
982
}

void AbstractRpmBasedQt4MaemoTarget::handleTargetAddedSpecial()
{
983
984
985
986
    m_specFile = new WatchableFile(specFilePath(), this);
    connect(m_specFile, SIGNAL(modified()), SIGNAL(specFileChanged()));
    Core::FileManager::instance()->addFile(m_specFile);
    emit specFileChanged();
987
988
989
990
991
992
993
994
995
}

bool AbstractRpmBasedQt4MaemoTarget::targetCanBeRemoved() const
{
    return QFileInfo(specFilePath()).exists();
}

void AbstractRpmBasedQt4MaemoTarget::removeTarget()
{
996
    QFile::remove(specFilePath());
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
}

bool AbstractRpmBasedQt4MaemoTarget::initAdditionalPackagingSettingsFromOtherTarget()
{
    // Nothing to do here for now.
    return true;
}

QByteArray AbstractRpmBasedQt4MaemoTarget::getValueForTag(const QByteArray &tag,
    QString *error) const
{
1008
1009
    Utils::FileReader reader;
    if (!reader.fetch(specFilePath(), error))
1010
        return QByteArray();
1011
    const QByteArray &content = reader.data();
1012
1013
1014
1015
1016
1017
1018
1019
    const QByteArray completeTag = tag.toLower() + ':';
    int index = content.toLower().indexOf(completeTag);
    if (index == -1)
        return QByteArray();
    index += completeTag.count();
    int endIndex = content.indexOf('\n', index);
    if (endIndex == -1)
        endIndex = content.count();
Christian Kandeler's avatar
Christian Kandeler committed
1020
    return content.mid(index, endIndex - index).trimmed();
1021
1022
1023
1024
1025
}

bool AbstractRpmBasedQt4MaemoTarget::setValueForTag(const QByteArray &tag,
    const QByteArray &value, QString *error)
{
1026
1027
    Utils::FileReader reader;
    if (!reader.fetch(specFilePath(), error))
1028
        return false;
1029
    QByteArray content = reader.data();
1030
    if (adaptTagValue(content, tag, value, false)) {
1031
1032
1033
        Utils::FileSaver saver(specFilePath());
        saver.write(content);
        return saver.finalize(error);
1034
1035
1036
1037
    }
    return true;
}

1038
Qt4Maemo5Target::Qt4Maemo5Target(Qt4Project *parent, const QString &id)
1039
        : AbstractDebBasedQt4MaemoTarget(parent, id)
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
{
    setDisplayName(defaultDisplayName());
}

Qt4Maemo5Target::~Qt4Maemo5Target() {}

QString Qt4Maemo5Target::defaultDisplayName()
{
    return QApplication::translate("Qt4ProjectManager::Qt4Target", "Maemo5",
        "Qt4 Maemo5 target display name");
}

1052
1053
1054
1055
1056
void Qt4Maemo5Target::addAdditionalControlFileFields(QByteArray &controlContents)
{
    Q_UNUSED(controlContents);
}

1057
1058
1059
1060
1061
QString Qt4Maemo5Target::debianDirName() const
{
    return QLatin1String("debian_fremantle");
}

1062
1063
1064
1065
1066
QByteArray Qt4Maemo5Target::packageManagerNameFieldName() const
{
    return "XB-Maemo-Display-Name";
}

1067
1068
1069
1070
1071
QByteArray Qt4Maemo5Target::defaultSection() const
{
    return "user/hidden";
}

1072
1073
Qt4HarmattanTarget::Qt4HarmattanTarget(Qt4Project *parent, const QString &id)
        : AbstractDebBasedQt4MaemoTarget(parent, id)
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
{
    setDisplayName(defaultDisplayName());
}

Qt4HarmattanTarget::~Qt4HarmattanTarget() {}

QString Qt4HarmattanTarget::defaultDisplayName()
{
    return QApplication::translate("Qt4ProjectManager::Qt4Target", "Harmattan",
        "Qt4 Harmattan target display name");
}

1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
QString Qt4HarmattanTarget::aegisManifestFileName()
{
    return QLatin1String("manifest.aegis");
}

void Qt4HarmattanTarget::handleTargetAddedSpecial()
{
    AbstractDebBasedQt4MaemoTarget::handleTargetAddedSpecial();
    QFile(debianDirPath() + QLatin1Char('/') + aegisManifestFileName()).open(QIODevice::WriteOnly);
}

1097
1098
void Qt4HarmattanTarget::addAdditionalControlFileFields(QByteArray &controlContents)
{
1099
    Q_UNUSED(controlContents);
1100
1101
}

1102
1103
1104
1105
QString Qt4HarmattanTarget::debianDirName() const
{
    return QLatin1String("debian_harmattan");
}
1106

1107
1108
1109
1110
1111
QByteArray Qt4HarmattanTarget::packageManagerNameFieldName() const
{
    return "XSBC-Maemo-Display-Name";
}

1112
1113
1114
1115
1116
QByteArray Qt4HarmattanTarget::defaultSection() const
{
    return "user/other";
}

1117

Christian Kandeler's avatar
Christian Kandeler committed
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
Qt4MeegoTarget::Qt4MeegoTarget(Qt4Project *parent, const QString &id)
       : AbstractRpmBasedQt4MaemoTarget(parent, id)
{
    setDisplayName(defaultDisplayName());
}

Qt4MeegoTarget::~Qt4MeegoTarget() {}

QString Qt4MeegoTarget::defaultDisplayName()
{
    return QApplication::translate("Qt4ProjectManager::Qt4Target",
        "Meego", "Qt4 Meego target display name");
}

QString Qt4MeegoTarget::specFileName() const
{
    return QLatin1String("meego.spec");
}
1136
1137
1138

} // namespace Internal
} // namespace RemoteLinux