qmlimportscanner: Prefer directories with qmldir

With the new CMake module API, we often have both the source and the
binary dir as an import path, with the qmldir only being available in
the binary directory. For deployment to work correctly, we need to pick
up the qmldir. Thus, when finding a module both with and without a
qmldir in different paths, prefer the version with the qmldir.

Pick-to: 6.2
Change-Id: I12efeae321da60b1b5ffe5c6d950ba486887ceb1
Reviewed-by: Craig Scott <craig.scott@qt.io>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Fabian Kosmale 2021-11-02 14:20:46 +01:00
parent b395e5c5c3
commit 70ecbb690c
7 changed files with 61 additions and 5 deletions

View File

@ -0,0 +1,3 @@
import QtQml
QtObject {}

View File

@ -0,0 +1,3 @@
module Module
A 1.0 A.qml

View File

@ -0,0 +1,3 @@
import QtQml
QtObject {}

View File

@ -0,0 +1,3 @@
import Module
A {}

View File

@ -57,5 +57,9 @@
"name": "Imports",
"relativePath": "Imports",
"type": "module"
},
{
"name": "Module",
"type": "module"
}
]

View File

@ -46,6 +46,7 @@ private Q_SLOTS:
void rootPath();
void modules_data();
void modules();
void qmldirPreference();
private:
void runQmlimportscanner(const QString &mode, const QString &fileToScan,
@ -123,6 +124,31 @@ void TestQmlimportscanner::modules()
runQmlimportscanner("-qmlFiles", qmlFile.fileName(), testFile(name + ".json"));
}
void TestQmlimportscanner::qmldirPreference()
{
// ###
QStringList with {u"-importPath"_qs, testFile("With")};
QStringList withOut {u"-importPath"_qs, testFile("WithOut")};
QStringList genericArgs {u"-qmlFiles"_qs, testFile("qmldirpref.qml"), u"-importPath"_qs,
QLibraryInfo::path(QLibraryInfo::QmlImportsPath)};
// found path should not depend on order of importPath arguments
QStringList argcombis[2] { genericArgs + with + withOut, genericArgs + withOut + with };
for (const auto &allArgs: argcombis) {
QProcess process;
process.start(m_qmlimportscannerPath, allArgs);
QVERIFY(process.waitForFinished());
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
QCOMPARE(process.exitCode(), 0);
QVERIFY(process.readAllStandardError().isEmpty());
auto output = process.readAllStandardOutput();
// check that the "With" path is used, and the "WithOut" path is ignored
QVERIFY(output.contains("With/Module"));
QVERIFY(!output.contains("WithOut/Module"));
}
}
void TestQmlimportscanner::runQmlimportscanner(const QString &mode, const QString &pathToScan,
const QString &resultFile)
{

View File

@ -253,6 +253,7 @@ QPair<QString, QString> resolveImportPath(const QString &uri, const QString &ver
const QStringList parts = uri.split(dot, Qt::SkipEmptyParts);
QString ver = version;
QPair<QString, QString> candidate;
while (true) {
for (const QString &qmlImportPath : qAsConst(g_qmlImportPaths)) {
// Search for the most specific version first, and search
@ -267,8 +268,15 @@ QPair<QString, QString> resolveImportPath(const QString &uri, const QString &ver
if (relativePath.endsWith(slash))
relativePath.chop(1);
const QString candidatePath = QDir::cleanPath(qmlImportPath + slash + relativePath);
if (QDir(candidatePath).exists())
return qMakePair(candidatePath, relativePath); // import found
const QDir candidateDir(candidatePath);
if (candidateDir.exists()) {
const auto newCandidate = qMakePair(candidatePath, relativePath); // import found
if (candidateDir.exists(u"qmldir"_qs)) // if it has a qmldir, we are fine
return newCandidate;
else if (candidate.first.isEmpty())
candidate = newCandidate;
// otherwise we keep looking if we can find the module again (with a qmldir this time)
}
} else {
for (int index = parts.count() - 1; index >= 0; --index) {
QString relativePath = parts.mid(0, index + 1).join(slash)
@ -276,8 +284,14 @@ QPair<QString, QString> resolveImportPath(const QString &uri, const QString &ver
if (relativePath.endsWith(slash))
relativePath.chop(1);
const QString candidatePath = QDir::cleanPath(qmlImportPath + slash + relativePath);
if (QDir(candidatePath).exists())
return qMakePair(candidatePath, relativePath); // import found
const QDir candidateDir(candidatePath);
if (candidateDir.exists()) {
const auto newCandidate = qMakePair(candidatePath, relativePath); // import found
if (candidateDir.exists(u"qmldir"_qs))
return newCandidate;
else if (candidate.first.isEmpty())
candidate = newCandidate;
}
}
}
}
@ -293,7 +307,7 @@ QPair<QString, QString> resolveImportPath(const QString &uri, const QString &ver
ver = ver.mid(0, lastDot);
}
return QPair<QString, QString>(); // not found
return candidate;
}
// Find absolute file system paths and plugins for a list of modules.