OSDN Git Service

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