QmlJS: Scan app.qmltypes and lib.qmltypes for type information

app.qmltypes is being added as a new convention to Qt 5.15. These files
are to be found next to application binaries, and contain the QML types
those application register themselves, in contrast to QML types
registered from plugins loaded via the regular import mechanism.

lib.qmltypes works the same way, in principle, for libraries. This
change only adds it to the possible names for qmltypes files, though.

Change-Id: I1d7c5835c8c3e988d214c5cdb949ca677b48dfc5
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Ulf Hermann
2019-10-22 16:29:40 +02:00
parent 7a4cf827d9
commit 1e587831d1
8 changed files with 109 additions and 17 deletions

View File

@@ -370,6 +370,13 @@ LibraryInfo::LibraryInfo(Status status)
updateFingerprint();
}
LibraryInfo::LibraryInfo(const QmlDirParser::TypeInfo &typeInfo)
: _status(Found)
{
_typeinfos.append(typeInfo);
updateFingerprint();
}
LibraryInfo::LibraryInfo(const QmlDirParser &parser, const QByteArray &fingerprint)
: _status(Found)
, _components(parser.components().values())

View File

@@ -166,6 +166,7 @@ private:
public:
LibraryInfo();
explicit LibraryInfo(Status status);
explicit LibraryInfo(const QmlDirParser::TypeInfo &typeInfo);
explicit LibraryInfo(const QmlDirParser &parser, const QByteArray &fingerprint = QByteArray());
~LibraryInfo() = default;
LibraryInfo(const LibraryInfo &other) = default;

View File

@@ -111,6 +111,7 @@ private:
Snapshot m_snapshot;
ValueOwner *m_valueOwner = nullptr;
QStringList m_importPaths;
QStringList m_applicationDirectories;
LibraryInfo m_builtins;
ViewerContext m_vContext;
@@ -140,6 +141,7 @@ Link::Link(const Snapshot &snapshot, const ViewerContext &vContext, const Librar
d->m_valueOwner = new ValueOwner;
d->m_snapshot = snapshot;
d->m_importPaths = vContext.paths;
d->m_applicationDirectories = vContext.applicationDirectories;
d->m_builtins = builtins;
d->m_vContext = vContext;
@@ -381,6 +383,14 @@ Import LinkPrivate::importNonFile(const Document::Ptr &doc, const ImportInfo &im
QString libraryPath = modulePath(packageName, version.toString(), m_importPaths);
bool importFound = !libraryPath.isEmpty() && importLibrary(doc, libraryPath, &import);
if (!importFound) {
for (const QString &dir : qAsConst(m_applicationDirectories)) {
// This adds the types to the C++ types, to be found below if applicable.
if (QFile::exists(dir + "/app.qmltypes"))
importLibrary(doc, dir, &import);
}
}
// if there are cpp-based types for this package, use them too
if (m_valueOwner->cppQmlTypes().hasModule(packageName)) {
importFound = true;

View File

@@ -605,9 +605,11 @@ ModelManagerInterface::ProjectInfo ModelManagerInterface::projectInfoForPath(
for (const ProjectInfo &pInfo : allProjectInfos) {
if (res.qtQmlPath.isEmpty())
res.qtQmlPath = pInfo.qtQmlPath;
res.applicationDirectories.append(pInfo.applicationDirectories);
for (const auto &importPath : pInfo.importPaths)
res.importPaths.maybeInsert(importPath);
}
res.applicationDirectories = Utils::filteredUnique(res.applicationDirectories);
return res;
}
@@ -745,6 +747,55 @@ static void findNewFileImports(const Document::Ptr &doc, const Snapshot &snapsho
}
}
enum class LibraryStatus {
Accepted,
Rejected,
Unknown
};
static LibraryStatus libraryStatus(const QString &path, const Snapshot &snapshot,
QSet<QString> *newLibraries)
{
if (path.isEmpty())
return LibraryStatus::Rejected;
// if we know there is a library, done
const LibraryInfo &existingInfo = snapshot.libraryInfo(path);
if (existingInfo.isValid())
return LibraryStatus::Accepted;
if (newLibraries->contains(path))
return LibraryStatus::Accepted;
// if we looked at the path before, done
return existingInfo.wasScanned()
? LibraryStatus::Rejected
: LibraryStatus::Unknown;
}
static bool findNewQmlApplicationInPath(const QString &path,
const Snapshot &snapshot,
ModelManagerInterface *modelManager,
QSet<QString> *newLibraries)
{
switch (libraryStatus(path, snapshot, newLibraries)) {
case LibraryStatus::Accepted: return true;
case LibraryStatus::Rejected: return false;
default: break;
}
const QDir dir(path);
const QLatin1String appQmltypes("app.qmltypes");
QFile appQmltypesFile(dir.filePath(appQmltypes));
if (!appQmltypesFile.exists())
return false;
LibraryInfo libraryInfo = LibraryInfo(QmlDirParser::TypeInfo(appQmltypes));
const QString libraryPath = dir.absolutePath();
newLibraries->insert(libraryPath);
modelManager->updateLibraryInfo(path, libraryInfo);
modelManager->loadPluginTypes(QFileInfo(libraryPath).canonicalFilePath(), libraryPath,
QString(), QString());
return true;
}
static bool findNewQmlLibraryInPath(const QString &path,
const Snapshot &snapshot,
ModelManagerInterface *modelManager,
@@ -753,15 +804,11 @@ static bool findNewQmlLibraryInPath(const QString &path,
QSet<QString> *newLibraries,
bool ignoreMissing)
{
// if we know there is a library, done
const LibraryInfo &existingInfo = snapshot.libraryInfo(path);
if (existingInfo.isValid())
return true;
if (newLibraries->contains(path))
return true;
// if we looked at the path before, done
if (existingInfo.wasScanned())
return false;
switch (libraryStatus(path, snapshot, newLibraries)) {
case LibraryStatus::Accepted: return true;
case LibraryStatus::Rejected: return false;
default: break;
}
const QDir dir(path);
QFile qmldirFile(dir.filePath(QLatin1String("qmldir")));
@@ -1097,6 +1144,7 @@ void ModelManagerInterface::updateImportPaths()
if (m_indexerDisabled)
return;
PathsAndLanguages allImportPaths;
QStringList allApplicationDirectories;
QmlLanguageBundles activeBundles;
QmlLanguageBundles extendedBundles;
for (const ProjectInfo &pInfo : qAsConst(m_projects)) {
@@ -1107,11 +1155,13 @@ void ModelManagerInterface::updateImportPaths()
importPath.language());
}
}
allApplicationDirectories.append(pInfo.applicationDirectories);
}
for (const ViewerContext &vContext : qAsConst(m_defaultVContexts)) {
for (const QString &path : vContext.paths)
allImportPaths.maybeInsert(Utils::FilePath::fromString(path), vContext.language);
allApplicationDirectories.append(vContext.applicationDirectories);
}
for (const ProjectInfo &pInfo : qAsConst(m_projects)) {
@@ -1143,6 +1193,7 @@ void ModelManagerInterface::updateImportPaths()
for (const QString &path : qAsConst(m_defaultImportPaths))
allImportPaths.maybeInsert(Utils::FilePath::fromString(path), Dialect::Qml);
allImportPaths.compact();
allApplicationDirectories = Utils::filteredUnique(allApplicationDirectories);
{
QMutexLocker l(&m_mutex);
@@ -1159,6 +1210,8 @@ void ModelManagerInterface::updateImportPaths()
QSet<QString> newLibraries;
for (const Document::Ptr &doc : qAsConst(snapshot))
findNewLibraryImports(doc, snapshot, this, &importedFiles, &scannedPaths, &newLibraries);
for (const QString &path : allApplicationDirectories)
findNewQmlApplicationInPath(path, snapshot, this, &newLibraries);
updateSourceFiles(importedFiles, true);
@@ -1366,6 +1419,8 @@ ViewerContext ModelManagerInterface::completeVContext(const ViewerContext &vCtx,
ProjectInfo defaultInfo = defaultProjectInfo();
if (info.qtQmlPath.isEmpty())
info.qtQmlPath = defaultInfo.qtQmlPath;
info.applicationDirectories = Utils::filteredUnique(info.applicationDirectories
+ defaultInfo.applicationDirectories);
switch (res.flags) {
case ViewerContext::Complete:
break;
@@ -1433,6 +1488,7 @@ ViewerContext ModelManagerInterface::completeVContext(const ViewerContext &vCtx,
break;
}
res.flags = ViewerContext::Complete;
res.applicationDirectories = info.applicationDirectories;
return res;
}

View File

@@ -71,6 +71,7 @@ public:
QStringList activeResourceFiles;
QStringList allResourceFiles;
QHash<QString, QString> resourceFileContents;
QStringList applicationDirectories;
// whether trying to run qmldump makes sense
bool tryQmlDump = false;

View File

@@ -41,6 +41,12 @@
using namespace LanguageUtils;
using namespace QmlJS;
static const QStringList qmltypesFileNames = {
QLatin1String("plugins.qmltypes"),
QLatin1String("app.qmltypes"),
QLatin1String("lib.qmltypes")
};
PluginDumper::PluginDumper(ModelManagerInterface *modelManager)
: QObject(modelManager)
, m_modelManager(modelManager)
@@ -146,10 +152,11 @@ void PluginDumper::onLoadPluginTypes(const QString &libraryPath, const QString &
plugin.importVersion = importVersion;
// add default qmltypes file if it exists
const QLatin1String defaultQmltypesFileName("plugins.qmltypes");
const QString defaultQmltypesPath = makeAbsolute(defaultQmltypesFileName, canonicalLibraryPath);
for (const QString &qmltypesFileName : qmltypesFileNames) {
const QString defaultQmltypesPath = makeAbsolute(qmltypesFileName, canonicalLibraryPath);
if (!plugin.typeInfoPaths.contains(defaultQmltypesPath) && QFile::exists(defaultQmltypesPath))
plugin.typeInfoPaths += defaultQmltypesPath;
}
// add typeinfo files listed in qmldir
foreach (const QmlDirParser::TypeInfo &typeInfo, libraryInfo.typeInfos()) {
@@ -398,10 +405,11 @@ QString PluginDumper::buildQmltypesPath(const QString &name) const
if (path.isEmpty())
return QString();
const QString filename = path + QLatin1String("/plugins.qmltypes");
for (const QString &qmltypesFileName : qmltypesFileNames) {
const QString filename = path + QLatin1Char('/') + qmltypesFileName;
if (QFile::exists(filename))
return filename;
}
return QString();
}

View File

@@ -44,6 +44,7 @@ struct QMLJS_EXPORT ViewerContext
QStringList selectors;
QStringList paths;
QStringList applicationDirectories;
Dialect language = Dialect::Qml;
Flags flags = AddAllPaths;
};

View File

@@ -41,6 +41,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <qmljs/qmljsbind.h>
@@ -139,6 +140,13 @@ ModelManagerInterface::ProjectInfo ModelManager::defaultProjectInfoForProject(
// plugins that are not installed in default Qt qml installation directory.
projectInfo.qmlDumpEnvironment.appendOrSet("QML2_IMPORT_PATH", bc->environment().expandedValueForKey("QML2_IMPORT_PATH"), ":");
}
const auto appTargets = activeTarget->applicationTargets();
for (const auto &target : appTargets) {
if (target.targetFilePath.isEmpty())
continue;
projectInfo.applicationDirectories.append(target.targetFilePath.parentDir().toString());
}
}
if (!setPreferDump && qtVersion)
preferDebugDump = (qtVersion->defaultBuildConfig() & QtSupport::BaseQtVersion::DebugBuild);