forked from qt-creator/qt-creator
qmljs: Make the QMLJsPluginDumper asynchronous to avoid eventloop hangs
Change-Id: I3f6e6acaaf3781d86a0fa5fb100219f92b70f0b5 Fixes: QTCREATORBUG-20243 Task-number: QTCREATORBUG-18533 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Fawzi Mohamed <fawzi.mohamed@qt.io>
This commit is contained in:
@@ -34,6 +34,7 @@
|
|||||||
#include <utils/filesystemwatcher.h>
|
#include <utils/filesystemwatcher.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
|
#include <utils/runextensions.h>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
@@ -283,36 +284,61 @@ void PluginDumper::qmlPluginTypeDumpDone(int exitCode)
|
|||||||
if (!privatePlugin)
|
if (!privatePlugin)
|
||||||
ModelManagerInterface::writeWarning(qmldumpErrorMessage(libraryPath, errorMessages));
|
ModelManagerInterface::writeWarning(qmldumpErrorMessage(libraryPath, errorMessages));
|
||||||
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, qmldumpFailedMessage(libraryPath, errorMessages));
|
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, qmldumpFailedMessage(libraryPath, errorMessages));
|
||||||
}
|
|
||||||
|
|
||||||
const QByteArray output = process->readAllStandardOutput();
|
const QByteArray output = process->readAllStandardOutput();
|
||||||
|
|
||||||
|
class CppQmlTypesInfo {
|
||||||
|
public:
|
||||||
QString error;
|
QString error;
|
||||||
QString warning;
|
QString warning;
|
||||||
CppQmlTypesLoader::BuiltinObjects objectsList;
|
CppQmlTypesLoader::BuiltinObjects objectsList;
|
||||||
QList<ModuleApiInfo> moduleApis;
|
QList<ModuleApiInfo> moduleApis;
|
||||||
QStringList dependencies;
|
QStringList dependencies;
|
||||||
CppQmlTypesLoader::parseQmlTypeDescriptions(output, &objectsList, &moduleApis, &dependencies,
|
};
|
||||||
&error, &warning,
|
|
||||||
QLatin1String("<dump of ") + libraryPath + QLatin1Char('>'));
|
auto watcher = QSharedPointer<QFutureWatcher<CppQmlTypesInfo>>(new QFutureWatcher<CppQmlTypesInfo>());
|
||||||
if (exitCode == 0) {
|
|
||||||
if (!error.isEmpty()) {
|
connect(watcher.data(), &QFutureWatcher<CppQmlTypesInfo>::finished, this,
|
||||||
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError,
|
[this, watcher, libraryInfo, privatePlugin, libraryPath] {
|
||||||
qmldumpErrorMessage(libraryPath, error));
|
CppQmlTypesInfo infos = watcher->result();
|
||||||
|
|
||||||
|
LibraryInfo libInfo = libraryInfo;
|
||||||
|
|
||||||
|
if (!infos.error.isEmpty()) {
|
||||||
|
libInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError,
|
||||||
|
qmldumpErrorMessage(libraryPath, infos.error));
|
||||||
if (!privatePlugin)
|
if (!privatePlugin)
|
||||||
printParseWarnings(libraryPath, libraryInfo.pluginTypeInfoError());
|
printParseWarnings(libraryPath, libInfo.pluginTypeInfoError());
|
||||||
|
} else {
|
||||||
|
libInfo.setMetaObjects(infos.objectsList.values());
|
||||||
|
libInfo.setModuleApis(infos.moduleApis);
|
||||||
|
libInfo.setPluginTypeInfoStatus(LibraryInfo::DumpDone);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!infos.warning.isEmpty())
|
||||||
|
printParseWarnings(libraryPath, infos.warning);
|
||||||
|
|
||||||
|
libInfo.updateFingerprint();
|
||||||
|
|
||||||
|
m_modelManager->updateLibraryInfo(libraryPath, libInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto future = Utils::runAsync([output, libraryPath](QFutureInterface<CppQmlTypesInfo> &future)
|
||||||
|
{
|
||||||
|
CppQmlTypesInfo infos;
|
||||||
|
CppQmlTypesLoader::parseQmlTypeDescriptions(output, &infos.objectsList, &infos.moduleApis, &infos.dependencies,
|
||||||
|
&infos.error, &infos.warning,
|
||||||
|
QLatin1String("<dump of ") + libraryPath + QLatin1Char('>'));
|
||||||
|
future.reportFinished(&infos);
|
||||||
|
});
|
||||||
|
|
||||||
|
watcher->setFuture(future);
|
||||||
} else {
|
} else {
|
||||||
libraryInfo.setMetaObjects(objectsList.values());
|
|
||||||
libraryInfo.setModuleApis(moduleApis);
|
|
||||||
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpDone);
|
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpDone);
|
||||||
}
|
|
||||||
|
|
||||||
if (!warning.isEmpty())
|
|
||||||
printParseWarnings(libraryPath, warning);
|
|
||||||
}
|
|
||||||
libraryInfo.updateFingerprint();
|
libraryInfo.updateFingerprint();
|
||||||
|
|
||||||
m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
|
m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PluginDumper::qmlPluginTypeDumpError(QProcess::ProcessError)
|
void PluginDumper::qmlPluginTypeDumpError(QProcess::ProcessError)
|
||||||
{
|
{
|
||||||
@@ -344,16 +370,15 @@ void PluginDumper::pluginChanged(const QString &pluginLibrary)
|
|||||||
dump(plugin);
|
dump(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginDumper::loadQmlTypeDescription(const QStringList &paths,
|
QFuture<PluginDumper::QmlTypeDescription> PluginDumper::loadQmlTypeDescription(const QStringList &paths) const {
|
||||||
QStringList &errors,
|
auto future = Utils::runAsync([=](QFutureInterface<PluginDumper::QmlTypeDescription> &future)
|
||||||
QStringList &warnings,
|
{
|
||||||
QList<FakeMetaObject::ConstPtr> &objects,
|
PluginDumper::QmlTypeDescription result;
|
||||||
QList<ModuleApiInfo> *moduleApi,
|
|
||||||
QStringList *dependencies) const {
|
|
||||||
for (const QString &p: paths) {
|
for (const QString &p: paths) {
|
||||||
Utils::FileReader reader;
|
Utils::FileReader reader;
|
||||||
if (!reader.fetch(p, QFile::Text)) {
|
if (!reader.fetch(p, QFile::Text)) {
|
||||||
errors += reader.errorString();
|
result.errors += reader.errorString();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QString error;
|
QString error;
|
||||||
@@ -364,18 +389,23 @@ void PluginDumper::loadQmlTypeDescription(const QStringList &paths,
|
|||||||
CppQmlTypesLoader::parseQmlTypeDescriptions(reader.data(), &objs, &apis, &deps,
|
CppQmlTypesLoader::parseQmlTypeDescriptions(reader.data(), &objs, &apis, &deps,
|
||||||
&error, &warning, p);
|
&error, &warning, p);
|
||||||
if (!error.isEmpty()) {
|
if (!error.isEmpty()) {
|
||||||
errors += tr("Failed to parse \"%1\".\nError: %2").arg(p, error);
|
result.errors += tr("Failed to parse \"%1\".\nError: %2").arg(p, error);
|
||||||
} else {
|
} else {
|
||||||
objects += objs.values();
|
result.objects += objs.values();
|
||||||
if (moduleApi)
|
result.moduleApis += apis;
|
||||||
*moduleApi += apis;
|
|
||||||
if (!deps.isEmpty())
|
if (!deps.isEmpty())
|
||||||
*dependencies += deps;
|
result.dependencies += deps;
|
||||||
}
|
}
|
||||||
if (!warning.isEmpty())
|
if (!warning.isEmpty())
|
||||||
warnings += warning;
|
result.warnings += warning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
future.reportFinished(&result);
|
||||||
|
});
|
||||||
|
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Build the path of an existing qmltypes file from a module name.
|
* \brief Build the path of an existing qmltypes file from a module name.
|
||||||
* \param name
|
* \param name
|
||||||
@@ -421,16 +451,14 @@ QString PluginDumper::buildQmltypesPath(const QString &name) const
|
|||||||
* Recursively load type descriptions of dependencies, collecting results
|
* Recursively load type descriptions of dependencies, collecting results
|
||||||
* in \a objects.
|
* in \a objects.
|
||||||
*/
|
*/
|
||||||
void PluginDumper::loadDependencies(const QStringList &dependencies,
|
QFuture<PluginDumper::DependencyInfo> PluginDumper::loadDependencies(const QStringList &dependencies,
|
||||||
QStringList &errors,
|
QSharedPointer<QSet<QString>> visited) const
|
||||||
QStringList &warnings,
|
|
||||||
QList<FakeMetaObject::ConstPtr> &objects,
|
|
||||||
QSet<QString> *visited) const
|
|
||||||
{
|
{
|
||||||
if (dependencies.isEmpty())
|
auto iface = QSharedPointer<QFutureInterface<PluginDumper::DependencyInfo>>(new QFutureInterface<PluginDumper::DependencyInfo>);
|
||||||
return;
|
|
||||||
|
|
||||||
QScopedPointer<QSet<QString>> visitedPtr(visited ? visited : new QSet<QString>());
|
if (visited.isNull()) {
|
||||||
|
visited = QSharedPointer<QSet<QString>>(new QSet<QString>());
|
||||||
|
}
|
||||||
|
|
||||||
QStringList dependenciesPaths;
|
QStringList dependenciesPaths;
|
||||||
QString path;
|
QString path;
|
||||||
@@ -438,44 +466,84 @@ void PluginDumper::loadDependencies(const QStringList &dependencies,
|
|||||||
path = buildQmltypesPath(name);
|
path = buildQmltypesPath(name);
|
||||||
if (!path.isNull())
|
if (!path.isNull())
|
||||||
dependenciesPaths << path;
|
dependenciesPaths << path;
|
||||||
visitedPtr->insert(name);
|
visited->insert(name);
|
||||||
}
|
}
|
||||||
QStringList newDependencies;
|
|
||||||
loadQmlTypeDescription(dependenciesPaths, errors, warnings, objects, nullptr, &newDependencies);
|
auto typesWatcher = QSharedPointer<QFutureWatcher<PluginDumper::QmlTypeDescription>>(new QFutureWatcher<PluginDumper::QmlTypeDescription>());
|
||||||
newDependencies = Utils::toList(Utils::toSet(newDependencies) - *visitedPtr);
|
connect(typesWatcher.data(), &QFutureWatcher<PluginDumper::QmlTypeDescription>::finished, this, [this, iface, visited, typesWatcher] {
|
||||||
if (!newDependencies.isEmpty())
|
QStringList newDependencies = typesWatcher->result().dependencies;
|
||||||
loadDependencies(newDependencies, errors, warnings, objects, visitedPtr.take());
|
newDependencies = Utils::toList(Utils::toSet(newDependencies) - *visited.data());
|
||||||
|
|
||||||
|
if (!newDependencies.isEmpty()) {
|
||||||
|
auto loadWatcher = QSharedPointer<QFutureWatcher<PluginDumper::DependencyInfo>>(new QFutureWatcher<PluginDumper::DependencyInfo>());
|
||||||
|
connect(loadWatcher.data(), &QFutureWatcher<PluginDumper::DependencyInfo>::finished, this, [iface, newDependencies, visited, typesWatcher, loadWatcher] {
|
||||||
|
PluginDumper::DependencyInfo result = loadWatcher->result();
|
||||||
|
|
||||||
|
result.errors += typesWatcher->result().errors;
|
||||||
|
result.objects += typesWatcher->result().objects;
|
||||||
|
result.warnings+= typesWatcher->result().warnings;
|
||||||
|
|
||||||
|
iface->reportFinished(&result);
|
||||||
|
});
|
||||||
|
|
||||||
|
loadWatcher->setFuture(loadDependencies(newDependencies, visited));
|
||||||
|
} else {
|
||||||
|
PluginDumper::DependencyInfo result;
|
||||||
|
result.errors += typesWatcher->result().errors;
|
||||||
|
result.objects += typesWatcher->result().objects;
|
||||||
|
result.warnings+= typesWatcher->result().warnings;
|
||||||
|
iface->reportFinished(&result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
typesWatcher->setFuture(loadQmlTypeDescription(dependenciesPaths));
|
||||||
|
|
||||||
|
return iface->future();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginDumper::loadQmltypesFile(const QStringList &qmltypesFilePaths,
|
void PluginDumper::loadQmltypesFile(const QStringList &qmltypesFilePaths,
|
||||||
const QString &libraryPath,
|
const QString &libraryPath,
|
||||||
QmlJS::LibraryInfo libraryInfo)
|
QmlJS::LibraryInfo libraryInfo)
|
||||||
{
|
{
|
||||||
QStringList errors;
|
auto typesWatcher = QSharedPointer<QFutureWatcher<PluginDumper::QmlTypeDescription>>(new QFutureWatcher<PluginDumper::QmlTypeDescription>());
|
||||||
QStringList warnings;
|
connect(typesWatcher.data(), &QFutureWatcher<PluginDumper::QmlTypeDescription>::finished, this, [this, typesWatcher, libraryPath, libraryInfo] {
|
||||||
QList<FakeMetaObject::ConstPtr> objects;
|
|
||||||
QList<ModuleApiInfo> moduleApis;
|
|
||||||
QStringList dependencies;
|
|
||||||
|
|
||||||
loadQmlTypeDescription(qmltypesFilePaths, errors, warnings, objects, &moduleApis, &dependencies);
|
auto loadWatcher = QSharedPointer<QFutureWatcher<PluginDumper::DependencyInfo>>(new QFutureWatcher<PluginDumper::DependencyInfo>());
|
||||||
loadDependencies(dependencies, errors, warnings, objects);
|
connect(loadWatcher.data(), &QFutureWatcher<PluginDumper::DependencyInfo>::finished, this, [this, typesWatcher, loadWatcher, libraryPath, libraryInfo] {
|
||||||
|
|
||||||
|
QStringList deps = typesWatcher->result().dependencies;
|
||||||
|
QStringList errors = typesWatcher->result().errors;
|
||||||
|
QStringList warnings = typesWatcher->result().errors;
|
||||||
|
QList<FakeMetaObject::ConstPtr> objects = typesWatcher->result().objects;
|
||||||
|
|
||||||
|
errors += loadWatcher->result().errors;
|
||||||
|
warnings += loadWatcher->result().warnings;
|
||||||
|
objects += loadWatcher->result().objects;
|
||||||
|
|
||||||
|
QmlJS::LibraryInfo libInfo = libraryInfo;
|
||||||
|
|
||||||
|
libInfo.setMetaObjects(objects);
|
||||||
|
libInfo.setModuleApis(typesWatcher->result().moduleApis);
|
||||||
|
libInfo.setDependencies(typesWatcher->result().dependencies);
|
||||||
|
|
||||||
libraryInfo.setMetaObjects(objects);
|
|
||||||
libraryInfo.setModuleApis(moduleApis);
|
|
||||||
libraryInfo.setDependencies(dependencies);
|
|
||||||
if (errors.isEmpty()) {
|
if (errors.isEmpty()) {
|
||||||
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileDone);
|
libInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileDone);
|
||||||
} else {
|
} else {
|
||||||
printParseWarnings(libraryPath, errors.join(QLatin1Char('\n')));
|
printParseWarnings(libraryPath, errors.join(QLatin1Char('\n')));
|
||||||
errors.prepend(tr("Errors while reading typeinfo files:"));
|
errors.prepend(tr("Errors while reading typeinfo files:"));
|
||||||
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileError, errors.join(QLatin1Char('\n')));
|
libInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileError, errors.join(QLatin1Char('\n')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!warnings.isEmpty())
|
if (!warnings.isEmpty())
|
||||||
printParseWarnings(libraryPath, warnings.join(QLatin1String("\n")));
|
printParseWarnings(libraryPath, warnings.join(QLatin1String("\n")));
|
||||||
|
|
||||||
libraryInfo.updateFingerprint();
|
libInfo.updateFingerprint();
|
||||||
m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
|
m_modelManager->updateLibraryInfo(libraryPath, libInfo);
|
||||||
|
});
|
||||||
|
if (!typesWatcher->result().dependencies.isEmpty()) {
|
||||||
|
loadWatcher->setFuture(loadDependencies(typesWatcher->result().dependencies, QSharedPointer<QSet<QString>>()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
typesWatcher->setFuture(loadQmlTypeDescription(qmltypesFilePaths));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginDumper::runQmlDump(const QmlJS::ModelManagerInterface::ProjectInfo &info,
|
void PluginDumper::runQmlDump(const QmlJS::ModelManagerInterface::ProjectInfo &info,
|
||||||
|
@@ -71,18 +71,30 @@ private:
|
|||||||
QStringList typeInfoPaths;
|
QStringList typeInfoPaths;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class QmlTypeDescription {
|
||||||
|
public:
|
||||||
|
QStringList errors;
|
||||||
|
QStringList warnings;
|
||||||
|
QList<LanguageUtils::FakeMetaObject::ConstPtr> objects;
|
||||||
|
QList<ModuleApiInfo> moduleApis;
|
||||||
|
QStringList dependencies;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DependencyInfo {
|
||||||
|
public:
|
||||||
|
QStringList errors;
|
||||||
|
QStringList warnings;
|
||||||
|
QList<LanguageUtils::FakeMetaObject::ConstPtr> objects;
|
||||||
|
};
|
||||||
|
|
||||||
void runQmlDump(const QmlJS::ModelManagerInterface::ProjectInfo &info, const QStringList &arguments, const QString &importPath);
|
void runQmlDump(const QmlJS::ModelManagerInterface::ProjectInfo &info, const QStringList &arguments, const QString &importPath);
|
||||||
void dump(const Plugin &plugin);
|
void dump(const Plugin &plugin);
|
||||||
void loadQmlTypeDescription(const QStringList &path, QStringList &errors, QStringList &warnings,
|
QFuture<QmlTypeDescription> loadQmlTypeDescription(const QStringList &path) const;
|
||||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> &objects,
|
|
||||||
QList<ModuleApiInfo> *moduleApi,
|
|
||||||
QStringList *dependencies) const;
|
|
||||||
QString buildQmltypesPath(const QString &name) const;
|
QString buildQmltypesPath(const QString &name) const;
|
||||||
void loadDependencies(const QStringList &dependencies,
|
|
||||||
QStringList &errors,
|
QFuture<PluginDumper::DependencyInfo> loadDependencies(const QStringList &dependencies,
|
||||||
QStringList &warnings,
|
QSharedPointer<QSet<QString>> visited) const;
|
||||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> &objects,
|
|
||||||
QSet<QString> *visited = nullptr) const;
|
|
||||||
void loadQmltypesFile(const QStringList &qmltypesFilePaths,
|
void loadQmltypesFile(const QStringList &qmltypesFilePaths,
|
||||||
const QString &libraryPath,
|
const QString &libraryPath,
|
||||||
QmlJS::LibraryInfo libraryInfo);
|
QmlJS::LibraryInfo libraryInfo);
|
||||||
|
Reference in New Issue
Block a user