OSDN Git Service

Maemo: Adapt to new deploy configuration approach.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qt4projectmanager / qt-maemo / maemopackagecreationstep.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of Qt Creator.
8 **
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
14 ** this package.
15 **
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.
23 **
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.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "maemopackagecreationstep.h"
43
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"
51
52 #include <projectexplorer/buildsteplist.h>
53 #include <projectexplorer/projectexplorerconstants.h>
54 #include <qt4buildconfiguration.h>
55 #include <qt4project.h>
56 #include <qt4target.h>
57
58 #include <QtCore/QProcess>
59 #include <QtCore/QProcessEnvironment>
60 #include <QtCore/QStringBuilder>
61 #include <QtGui/QWidget>
62
63 namespace {
64     const QLatin1String PackagingEnabledKey("Packaging Enabled");
65     const QLatin1String DefaultVersionNumber("0.0.1");
66     const QLatin1String VersionNumberKey("Version Number");
67 }
68
69 using namespace ProjectExplorer::Constants;
70 using ProjectExplorer::BuildStepList;
71 using ProjectExplorer::BuildStepConfigWidget;
72 using ProjectExplorer::Task;
73
74 namespace Qt4ProjectManager {
75 namespace Internal {
76
77 MaemoPackageCreationStep::MaemoPackageCreationStep(BuildStepList *bsl)
78     : ProjectExplorer::BuildStep(bsl, CreatePackageId),
79       m_packagingEnabled(true),
80       m_versionString(DefaultVersionNumber)
81 {
82     ctor();
83 }
84
85 MaemoPackageCreationStep::MaemoPackageCreationStep(BuildStepList *bsl,
86     MaemoPackageCreationStep *other)
87     : BuildStep(bsl, other),
88       m_packagingEnabled(other->m_packagingEnabled),
89       m_versionString(other->m_versionString)
90 {
91     ctor();
92 }
93
94 MaemoPackageCreationStep::~MaemoPackageCreationStep()
95 {
96 }
97
98 void MaemoPackageCreationStep::ctor()
99 {
100     connect(buildConfiguration(), SIGNAL(buildDirectoryChanged()), this,
101         SIGNAL(packageFilePathChanged()));
102 }
103
104 bool MaemoPackageCreationStep::init()
105 {
106     return true;
107 }
108
109 QVariantMap MaemoPackageCreationStep::toMap() const
110 {
111     QVariantMap map(ProjectExplorer::BuildStep::toMap());
112     map.insert(PackagingEnabledKey, m_packagingEnabled);
113     map.insert(VersionNumberKey, m_versionString);
114     return map;
115 }
116
117 bool MaemoPackageCreationStep::fromMap(const QVariantMap &map)
118 {
119     m_packagingEnabled = map.value(PackagingEnabledKey, true).toBool();
120     m_versionString = map.value(VersionNumberKey, DefaultVersionNumber).toString();
121     return ProjectExplorer::BuildStep::fromMap(map);
122 }
123
124 void MaemoPackageCreationStep::run(QFutureInterface<bool> &fi)
125 {
126     fi.reportResult(m_packagingEnabled ? createPackage() : true);
127 }
128
129 BuildStepConfigWidget *MaemoPackageCreationStep::createConfigWidget()
130 {
131     return new MaemoPackageCreationWidget(this);
132 }
133
134 bool MaemoPackageCreationStep::createPackage()
135 {
136     if (!packagingNeeded())
137         return true;
138
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)));
144         return false;
145     }
146
147     QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
148     const QString &path = QDir::toNativeSeparators(maddeRoot() + QLatin1Char('/'));
149
150     const QLatin1String key("PATH");
151     QString colon = QLatin1String(":");
152 #ifdef Q_OS_WIN
153     colon = QLatin1String(";");
154     env.insert(key, path % QLatin1String("bin") % colon % env.value(key));
155 #endif
156
157     env.insert(key, targetRoot() % "/bin" % colon % env.value(key));
158     env.insert(key, path % QLatin1String("madbin") % colon % env.value(key));
159
160     QString perlLib = QDir::fromNativeSeparators(path % QLatin1String("madlib/perl5"));
161 #ifdef Q_OS_WIN
162     perlLib = perlLib.remove(QLatin1Char(':'));
163     perlLib = perlLib.prepend(QLatin1Char('/'));
164 #endif
165     env.insert(QLatin1String("PERL5LIB"), perlLib);
166
167     const QString buildDir = buildDirectory();
168     env.insert(QLatin1String("PWD"), buildDir);
169
170     const QRegExp envPattern(QLatin1String("([^=]+)=[\"']?([^;\"']+)[\"']? ;.*"));
171     QByteArray line;
172     do {
173         line = configFile.readLine(200);
174         if (envPattern.exactMatch(line))
175             env.insert(envPattern.cap(1), envPattern.cap(2));
176     } while (!line.isEmpty());
177     
178
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();
188
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();
193
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))
198             return false;
199
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)));
204             return false;
205         }
206
207         QByteArray rulesContents = rulesFile.readAll();
208         rulesContents.replace("DESTDIR", "INSTALL_ROOT");
209         rulesContents.replace("dh_shlibdeps", "# dh_shlibdeps");
210
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=.");
214
215         rulesFile.resize(0);
216         rulesFile.write(rulesContents);
217         if (rulesFile.error() != QFile::NoError) {
218             raiseError(tr("Packaging Error: Cannot write file '%1'.")
219                        .arg(nativePath(rulesFile)));
220             return false;
221         }
222     }
223
224     {
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(')'));
231             changeLog.resize(0);
232             changeLog.write(content.toUtf8());
233         }
234     }
235
236     if (!runCommand(QLatin1String("dpkg-buildpackage -nc -uc -us")))
237         return false;
238
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));
258             return false;
259         }
260     }
261
262     emit addOutput(tr("Package created."), BuildStep::MessageOutput);
263     deployStep()->deployables()->setUnmodified();
264     return true;
265 }
266
267 bool MaemoPackageCreationStep::runCommand(const QString &command)
268 {
269     emit addOutput(tr("Package Creation: Running command '%1'.").arg(command), BuildStep::MessageOutput);
270     QString perl;
271 #ifdef Q_OS_WIN
272     perl = maddeRoot() + QLatin1String("/bin/perl.exe ");
273 #endif
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()));
279         return false;
280     }
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.")
285             .arg(command);
286         if (m_buildProc->error() != QProcess::UnknownError)
287             mainMessage += tr(" Reason: %1").arg(m_buildProc->errorString());
288         else
289             mainMessage += tr("Exit code: %1").arg(m_buildProc->exitCode());
290         raiseError(mainMessage);
291         return false;
292     }
293     return true;
294 }
295
296 void MaemoPackageCreationStep::handleBuildOutput()
297 {
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);
304     }
305 }
306
307 const Qt4BuildConfiguration *MaemoPackageCreationStep::qt4BuildConfiguration() const
308 {
309     return static_cast<Qt4BuildConfiguration *>(buildConfiguration());
310 }
311
312 QString MaemoPackageCreationStep::buildDirectory() const
313 {
314     return qt4BuildConfiguration()->buildDirectory();
315 }
316
317 QString MaemoPackageCreationStep::projectName() const
318 {
319     return qt4BuildConfiguration()->qt4Target()->qt4Project()
320         ->rootProjectNode()->displayName().toLower();
321 }
322
323 const MaemoToolChain *MaemoPackageCreationStep::maemoToolChain() const
324 {
325     return static_cast<MaemoToolChain *>(qt4BuildConfiguration()->toolChain());
326 }
327
328 MaemoDeployStep *MaemoPackageCreationStep::deployStep() const
329 {
330     MaemoDeployStep * const deployStep
331         = MaemoGlobal::buildStep<MaemoDeployStep>(target()->activeDeployConfiguration());
332     Q_ASSERT(deployStep &&
333         "Fatal error: Maemo build configuration without deploy step.");
334     return deployStep;
335 }
336
337 QString MaemoPackageCreationStep::maddeRoot() const
338 {
339     return maemoToolChain()->maddeRoot();
340 }
341
342 QString MaemoPackageCreationStep::targetRoot() const
343 {
344     return maemoToolChain()->targetRoot();
345 }
346
347 bool MaemoPackageCreationStep::packagingNeeded() const
348 {
349     const MaemoDeployables * const deployables = deployStep()->deployables();
350     QFileInfo packageInfo(packageFilePath());
351     if (!packageInfo.exists() || deployables->isModified())
352         return true;
353
354     const int deployableCount = deployables->deployableCount();
355     for (int i = 0; i < deployableCount; ++i) {
356         if (packageInfo.lastModified()
357             <= QFileInfo(deployables->deployableAt(i).localFilePath)
358                .lastModified())
359             return true;
360     }
361
362     return false;
363 }
364
365 QString MaemoPackageCreationStep::packageFilePath() const
366 {
367     return buildDirectory() % '/' % projectName()
368         % QLatin1Char('_') % versionString() % QLatin1String("_armel.deb");
369 }
370
371 QString MaemoPackageCreationStep::versionString() const
372 {
373     return m_versionString;
374 }
375
376 void MaemoPackageCreationStep::setVersionString(const QString &version)
377 {
378     m_versionString = version;
379     emit packageFilePathChanged();
380 }
381
382 QString MaemoPackageCreationStep::nativePath(const QFile &file) const
383 {
384     return QDir::toNativeSeparators(QFileInfo(file).filePath());
385 }
386
387 void MaemoPackageCreationStep::raiseError(const QString &shortMsg,
388                                           const QString &detailedMsg)
389 {
390     emit addOutput(detailedMsg.isNull() ? shortMsg : detailedMsg, BuildStep::ErrorOutput);
391     emit addTask(Task(Task::Error, shortMsg, QString(), -1,
392                       TASK_CATEGORY_BUILDSYSTEM));
393 }
394
395 const QLatin1String MaemoPackageCreationStep::CreatePackageId("Qt4ProjectManager.MaemoPackageCreationStep");
396
397 } // namespace Internal
398 } // namespace Qt4ProjectManager