1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "qt4maemotarget.h"
36 #include "maemoglobal.h"
37 #include "maemopackagecreationstep.h"
38 #include "maemopertargetdeviceconfigurationlistmodel.h"
39 #include "maemorunconfiguration.h"
40 #include "maemotoolchain.h"
41 #include "qt4maemodeployconfiguration.h"
43 #include <coreplugin/icore.h>
44 #include <coreplugin/iversioncontrol.h>
45 #include <coreplugin/vcsmanager.h>
46 #include <projectexplorer/customexecutablerunconfiguration.h>
47 #include <projectexplorer/projectexplorer.h>
48 #include <projectexplorer/projectnodes.h>
49 #include <qt4projectmanager/qt4project.h>
51 #include <QtGui/QApplication>
52 #include <QtGui/QMainWindow>
53 #include <QtCore/QBuffer>
54 #include <QtCore/QRegExp>
55 #include <QtCore/QDir>
56 #include <QtCore/QFile>
57 #include <QtCore/QFileInfo>
58 #include <QtCore/QProcess>
59 #include <QtCore/QStringList>
60 #include <QtGui/QIcon>
61 #include <QtGui/QMessageBox>
65 using namespace Qt4ProjectManager;
66 using namespace Qt4ProjectManager::Internal;
69 const QByteArray NameFieldName("Package");
70 const QByteArray IconFieldName("XB-Maemo-Icon-26");
71 const QByteArray ShortDescriptionFieldName("Description");
72 const QByteArray PackageFieldName("Package");
73 const QLatin1String PackagingDirName("qtc_packaging");
74 const QByteArray NameTag("Name");
75 const QByteArray SummaryTag("Summary");
76 const QByteArray VersionTag("Version");
77 const QByteArray ReleaseTag("Release");
79 bool adaptTagValue(QByteArray &document, const QByteArray &fieldName,
80 const QByteArray &newFieldValue, bool caseSensitive)
82 QByteArray adaptedLine = fieldName + ": " + newFieldValue;
83 const QByteArray completeTag = fieldName + ":";
84 const int lineOffset = caseSensitive ? document.indexOf(completeTag)
85 : document.toLower().indexOf(completeTag.toLower());
86 if (lineOffset == -1) {
87 document.append(adaptedLine).append('\n');
91 int newlineOffset = document.indexOf('\n', lineOffset);
93 if (newlineOffset == -1) {
94 newlineOffset = document.length();
98 const int replaceCount = newlineOffset - lineOffset;
99 if (!updated && document.mid(lineOffset, replaceCount) != adaptedLine)
102 document.replace(lineOffset, replaceCount, adaptedLine);
107 } // anonymous namespace
110 AbstractQt4MaemoTarget::AbstractQt4MaemoTarget(Qt4Project *parent, const QString &id) :
111 Qt4BaseTarget(parent, id),
112 m_filesWatcher(new QFileSystemWatcher(this)),
113 m_buildConfigurationFactory(new Qt4BuildConfigurationFactory(this)),
114 m_deployConfigurationFactory(new Qt4MaemoDeployConfigurationFactory(this)),
115 m_isInitialized(false)
117 setIcon(QIcon(":/projectexplorer/images/MaemoDevice.png"));
118 connect(parent, SIGNAL(addedTarget(ProjectExplorer::Target*)),
119 this, SLOT(handleTargetAdded(ProjectExplorer::Target*)));
120 connect(parent, SIGNAL(fromMapFinished()),
121 this, SLOT(handleFromMapFinished()));
124 AbstractQt4MaemoTarget::~AbstractQt4MaemoTarget()
127 AbstractQt4MaemoTarget::DebugArchitecture AbstractQt4MaemoTarget::debugArchitecture() const
130 = MaemoGlobal::architecture(activeBuildConfiguration()->qtVersion());
131 if (arch.startsWith(QLatin1String("arm"))) {
132 return DebugArchitecture(QLatin1String("arm"),
133 QLatin1String("arm-none-linux-gnueabi"));
134 } else if (arch.startsWith(QLatin1String("x86_64"))) {
135 return DebugArchitecture(QLatin1String("i386:x86-64"),
136 QLatin1String("x86_64-unknown-linux-gnu "));
138 return DebugArchitecture(QLatin1String("x86"),
139 QLatin1String("i386-unknown-linux-gnu "));
143 QList<ProjectExplorer::ToolChain *> AbstractQt4MaemoTarget::possibleToolChains(ProjectExplorer::BuildConfiguration *bc) const
145 QList<ProjectExplorer::ToolChain *> result;
147 Qt4BuildConfiguration *qt4Bc = qobject_cast<Qt4BuildConfiguration *>(bc);
151 QList<ProjectExplorer::ToolChain *> candidates = Qt4BaseTarget::possibleToolChains(bc);
152 foreach (ProjectExplorer::ToolChain *i, candidates) {
153 MaemoToolChain *tc = dynamic_cast<MaemoToolChain *>(i);
156 if (tc->qtVersionId() == qt4Bc->qtVersion()->uniqueId())
164 Qt4BuildConfigurationFactory *AbstractQt4MaemoTarget::buildConfigurationFactory() const
166 return m_buildConfigurationFactory;
169 ProjectExplorer::DeployConfigurationFactory *AbstractQt4MaemoTarget::deployConfigurationFactory() const
171 return m_deployConfigurationFactory;
174 QString AbstractQt4MaemoTarget::defaultBuildDirectory() const
177 #if defined(Q_OS_WIN)
178 return project()->projectDirectory();
180 return Qt4BaseTarget::defaultBuildDirectory();
183 void AbstractQt4MaemoTarget::createApplicationProFiles()
185 removeUnconfiguredCustomExectutableRunConfigurations();
187 QList<Qt4ProFileNode *> profiles = qt4Project()->applicationProFiles();
189 foreach (Qt4ProFileNode *pro, profiles)
190 paths << pro->path();
192 foreach (ProjectExplorer::RunConfiguration *rc, runConfigurations())
193 if (MaemoRunConfiguration *qt4rc = qobject_cast<MaemoRunConfiguration *>(rc))
194 paths.remove(qt4rc->proFilePath());
196 // Only add new runconfigurations if there are none.
197 foreach (const QString &path, paths)
198 addRunConfiguration(new MaemoRunConfiguration(this, path));
200 // Oh still none? Add a custom executable runconfiguration
201 if (runConfigurations().isEmpty()) {
202 addRunConfiguration(new ProjectExplorer::CustomExecutableRunConfiguration(this));
206 QList<ProjectExplorer::RunConfiguration *> AbstractQt4MaemoTarget::runConfigurationsForNode(ProjectExplorer::Node *n)
208 QList<ProjectExplorer::RunConfiguration *> result;
209 foreach (ProjectExplorer::RunConfiguration *rc, runConfigurations())
210 if (MaemoRunConfiguration *mrc = qobject_cast<MaemoRunConfiguration *>(rc))
211 if (mrc->proFilePath() == n->path())
216 bool AbstractQt4MaemoTarget::setProjectVersion(const QString &version,
220 foreach (Target * const target, project()->targets()) {
221 AbstractQt4MaemoTarget * const maemoTarget
222 = qobject_cast<AbstractQt4MaemoTarget *>(target);
224 if (!maemoTarget->setProjectVersionInternal(version, error))
231 bool AbstractQt4MaemoTarget::setPackageName(const QString &name)
234 foreach (Target * const target, project()->targets()) {
235 AbstractQt4MaemoTarget * const maemoTarget
236 = qobject_cast<AbstractQt4MaemoTarget *>(target);
238 if (!maemoTarget->setPackageNameInternal(name))
245 bool AbstractQt4MaemoTarget::setShortDescription(const QString &description)
248 foreach (Target * const target, project()->targets()) {
249 AbstractQt4MaemoTarget * const maemoTarget
250 = qobject_cast<AbstractQt4MaemoTarget *>(target);
252 if (!maemoTarget->setShortDescriptionInternal(description))
259 QSharedPointer<QFile> AbstractQt4MaemoTarget::openFile(const QString &filePath,
260 QIODevice::OpenMode mode, QString *error) const
262 const QString nativePath = QDir::toNativeSeparators(filePath);
263 QSharedPointer<QFile> file(new QFile(filePath));
264 if (mode == QIODevice::ReadOnly && !file->exists()) {
266 *error = tr("File '%1' does not exist").arg(nativePath);
268 } else if (!file->open(mode)) {
270 *error = tr("Cannot open file '%1': %2")
271 .arg(nativePath, file->errorString());
278 void AbstractQt4MaemoTarget::handleFromMapFinished()
280 handleTargetAdded(this);
283 void AbstractQt4MaemoTarget::handleTargetAdded(ProjectExplorer::Target *target)
288 if (!project()->rootProjectNode()) {
289 // Project is not fully setup yet, happens on new project
290 // we wait for the fromMapFinished that comes afterwards
294 disconnect(project(), SIGNAL(fromMapFinished()),
295 this, SLOT(handleFromMapFinished()));
296 disconnect(project(), SIGNAL(addedTarget(ProjectExplorer::Target*)),
297 this, SLOT(handleTargetAdded(ProjectExplorer::Target*)));
298 connect(project(), SIGNAL(aboutToRemoveTarget(ProjectExplorer::Target*)),
299 SLOT(handleTargetToBeRemoved(ProjectExplorer::Target*)));
300 if (createTemplates() == ActionFailed)
302 initPackagingSettingsFromOtherTarget();
303 handleTargetAddedSpecial();
304 m_isInitialized = true;
307 void AbstractQt4MaemoTarget::handleTargetToBeRemoved(ProjectExplorer::Target *target)
311 if (!targetCanBeRemoved())
314 Core::ICore * const core = Core::ICore::instance();
315 const int answer = QMessageBox::warning(core->mainWindow(),
316 tr("Qt Creator"), tr("Do you want to remove the packaging file(s) "
317 "associated with the target '%1'?").arg(displayName()),
318 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
319 if (answer == QMessageBox::No)
321 const QStringList pkgFilePaths = packagingFilePaths();
322 project()->rootProjectNode()->removeFiles(ProjectExplorer::UnknownFileType,
324 Core::IVersionControl * const vcs = core->vcsManager()
325 ->findVersionControlForDirectory(QFileInfo(packagingFilePaths().first()).dir().path());
326 if (vcs && vcs->supportsOperation(Core::IVersionControl::DeleteOperation)) {
327 foreach (const QString &filePath, pkgFilePaths)
328 vcs->vcsDelete(filePath);
332 const QString packagingPath = project()->projectDirectory()
333 + QLatin1Char('/') + PackagingDirName;
334 const QStringList otherContents = QDir(packagingPath).entryList(QDir::Dirs
335 | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot);
336 if (otherContents.isEmpty()) {
337 if (!MaemoGlobal::removeRecursively(packagingPath, error))
338 qDebug("%s", qPrintable(error));
342 AbstractQt4MaemoTarget::ActionStatus AbstractQt4MaemoTarget::createTemplates()
344 QDir projectDir(project()->projectDirectory());
345 if (!projectDir.exists(PackagingDirName)
346 && !projectDir.mkdir(PackagingDirName)) {
347 raiseError(tr("Error creating Maemo packaging directory '%1'.")
348 .arg(PackagingDirName));
352 const ActionStatus actionStatus = createSpecialTemplates();
353 if (actionStatus == ActionFailed)
355 if (actionStatus == ActionSuccessful) {
356 const QStringList &files = packagingFilePaths();
357 QMessageBox::StandardButton button
358 = QMessageBox::question(Core::ICore::instance()->mainWindow(),
359 tr("Add Packaging Files to Project"),
360 tr("Qt Creator has set up the following files to enable "
361 "packaging:\n %1\nDo you want to add them to the project?")
362 .arg(files.join(QLatin1String("\n "))),
363 QMessageBox::Yes | QMessageBox::No);
364 if (button == QMessageBox::Yes)
365 ProjectExplorer::ProjectExplorerPlugin::instance()->addExistingFiles(project()->rootProjectNode(), files);
370 bool AbstractQt4MaemoTarget::initPackagingSettingsFromOtherTarget()
373 foreach (const Target * const target, project()->targets()) {
374 const AbstractQt4MaemoTarget * const maemoTarget
375 = qobject_cast<const AbstractQt4MaemoTarget *>(target);
376 if (maemoTarget && maemoTarget != this && maemoTarget->m_isInitialized) {
377 if (!setProjectVersionInternal(maemoTarget->projectVersion()))
379 if (!setPackageNameInternal(maemoTarget->packageName()))
381 if (!setShortDescriptionInternal(maemoTarget->shortDescription()))
386 return initAdditionalPackagingSettingsFromOtherTarget();
389 void AbstractQt4MaemoTarget::initDeviceConfigurationsModel()
391 m_deviceConfigurationsListModel
392 = new MaemoPerTargetDeviceConfigurationListModel(this);
395 void AbstractQt4MaemoTarget::raiseError(const QString &reason)
397 QMessageBox::critical(0, tr("Error creating Maemo templates"), reason);
400 AbstractDebBasedQt4MaemoTarget::AbstractDebBasedQt4MaemoTarget(Qt4Project *parent,
401 const QString &id) : AbstractQt4MaemoTarget(parent, id)
405 AbstractDebBasedQt4MaemoTarget::~AbstractDebBasedQt4MaemoTarget() {}
407 QString AbstractDebBasedQt4MaemoTarget::projectVersion(QString *error) const
409 QSharedPointer<QFile> changeLog = openFile(changeLogFilePath(),
410 QIODevice::ReadOnly, error);
413 const QByteArray &firstLine = changeLog->readLine();
414 const int openParenPos = firstLine.indexOf('(');
415 if (openParenPos == -1) {
417 *error = tr("Debian changelog file '%1' has unexpected format.")
418 .arg(QDir::toNativeSeparators(changeLog->fileName()));
422 const int closeParenPos = firstLine.indexOf(')', openParenPos);
423 if (closeParenPos == -1) {
425 *error = tr("Debian changelog file '%1' has unexpected format.")
426 .arg(QDir::toNativeSeparators(changeLog->fileName()));
430 return QString::fromUtf8(firstLine.mid(openParenPos + 1,
431 closeParenPos - openParenPos - 1).data());
434 bool AbstractDebBasedQt4MaemoTarget::setProjectVersionInternal(const QString &version,
437 const QString filePath = changeLogFilePath();
438 MaemoGlobal::FileUpdate update(filePath);
439 QSharedPointer<QFile> changeLog
440 = openFile(filePath, QIODevice::ReadWrite, error);
444 QString content = QString::fromUtf8(changeLog->readAll());
445 content.replace(QRegExp(QLatin1String("\\([a-zA-Z0-9_\\.]+\\)")),
446 QLatin1Char('(') + version + QLatin1Char(')'));
447 changeLog->resize(0);
448 changeLog->write(content.toUtf8());
450 if (changeLog->error() != QFile::NoError) {
452 *error = tr("Error writing Debian changelog file '%1': %2")
453 .arg(QDir::toNativeSeparators(changeLog->fileName()),
454 changeLog->errorString());
461 QIcon AbstractDebBasedQt4MaemoTarget::packageManagerIcon(QString *error) const
463 const QByteArray &base64Icon = controlFileFieldValue(IconFieldName, true);
464 if (base64Icon.isEmpty())
467 if (!pixmap.loadFromData(QByteArray::fromBase64(base64Icon))) {
469 *error = tr("Invalid icon data in Debian control file.");
472 return QIcon(pixmap);
475 bool AbstractDebBasedQt4MaemoTarget::setPackageManagerIconInternal(const QString &iconFilePath,
478 const QString filePath = controlFilePath();
479 MaemoGlobal::FileUpdate update(filePath);
480 const QSharedPointer<QFile> controlFile
481 = openFile(filePath, QIODevice::ReadWrite, error);
484 const QPixmap pixmap(iconFilePath);
485 if (pixmap.isNull()) {
487 *error = tr("Could not read image file '%1'.").arg(iconFilePath);
491 QByteArray iconAsBase64;
492 QBuffer buffer(&iconAsBase64);
493 buffer.open(QIODevice::WriteOnly);
494 if (!pixmap.scaled(48, 48).save(&buffer,
495 QFileInfo(iconFilePath).suffix().toAscii())) {
497 *error = tr("Could not export image file '%1'.").arg(iconFilePath);
501 iconAsBase64 = iconAsBase64.toBase64();
502 QByteArray contents = controlFile->readAll();
503 const QByteArray iconFieldNameWithColon = IconFieldName + ':';
504 const int iconFieldPos = contents.startsWith(iconFieldNameWithColon)
505 ? 0 : contents.indexOf('\n' + iconFieldNameWithColon);
506 if (iconFieldPos == -1) {
507 if (!contents.endsWith('\n'))
509 contents.append(iconFieldNameWithColon).append(' ').append(iconAsBase64)
512 const int oldIconStartPos = (iconFieldPos != 0) + iconFieldPos
513 + iconFieldNameWithColon.length();
514 int nextEolPos = contents.indexOf('\n', oldIconStartPos);
515 while (nextEolPos != -1 && nextEolPos != contents.length() - 1
516 && contents.at(nextEolPos + 1) != '\n'
517 && (contents.at(nextEolPos + 1) == '#'
518 || std::isspace(contents.at(nextEolPos + 1))))
519 nextEolPos = contents.indexOf('\n', nextEolPos + 1);
520 if (nextEolPos == -1)
521 nextEolPos = contents.length();
522 contents.replace(oldIconStartPos, nextEolPos - oldIconStartPos,
525 controlFile->resize(0);
526 controlFile->write(contents);
527 if (controlFile->error() != QFile::NoError) {
529 *error = tr("Error writing file '%1': %2")
530 .arg(QDir::toNativeSeparators(controlFile->fileName()),
531 controlFile->errorString());
538 QString AbstractDebBasedQt4MaemoTarget::packageName() const
540 return QString::fromUtf8(controlFileFieldValue(NameFieldName, false));
543 bool AbstractDebBasedQt4MaemoTarget::setPackageNameInternal(const QString &packageName)
545 const QString oldPackageName = this->packageName();
547 if (!setControlFieldValue(NameFieldName, packageName.toUtf8()))
549 if (!setControlFieldValue("Source", packageName.toUtf8()))
552 QSharedPointer<QFile> changelogFile
553 = openFile(changeLogFilePath(), QIODevice::ReadWrite, 0);
556 QString changelogContents = QString::fromUtf8(changelogFile->readAll());
557 QRegExp pattern(QLatin1String("[^\\s]+( \\(\\d\\.\\d\\.\\d\\))"));
558 changelogContents.replace(pattern, packageName + QLatin1String("\\1"));
559 if (!changelogFile->resize(0))
561 changelogFile->write(changelogContents.toUtf8());
563 QSharedPointer<QFile> rulesFile
564 = openFile(rulesFilePath(), QIODevice::ReadWrite, 0);
567 QByteArray rulesContents = rulesFile->readAll();
568 const QString oldString = QLatin1String("debian/") + oldPackageName;
569 const QString newString = QLatin1String("debian/") + packageName;
570 rulesContents.replace(oldString.toUtf8(), newString.toUtf8());
571 rulesFile->resize(0);
572 rulesFile->write(rulesContents);
573 if (rulesFile->error() != QFile::NoError
574 || changelogFile->error() != QFile::NoError) {
580 QString AbstractDebBasedQt4MaemoTarget::packageManagerName() const
582 return QString::fromUtf8(controlFileFieldValue(packageManagerNameFieldName(), false));
585 bool AbstractDebBasedQt4MaemoTarget::setPackageManagerName(const QString &name,
589 foreach (Target * const t, project()->targets()) {
590 AbstractDebBasedQt4MaemoTarget * const target
591 = qobject_cast<AbstractDebBasedQt4MaemoTarget *>(t);
593 if (!target->setPackageManagerNameInternal(name, error))
600 bool AbstractDebBasedQt4MaemoTarget::setPackageManagerNameInternal(const QString &name,
604 return setControlFieldValue(packageManagerNameFieldName(), name.toUtf8());
607 QString AbstractDebBasedQt4MaemoTarget::shortDescription() const
609 return QString::fromUtf8(controlFileFieldValue(ShortDescriptionFieldName, false));
612 QString AbstractDebBasedQt4MaemoTarget::packageFileName() const
614 return QString::fromUtf8(controlFileFieldValue(PackageFieldName, false))
615 + QLatin1Char('_') + projectVersion() + QLatin1String("_armel.deb");
618 bool AbstractDebBasedQt4MaemoTarget::setShortDescriptionInternal(const QString &description)
620 return setControlFieldValue(ShortDescriptionFieldName, description.toUtf8());
623 QString AbstractDebBasedQt4MaemoTarget::debianDirPath() const
625 return project()->projectDirectory() + QLatin1Char('/') + PackagingDirName
626 + QLatin1Char('/') + debianDirName();
629 QStringList AbstractDebBasedQt4MaemoTarget::debianFiles() const
631 return QDir(debianDirPath())
632 .entryList(QDir::Files, QDir::Name | QDir::IgnoreCase);
635 QString AbstractDebBasedQt4MaemoTarget::changeLogFilePath() const
637 return debianDirPath() + QLatin1String("/changelog");
640 QString AbstractDebBasedQt4MaemoTarget::controlFilePath() const
642 return debianDirPath() + QLatin1String("/control");
645 QString AbstractDebBasedQt4MaemoTarget::rulesFilePath() const
647 return debianDirPath() + QLatin1String("/rules");
650 QByteArray AbstractDebBasedQt4MaemoTarget::controlFileFieldValue(const QString &key,
651 bool multiLine) const
654 QFile controlFile(controlFilePath());
655 if (!controlFile.open(QIODevice::ReadOnly))
657 const QByteArray &contents = controlFile.readAll();
658 const int keyPos = contents.indexOf(key.toUtf8() + ':');
661 int valueStartPos = keyPos + key.length() + 1;
662 int valueEndPos = contents.indexOf('\n', keyPos);
663 if (valueEndPos == -1)
664 valueEndPos = contents.count();
665 value = contents.mid(valueStartPos, valueEndPos - valueStartPos).trimmed();
668 valueStartPos = valueEndPos + 1;
669 if (valueStartPos >= contents.count())
671 const char firstChar = contents.at(valueStartPos);
672 if (firstChar == '#' || isspace(firstChar)) {
673 valueEndPos = contents.indexOf('\n', valueStartPos);
674 if (valueEndPos == -1)
675 valueEndPos = contents.count();
676 if (firstChar != '#') {
677 value += contents.mid(valueStartPos,
678 valueEndPos - valueStartPos).trimmed();
688 bool AbstractDebBasedQt4MaemoTarget::setControlFieldValue(const QByteArray &fieldName,
689 const QByteArray &fieldValue)
691 QFile controlFile(controlFilePath());
692 MaemoGlobal::FileUpdate update(controlFile.fileName());
693 if (!controlFile.open(QIODevice::ReadWrite))
695 QByteArray contents = controlFile.readAll();
696 if (adaptControlFileField(contents, fieldName, fieldValue)) {
697 controlFile.resize(0);
698 controlFile.write(contents);
703 bool AbstractDebBasedQt4MaemoTarget::adaptControlFileField(QByteArray &document,
704 const QByteArray &fieldName, const QByteArray &newFieldValue)
706 return adaptTagValue(document, fieldName, newFieldValue, true);
709 void AbstractDebBasedQt4MaemoTarget::handleTargetAddedSpecial()
711 if (controlFileFieldValue(IconFieldName, true).isEmpty()) {
712 // Such a file is created by the mobile wizards.
713 const QString iconPath = project()->projectDirectory()
714 + QLatin1Char('/') + project()->displayName()
715 + QLatin1String(".png");
716 if (QFileInfo(iconPath).exists())
717 setPackageManagerIcon(iconPath);
719 m_filesWatcher->addPath(debianDirPath());
720 m_filesWatcher->addPath(changeLogFilePath());
721 m_filesWatcher->addPath(controlFilePath());
722 connect(m_filesWatcher, SIGNAL(directoryChanged(QString)), this,
723 SLOT(handleDebianDirContentsChanged()));
724 connect(m_filesWatcher, SIGNAL(fileChanged(QString)), this,
725 SLOT(handleDebianFileChanged(QString)));
726 handleDebianDirContentsChanged();
727 handleDebianFileChanged(changeLogFilePath());
728 handleDebianFileChanged(controlFilePath());
731 bool AbstractDebBasedQt4MaemoTarget::targetCanBeRemoved() const
733 return QFileInfo(debianDirPath()).exists();
736 void AbstractDebBasedQt4MaemoTarget::removeTarget()
739 MaemoGlobal::removeRecursively(debianDirPath(), error);
742 void AbstractDebBasedQt4MaemoTarget::handleDebianFileChanged(const QString &filePath)
744 if (filePath == changeLogFilePath())
745 emit changeLogChanged();
746 else if (filePath == controlFilePath())
747 emit controlChanged();
750 void AbstractDebBasedQt4MaemoTarget::handleDebianDirContentsChanged()
752 emit debianDirContentsChanged();
755 AbstractQt4MaemoTarget::ActionStatus AbstractDebBasedQt4MaemoTarget::createSpecialTemplates()
757 if (QFileInfo(debianDirPath()).exists())
758 return NoActionRequired;
759 QDir projectDir(project()->projectDirectory());
760 QProcess dh_makeProc;
762 const Qt4BuildConfiguration * const bc = activeBuildConfiguration();
763 MaemoPackageCreationStep::preparePackagingProcess(&dh_makeProc, bc,
764 projectDir.path() + QLatin1Char('/') + PackagingDirName);
765 const QString dhMakeDebianDir = projectDir.path() + QLatin1Char('/')
766 + PackagingDirName + QLatin1String("/debian");
767 MaemoGlobal::removeRecursively(dhMakeDebianDir, error);
768 const QStringList dh_makeArgs = QStringList() << QLatin1String("dh_make")
769 << QLatin1String("-s") << QLatin1String("-n") << QLatin1String("-p")
770 << (defaultPackageFileName() + QLatin1Char('_')
771 + MaemoPackageCreationStep::DefaultVersionNumber);
772 if (!MaemoGlobal::callMad(dh_makeProc, dh_makeArgs, activeBuildConfiguration()->qtVersion(), true)
773 || !dh_makeProc.waitForStarted()) {
774 raiseError(tr("Unable to create Debian templates: dh_make failed (%1)")
775 .arg(dh_makeProc.errorString()));
778 dh_makeProc.write("\n"); // Needs user input.
779 dh_makeProc.waitForFinished(-1);
780 if (dh_makeProc.error() != QProcess::UnknownError
781 || dh_makeProc.exitCode() != 0) {
782 raiseError(tr("Unable to create debian templates: dh_make failed (%1)")
783 .arg(dh_makeProc.errorString()));
787 if (!QFile::rename(dhMakeDebianDir, debianDirPath())) {
788 raiseError(tr("Unable to move new debian directory to '%1'.")
789 .arg(QDir::toNativeSeparators(debianDirPath())));
790 MaemoGlobal::removeRecursively(dhMakeDebianDir, error);
794 QDir debianDir(debianDirPath());
795 const QStringList &files = debianDir.entryList(QDir::Files);
796 foreach (const QString &fileName, files) {
797 if (fileName.endsWith(QLatin1String(".ex"), Qt::CaseInsensitive)
798 || fileName.compare(QLatin1String("README.debian"), Qt::CaseInsensitive) == 0
799 || fileName.compare(QLatin1String("dirs"), Qt::CaseInsensitive) == 0
800 || fileName.compare(QLatin1String("docs"), Qt::CaseInsensitive) == 0) {
801 debianDir.remove(fileName);
805 return adaptRulesFile() && adaptControlFile()
806 ? ActionSuccessful : ActionFailed;
809 bool AbstractDebBasedQt4MaemoTarget::adaptRulesFile()
811 QFile rulesFile(rulesFilePath());
812 if (!rulesFile.open(QIODevice::ReadWrite)) {
813 raiseError(tr("Packaging Error: Cannot open file '%1'.")
814 .arg(QDir::toNativeSeparators(rulesFilePath())));
817 QByteArray rulesContents = rulesFile.readAll();
818 const QByteArray comment("# Uncomment this line for use without Qt Creator");
819 rulesContents.replace("DESTDIR", "INSTALL_ROOT");
820 rulesContents.replace("dh_shlibdeps", "# dh_shlibdeps " + comment);
821 rulesContents.replace("# Add here commands to configure the package.",
822 "# qmake PREFIX=/usr" + comment);
823 rulesContents.replace("$(MAKE)\n", "# $(MAKE) " + comment + '\n');
825 // Would be the right solution, but does not work (on Windows),
826 // because dpkg-genchanges doesn't know about it (and can't be told).
827 // rulesContents.replace("dh_builddeb", "dh_builddeb --destdir=.");
830 rulesFile.write(rulesContents);
832 if (rulesFile.error() != QFile::NoError) {
833 raiseError(tr("Packaging Error: Cannot write file '%1'.")
834 .arg(QDir::toNativeSeparators(rulesFilePath())));
840 bool AbstractDebBasedQt4MaemoTarget::adaptControlFile()
842 QFile controlFile(controlFilePath());
843 if (!controlFile.open(QIODevice::ReadWrite)) {
844 raiseError(tr("Packaging Error: Cannot open file '%1'.")
845 .arg(QDir::toNativeSeparators(controlFilePath())));
849 QByteArray controlContents = controlFile.readAll();
851 adaptControlFileField(controlContents, "Section", "user/hidden");
852 adaptControlFileField(controlContents, "Priority", "optional");
853 adaptControlFileField(controlContents, packageManagerNameFieldName(),
854 project()->displayName().toUtf8());
855 const int buildDependsOffset = controlContents.indexOf("Build-Depends:");
856 if (buildDependsOffset == -1) {
857 qDebug("Unexpected: no Build-Depends field in debian control file.");
859 int buildDependsNewlineOffset
860 = controlContents.indexOf('\n', buildDependsOffset);
861 if (buildDependsNewlineOffset == -1) {
862 controlContents += '\n';
863 buildDependsNewlineOffset = controlContents.length() - 1;
865 controlContents.insert(buildDependsNewlineOffset,
869 addAdditionalControlFileFields(controlContents);
870 controlFile.resize(0);
871 controlFile.write(controlContents);
873 if (controlFile.error() != QFile::NoError) {
874 raiseError(tr("Packaging Error: Cannot write file '%1'.")
875 .arg(QDir::toNativeSeparators(controlFilePath())));
881 bool AbstractDebBasedQt4MaemoTarget::initAdditionalPackagingSettingsFromOtherTarget()
883 foreach (const Target * const t, project()->targets()) {
884 const AbstractDebBasedQt4MaemoTarget *target
885 = qobject_cast<const AbstractDebBasedQt4MaemoTarget *>(t);
886 if (target && target != this) {
887 return setControlFieldValue(IconFieldName,
888 target->controlFileFieldValue(IconFieldName, true));
894 QStringList AbstractDebBasedQt4MaemoTarget::packagingFilePaths() const
896 QStringList filePaths;
897 const QString parentDir = debianDirPath();
898 foreach (const QString &fileName, debianFiles())
899 filePaths << parentDir + QLatin1Char('/') + fileName;
903 QString AbstractDebBasedQt4MaemoTarget::defaultPackageFileName() const
905 QString packageName = project()->displayName().toLower();
907 // We also replace dots, because OVI store chokes on them.
908 const QRegExp legalLetter(QLatin1String("[a-z0-9+-]"), Qt::CaseSensitive,
909 QRegExp::WildcardUnix);
911 for (int i = 0; i < packageName.length(); ++i) {
912 if (!legalLetter.exactMatch(packageName.mid(i, 1)))
913 packageName[i] = QLatin1Char('-');
918 bool AbstractDebBasedQt4MaemoTarget::setPackageManagerIcon(const QString &iconFilePath,
922 foreach (Target * const target, project()->targets()) {
923 AbstractDebBasedQt4MaemoTarget* const maemoTarget
924 = qobject_cast<AbstractDebBasedQt4MaemoTarget*>(target);
926 if (!maemoTarget->setPackageManagerIconInternal(iconFilePath, error))
934 AbstractRpmBasedQt4MaemoTarget::AbstractRpmBasedQt4MaemoTarget(Qt4Project *parent,
935 const QString &id) : AbstractQt4MaemoTarget(parent, id)
939 AbstractRpmBasedQt4MaemoTarget::~AbstractRpmBasedQt4MaemoTarget()
943 QString AbstractRpmBasedQt4MaemoTarget::specFilePath() const
945 const QLatin1Char sep('/');
946 return project()->projectDirectory() + sep + PackagingDirName + sep
950 QString AbstractRpmBasedQt4MaemoTarget::projectVersion(QString *error) const
952 return QString::fromUtf8(getValueForTag(VersionTag, error));
955 bool AbstractRpmBasedQt4MaemoTarget::setProjectVersionInternal(const QString &version,
958 return setValueForTag(VersionTag, version.toUtf8(), error);
961 QString AbstractRpmBasedQt4MaemoTarget::packageName() const
963 return QString::fromUtf8(getValueForTag(NameTag, 0));
966 bool AbstractRpmBasedQt4MaemoTarget::setPackageNameInternal(const QString &name)
968 return setValueForTag(NameTag, name.toUtf8(), 0);
971 QString AbstractRpmBasedQt4MaemoTarget::shortDescription() const
973 return QString::fromUtf8(getValueForTag(SummaryTag, 0));
976 QString AbstractRpmBasedQt4MaemoTarget::packageFileName() const
978 return packageName() + QLatin1Char('-') + projectVersion() + QLatin1Char('-')
979 + QString::fromUtf8(getValueForTag(ReleaseTag, 0)) + QLatin1Char('.')
980 + MaemoGlobal::architecture(activeBuildConfiguration()->qtVersion())
981 + QLatin1String(".rpm");
984 bool AbstractRpmBasedQt4MaemoTarget::setShortDescriptionInternal(const QString &description)
986 return setValueForTag(SummaryTag, description.toUtf8(), 0);
989 AbstractQt4MaemoTarget::ActionStatus AbstractRpmBasedQt4MaemoTarget::createSpecialTemplates()
991 if (QFileInfo(specFilePath()).exists())
992 return NoActionRequired;
993 QSharedPointer<QFile> specFile
994 = openFile(specFilePath(), QIODevice::WriteOnly, 0);
997 QByteArray initialContent(
999 "Summary: <insert short description here>\n"
1002 "License: <Enter your application's license here>\n"
1003 "Group: <Set your application's group here>\n"
1005 "<Insert longer, multi-line description\n"
1012 "# You can leave this empty for use with Qt Creator."
1015 "rm -rf %{buildroot}\n"
1016 "make INSTALL_ROOT=%{buildroot} install\n"
1019 "rm -rf %{buildroot}\n"
1022 "# %define _unpackaged_files_terminate_build 0\n"
1024 "%defattr(-,root,root,-)"
1027 "# Add additional files to be included in the package here.\n"
1029 "# Add pre-install scripts here."
1031 "/sbin/ldconfig # For shared libraries\n"
1033 "# Add pre-uninstall scripts here."
1035 "# Add post-uninstall scripts here."
1037 initialContent.replace("%%name%%", project()->displayName().toUtf8());
1038 return specFile->write(initialContent) == initialContent.count()
1039 ? ActionSuccessful : ActionFailed;
1042 void AbstractRpmBasedQt4MaemoTarget::handleTargetAddedSpecial()
1044 m_filesWatcher->addPath(specFilePath());
1045 connect(m_filesWatcher, SIGNAL(fileChanged(QString)), this,
1046 SIGNAL(specFileChanged()));
1049 bool AbstractRpmBasedQt4MaemoTarget::targetCanBeRemoved() const
1051 return QFileInfo(specFilePath()).exists();
1054 void AbstractRpmBasedQt4MaemoTarget::removeTarget()
1056 QFile::remove(specFilePath());
1059 bool AbstractRpmBasedQt4MaemoTarget::initAdditionalPackagingSettingsFromOtherTarget()
1061 // Nothing to do here for now.
1065 QByteArray AbstractRpmBasedQt4MaemoTarget::getValueForTag(const QByteArray &tag,
1066 QString *error) const
1068 QSharedPointer<QFile> specFile
1069 = openFile(specFilePath(), QIODevice::ReadOnly, error);
1071 return QByteArray();
1072 const QByteArray &content = specFile->readAll();
1073 const QByteArray completeTag = tag.toLower() + ':';
1074 int index = content.toLower().indexOf(completeTag);
1076 return QByteArray();
1077 index += completeTag.count();
1078 int endIndex = content.indexOf('\n', index);
1080 endIndex = content.count();
1081 return content.mid(index, endIndex - index).trimmed();
1084 bool AbstractRpmBasedQt4MaemoTarget::setValueForTag(const QByteArray &tag,
1085 const QByteArray &value, QString *error)
1087 QSharedPointer<QFile> specFile
1088 = openFile(specFilePath(), QIODevice::ReadWrite, error);
1091 QByteArray content = specFile->readAll();
1092 if (adaptTagValue(content, tag, value, false)) {
1093 specFile->resize(0);
1094 specFile->write(content);
1099 Qt4Maemo5Target::Qt4Maemo5Target(Qt4Project *parent, const QString &id)
1100 : AbstractDebBasedQt4MaemoTarget(parent, id)
1102 setDisplayName(defaultDisplayName());
1103 initDeviceConfigurationsModel();
1106 Qt4Maemo5Target::~Qt4Maemo5Target() {}
1108 QString Qt4Maemo5Target::defaultDisplayName()
1110 return QApplication::translate("Qt4ProjectManager::Qt4Target", "Maemo5",
1111 "Qt4 Maemo5 target display name");
1114 void Qt4Maemo5Target::addAdditionalControlFileFields(QByteArray &controlContents)
1116 Q_UNUSED(controlContents);
1119 QString Qt4Maemo5Target::debianDirName() const
1121 return QLatin1String("debian_fremantle");
1124 QByteArray Qt4Maemo5Target::packageManagerNameFieldName() const
1126 return "XB-Maemo-Display-Name";
1129 Qt4HarmattanTarget::Qt4HarmattanTarget(Qt4Project *parent, const QString &id)
1130 : AbstractDebBasedQt4MaemoTarget(parent, id)
1132 setDisplayName(defaultDisplayName());
1133 initDeviceConfigurationsModel();
1136 Qt4HarmattanTarget::~Qt4HarmattanTarget() {}
1138 QString Qt4HarmattanTarget::defaultDisplayName()
1140 return QApplication::translate("Qt4ProjectManager::Qt4Target", "Harmattan",
1141 "Qt4 Harmattan target display name");
1144 void Qt4HarmattanTarget::addAdditionalControlFileFields(QByteArray &controlContents)
1146 adaptControlFileField(controlContents, "XB-Meego-Desktop-Entry", "");
1147 adaptControlFileField(controlContents, "XB-MeeGo-Desktop-Entry-Filename", "");
1150 QString Qt4HarmattanTarget::debianDirName() const
1152 return QLatin1String("debian_harmattan");
1155 QByteArray Qt4HarmattanTarget::packageManagerNameFieldName() const
1157 return "XSBC-Maemo-Display-Name";
1161 Qt4MeegoTarget::Qt4MeegoTarget(Qt4Project *parent, const QString &id)
1162 : AbstractRpmBasedQt4MaemoTarget(parent, id)
1164 setDisplayName(defaultDisplayName());
1165 initDeviceConfigurationsModel();
1168 Qt4MeegoTarget::~Qt4MeegoTarget() {}
1170 QString Qt4MeegoTarget::defaultDisplayName()
1172 return QApplication::translate("Qt4ProjectManager::Qt4Target",
1173 "Meego", "Qt4 Meego target display name");
1176 QString Qt4MeegoTarget::specFileName() const
1178 return QLatin1String("meego.spec");