1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of Qt Creator.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
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
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "maemopackagecreationstep.h"
44 #include "maemoconstants.h"
45 #include "maemodeployables.h"
46 #include "maemodeploystep.h"
47 #include "maemoglobal.h"
48 #include "maemopackagecreationwidget.h"
49 #include "maemoprofilewrapper.h"
50 #include "maemotoolchain.h"
52 #include <projectexplorer/buildsteplist.h>
53 #include <projectexplorer/projectexplorerconstants.h>
54 #include <qt4buildconfiguration.h>
55 #include <qt4project.h>
56 #include <qt4target.h>
58 #include <QtCore/QProcess>
59 #include <QtCore/QProcessEnvironment>
60 #include <QtCore/QStringBuilder>
61 #include <QtGui/QWidget>
64 const QLatin1String PackagingEnabledKey("Packaging Enabled");
65 const QLatin1String DefaultVersionNumber("0.0.1");
66 const QLatin1String VersionNumberKey("Version Number");
69 using namespace ProjectExplorer::Constants;
70 using ProjectExplorer::BuildStepList;
71 using ProjectExplorer::BuildStepConfigWidget;
72 using ProjectExplorer::Task;
74 namespace Qt4ProjectManager {
77 MaemoPackageCreationStep::MaemoPackageCreationStep(BuildStepList *bsl)
78 : ProjectExplorer::BuildStep(bsl, CreatePackageId),
79 m_packagingEnabled(true),
80 m_versionString(DefaultVersionNumber)
85 MaemoPackageCreationStep::MaemoPackageCreationStep(BuildStepList *bsl,
86 MaemoPackageCreationStep *other)
87 : BuildStep(bsl, other),
88 m_packagingEnabled(other->m_packagingEnabled),
89 m_versionString(other->m_versionString)
94 MaemoPackageCreationStep::~MaemoPackageCreationStep()
98 void MaemoPackageCreationStep::ctor()
100 connect(buildConfiguration(), SIGNAL(buildDirectoryChanged()), this,
101 SIGNAL(packageFilePathChanged()));
104 bool MaemoPackageCreationStep::init()
109 QVariantMap MaemoPackageCreationStep::toMap() const
111 QVariantMap map(ProjectExplorer::BuildStep::toMap());
112 map.insert(PackagingEnabledKey, m_packagingEnabled);
113 map.insert(VersionNumberKey, m_versionString);
117 bool MaemoPackageCreationStep::fromMap(const QVariantMap &map)
119 m_packagingEnabled = map.value(PackagingEnabledKey, true).toBool();
120 m_versionString = map.value(VersionNumberKey, DefaultVersionNumber).toString();
121 return ProjectExplorer::BuildStep::fromMap(map);
124 void MaemoPackageCreationStep::run(QFutureInterface<bool> &fi)
126 fi.reportResult(m_packagingEnabled ? createPackage() : true);
129 BuildStepConfigWidget *MaemoPackageCreationStep::createConfigWidget()
131 return new MaemoPackageCreationWidget(this);
134 bool MaemoPackageCreationStep::createPackage()
136 if (!packagingNeeded())
139 emit addOutput(tr("Creating package file ..."), BuildStep::MessageOutput);
140 QFile configFile(targetRoot() % QLatin1String("/config.sh"));
141 if (!configFile.open(QIODevice::ReadOnly)) {
142 raiseError(tr("Cannot open MADDE config file '%1'.")
143 .arg(nativePath(configFile)));
147 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
148 const QString &path = QDir::toNativeSeparators(maddeRoot() + QLatin1Char('/'));
150 const QLatin1String key("PATH");
151 QString colon = QLatin1String(":");
153 colon = QLatin1String(";");
154 env.insert(key, path % QLatin1String("bin") % colon % env.value(key));
157 env.insert(key, targetRoot() % "/bin" % colon % env.value(key));
158 env.insert(key, path % QLatin1String("madbin") % colon % env.value(key));
160 QString perlLib = QDir::fromNativeSeparators(path % QLatin1String("madlib/perl5"));
162 perlLib = perlLib.remove(QLatin1Char(':'));
163 perlLib = perlLib.prepend(QLatin1Char('/'));
165 env.insert(QLatin1String("PERL5LIB"), perlLib);
167 const QString buildDir = buildDirectory();
168 env.insert(QLatin1String("PWD"), buildDir);
170 const QRegExp envPattern(QLatin1String("([^=]+)=[\"']?([^;\"']+)[\"']? ;.*"));
173 line = configFile.readLine(200);
174 if (envPattern.exactMatch(line))
175 env.insert(envPattern.cap(1), envPattern.cap(2));
176 } while (!line.isEmpty());
179 m_buildProc.reset(new QProcess);
180 connect(m_buildProc.data(), SIGNAL(readyReadStandardOutput()), this,
181 SLOT(handleBuildOutput()));
182 connect(m_buildProc.data(), SIGNAL(readyReadStandardError()), this,
183 SLOT(handleBuildOutput()));
184 m_buildProc->setProcessEnvironment(env);
185 m_buildProc->setWorkingDirectory(buildDir);
186 m_buildProc->start("cd " + buildDir);
187 m_buildProc->waitForFinished();
189 // cache those two since we can change the version number during packaging
190 // and might fail later to modify, copy, remove etc. the generated package
191 const QString version = versionString();
192 const QString pkgFilePath = packageFilePath();
194 if (!QFileInfo(buildDir + QLatin1String("/debian")).exists()) {
195 const QString command = QLatin1String("dh_make -s -n -p ")
196 % projectName() % QLatin1Char('_') % versionString();
197 if (!runCommand(command))
200 QFile rulesFile(buildDir + QLatin1String("/debian/rules"));
201 if (!rulesFile.open(QIODevice::ReadWrite)) {
202 raiseError(tr("Packaging Error: Cannot open file '%1'.")
203 .arg(nativePath(rulesFile)));
207 QByteArray rulesContents = rulesFile.readAll();
208 rulesContents.replace("DESTDIR", "INSTALL_ROOT");
209 rulesContents.replace("dh_shlibdeps", "# dh_shlibdeps");
211 // Would be the right solution, but does not work (on Windows),
212 // because dpkg-genchanges doesn't know about it (and can't be told).
213 // rulesContents.replace("dh_builddeb", "dh_builddeb --destdir=.");
216 rulesFile.write(rulesContents);
217 if (rulesFile.error() != QFile::NoError) {
218 raiseError(tr("Packaging Error: Cannot write file '%1'.")
219 .arg(nativePath(rulesFile)));
225 QFile::remove(buildDir + QLatin1String("/debian/files"));
226 QFile changeLog(buildDir + QLatin1String("/debian/changelog"));
227 if (changeLog.open(QIODevice::ReadWrite)) {
228 QString content = QString::fromUtf8(changeLog.readAll());
229 content.replace(QRegExp("\\([a-zA-Z0-9_\\.]+\\)"),
230 QLatin1Char('(') % version % QLatin1Char(')'));
232 changeLog.write(content.toUtf8());
236 if (!runCommand(QLatin1String("dpkg-buildpackage -nc -uc -us")))
239 // Workaround for non-working dh_builddeb --destdir=.
240 if (!QDir(buildDir).isRoot()) {
241 const QString packageFileName = QFileInfo(pkgFilePath).fileName();
242 const QString changesFileName = QFileInfo(packageFileName)
243 .completeBaseName() + QLatin1String(".changes");
244 const QString packageSourceDir = buildDir + QLatin1String("/../");
245 const QString packageSourceFilePath
246 = packageSourceDir + packageFileName;
247 const QString changesSourceFilePath
248 = packageSourceDir + changesFileName;
249 const QString changesTargetFilePath
250 = buildDir + QLatin1Char('/') + changesFileName;
251 QFile::remove(pkgFilePath);
252 QFile::remove(changesTargetFilePath);
253 if (!QFile::rename(packageSourceFilePath, pkgFilePath)
254 || !QFile::rename(changesSourceFilePath, changesTargetFilePath)) {
255 raiseError(tr("Packaging failed."),
256 tr("Could not move package files from %1 to %2.")
257 .arg(packageSourceDir, buildDir));
262 emit addOutput(tr("Package created."), BuildStep::MessageOutput);
263 deployStep()->deployables()->setUnmodified();
267 bool MaemoPackageCreationStep::runCommand(const QString &command)
269 emit addOutput(tr("Package Creation: Running command '%1'.").arg(command), BuildStep::MessageOutput);
272 perl = maddeRoot() + QLatin1String("/bin/perl.exe ");
274 m_buildProc->start(perl + maddeRoot() % QLatin1String("/madbin/") % command);
275 if (!m_buildProc->waitForStarted()) {
276 raiseError(tr("Packaging failed."),
277 tr("Packaging error: Could not start command '%1'. Reason: %2")
278 .arg(command).arg(m_buildProc->errorString()));
281 m_buildProc->write("\n"); // For dh_make
282 m_buildProc->waitForFinished(-1);
283 if (m_buildProc->error() != QProcess::UnknownError || m_buildProc->exitCode() != 0) {
284 QString mainMessage = tr("Packaging Error: Command '%1' failed.")
286 if (m_buildProc->error() != QProcess::UnknownError)
287 mainMessage += tr(" Reason: %1").arg(m_buildProc->errorString());
289 mainMessage += tr("Exit code: %1").arg(m_buildProc->exitCode());
290 raiseError(mainMessage);
296 void MaemoPackageCreationStep::handleBuildOutput()
298 const QByteArray &stdOut = m_buildProc->readAllStandardOutput();
299 if (!stdOut.isEmpty())
300 emit addOutput(QString::fromLocal8Bit(stdOut), BuildStep::NormalOutput);
301 const QByteArray &errorOut = m_buildProc->readAllStandardError();
302 if (!errorOut.isEmpty()) {
303 emit addOutput(QString::fromLocal8Bit(errorOut), BuildStep::ErrorOutput);
307 const Qt4BuildConfiguration *MaemoPackageCreationStep::qt4BuildConfiguration() const
309 return static_cast<Qt4BuildConfiguration *>(buildConfiguration());
312 QString MaemoPackageCreationStep::buildDirectory() const
314 return qt4BuildConfiguration()->buildDirectory();
317 QString MaemoPackageCreationStep::projectName() const
319 return qt4BuildConfiguration()->qt4Target()->qt4Project()
320 ->rootProjectNode()->displayName().toLower();
323 const MaemoToolChain *MaemoPackageCreationStep::maemoToolChain() const
325 return static_cast<MaemoToolChain *>(qt4BuildConfiguration()->toolChain());
328 MaemoDeployStep *MaemoPackageCreationStep::deployStep() const
330 MaemoDeployStep * const deployStep
331 = MaemoGlobal::buildStep<MaemoDeployStep>(target()->activeDeployConfiguration());
332 Q_ASSERT(deployStep &&
333 "Fatal error: Maemo build configuration without deploy step.");
337 QString MaemoPackageCreationStep::maddeRoot() const
339 return maemoToolChain()->maddeRoot();
342 QString MaemoPackageCreationStep::targetRoot() const
344 return maemoToolChain()->targetRoot();
347 bool MaemoPackageCreationStep::packagingNeeded() const
349 const MaemoDeployables * const deployables = deployStep()->deployables();
350 QFileInfo packageInfo(packageFilePath());
351 if (!packageInfo.exists() || deployables->isModified())
354 const int deployableCount = deployables->deployableCount();
355 for (int i = 0; i < deployableCount; ++i) {
356 if (packageInfo.lastModified()
357 <= QFileInfo(deployables->deployableAt(i).localFilePath)
365 QString MaemoPackageCreationStep::packageFilePath() const
367 return buildDirectory() % '/' % projectName()
368 % QLatin1Char('_') % versionString() % QLatin1String("_armel.deb");
371 QString MaemoPackageCreationStep::versionString() const
373 return m_versionString;
376 void MaemoPackageCreationStep::setVersionString(const QString &version)
378 m_versionString = version;
379 emit packageFilePathChanged();
382 QString MaemoPackageCreationStep::nativePath(const QFile &file) const
384 return QDir::toNativeSeparators(QFileInfo(file).filePath());
387 void MaemoPackageCreationStep::raiseError(const QString &shortMsg,
388 const QString &detailedMsg)
390 emit addOutput(detailedMsg.isNull() ? shortMsg : detailedMsg, BuildStep::ErrorOutput);
391 emit addTask(Task(Task::Error, shortMsg, QString(), -1,
392 TASK_CATEGORY_BUILDSYSTEM));
395 const QLatin1String MaemoPackageCreationStep::CreatePackageId("Qt4ProjectManager.MaemoPackageCreationStep");
397 } // namespace Internal
398 } // namespace Qt4ProjectManager