OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qt4projectmanager / qmldumptool.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
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 #include "qmldumptool.h"
34 #include "qt4project.h"
35 #include "qt4projectmanagerconstants.h"
36 #include "qtversionmanager.h"
37 #include "debugginghelperbuildtask.h"
38 #include <coreplugin/icore.h>
39 #include <coreplugin/progressmanager/progressmanager.h>
40
41 #include <projectexplorer/project.h>
42 #include <projectexplorer/projectexplorer.h>
43 #include <projectexplorer/runconfiguration.h>
44 #include <qtconcurrent/runextensions.h>
45 #include <qmljs/qmljsmodelmanagerinterface.h>
46 #include <utils/qtcassert.h>
47 #include <QtGui/QDesktopServices>
48 #include <QtCore/QCoreApplication>
49 #include <QtCore/QDir>
50 #include <QtCore/QDebug>
51 #include <QtCore/QHash>
52
53 namespace {
54
55 using namespace Qt4ProjectManager;
56 using Qt4ProjectManager::Internal::DebuggingHelperBuildTask;
57
58
59 class QmlDumpBuildTask;
60
61 typedef QHash<int, QmlDumpBuildTask *> QmlDumpByVersion;
62 Q_GLOBAL_STATIC(QmlDumpByVersion, qmlDumpBuilds)
63
64 // A task suitable to be run by QtConcurrent to build qmldump.
65 class QmlDumpBuildTask : public QObject {
66     Q_DISABLE_COPY(QmlDumpBuildTask)
67     Q_OBJECT
68 public:
69     explicit QmlDumpBuildTask(QtVersion *version)
70         : m_buildTask(new DebuggingHelperBuildTask(version, DebuggingHelperBuildTask::QmlDump))
71         , m_failed(false)
72     {
73         qmlDumpBuilds()->insert(version->uniqueId(), this);
74
75         connect(m_buildTask, SIGNAL(finished(int,QString,DebuggingHelperBuildTask::Tools)),
76                 this, SLOT(finish(int,QString,DebuggingHelperBuildTask::Tools)),
77                 Qt::QueuedConnection);
78     }
79
80     void run(QFutureInterface<void> &future)
81     {
82         m_buildTask->run(future);
83     }
84
85     void updateProjectWhenDone(QPointer<ProjectExplorer::Project> project, bool preferDebug)
86     {
87         foreach (const ProjectToUpdate &update, m_projectsToUpdate) {
88             if (update.project == project)
89                 return;
90         }
91
92         ProjectToUpdate update;
93         update.project = project;
94         update.preferDebug = preferDebug;
95         m_projectsToUpdate += update;
96     }
97
98     bool hasFailed() const
99     {
100         return m_failed;
101     }
102
103 private slots:
104     void finish(int qtId, const QString &output, DebuggingHelperBuildTask::Tools tools)
105     {
106         QtVersion *version = QtVersionManager::instance()->version(qtId);
107
108         QTC_ASSERT(tools == DebuggingHelperBuildTask::QmlDump, return);
109         QString errorMessage;
110         if (!version) {
111             m_failed = true;
112             errorMessage = QString::fromLatin1("Qt version became invalid");
113         } else {
114             version->invalidateCache();
115
116             if (!version->hasQmlDump()) {
117                 m_failed = true;
118                 errorMessage = QString::fromLatin1("Could not build QML plugin dumping helper for %1\n"
119                                                    "Output:\n%2").
120                         arg(version->displayName(), output);
121             }
122         }
123
124         if (m_failed) {
125             qWarning("%s", qPrintable(errorMessage));
126         }
127
128         // update qmldump path for all the project
129         QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
130         if (!modelManager)
131             return;
132
133         foreach (const ProjectToUpdate &update, m_projectsToUpdate) {
134             if (!update.project)
135                 continue;
136             QmlJS::ModelManagerInterface::ProjectInfo projectInfo = modelManager->projectInfo(update.project);
137             projectInfo.qmlDumpPath = version->qmlDumpTool(update.preferDebug);
138             if (projectInfo.qmlDumpPath.isEmpty())
139                 projectInfo.qmlDumpPath = version->qmlDumpTool(!update.preferDebug);
140             projectInfo.qmlDumpEnvironment = version->qmlToolsEnvironment();
141             modelManager->updateProjectInfo(projectInfo);
142         }
143
144         // clean up
145         qmlDumpBuilds()->remove(qtId);
146         deleteLater();
147     }
148
149 private:
150     class ProjectToUpdate {
151     public:
152         QPointer<ProjectExplorer::Project> project;
153         bool preferDebug;
154     };
155
156     QList<ProjectToUpdate> m_projectsToUpdate;
157     Internal::DebuggingHelperBuildTask *m_buildTask; // deletes itself after run()
158     bool m_failed;
159 };
160 } // end of anonymous namespace
161
162
163 namespace Qt4ProjectManager {
164
165 static inline QStringList validBinaryFilenames(bool debugBuild)
166 {
167     QStringList list = QStringList()
168             << QLatin1String("qmldump.exe")
169             << QLatin1String("qmldump")
170             << QLatin1String("qmldump.app/Contents/MacOS/qmldump");
171     if (debugBuild)
172         list.prepend(QLatin1String("debug/qmldump.exe"));
173     else
174         list.prepend(QLatin1String("release/qmldump.exe"));
175     return list;
176 }
177
178 bool QmlDumpTool::canBuild(const QtVersion *qtVersion)
179 {
180     const QString installHeaders = qtVersion->versionInfo().value("QT_INSTALL_HEADERS");
181     const QString header = installHeaders + QLatin1String("/QtDeclarative/private/qdeclarativemetatype_p.h");
182     return (qtVersion->supportsTargetId(Constants::DESKTOP_TARGET_ID)
183             || (qtVersion->supportsTargetId(Constants::QT_SIMULATOR_TARGET_ID)
184                 && (qtVersion->qtVersion() > QtVersionNumber(4, 7, 1))))
185             && QFile::exists(header);
186 }
187
188 static QtVersion *qtVersionForProject(ProjectExplorer::Project *project)
189 {
190     if (project && project->id() == Qt4ProjectManager::Constants::QT4PROJECT_ID) {
191         Qt4Project *qt4Project = static_cast<Qt4Project*>(project);
192         if (qt4Project && qt4Project->activeTarget()
193                 && qt4Project->activeTarget()->activeBuildConfiguration()) {
194             QtVersion *version = qt4Project->activeTarget()->activeBuildConfiguration()->qtVersion();
195             if (version->isValid())
196                 return version;
197         }
198         return 0;
199     }
200
201     if (project && project->id() == QLatin1String("QmlProjectManager.QmlProject")) {
202         // We cannot access the QmlProject interfaces here, therefore use the metatype system
203         if (!project->activeTarget() || !project->activeTarget()->activeRunConfiguration())
204             return 0;
205         QVariant variant = project->activeTarget()->activeRunConfiguration()->property("qtVersionId");
206         QTC_ASSERT(variant.isValid() && variant.canConvert(QVariant::Int), return 0);
207         QtVersion *version = QtVersionManager::instance()->version(variant.toInt());
208         if (version && version->isValid())
209             return version;
210         return 0;
211     }
212
213     // else, find any desktop or simulator Qt version that has qmldump, or
214     // - if there isn't any - one that could build it
215     QtVersion *canBuildQmlDump = 0;
216     QtVersionManager *qtVersions = QtVersionManager::instance();
217     foreach (QtVersion *version, qtVersions->validVersions()) {
218         if (version->supportsTargetId(Constants::DESKTOP_TARGET_ID)
219                 || version->supportsTargetId(Constants::QT_SIMULATOR_TARGET_ID)) {
220             if (version->hasQmlDump())
221                 return version;
222
223             if (!canBuildQmlDump && QmlDumpTool::canBuild(version)) {
224                 canBuildQmlDump = version;
225             }
226         }
227     }
228
229     return canBuildQmlDump;
230 }
231
232 QString QmlDumpTool::toolForProject(ProjectExplorer::Project *project, bool debugDump)
233 {
234     QtVersion *version = qtVersionForProject(project);
235     if (version) {
236         QString qtInstallData = version->versionInfo().value("QT_INSTALL_DATA");
237         QString toolPath = toolByInstallData(qtInstallData, debugDump);
238         return toolPath;
239     }
240
241     return QString();
242 }
243
244 static QString sourcePath()
245 {
246     return Core::ICore::instance()->resourcePath() + QLatin1String("/qml/qmldump/");
247 }
248
249 static QStringList sourceFileNames()
250 {
251     QStringList files;
252     files << QLatin1String("main.cpp") << QLatin1String("qmldump.pro")
253           << QLatin1String("qmlstreamwriter.cpp") << QLatin1String("qmlstreamwriter.h")
254           << QLatin1String("LICENSE.LGPL") << QLatin1String("LGPL_EXCEPTION.TXT");
255 #ifdef Q_OS_MAC
256     files << QLatin1String("Info.plist");
257 #endif
258     return files;
259 }
260
261 QString QmlDumpTool::toolByInstallData(const QString &qtInstallData, bool debugDump)
262 {
263     if (!Core::ICore::instance())
264         return QString();
265
266     const QString mainFilename = Core::ICore::instance()->resourcePath()
267             + QLatin1String("/qml/qmldump/main.cpp");
268     const QStringList directories = installDirectories(qtInstallData);
269     const QStringList binFilenames = validBinaryFilenames(debugDump);
270
271     return byInstallDataHelper(sourcePath(), sourceFileNames(), directories, binFilenames);
272 }
273
274 QStringList QmlDumpTool::locationsByInstallData(const QString &qtInstallData, bool debugDump)
275 {
276     QStringList result;
277     QFileInfo fileInfo;
278     const QStringList binFilenames = validBinaryFilenames(debugDump);
279     foreach(const QString &directory, installDirectories(qtInstallData)) {
280         if (getHelperFileInfoFor(binFilenames, directory, &fileInfo))
281             result << fileInfo.filePath();
282     }
283     return result;
284 }
285
286 bool QmlDumpTool::build(const QString &directory, const QString &makeCommand,
287                         const QString &qmakeCommand, const QString &mkspec,
288                         const Utils::Environment &env, const QString &targetMode,
289                         const QStringList &qmakeArguments, QString *output, QString *errorMessage)
290 {
291     return buildHelper(QCoreApplication::translate("Qt4ProjectManager::QmlDumpTool", "qmldump"), QLatin1String("qmldump.pro"),
292                        directory, makeCommand, qmakeCommand, mkspec, env, targetMode,
293                        qmakeArguments, output, errorMessage);
294 }
295
296 QString QmlDumpTool::copy(const QString &qtInstallData, QString *errorMessage)
297 {
298     const QStringList directories = QmlDumpTool::installDirectories(qtInstallData);
299
300     // Try to find a writeable directory.
301     foreach(const QString &directory, directories) {
302         if (copyFiles(sourcePath(), sourceFileNames(), directory, errorMessage)) {
303             return directory;
304         }
305     }
306     *errorMessage = QCoreApplication::translate("ProjectExplorer::QmlDumpTool",
307                                                 "qmldump could not be built in any of the directories:\n- %1\n\nReason: %2")
308                     .arg(directories.join(QLatin1String("\n- ")), *errorMessage);
309     return QString();
310 }
311
312 QStringList QmlDumpTool::installDirectories(const QString &qtInstallData)
313 {
314     const QChar slash = QLatin1Char('/');
315     const uint hash = qHash(qtInstallData);
316     QStringList directories;
317     directories
318             << (qtInstallData + QLatin1String("/qtc-qmldump/"))
319             << QDir::cleanPath((QCoreApplication::applicationDirPath() + QLatin1String("/../qtc-qmldump/") + QString::number(hash))) + slash
320             << (QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QLatin1String("/qtc-qmldump/") + QString::number(hash)) + slash;
321     return directories;
322 }
323
324 void QmlDumpTool::pathAndEnvironment(ProjectExplorer::Project *project, bool preferDebug,
325                                      QString *dumperPath, Utils::Environment *env)
326 {
327     QString path;
328
329     QtVersion *version = qtVersionForProject(project);
330     if (version && !version->hasQmlDump() && QmlDumpTool::canBuild(version)) {
331         QmlDumpBuildTask *qmlDumpBuildTask = qmlDumpBuilds()->value(version->uniqueId());
332         if (qmlDumpBuildTask) {
333             if (!qmlDumpBuildTask->hasFailed())
334                 qmlDumpBuildTask->updateProjectWhenDone(project, preferDebug);
335         } else {
336             QmlDumpBuildTask *buildTask = new QmlDumpBuildTask(version);
337             buildTask->updateProjectWhenDone(project, preferDebug);
338             QFuture<void> task = QtConcurrent::run(&QmlDumpBuildTask::run, buildTask);
339             const QString taskName = QmlDumpBuildTask::tr("Building helper");
340             Core::ICore::instance()->progressManager()->addTask(task, taskName,
341                                                                 QLatin1String("Qt4ProjectManager::BuildHelpers"));
342         }
343         return;
344     }
345
346     path = Qt4ProjectManager::QmlDumpTool::toolForProject(project, preferDebug);
347     if (path.isEmpty())
348         path = Qt4ProjectManager::QmlDumpTool::toolForProject(project, !preferDebug);
349
350     if (!path.isEmpty()) {
351         QFileInfo qmldumpFileInfo(path);
352         if (!qmldumpFileInfo.exists()) {
353             qWarning() << "QmlDumpTool::qmlDumpPath: qmldump executable does not exist at" << path;
354             path.clear();
355         } else if (!qmldumpFileInfo.isFile()) {
356             qWarning() << "QmlDumpTool::qmlDumpPath: " << path << " is not a file";
357             path.clear();
358         }
359     }
360
361     if (!path.isEmpty() && version && dumperPath && env) {
362         *dumperPath = path;
363         *env = version->qmlToolsEnvironment();
364     }
365 }
366
367 } // namespace Qt4ProjectManager
368
369 #include "qmldumptool.moc"