diff options
author | Christian Kamm <christian.d.kamm@nokia.com> | 2010-11-10 15:07:53 +0100 |
---|---|---|
committer | Christian Kamm <christian.d.kamm@nokia.com> | 2010-11-11 12:27:50 +0100 |
commit | 2b1fe086414a8bf1afc4968a74daae39c7c4d28f (patch) | |
tree | feedbb306504982a15d446b815f0ce888b8bb331 /src/plugins/qmljstools/qmljsplugindumper.cpp | |
parent | ca4439bcefa71fda00c7baa4f3b2dfba1c87e7dc (diff) | |
download | qt-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.cpp | 295 |
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 +} |