summaryrefslogtreecommitdiff
path: root/src/plugins/qmljstools/qmljsplugindumper.cpp
diff options
context:
space:
mode:
authorChristian Kamm <christian.d.kamm@nokia.com>2010-11-10 15:07:53 +0100
committerChristian Kamm <christian.d.kamm@nokia.com>2010-11-11 12:27:50 +0100
commit2b1fe086414a8bf1afc4968a74daae39c7c4d28f (patch)
treefeedbb306504982a15d446b815f0ce888b8bb331 /src/plugins/qmljstools/qmljsplugindumper.cpp
parentca4439bcefa71fda00c7baa4f3b2dfba1c87e7dc (diff)
downloadqt-creator-2b1fe086414a8bf1afc4968a74daae39c7c4d28f.tar.gz
QmlJS: Move plugin dumping code and redump on file change.
Task-number: QTCREATORBUG-3047 Reviewed-by: Erik Verbruggen
Diffstat (limited to 'src/plugins/qmljstools/qmljsplugindumper.cpp')
-rw-r--r--src/plugins/qmljstools/qmljsplugindumper.cpp295
1 files changed, 295 insertions, 0 deletions
diff --git a/src/plugins/qmljstools/qmljsplugindumper.cpp b/src/plugins/qmljstools/qmljsplugindumper.cpp
new file mode 100644
index 0000000000..a3da49c932
--- /dev/null
+++ b/src/plugins/qmljstools/qmljsplugindumper.cpp
@@ -0,0 +1,295 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "qmljsplugindumper.h"
+#include "qmljsmodelmanager.h"
+
+#include <qmljs/qmljsdocument.h>
+#include <qmljs/qmljsinterpreter.h>
+#include <projectexplorer/filewatcher.h>
+#include <projectexplorer/projectexplorer.h>
+#include <coreplugin/messagemanager.h>
+
+#include <QtCore/QDir>
+
+using namespace QmlJS;
+using namespace QmlJSTools;
+using namespace QmlJSTools::Internal;
+
+PluginDumper::PluginDumper(ModelManager *modelManager)
+ : QObject(modelManager)
+ , m_modelManager(modelManager)
+ , m_pluginWatcher(new ProjectExplorer::FileWatcher(this))
+{
+ connect(m_pluginWatcher, SIGNAL(fileChanged(QString)), SLOT(pluginChanged(QString)));
+}
+
+void PluginDumper::loadPluginTypes(const QString &libraryPath, const QString &importPath, const QString &importUri)
+{
+ // move to the owning thread
+ metaObject()->invokeMethod(this, "onLoadPluginTypes",
+ Q_ARG(QString, libraryPath),
+ Q_ARG(QString, importPath),
+ Q_ARG(QString, importUri));
+}
+
+void PluginDumper::onLoadPluginTypes(const QString &libraryPath, const QString &importPath, const QString &importUri)
+{
+ const QString canonicalLibraryPath = QDir::cleanPath(libraryPath);
+ if (m_runningQmldumps.values().contains(canonicalLibraryPath))
+ return;
+ const Snapshot snapshot = m_modelManager->snapshot();
+ if (snapshot.libraryInfo(canonicalLibraryPath).isDumped())
+ return;
+
+ // avoid inserting the same plugin twice
+ int index;
+ for (index = 0; index < m_plugins.size(); ++index) {
+ if (m_plugins.at(index).qmldirPath == libraryPath)
+ break;
+ }
+ if (index == m_plugins.size())
+ m_plugins.append(Plugin());
+
+ Plugin &plugin = m_plugins[index];
+ plugin.qmldirPath = canonicalLibraryPath;
+ plugin.importPath = importPath;
+ plugin.importUri = importUri;
+
+ foreach (const QmlDirParser::Plugin &plugin, snapshot.libraryInfo(canonicalLibraryPath).plugins()) {
+ const QString pluginLibrary = resolvePlugin(canonicalLibraryPath, plugin.path, plugin.name);
+ m_pluginWatcher->addFile(pluginLibrary);
+ m_libraryToPluginIndex.insert(pluginLibrary, index);
+ }
+
+ dump(plugin);
+}
+
+static QString qmldumpErrorMessage(const QString &libraryPath, const QString &error)
+{
+ return PluginDumper::tr("Type dump of QML plugin in %0 failed.\nErrors:\n%1\n").arg(libraryPath, error);
+}
+
+void PluginDumper::qmlPluginTypeDumpDone(int exitCode)
+{
+ QProcess *process = qobject_cast<QProcess *>(sender());
+ if (!process)
+ return;
+ process->deleteLater();
+
+ const QString libraryPath = m_runningQmldumps.take(process);
+ const Snapshot snapshot = m_modelManager->snapshot();
+ LibraryInfo libraryInfo = snapshot.libraryInfo(libraryPath);
+ libraryInfo.setDumped(true);
+
+ if (exitCode != 0) {
+ Core::MessageManager *messageManager = Core::MessageManager::instance();
+ messageManager->printToOutputPane(qmldumpErrorMessage(libraryPath, process->readAllStandardError()));
+ }
+
+ const QByteArray output = process->readAllStandardOutput();
+ QMap<QString, Interpreter::FakeMetaObject *> newObjects;
+ const QString error = Interpreter::CppQmlTypesLoader::parseQmlTypeXml(output, &newObjects);
+
+ if (exitCode == 0 && error.isEmpty()) {
+ // convert from QList<T *> to QList<const T *>
+ QList<const Interpreter::FakeMetaObject *> objectsList;
+ QMapIterator<QString, Interpreter::FakeMetaObject *> it(newObjects);
+ while (it.hasNext()) {
+ it.next();
+ objectsList.append(it.value());
+ }
+ libraryInfo.setMetaObjects(objectsList);
+ if (libraryPath.isEmpty())
+ Interpreter::CppQmlTypesLoader::builtinObjects.append(objectsList);
+ }
+
+ if (!libraryPath.isEmpty())
+ m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
+}
+
+void PluginDumper::qmlPluginTypeDumpError(QProcess::ProcessError)
+{
+ QProcess *process = qobject_cast<QProcess *>(sender());
+ if (!process)
+ return;
+ process->deleteLater();
+
+ const QString libraryPath = m_runningQmldumps.take(process);
+
+ Core::MessageManager *messageManager = Core::MessageManager::instance();
+ messageManager->printToOutputPane(qmldumpErrorMessage(libraryPath, process->readAllStandardError()));
+
+ if (!libraryPath.isEmpty()) {
+ const Snapshot snapshot = m_modelManager->snapshot();
+ LibraryInfo libraryInfo = snapshot.libraryInfo(libraryPath);
+ libraryInfo.setDumped(true);
+ m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
+ }
+}
+
+void PluginDumper::pluginChanged(const QString &pluginLibrary)
+{
+ const int pluginIndex = m_libraryToPluginIndex.value(pluginLibrary, -1);
+ if (pluginIndex == -1)
+ return;
+
+ const Plugin &plugin = m_plugins.at(pluginIndex);
+ dump(plugin);
+}
+
+void PluginDumper::dump(const Plugin &plugin)
+{
+ ProjectExplorer::Project *activeProject = ProjectExplorer::ProjectExplorerPlugin::instance()->startupProject();
+ if (!activeProject)
+ return;
+
+ ModelManagerInterface::ProjectInfo info = m_modelManager->projectInfo(activeProject);
+
+ if (info.qmlDumpPath.isEmpty())
+ return;
+
+ QProcess *process = new QProcess(this);
+ process->setEnvironment(info.qmlDumpEnvironment.toStringList());
+ connect(process, SIGNAL(finished(int)), SLOT(qmlPluginTypeDumpDone(int)));
+ connect(process, SIGNAL(error(QProcess::ProcessError)), SLOT(qmlPluginTypeDumpError(QProcess::ProcessError)));
+ QStringList args;
+ args << plugin.importPath;
+ args << plugin.importUri;
+ process->start(info.qmlDumpPath, args);
+ m_runningQmldumps.insert(process, plugin.qmldirPath);
+}
+
+/*!
+ Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
+ The \a prefix must contain the dot.
+
+ \a qmldirPath is the location of the qmldir file.
+
+ Adapted from QDeclarativeImportDatabase::resolvePlugin.
+*/
+QString PluginDumper::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
+ const QString &baseName, const QStringList &suffixes,
+ const QString &prefix)
+{
+ QStringList searchPaths;
+ searchPaths.append(QLatin1String("."));
+
+ bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
+ if (!qmldirPluginPathIsRelative)
+ searchPaths.prepend(qmldirPluginPath);
+
+ foreach (const QString &pluginPath, searchPaths) {
+
+ QString resolvedPath;
+
+ if (pluginPath == QLatin1String(".")) {
+ if (qmldirPluginPathIsRelative)
+ resolvedPath = qmldirPath.absoluteFilePath(qmldirPluginPath);
+ else
+ resolvedPath = qmldirPath.absolutePath();
+ } else {
+ resolvedPath = pluginPath;
+ }
+
+ QDir dir(resolvedPath);
+ foreach (const QString &suffix, suffixes) {
+ QString pluginFileName = prefix;
+
+ pluginFileName += baseName;
+ pluginFileName += suffix;
+
+ QFileInfo fileInfo(dir, pluginFileName);
+
+ if (fileInfo.exists())
+ return fileInfo.absoluteFilePath();
+ }
+ }
+
+ return QString();
+}
+
+/*!
+ Returns the result of the merge of \a baseName with \a dir and the platform suffix.
+
+ Adapted from QDeclarativeImportDatabase::resolvePlugin.
+
+ \table
+ \header \i Platform \i Valid suffixes
+ \row \i Windows \i \c .dll
+ \row \i Unix/Linux \i \c .so
+ \row \i AIX \i \c .a
+ \row \i HP-UX \i \c .sl, \c .so (HP-UXi)
+ \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so
+ \row \i Symbian \i \c .dll
+ \endtable
+
+ Version number on unix are ignored.
+*/
+QString PluginDumper::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
+ const QString &baseName)
+{
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+ return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
+ QStringList()
+ << QLatin1String("d.dll") // try a qmake-style debug build first
+ << QLatin1String(".dll"));
+#elif defined(Q_OS_DARWIN)
+ return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
+ QStringList()
+ << QLatin1String("_debug.dylib") // try a qmake-style debug build first
+ << QLatin1String(".dylib")
+ << QLatin1String(".so")
+ << QLatin1String(".bundle"),
+ QLatin1String("lib"));
+#else // Generic Unix
+ QStringList validSuffixList;
+
+# if defined(Q_OS_HPUX)
+/*
+ See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
+ "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
+ the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
+ */
+ validSuffixList << QLatin1String(".sl");
+# if defined __ia64
+ validSuffixList << QLatin1String(".so");
+# endif
+# elif defined(Q_OS_AIX)
+ validSuffixList << QLatin1String(".a") << QLatin1String(".so");
+# elif defined(Q_OS_UNIX)
+ validSuffixList << QLatin1String(".so");
+# endif
+
+ // Examples of valid library names:
+ // libfoo.so
+
+ return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
+#endif
+}