Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmlimport.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 Crimson AS <info@crimson.no>
2// Copyright (C) 2016 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qqmlimport_p.h"
6
7#include <QtCore/qdebug.h>
8#include <QtCore/qdir.h>
9#include <QtQml/qqmlfile.h>
10#include <QtCore/qfileinfo.h>
11#include <QtCore/qpluginloader.h>
12#include <QtCore/qlibraryinfo.h>
13#include <QtCore/qloggingcategory.h>
14#include <QtQml/qqmlextensioninterface.h>
15#include <QtQml/qqmlextensionplugin.h>
16#include <private/qqmlextensionplugin_p.h>
17#include <private/qqmlglobal_p.h>
18#include <private/qqmltypenamecache_p.h>
19#include <private/qqmlengine_p.h>
20#include <private/qqmltypemodule_p.h>
21#include <private/qqmltypeloaderqmldircontent_p.h>
22#include <private/qqmlpluginimporter_p.h>
23#include <QtCore/qjsonobject.h>
24#include <QtCore/qjsonarray.h>
25#include <QtQml/private/qqmltype_p_p.h>
26#include <QtQml/private/qqmlimportresolver_p.h>
27
28#ifdef Q_OS_MACOS
29#include "private/qcore_mac_p.h"
30#endif
31
32#include <algorithm>
33#include <functional>
34
35using namespace Qt::Literals::StringLiterals;
36
38
39DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
40
42{
43 Q_DISABLE_COPY_MOVE(QmlImportCategoryHolder);
44public:
45
46 QmlImportCategoryHolder() : m_category("qt.qml.import")
47 {
48 // We have to explicitly setEnabled() here because for categories that start with
49 // "qt." it won't accept QtDebugMsg as argument. Debug messages are off by default
50 // for all Qt logging categories.
51 if (qmlImportTrace())
52 m_category.setEnabled(QtDebugMsg, true);
53 }
54
56
57 const QLoggingCategory &category() const { return m_category; }
58
59private:
60 QLoggingCategory m_category;
61};
62
64{
65 static const QmlImportCategoryHolder holder;
66 return holder.category();
67}
68
69DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
70
71static const QLatin1Char Dot('.');
72static const QLatin1Char Slash('/');
73static const QLatin1Char Backslash('\\');
74static const QLatin1Char Colon(':');
75static const QLatin1String Slash_qmldir("/qmldir");
76static const QLatin1String String_qmldir("qmldir");
77static const QString dotqml_string(QStringLiteral(".qml"));
78static const QString dotuidotqml_string(QStringLiteral(".ui.qml"));
80
81namespace {
82
83QTypeRevision relevantVersion(const QString &uri, QTypeRevision version)
84{
85 return QQmlMetaType::latestModuleVersion(uri).isValid() ? version : QTypeRevision();
86}
87
88QQmlError moduleNotFoundError(const QString &uri, QTypeRevision version)
89{
91 if (version.hasMajorVersion()) {
92 error.setDescription(QQmlImportDatabase::tr(
93 "module \"%1\" version %2.%3 is not installed")
94 .arg(uri).arg(version.majorVersion())
95 .arg(version.hasMinorVersion()
96 ? QString::number(version.minorVersion())
97 : QLatin1String("x")));
98 } else {
99 error.setDescription(QQmlImportDatabase::tr("module \"%1\" is not installed")
100 .arg(uri));
101 }
102 return error;
103}
104
105QString resolveLocalUrl(const QString &url, const QString &relative)
106{
107 if (relative.contains(Colon)) {
108 // contains a host name
109 return QUrl(url).resolved(QUrl(relative)).toString();
110 } else if (relative.isEmpty()) {
111 return url;
112 } else if (relative.at(0) == Slash || !url.contains(Slash)) {
113 return relative;
114 } else {
115 const QStringView baseRef = QStringView{url}.left(url.lastIndexOf(Slash) + 1);
116 if (relative == QLatin1String("."))
117 return baseRef.toString();
118
119 QString base = baseRef + relative;
120
121 // Remove any relative directory elements in the path
122 int length = base.size();
123 int index = 0;
124 while ((index = base.indexOf(QLatin1String("/."), index)) != -1) {
125 if ((length > (index + 2)) && (base.at(index + 2) == Dot) &&
126 (length == (index + 3) || (base.at(index + 3) == Slash))) {
127 // Either "/../" or "/..<END>"
128 int previous = base.lastIndexOf(Slash, index - 1);
129 if (previous == -1)
130 break;
131
132 int removeLength = (index - previous) + 3;
133 base.remove(previous + 1, removeLength);
134 length -= removeLength;
135 index = previous;
136 } else if ((length == (index + 2)) || (base.at(index + 2) == Slash)) {
137 // Either "/./" or "/.<END>"
138 base.remove(index, 2);
139 length -= 2;
140 } else {
141 ++index;
142 }
143 }
144
145 return base;
146 }
147}
148
149bool isPathAbsolute(const QString &path)
150{
151#if defined(Q_OS_UNIX)
152 return (path.at(0) == Slash);
153#else
154 QFileInfo fi(path);
155 return fi.isAbsolute();
156#endif
157}
158
159} // namespace
160
201{
202 // If the given version is invalid, return a valid but useless version to signal "It's OK".
203 return version.isValid() ? version : QTypeRevision::fromMinorVersion(0);
204}
205
209void QQmlImports::setBaseUrl(const QUrl& url, const QString &urlString)
210{
211 m_baseUrl = url;
212
213 if (urlString.isEmpty())
214 m_base = url.toString();
215 else
216 m_base = urlString;
217}
218
226/*
227 \internal
228
229 This method is responsible for populating data of all types visible in this
230 document's imports into the \a cache for resolution elsewhere (e.g. in JS,
231 or when loading additional types).
232
233 \note This is for C++ types only. Composite types are handled separately,
234 as they do not have a QQmlTypeModule.
235*/
237{
238 const QQmlImportNamespace &set = m_unqualifiedset;
239
240 for (int ii = set.imports.size() - 1; ii >= 0; --ii) {
241 const QQmlImportInstance *import = set.imports.at(ii);
242 QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->version);
243 if (module) {
244 cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, import->version));
245 }
246 }
247
248 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
249
250 const QQmlImportNamespace &set = *ns;
251
252 // positioning is important; we must create the namespace even if there is no module.
253 QQmlImportRef &typeimport = cache->m_namedImports[set.prefix];
254 typeimport.m_qualifier = set.prefix;
255
256 for (int ii = set.imports.size() - 1; ii >= 0; --ii) {
257 const QQmlImportInstance *import = set.imports.at(ii);
258 QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->version);
259 if (module) {
260 QQmlImportRef &typeimport = cache->m_namedImports[set.prefix];
261 typeimport.modules.append(QQmlTypeModuleVersion(module, import->version));
262 }
263 }
264 }
265}
266
267// We need to exclude the entry for the current baseUrl. This can happen for example
268// when handling qmldir files on the remote dir case and the current type is marked as
269// singleton.
270bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QString &baseUrl)
271{
272 if (importUrl.isEmpty())
273 return false;
274
275 if (baseUrl.startsWith(importUrl))
276 {
277 if (fileName == QStringView{baseUrl}.mid(importUrl.size()))
278 return false;
279 }
280
281 return true;
282}
283
284void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports::CompositeSingletonReference> &resultList, const QUrl &baseUrl)
285{
286 typedef QQmlDirComponents::const_iterator ConstIterator;
287
288 for (int ii = set.imports.size() - 1; ii >= 0; --ii) {
289 const QQmlImportInstance *import = set.imports.at(ii);
290
291 const QQmlDirComponents &components = import->qmlDirComponents;
292
293 const QTypeRevision importVersion = import->version;
294 auto shouldSkipSingleton = [importVersion](QTypeRevision singletonVersion) -> bool {
295 return importVersion.hasMajorVersion() &&
296 (singletonVersion.majorVersion() > importVersion.majorVersion()
297 || (singletonVersion.majorVersion() == importVersion.majorVersion()
298 && singletonVersion.minorVersion() > importVersion.minorVersion()));
299 };
300
301 ConstIterator cend = components.constEnd();
302 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
303 if (cit->singleton && excludeBaseUrl(import->url, cit->fileName, baseUrl.toString())) {
304 if (shouldSkipSingleton(cit->version))
305 continue;
307 ref.typeName = cit->typeName;
308 ref.prefix = set.prefix;
309 ref.version = cit->version;
310 resultList.append(ref);
311 }
312 }
313
314 if (QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->version)) {
315 module->walkCompositeSingletons([&resultList, &set, &shouldSkipSingleton](const QQmlType &singleton) {
316 if (shouldSkipSingleton(singleton.version()))
317 return;
319 ref.typeName = singleton.elementName();
320 ref.prefix = set.prefix;
321 ref.version = singleton.version();
322 resultList.append(ref);
323 });
324 }
325 }
326}
327
328/*
329 \internal
330
331 Returns a list of all composite singletons present in this document's
332 imports.
333
334 This information is used by QQmlTypeLoader to ensure that composite singletons
335 are marked as dependencies during type loading.
336*/
337QList<QQmlImports::CompositeSingletonReference> QQmlImports::resolvedCompositeSingletons() const
338{
339 QList<QQmlImports::CompositeSingletonReference> compositeSingletons;
340
341 const QQmlImportNamespace &set = m_unqualifiedset;
342 findCompositeSingletons(set, compositeSingletons, baseUrl());
343
344 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
345 const QQmlImportNamespace &set = *ns;
346 findCompositeSingletons(set, compositeSingletons, baseUrl());
347 }
348
349 std::stable_sort(compositeSingletons.begin(), compositeSingletons.end(),
352 if (lhs.prefix != rhs.prefix)
353 return lhs.prefix < rhs.prefix;
354
355 if (lhs.typeName != rhs.typeName)
356 return lhs.typeName < rhs.typeName;
357
358 return lhs.version.majorVersion() != rhs.version.majorVersion()
359 ? lhs.version.majorVersion() < rhs.version.majorVersion()
360 : lhs.version.minorVersion() < rhs.version.minorVersion();
361 });
362
363 return compositeSingletons;
364}
365
366/*
367 \internal
368
369 Returns a list of scripts imported by this document. This is used by
370 QQmlTypeLoader to properly handle dependencies on imported scripts.
371*/
372QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const
373{
374 QList<QQmlImports::ScriptReference> scripts;
375
376 const QQmlImportNamespace &set = m_unqualifiedset;
377
378 for (int ii = set.imports.size() - 1; ii >= 0; --ii) {
379 const QQmlImportInstance *import = set.imports.at(ii);
380
381 for (const QQmlDirParser::Script &script : import->qmlDirScripts) {
383 ref.nameSpace = script.nameSpace;
384 ref.location = QUrl(import->url).resolved(QUrl(script.fileName));
385 scripts.append(ref);
386 }
387 }
388
389 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
390 const QQmlImportNamespace &set = *ns;
391
392 for (int ii = set.imports.size() - 1; ii >= 0; --ii) {
393 const QQmlImportInstance *import = set.imports.at(ii);
394
395 for (const QQmlDirParser::Script &script : import->qmlDirScripts) {
397 ref.nameSpace = script.nameSpace;
398 ref.qualifier = set.prefix;
399 ref.location = QUrl(import->url).resolved(QUrl(script.fileName));
400 scripts.append(ref);
401 }
402 }
403 }
404
405 return scripts;
406}
407
419 QTypeRevision version)
420{
421 QStringList paths = qQmlResolveImportPaths(uri, basePaths, version);
422 for (QString &path : paths)
424 return paths;
425}
426
428{
429 if (versionMode == QQmlImports::FullyVersioned) {
430 // extension with fully encoded version number (eg. MyModule.3.2)
431 return QString::asprintf(".%d.%d", version.majorVersion(), version.minorVersion());
432 } else if (versionMode == QQmlImports::PartiallyVersioned) {
433 // extension with encoded version major (eg. MyModule.3)
434 return QString::asprintf(".%d", version.majorVersion());
435 } // else extension without version number (eg. MyModule)
436 return QString();
437}
438
453 QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QQmlType *type_return,
454 QTypeRevision *version_return, QQmlImportNamespace **ns_return, QList<QQmlError> *errors,
455 QQmlType::RegistrationType registrationType, bool *typeRecursionDetected) const
456{
457 QQmlImportNamespace *ns = findQualifiedNamespace(type);
458 if (ns) {
459 if (ns_return)
460 *ns_return = ns;
461 return true;
462 }
463 if (type_return) {
464 if (resolveType(typeLoader, type, version_return, type_return, errors, registrationType,
465 typeRecursionDetected)) {
466 if (lcQmlImport().isDebugEnabled()) {
467#define RESOLVE_TYPE_DEBUG qCDebug(lcQmlImport) \
468 << "resolveType:" << qPrintable(baseUrl().toString()) << type.toString() << " => "
469
470 if (type_return && type_return->isValid()) {
471 if (type_return->isCompositeSingleton())
472 RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL-SINGLETON";
473 else if (type_return->isComposite())
474 RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL";
475 else if (type_return->isInlineComponentType())
476 RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE(INLINECOMPONENT)";
477 else
478 RESOLVE_TYPE_DEBUG << type_return->typeName() << " TYPE";
479 }
480#undef RESOLVE_TYPE_DEBUG
481 }
482 return true;
483 }
484 }
485 return false;
486}
487
489 const QQmlTypeLoaderQmldirContent &qmldir,
490 QQmlImportNamespace *nameSpace, QList<QQmlError> *errors)
491{
492 Q_ASSERT(resolvedUrl.endsWith(Slash));
494
495 qmlDirComponents = qmldir.components();
496
497 const QQmlDirScripts &scripts = qmldir.scripts();
498 if (!scripts.isEmpty()) {
499 // Verify that we haven't imported these scripts already
501 it != nameSpace->imports.constEnd(); ++it) {
502 if ((*it != this) && ((*it)->uri == uri)) {
504 error.setDescription(
505 QQmlImportDatabase::tr("\"%1\" is ambiguous. Found in %2 and in %3")
506 .arg(uri, url, (*it)->url));
507 errors->prepend(error);
508 return false;
509 }
510 }
511
513 }
514
515 return true;
516}
517
519 QTypeRevision version)
520{
521 QMap<QString, QQmlDirParser::Script> versioned;
522
523 for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin();
524 sit != qmldirscripts.constEnd(); ++sit) {
525 // Only include scripts that match our requested version
526 if ((!version.hasMajorVersion() || (sit->version.majorVersion() == version.majorVersion()))
528 || (sit->version.minorVersion() <= version.minorVersion()))) {
529 // Load the highest version that matches
530 const auto vit = versioned.constFind(sit->nameSpace);
531 if (vit == versioned.cend()
532 || (vit->version.minorVersion() < sit->version.minorVersion())) {
533 versioned.insert(sit->nameSpace, *sit);
534 }
535 }
536 }
537
538 return versioned.values();
539}
540
554 QTypeRevision *version_return, QQmlType *type_return,
555 const QString *base, bool *typeRecursionDetected,
556 QQmlType::RegistrationType registrationType,
557 QQmlImport::RecursionRestriction recursionRestriction,
558 QList<QQmlError> *errors) const
559{
561 if (t.isValid()) {
562 if (version_return)
563 *version_return = version;
564 if (type_return)
565 *type_return = t;
566 return true;
567 }
568
569 const QString typeStr = type.toString();
570 if (isInlineComponent) {
571 Q_ASSERT(type_return);
572 bool ret = uri == typeStr;
573 if (ret) {
574 Q_ASSERT(!type_return->isValid());
576 }
577 return ret;
578 }
580 if (it != end) {
581 QString componentUrl;
584 for ( ; it != end && it.key() == typeStr; ++it) {
585 const QQmlDirParser::Component &c = *it;
586 switch (registrationType) {
588 break;
590 if (!c.singleton)
591 continue;
592 break;
593 default:
594 if (c.singleton)
595 continue;
596 break;
597 }
598
599 // importing invalid version means import ALL versions
600 if (!version.hasMajorVersion() || (implicitlyImported && c.internal)
601 // allow the implicit import of internal types
602 || (c.version.majorVersion() == version.majorVersion()
603 && c.version.minorVersion() <= version.minorVersion())) {
604 // Is this better than the previous candidate?
605 if ((candidate == end)
606 || (c.version.majorVersion() > candidate->version.majorVersion())
607 || ((c.version.majorVersion() == candidate->version.majorVersion())
608 && (c.version.minorVersion() > candidate->version.minorVersion()))) {
609 if (base) {
610 componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName);
611 if (c.internal) {
612 if (resolveLocalUrl(*base, c.fileName) != componentUrl)
613 continue; // failed attempt to access an internal type
614 }
615
616 const bool recursion = *base == componentUrl;
617 if (typeRecursionDetected)
618 *typeRecursionDetected = recursion;
619
620 if (recursionRestriction == QQmlImport::PreventRecursion && recursion) {
621 continue; // no recursion
622 }
623 }
624
625 // This is our best candidate so far
626 candidate = it;
628 }
629 }
630 }
631
632 if (candidate != end) {
633 if (!base) // ensure we have a componentUrl
634 componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName);
635 QQmlType returnType = QQmlMetaType::typeForUrl(componentUrl, type, lookupMode,
636 nullptr, candidate->version);
637 if (version_return)
638 *version_return = candidate->version;
639 if (type_return)
640 *type_return = returnType;
641 return returnType.isValid();
642 }
643 } else if (!isLibrary) {
644 // the base path of the import if it's a local file
645 const QString localDirectoryPath = QQmlFile::urlToLocalFileOrQrc(url);
646 if (localDirectoryPath.isEmpty())
647 return false;
648
649 QString qmlUrl;
650 bool exists = false;
651
652 const QString urlsToTry[2] = {
653 typeStr + dotqml_string, // Type -> Type.qml
654 typeStr + dotuidotqml_string // Type -> Type.ui.qml
655 };
656 for (const QString &urlToTry : urlsToTry) {
657 exists = typeLoader->fileExists(localDirectoryPath, urlToTry);
658 if (exists) {
659#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
660 // don't let function.qml confuse the use of "new Function(...)" for example.
661 if (!QQml_isFileCaseCorrect(localDirectoryPath + urlToTry)) {
662 exists = false;
663 if (errors) {
664 QQmlError caseError;
665 caseError.setDescription(QLatin1String("File name case mismatch"));
666 errors->append(caseError);
667 }
668 break;
669 }
670#else
671 Q_UNUSED(errors);
672#endif
673 qmlUrl = url + urlToTry;
674 break;
675 }
676 }
677
678 if (exists) {
679 const bool recursion = base && *base == qmlUrl;
680 if (typeRecursionDetected)
681 *typeRecursionDetected = recursion;
682 if (recursionRestriction == QQmlImport::AllowRecursion || !recursion) {
684 qmlUrl, type, registrationType == QQmlType::CompositeSingletonType
687 errors);
688 if (type_return)
689 *type_return = returnType;
690 return returnType.isValid();
691 }
692 }
693 }
694
695 return false;
696}
697
699 QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QTypeRevision *version_return,
700 QQmlType *type_return, QList<QQmlError> *errors,
701 QQmlType::RegistrationType registrationType, bool *typeRecursionDetected) const
702{
703 const QVector<QHashedStringRef> splitName = type.split(Dot);
704 auto resolveTypeInNamespace = [&](
705 QHashedStringRef unqualifiedtype, QQmlImportNamespace *nameSpace,
706 QList<QQmlError> *errors) -> bool {
707 if (nameSpace->resolveType(
708 typeLoader, unqualifiedtype, version_return, type_return, &m_base, errors,
709 registrationType, typeRecursionDetected))
710 return true;
711 if (nameSpace->imports.size() == 1
712 && !nameSpace->imports.at(0)->isLibrary
713 && type_return
714 && nameSpace != &m_unqualifiedset) {
715 // qualified, and only 1 url
716 *type_return = QQmlMetaType::typeForUrl(
717 resolveLocalUrl(nameSpace->imports.at(0)->url,
718 unqualifiedtype.toString() + QLatin1String(".qml")),
720 return type_return->isValid();
721 }
722 return false;
723 };
724 switch (splitName.size()) {
725 case 1: {
726 // must be a simple type
727 return resolveTypeInNamespace(type, &m_unqualifiedset, errors);
728 }
729 case 2: {
730 // either namespace + simple type OR simple type + inline component
731 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
732 if (s) {
733 // namespace + simple type
734 return resolveTypeInNamespace(splitName.at(1), s, errors);
735 } else {
736 if (resolveTypeInNamespace(splitName.at(0), &m_unqualifiedset, nullptr)) {
737 // either simple type + inline component
739 *type_return, splitName.at(1).toString());
740 return true;
741 } else {
742 // or a failure
743 if (errors) {
745 error.setDescription(QQmlImportDatabase::tr("- %1 is neither a type nor a namespace").arg(splitName.at(0).toString()));
746 errors->prepend(error);
747 }
748 return false;
749 }
750 }
751 }
752 case 3: {
753 // must be namespace + simple type + inline component
754 QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
756 if (!s) {
757 error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(splitName.at(0).toString()));
758 } else {
759 if (resolveTypeInNamespace(splitName.at(1), s, nullptr)) {
761 *type_return, splitName.at(2).toString());
762 return true;
763 } else {
764 error.setDescription(QQmlImportDatabase::tr("- %1 is not a type").arg(splitName.at(1).toString()));
765 }
766 }
767 if (errors) {
768 errors->prepend(error);
769 }
770 return false;
771 }
772 default: {
773 // all other numbers suggest a user error
774 if (errors) {
776 error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
777 errors->prepend(error);
778 }
779 return false;
780 }
781 }
782 Q_UNREACHABLE();
783}
784
786{
787 for (QQmlImportInstance *import : imports) {
788 if (import->uri == uri)
789 return import;
790 }
791 return nullptr;
792}
793
795 QTypeRevision *version_return, QQmlType *type_return,
796 const QString *base, QList<QQmlError> *errors,
797 QQmlType::RegistrationType registrationType,
798 bool *typeRecursionDetected)
799{
800 QQmlImport::RecursionRestriction recursionRestriction =
802
803 bool localTypeRecursionDetected = false;
804 if (!typeRecursionDetected)
805 typeRecursionDetected = &localTypeRecursionDetected;
806
807 // TODO: move the sorting somewhere else and make resolveType() const.
808 if (needsSorting()) {
809 std::stable_partition(imports.begin(), imports.end(), [](QQmlImportInstance *import) {
810 return import->isInlineComponent;
811 });
812 setNeedsSorting(false);
813 }
814 for (int i=0; i<imports.size(); ++i) {
815 const QQmlImportInstance *import = imports.at(i);
816 if (import->resolveType(typeLoader, type, version_return, type_return, base,
817 typeRecursionDetected, registrationType, recursionRestriction, errors)) {
818 if (qmlCheckTypes()) {
819 // check for type clashes
820 for (int j = i+1; j<imports.size(); ++j) {
821 const QQmlImportInstance *import2 = imports.at(j);
822 if (import2->resolveType(typeLoader, type, version_return, nullptr, base,
823 nullptr, registrationType)) {
824 if (errors) {
825 QString u1 = import->url;
826 QString u2 = import2->url;
827 if (base) {
829 int dot = b.lastIndexOf(Dot);
830 if (dot >= 0) {
831 b = b.left(dot+1);
832 QStringView l = b.left(dot);
833 if (u1.startsWith(b))
834 u1 = u1.mid(b.size());
835 else if (u1 == l)
836 u1 = QQmlImportDatabase::tr("local directory");
837 if (u2.startsWith(b))
838 u2 = u2.mid(b.size());
839 else if (u2 == l)
840 u2 = QQmlImportDatabase::tr("local directory");
841 }
842 }
843
845 if (u1 != u2) {
846 error.setDescription(
847 QQmlImportDatabase::tr(
848 "is ambiguous. Found in %1 and in %2")
849 .arg(u1, u2));
850 } else {
851 error.setDescription(
852 QQmlImportDatabase::tr(
853 "is ambiguous. Found in %1 in version "
854 "%2.%3 and %4.%5")
855 .arg(u1)
856 .arg(import->version.majorVersion())
857 .arg(import->version.minorVersion())
858 .arg(import2->version.majorVersion())
859 .arg(import2->version.minorVersion()));
860 }
861 errors->prepend(error);
862 }
863 return false;
864 }
865 }
866 }
867 return true;
868 }
869 }
870 if (errors) {
872 if (*typeRecursionDetected)
873 error.setDescription(QQmlImportDatabase::tr("is instantiated recursively"));
874 else
875 error.setDescription(QQmlImportDatabase::tr("is not a type"));
876 errors->prepend(error);
877 }
878 return false;
879}
880
881QQmlImportNamespace *QQmlImports::findQualifiedNamespace(const QHashedStringRef &prefix) const
882{
883 for (QQmlImportNamespace *ns = m_qualifiedSets.first(); ns; ns = m_qualifiedSets.next(ns)) {
884 if (prefix == ns->prefix)
885 return ns;
886 }
887 return nullptr;
888}
889
890/*
891Import an extension defined by a qmldir file.
892*/
893QTypeRevision QQmlImports::importExtension(
894 QQmlTypeLoader *typeLoader, const QString &uri, QTypeRevision version,
895 const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
896{
897 Q_ASSERT(qmldir->hasContent());
898
900 << "importExtension:" << qPrintable(m_base) << "loaded" << qmldir->qmldirLocation();
901
902 if (designerSupportRequired && !qmldir->designerSupported()) {
903 if (errors) {
905 error.setDescription(
906 QQmlImportDatabase::tr("module does not support the designer \"%1\"")
907 .arg(qmldir->typeNamespace()));
908 error.setUrl(QUrl::fromLocalFile(qmldir->qmldirLocation()));
909 errors->prepend(error);
910 }
911 return QTypeRevision();
912 }
913
914 if (qmldir->plugins().isEmpty()) {
915 // If the qmldir does not register a plugin, we might still have declaratively
916 // registered types (if we are dealing with an application instead of a library)
917 if (!QQmlMetaType::typeModule(uri, version))
919 return validVersion(version);
920 }
921
922 QQmlPluginImporter importer(
923 uri, version, typeLoader->importDatabase(), qmldir, typeLoader, errors);
924 return importer.importPlugins();
925}
926
927QString QQmlImports::redirectQmldirContent(
928 QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir)
929{
930 const QString preferredPath = qmldir->preferredPath();
931 const QString url = preferredPath.startsWith(u':')
932 ? QStringLiteral("qrc") + preferredPath
933 : QUrl::fromLocalFile(preferredPath).toString();
934
936 = typeLoader->qmldirContent(url + QLatin1String("qmldir"));
937
938 // Ignore errors: If the qmldir doesn't exist, stick to the old one.
939 if (redirected.hasContent() && !redirected.hasError())
940 *qmldir = std::move(redirected);
941 return url;
942}
943
944bool QQmlImports::getQmldirContent(
945 QQmlTypeLoader *typeLoader, const QString &qmldirIdentifier, const QString &uri,
946 QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
947{
948 Q_ASSERT(errors);
949 Q_ASSERT(qmldir);
950
951 *qmldir = typeLoader->qmldirContent(qmldirIdentifier);
952 if (!qmldir->hasContent() || !qmldir->hasError())
953 return true;
954
955 errors->append(qmldir->errors(uri, QUrl::fromLocalFile(qmldirIdentifier)));
956 return false;
957}
958
959QString QQmlImports::resolvedUri(const QString &dir_arg, QQmlImportDatabase *database)
960{
961 QString dir = dir_arg;
963 dir.chop(1);
964
965 QStringList paths = database->fileImportPath;
966 if (!paths.isEmpty())
967 std::sort(paths.begin(), paths.end(), std::greater<QString>()); // Ensure subdirs preceed their parents.
968
969 QString stableRelativePath = dir;
970 for (const QString &path : std::as_const(paths)) {
971 if (dir.startsWith(path)) {
972 stableRelativePath = dir.mid(path.size()+1);
973 break;
974 }
975 }
976
977 stableRelativePath.replace(Backslash, Slash);
978
979 // remove optional versioning in dot notation from uri
980 int versionDot = stableRelativePath.lastIndexOf(Dot);
981 if (versionDot >= 0) {
982 int nextSlash = stableRelativePath.indexOf(Slash, versionDot);
983 if (nextSlash >= 0)
984 stableRelativePath.remove(versionDot, nextSlash - versionDot);
985 else
986 stableRelativePath = stableRelativePath.left(versionDot);
987 }
988
989 stableRelativePath.replace(Slash, Dot);
990
991 return stableRelativePath;
992}
993
1011QTypeRevision QQmlImports::matchingQmldirVersion(
1012 const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, QTypeRevision version,
1013 QList<QQmlError> *errors)
1014{
1015 int bestMajorVersion = -1;
1016 quint8 lowestMinorVersion = std::numeric_limits<quint8>::max();
1017 quint8 highestMinorVersion = 0;
1018
1019 auto addVersion = [&](QTypeRevision newVersion) {
1020 if (!newVersion.hasMajorVersion())
1021 return;
1022 if (!version.hasMajorVersion() || version.majorVersion() == newVersion.majorVersion()) {
1023 if (newVersion.majorVersion() > bestMajorVersion) {
1024 bestMajorVersion = newVersion.majorVersion();
1025 if (newVersion.hasMinorVersion()) {
1026 lowestMinorVersion = newVersion.minorVersion();
1027 highestMinorVersion = newVersion.minorVersion();
1028 }
1029 } else if (newVersion.majorVersion() == bestMajorVersion
1030 && newVersion.hasMinorVersion()) {
1031 lowestMinorVersion = qMin(lowestMinorVersion, newVersion.minorVersion());
1032 highestMinorVersion = qMax(highestMinorVersion, newVersion.minorVersion());
1033 }
1034 }
1035 };
1036
1037 typedef QQmlDirComponents::const_iterator ConstIterator;
1038 const QQmlDirComponents &components = qmldir.components();
1039
1040 ConstIterator cend = components.constEnd();
1041 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
1042 for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
1043 if (cit2->typeName == cit->typeName && cit2->version == cit->version) {
1044 // This entry clashes with a predecessor
1046 error.setDescription(
1047 QQmlImportDatabase::tr(
1048 "\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1049 .arg(cit->typeName).arg(cit->version.majorVersion())
1050 .arg(cit->version.minorVersion()).arg(uri));
1051 errors->prepend(error);
1052 return QTypeRevision();
1053 }
1054 }
1055
1056 addVersion(cit->version);
1057 }
1058
1059 typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
1060 const QQmlDirScripts &scripts = qmldir.scripts();
1061
1062 SConstIterator send = scripts.constEnd();
1063 for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) {
1064 for (SConstIterator sit2 = scripts.constBegin(); sit2 != sit; ++sit2) {
1065 if (sit2->nameSpace == sit->nameSpace && sit2->version == sit->version) {
1066 // This entry clashes with a predecessor
1068 error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
1069 .arg(sit->nameSpace).arg(sit->version.majorVersion())
1070 .arg(sit->version.minorVersion()).arg(uri));
1071 errors->prepend(error);
1072 return QTypeRevision();
1073 }
1074 }
1075
1076 addVersion(sit->version);
1077 }
1078
1079 // Failure to find a match is only an error if we were asking for a specific version ...
1080 if (version.hasMajorVersion()
1081 && (bestMajorVersion < 0
1082 || (version.hasMinorVersion()
1083 && (lowestMinorVersion > version.minorVersion()
1084 || highestMinorVersion < version.minorVersion())))) {
1085 errors->prepend(moduleNotFoundError(uri, version));
1086 return QTypeRevision();
1087 }
1088
1089 // ... otherwise, anything is valid.
1090 if (bestMajorVersion < 0)
1091 return validVersion();
1092
1094 bestMajorVersion,
1095 (version.hasMajorVersion() && version.hasMinorVersion())
1096 ? version.minorVersion()
1097 : highestMinorVersion);
1098}
1099
1100QQmlImportNamespace *QQmlImports::importNamespace(const QString &prefix)
1101{
1102 QQmlImportNamespace *nameSpace = nullptr;
1103
1104 if (prefix.isEmpty()) {
1105 nameSpace = &m_unqualifiedset;
1106 } else {
1107 nameSpace = findQualifiedNamespace(prefix);
1108
1109 if (!nameSpace) {
1110 nameSpace = new QQmlImportNamespace;
1111 nameSpace->prefix = prefix;
1112 m_qualifiedSets.append(nameSpace);
1113 }
1114 }
1115
1116 return nameSpace;
1117}
1118
1120 QQmlImportNamespace *nameSpace, const QString &uri, const QString &url, QTypeRevision version,
1121 QV4::CompiledData::Import::ImportType type, QList<QQmlError> *errors, quint16 precedence)
1122{
1123 Q_ASSERT(nameSpace);
1124 Q_ASSERT(errors);
1125 Q_UNUSED(errors);
1126 Q_ASSERT(url.isEmpty() || url.endsWith(Slash));
1127
1128 QQmlImportInstance *import = new QQmlImportInstance;
1129 import->uri = uri;
1130 import->url = url;
1131 import->version = version;
1132 import->isLibrary = (type == QV4::CompiledData::Import::ImportLibrary);
1133 import->precedence = precedence;
1134 import->implicitlyImported = precedence >= QQmlImportInstance::Implicit;
1135
1136 for (auto it = nameSpace->imports.cbegin(), end = nameSpace->imports.cend();
1137 it != end; ++it) {
1138 if ((*it)->precedence < precedence)
1139 continue;
1140
1141 nameSpace->imports.insert(it, import);
1142 return import;
1143 }
1144 nameSpace->imports.append(import);
1145 return import;
1146}
1147
1149 QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
1150 QTypeRevision version, const QString &qmldirIdentifier, const QString &qmldirUrl,
1151 ImportFlags flags, quint16 precedence, QList<QQmlError> *errors)
1152{
1153 Q_ASSERT(typeLoader);
1154 Q_ASSERT(errors);
1155
1157 << "addLibraryImport:" << qPrintable(baseUrl().toString())
1158 << uri << "version '" << version << "'" << "as" << prefix;
1159
1160 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1161 Q_ASSERT(nameSpace);
1162
1164 nameSpace, uri, qmldirUrl, version,
1166 precedence);
1167 Q_ASSERT(inserted);
1168
1171
1172 if (!qmldirIdentifier.isEmpty()) {
1173 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors))
1174 return QTypeRevision();
1175
1176 if (qmldir.hasContent()) {
1177 version = importExtension(typeLoader, uri, version, &qmldir, errors);
1178 if (!version.isValid())
1179 return QTypeRevision();
1180
1181 const QString resolvedUrl = qmldir.hasRedirection()
1182 ? redirectQmldirContent(typeLoader, &qmldir)
1183 : qmldirUrl;
1184
1185 if (!inserted->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors))
1186 return QTypeRevision();
1187 }
1188 }
1189
1190 // Ensure that we are actually providing something
1191 const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version);
1192 if (matchingVersion.isValid())
1193 return matchingVersion;
1194
1195 if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
1196 if (qmldir.plugins().isEmpty()) {
1197 if (!qmldir.imports().isEmpty())
1198 return validVersion(); // This is a pure redirection
1199 if (qmldir.hasTypeInfo())
1200 return validVersion(); // A pure C++ module without plugin
1201 }
1202 errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version)));
1203 return QTypeRevision();
1204 } else if (qmldir.hasContent()) {
1205 // Verify that the qmldir content is valid for this version
1206 version = matchingQmldirVersion(qmldir, uri, version, errors);
1207 if (!version.isValid())
1208 return QTypeRevision();
1209 }
1210 }
1211
1212 return validVersion(version);
1213}
1214
1237 QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
1238 QTypeRevision version, ImportFlags flags, quint16 precedence, QString *localQmldir,
1239 QList<QQmlError> *errors)
1240{
1241 Q_ASSERT(typeLoader);
1242 Q_ASSERT(errors);
1243
1245 << "addFileImport:" << qPrintable(baseUrl().toString())
1246 << uri << version << "as" << prefix;
1247
1248 if (uri.startsWith(Slash) || uri.startsWith(Colon)) {
1250 const QString fix = uri.startsWith(Slash) ? QLatin1String("file:") + uri
1251 : QLatin1String("qrc") + uri;
1252 error.setDescription(QQmlImportDatabase::tr(
1253 "\"%1\" is not a valid import URL. "
1254 "You can pass relative paths or URLs with schema, but not "
1255 "absolute paths or resource paths. Try \"%2\".").arg(uri, fix));
1256 errors->prepend(error);
1257 return QTypeRevision();
1258 }
1259
1260 Q_ASSERT(errors);
1261
1262 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1263 Q_ASSERT(nameSpace);
1264
1265 // The uri for this import. For library imports this is the same as uri
1266 // specified by the user, but it may be different in the case of file imports.
1267 QString importUri = uri;
1268 QString qmldirUrl = resolveLocalUrl(m_base, importUri + (importUri.endsWith(Slash)
1270 : Slash_qmldir));
1271 qmldirUrl = typeLoader->engine()->interceptUrl(
1273 QString qmldirIdentifier;
1274
1275 if (QQmlFile::isLocalFile(qmldirUrl)) {
1276
1277 QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
1278 Q_ASSERT(!localFileOrQrc.isEmpty());
1279
1280 const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1);
1281 if (!typeLoader->directoryExists(dir)) {
1282 if (precedence < QQmlImportInstance::Implicit) {
1284 error.setDescription(QQmlImportDatabase::tr("\"%1\": no such directory").arg(uri));
1285 error.setUrl(QUrl(qmldirUrl));
1286 errors->prepend(error);
1287 }
1288 return QTypeRevision();
1289 }
1290
1291 // Transforms the (possible relative) uri into our best guess relative to the
1292 // import paths.
1293 importUri = resolvedUri(dir, typeLoader->importDatabase());
1294 if (importUri.endsWith(Slash))
1295 importUri.chop(1);
1296
1297 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
1298 qmldirIdentifier = std::move(localFileOrQrc);
1299 if (localQmldir)
1300 *localQmldir = qmldirIdentifier;
1301 }
1302
1303 } else if (nameSpace->prefix.isEmpty() && !(flags & QQmlImports::ImportIncomplete)) {
1304
1305 if (precedence < QQmlImportInstance::Implicit) {
1307 error.setDescription(QQmlImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(importUri));
1308 error.setUrl(QUrl(qmldirUrl));
1309 errors->prepend(error);
1310 }
1311
1312 return QTypeRevision();
1313
1314 }
1315
1316 // The url for the path containing files for this import
1317 QString url = resolveLocalUrl(m_base, uri);
1318 if (url.isEmpty()) {
1320 error.setDescription(
1321 QQmlImportDatabase::tr("Cannot resolve URL for import \"%1\"").arg(uri));
1322 error.setUrl(m_baseUrl);
1323 errors->prepend(error);
1324 return QTypeRevision();
1325 }
1326
1327 if (!url.endsWith(Slash) && !url.endsWith(Backslash))
1328 url += Slash;
1329
1330 // ### For enum support, we are now adding the implicit import always (and earlier). Bail early
1331 // if the implicit import has already been explicitly added, otherwise we can run into issues
1332 // with duplicate imports. However remember that we attempted to add this as implicit import, to
1333 // allow for the loading of internal types.
1334 if (precedence >= QQmlImportInstance::Implicit) {
1336 it != nameSpace->imports.constEnd(); ++it) {
1337 if ((*it)->url == url) {
1338 (*it)->implicitlyImported = true;
1339 return validVersion(version);
1340 }
1341 }
1342 }
1343
1344 if (!(flags & QQmlImports::ImportIncomplete) && !qmldirIdentifier.isEmpty()) {
1346 if (!getQmldirContent(typeLoader, qmldirIdentifier, importUri, &qmldir, errors))
1347 return QTypeRevision();
1348
1349 if (qmldir.hasContent()) {
1350 // Prefer the qmldir URI. Unless it doesn't exist.
1351 const QString qmldirUri = qmldir.typeNamespace();
1352 if (!qmldirUri.isEmpty())
1353 importUri = qmldirUri;
1354
1356 nameSpace, importUri, url, version, QV4::CompiledData::Import::ImportFile,
1357 errors, precedence);
1358 Q_ASSERT(inserted);
1359
1360 version = importExtension(typeLoader, importUri, version, &qmldir, errors);
1361 if (!version.isValid())
1362 return QTypeRevision();
1363
1364 if (qmldir.hasRedirection())
1365 url = redirectQmldirContent(typeLoader, &qmldir);
1366
1367 if (!inserted->setQmldirContent(url, qmldir, nameSpace, errors))
1368 return QTypeRevision();
1369
1370 return validVersion(version);
1371 }
1372 }
1373
1375 nameSpace, importUri, url, version, QV4::CompiledData::Import::ImportFile,
1376 errors, precedence);
1377 Q_ASSERT(inserted);
1378 return validVersion(version);
1379}
1380
1382 QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
1383 const QString &qmldirIdentifier, const QString &qmldirUrl, QList<QQmlError> *errors)
1384{
1385 Q_ASSERT(typeLoader);
1386 Q_ASSERT(errors);
1387
1389 << "updateQmldirContent:" << qPrintable(baseUrl().toString())
1390 << uri << "to" << qmldirUrl << "as" << prefix;
1391
1392 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1393 Q_ASSERT(nameSpace);
1394
1395 if (QQmlImportInstance *import = nameSpace->findImport(uri)) {
1397 if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors))
1398 return QTypeRevision();
1399
1400 if (qmldir.hasContent()) {
1401 QTypeRevision version = importExtension(
1402 typeLoader, uri, import->version, &qmldir, errors);
1403 if (!version.isValid())
1404 return QTypeRevision();
1405
1406 const QString resolvedUrl = qmldir.hasRedirection()
1407 ? redirectQmldirContent(typeLoader, &qmldir)
1408 : qmldirUrl;
1409
1410 if (import->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) {
1411 if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
1412 // The implicit import qmldir can be empty, and plugins have no extra versions
1413 if (uri != QLatin1String(".") && !QQmlMetaType::matchingModuleVersion(uri, version).isValid()) {
1414 errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version)));
1415 return QTypeRevision();
1416 }
1417 } else {
1418 // Verify that the qmldir content is valid for this version
1419 version = matchingQmldirVersion(qmldir, uri, version, errors);
1420 if (!version.isValid())
1421 return QTypeRevision();
1422 }
1423 return validVersion(version);
1424 }
1425 }
1426 }
1427
1428 if (errors->isEmpty()) {
1430 error.setDescription(QQmlTypeLoader::tr("Cannot update qmldir content for '%1'").arg(uri));
1431 errors->prepend(error);
1432 }
1433
1434 return QTypeRevision();
1435}
1436
1451bool QQmlImports::addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl)
1452{
1453 importInstance->url = importUrl.toString();
1454 importInstance->uri = name;
1455 importInstance->isInlineComponent = true;
1456 importInstance->version = QTypeRevision::zero();
1457 m_unqualifiedset.imports.push_back(importInstance);
1458 m_unqualifiedset.setNeedsSorting(true);
1459 return true;
1460}
1461
1463{
1464 QUrl url(QLatin1String(file.at(0) == Colon ? "qrc" : "") + file);
1465
1466 // We don't support single character schemes as those conflict with windows drive letters.
1467 if (url.scheme().size() < 2)
1468 return QUrl::fromLocalFile(file);
1469 return url;
1470}
1471
1476
1477static QStringList parseEnvPath(const QString &envImportPath)
1478{
1479 if (QDir::listSeparator() == u':') {
1480 // Double colons are interpreted as separator + resource path.
1481 QStringList paths = envImportPath.split(u':');
1482 bool wasEmpty = false;
1483 for (auto it = paths.begin(); it != paths.end();) {
1484 if (it->isEmpty()) {
1485 wasEmpty = true;
1486 it = paths.erase(it);
1487 } else {
1488 if (wasEmpty) {
1489 it->prepend(u':');
1490 wasEmpty = false;
1491 }
1492 ++it;
1493 }
1494 }
1495 return paths;
1496 } else {
1497 return envImportPath.split(QDir::listSeparator(), Qt::SkipEmptyParts);
1498 }
1499}
1500
1507: engine(e)
1508{
1509 filePluginPath << QLatin1String(".");
1510 // Search order is:
1511 // 1. android or macos specific bundle paths.
1512 // 2. applicationDirPath()
1513 // 3. qrc:/qt-project.org/imports
1514 // 4. qrc:/qt/qml
1515 // 5. $QML2_IMPORT_PATH
1516 // 6. $QML_IMPORT_PATH
1517 // 7. QLibraryInfo::QmlImportsPath
1518
1520 addImportPath(installImportsPath);
1521
1522 auto addEnvImportPath = [this](const char *var) {
1525 for (int ii = paths.size() - 1; ii >= 0; --ii)
1526 addImportPath(paths.at(ii));
1527 }
1528 };
1529
1530 // env import paths
1531 addEnvImportPath("QML_IMPORT_PATH");
1532 addEnvImportPath("QML2_IMPORT_PATH");
1533
1534 addImportPath(QStringLiteral("qrc:/qt/qml"));
1535 addImportPath(QStringLiteral("qrc:/qt-project.org/imports"));
1537
1538 auto addEnvPluginPath = [this](const char *var) {
1541 for (int ii = paths.size() - 1; ii >= 0; --ii)
1542 addPluginPath(paths.at(ii));
1543 }
1544 };
1545
1546 addEnvPluginPath("QML_PLUGIN_PATH");
1547#if defined(Q_OS_ANDROID)
1548 addImportPath(QStringLiteral("qrc:/android_rcc_bundle/qml"));
1549 addEnvPluginPath("QT_BUNDLED_LIBS_PATH");
1550#elif defined(Q_OS_MACOS)
1551 // Add the main bundle's Resources/qml directory as an import path, so that QML modules are
1552 // found successfully when running the app from its build dir.
1553 // This is where macdeployqt and our CMake deployment logic puts Qt and user qmldir files.
1554 if (CFBundleRef bundleRef = CFBundleGetMainBundle()) {
1555 if (QCFType<CFURLRef> urlRef = CFBundleCopyResourceURL(
1556 bundleRef,
1557 QCFString(QLatin1String("qml")), 0, 0)) {
1558 if (QCFType<CFURLRef> absoluteUrlRef = CFURLCopyAbsoluteURL(urlRef)) {
1559 if (QCFString path = CFURLCopyFileSystemPath(absoluteUrlRef, kCFURLPOSIXPathStyle)) {
1560 if (QFile::exists(path)) {
1562 }
1563 }
1564 }
1565 }
1566 }
1567#endif // Q_OS_DARWIN
1568}
1569
1574{
1575 qCDebug(lcQmlImport) << "setPluginPathList:" << paths;
1576 filePluginPath = paths;
1577}
1578
1583{
1584 qCDebug(lcQmlImport) << "addPluginPath:" << path;
1585
1586 QUrl url = QUrl(path);
1587 if (url.isRelative() || url.scheme() == QLatin1String("file")
1588 || (url.scheme().size() == 1 && QFile::exists(path)) ) { // windows path
1589 QDir dir = QDir(path);
1590 filePluginPath.prepend(dir.canonicalPath());
1591 } else {
1592 filePluginPath.prepend(path);
1593 }
1594}
1595
1596QString QQmlImportDatabase::absoluteFilePath(const QString &path) const
1597{
1598 return QQmlEnginePrivate::get(engine)->typeLoader.absoluteFilePath(path);
1599}
1600
1605{
1606 qCDebug(lcQmlImport) << "addImportPath:" << path;
1607
1608 if (path.isEmpty())
1609 return;
1610
1611 QUrl url = QUrl(path);
1612 QString cPath;
1613
1614 if (url.scheme() == QLatin1String("file")) {
1616 } else if (path.startsWith(QLatin1Char(':'))) {
1617 // qrc directory, e.g. :/foo
1618 // need to convert to a qrc url, e.g. qrc:/foo
1619 cPath = QLatin1String("qrc") + path;
1620 cPath.replace(Backslash, Slash);
1621 } else if (url.isRelative() ||
1622 (url.scheme().size() == 1 && QFile::exists(path)) ) { // windows path
1623 QDir dir = QDir(path);
1624 cPath = dir.canonicalPath();
1625 } else {
1626 cPath = path;
1627 cPath.replace(Backslash, Slash);
1628 }
1629
1630 if (!cPath.isEmpty()) {
1631 if (fileImportPath.contains(cPath))
1632 fileImportPath.move(fileImportPath.indexOf(cPath), 0);
1633 else
1634 fileImportPath.prepend(cPath);
1635 }
1636}
1637
1642{
1643 if (type == LocalOrRemote)
1644 return fileImportPath;
1645
1647 for (const QString &path : fileImportPath) {
1648 bool localPath = isPathAbsolute(path) || QQmlFile::isLocalFile(path);
1649 if (localPath == (type == Local))
1650 list.append(path);
1651 }
1652
1653 return list;
1654}
1655
1660{
1661 qCDebug(lcQmlImport) << "setImportPathList:" << paths;
1662
1663 fileImportPath.clear();
1664 for (auto it = paths.crbegin(); it != paths.crend(); ++it)
1665 addImportPath(*it);
1666
1667 // Our existing cached paths may have been invalidated
1668 clearDirCache();
1669}
1670
1675 QTypeRevision version, QList<QQmlError> *errors)
1676{
1677 if (!version.hasMajorVersion()) {
1678 version = QQmlMetaType::latestModuleVersion(uri);
1679 if (!version.isValid())
1680 errors->prepend(moduleNotFoundError(uri, version));
1681 }
1682 if (version.hasMajorVersion() && !typeNamespace.isEmpty()
1683 && !QQmlMetaType::protectModule(uri, version, true)) {
1684 // Not being able to protect the module means there are not types registered for it,
1685 // means the plugin we loaded didn't provide any, means we didn't find the module.
1686 // We output the generic error message as depending on the load order of imports we may
1687 // hit this path or another one that only figures "plugin is already loaded but module
1688 // unavailable" and doesn't try to protect it anymore.
1689 errors->prepend(moduleNotFoundError(uri, version));
1690 return QTypeRevision();
1691 }
1692
1693 return version;
1694}
1695
1697{
1698 return QQmlPluginImporter::removePlugin(pluginId);
1699}
1700
1705
1706void QQmlImportDatabase::clearDirCache()
1707{
1709 while (itr != qmldirCache.constEnd()) {
1710 QmldirCache *cache = *itr;
1711 do {
1712 QmldirCache *nextCache = cache->next;
1713 delete cache;
1714 cache = nextCache;
1715 } while (cache);
1716
1717 ++itr;
1718 }
1719 qmldirCache.clear();
1720}
1721
static QString applicationDirPath()
Returns the directory that contains the application executable.
\inmodule QtCore
Definition qdir.h:20
static constexpr QChar listSeparator() noexcept
Definition qdir.h:200
static N * next(N *v)
N * first() const
void append(N *)
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
static QString path(LibraryPath p)
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:488
void push_back(parameter_type t)
Definition qlist.h:675
iterator end()
Definition qlist.h:626
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
const_iterator constBegin() const noexcept
Definition qlist.h:632
iterator begin()
Definition qlist.h:625
const_iterator cend() const noexcept
Definition qlist.h:631
void append(parameter_type t)
Definition qlist.h:458
const_iterator constEnd() const noexcept
Definition qlist.h:633
const_iterator cbegin() const noexcept
Definition qlist.h:630
\inmodule QtCore
iterator find(const Key &key)
Definition qhash.h:2021
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1922
static QQmlEnginePrivate * get(QQmlEngine *e)
The QQmlEngine class provides an environment for instantiating QML components.
Definition qqmlengine.h:57
QUrl interceptUrl(const QUrl &url, QQmlAbstractUrlInterceptor::DataType type) const
Run the current URL interceptors on the given url of the given type and return the result.
The QQmlError class encapsulates a QML error.
Definition qqmlerror.h:18
void setDescription(const QString &)
Sets the error description.
static bool isLocalFile(const QString &url)
Returns true if url is a local file that can be opened with \l{QFile}.
Definition qqmlfile.cpp:644
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to \l{QFile}.
Definition qqmlfile.cpp:742
The QQmlImportDatabase class manages the QML imports for a QQmlEngine.
void addPluginPath(const QString &path)
QStringList dynamicPlugins() const
QStringList importPathList(PathType type=LocalOrRemote) const
bool removeDynamicPlugin(const QString &pluginId)
void setPluginPathList(const QStringList &paths)
void addImportPath(const QString &dir)
static QTypeRevision lockModule(const QString &uri, const QString &typeNamespace, QTypeRevision version, QList< QQmlError > *errors)
QQmlImportDatabase(QQmlEngine *)
void setImportPathList(const QStringList &paths)
bool needsSorting() const
QQmlImportInstance * findImport(const QString &uri) const
QHashedString prefix
bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QTypeRevision *version_return, QQmlType *type_return, const QString *base=nullptr, QList< QQmlError > *errors=nullptr, QQmlType::RegistrationType registrationType=QQmlType::AnyRegistrationType, bool *typeRecursionDeteced=nullptr)
QList< QQmlImportInstance * > imports
void setNeedsSorting(bool needsSorting)
void populateCache(QQmlTypeNameCache *cache) const
QList< CompositeSingletonReference > resolvedCompositeSingletons() const
static QString versionString(QTypeRevision version, ImportVersion importVersion)
QList< ScriptReference > resolvedScripts() const
QTypeRevision updateQmldirContent(QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix, const QString &qmldirIdentifier, const QString &qmldirUrl, QList< QQmlError > *errors)
bool addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl)
void setBaseUrl(const QUrl &url, const QString &urlString=QString())
Sets the base URL to be used for all relative file imports added.
QUrl baseUrl() const
static QUrl urlFromLocalFileOrQrcOrUrl(const QString &)
static void setDesignerSupportRequired(bool b)
bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QQmlType *type_return, QTypeRevision *version_return, QQmlImportNamespace **ns_return, QList< QQmlError > *errors=nullptr, QQmlType::RegistrationType registrationType=QQmlType::AnyRegistrationType, bool *typeRecursionDetected=nullptr) const
QTypeRevision addFileImport(QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix, QTypeRevision version, ImportFlags flags, quint16 precedence, QString *localQmldir, QList< QQmlError > *errors)
QTypeRevision addLibraryImport(QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix, QTypeRevision version, const QString &qmldirIdentifier, const QString &qmldirUrl, ImportFlags flags, quint16 precedence, QList< QQmlError > *errors)
static QStringList completeQmldirPaths(const QString &uri, const QStringList &basePaths, QTypeRevision version)
Forms complete paths to a qmldir file, from a base URL, a module URI and version specification.
static QTypeRevision validVersion(QTypeRevision version=QTypeRevision())
static QQmlType inlineComponentType(const QQmlType &outerType, const QString &name)
static bool protectModule(const QString &uri, QTypeRevision version, bool weakProtectAllVersions=false)
static QQmlType fetchOrCreateInlineComponentTypeForUrl(const QUrl &url)
static QTypeRevision matchingModuleVersion(const QString &module, QTypeRevision version)
static QTypeRevision latestModuleVersion(const QString &uri)
static QQmlTypeModule * typeModule(const QString &uri, QTypeRevision version)
static bool qmlRegisterModuleTypes(const QString &uri)
static QQmlType typeForUrl(const QString &urlString, const QHashedStringRef &typeName, CompositeTypeLookupMode mode, QList< QQmlError > *errors, QTypeRevision version=QTypeRevision())
static QQmlType qmlType(const QString &qualifiedName, QTypeRevision version)
Returns the type (if any) of URI-qualified named qualifiedName and version specified by version_major...
static bool removePlugin(const QString &pluginId)
static QStringList plugins()
QList< QQmlError > errors(const QString &uri, const QUrl &url) const
The QQmlTypeLoader class abstracts loading files and their dependencies over the network.
QQmlEngine * engine() const
Return the QQmlEngine associated with this loader.
QString absoluteFilePath(const QString &path)
Returns the absolute filename of path via a directory cache.
QQmlImportDatabase * importDatabase() const
bool directoryExists(const QString &path)
Returns true if the path is a directory via a directory cache.
const QQmlTypeLoaderQmldirContent qmldirContent(const QString &filePath)
Return a QQmlTypeLoaderQmldirContent for absoluteFilePath.
bool fileExists(const QString &path, const QString &file)
@ AnyRegistrationType
Definition qqmltype_p.h:167
@ CompositeSingletonType
Definition qqmltype_p.h:164
bool isValid() const
Definition qqmltype_p.h:54
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
bool isEmpty() const
Definition qset.h:52
iterator erase(const_iterator i)
Definition qset.h:145
ConstIterator constEnd() const
ConstIterator constBegin() const
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
constexpr QStringView left(qsizetype n) const noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString left(qsizetype n) const &
Definition qstring.h:363
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:296
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5455
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3824
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6340
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5506
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1369
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7263
\inmodule QtCore
static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion)
Produces a QTypeRevision from the given majorVersion and minorVersion, both of which need to be a val...
static constexpr QTypeRevision fromMinorVersion(Minor minorVersion)
Produces a QTypeRevision from the given minorVersion with an invalid major version.
constexpr bool hasMinorVersion() const
Returns true if the minor version is known, otherwise false.
static constexpr QTypeRevision zero()
Produces a QTypeRevision with major and minor version {0}.
constexpr bool hasMajorVersion() const
Returns true if the major version is known, otherwise false.
constexpr quint8 minorVersion() const
Returns the minor version encoded in the revision.
constexpr bool isValid() const
Returns true if the major version or the minor version is known, otherwise false.
constexpr quint8 majorVersion() const
Returns the major version encoded in the revision.
\inmodule QtCore
Definition qurl.h:94
static QUrl fromLocalFile(const QString &localfile)
Returns a QUrl representation of localFile, interpreted as a local file.
Definition qurl.cpp:3368
QUrl resolved(const QUrl &relative) const
Returns the result of the merge of this URL with relative.
Definition qurl.cpp:2725
bool isRelative() const
Returns true if the URL is relative; otherwise returns false.
Definition qurl.cpp:2800
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1896
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1991
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2831
const QLoggingCategory & category() const
~QmlImportCategoryHolder()=default
QCache< int, Employee > cache
[0]
QSet< QString >::iterator it
const PluginKeyMapConstIterator cend
Combined button and popup list for selecting options.
@ SkipEmptyParts
Definition qnamespace.h:128
#define Q_UNLIKELY(x)
DBusConnection const char DBusError * error
#define qDebug
[1]
Definition qlogging.h:164
@ QtDebugMsg
Definition qlogging.h:30
#define qCDebug(category,...)
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLint GLenum GLint components
GLuint index
[2]
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLenum type
GLsizei const GLuint * paths
GLbitfield flags
GLint ref
GLuint name
GLdouble s
[6]
Definition qopenglext.h:235
GLfixed GLfixed u2
const GLubyte * c
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLfixed u1
static qreal dot(const QPointF &a, const QPointF &b)
bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn)
Returns true if the case of fileName is equivalent to the file case of fileName on disk,...
#define DEFINE_BOOL_CONFIG_OPTION(name, var)
#define RESOLVE_TYPE_DEBUG
static const QLatin1Char Backslash('\\')
static const QLatin1Char Dot('.')
static const QString dotuidotqml_string(QStringLiteral(".ui.qml"))
static const QLatin1Char Colon(':')
static const QLatin1String Slash_qmldir("/qmldir")
static const QLatin1Char Slash('/')
void findCompositeSingletons(const QQmlImportNamespace &set, QList< QQmlImports::CompositeSingletonReference > &resultList, const QUrl &baseUrl)
static bool designerSupportRequired
static QQmlImportInstance * addImportToNamespace(QQmlImportNamespace *nameSpace, const QString &uri, const QString &url, QTypeRevision version, QV4::CompiledData::Import::ImportType type, QList< QQmlError > *errors, quint16 precedence)
bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QString &baseUrl)
static const QLatin1String String_qmldir("qmldir")
static QStringList parseEnvPath(const QString &envImportPath)
static const QString dotqml_string(QStringLiteral(".qml"))
const QLoggingCategory & lcQmlImport()
const QLoggingCategory & lcQmlImport()
QStringList qQmlResolveImportPaths(QStringView uri, const QStringList &basePaths, QTypeRevision version)
static QUrl resolvedUrl(const QUrl &url, const QQmlRefPointer< QQmlContextData > &context)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static QLatin1StringView typeStr(QShaderDescription::VariableType t)
SSL_CTX int void * arg
static QString canonicalPath(const QString &rootPath)
#define qPrintable(string)
Definition qstring.h:1531
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
Q_CORE_EXPORT bool qEnvironmentVariableIsEmpty(const char *varName) noexcept
#define Q_UNUSED(x)
unsigned short quint16
Definition qtypes.h:48
unsigned char quint8
Definition qtypes.h:46
static const uint base
Definition qurlidna.cpp:20
static QV4::CompiledData::Lookup::Mode lookupMode(QV4::Compiler::JSUnitGenerator::LookupMode mode)
QList< int > list
[14]
QFuture< QSet< QChar > > set
[10]
QFile file
[0]
QUrl url("example.com")
[constructor-url-reference]
QUrl baseUrl
QString dir
[11]
char * toString(const MyType &t)
[31]
QJSEngine engine
[0]
\inmodule QtCore \reentrant
Definition qchar.h:18
QQmlDirComponents qmlDirComponents
bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QTypeRevision *version_return, QQmlType *type_return, const QString *base=nullptr, bool *typeRecursionDetected=nullptr, QQmlType::RegistrationType=QQmlType::AnyRegistrationType, QQmlImport::RecursionRestriction recursionRestriction=QQmlImport::PreventRecursion, QList< QQmlError > *errors=nullptr) const
bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent &qmldir, QQmlImportNamespace *nameSpace, QList< QQmlError > *errors)
QQmlDirScripts qmlDirScripts
QTypeRevision version
static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, QTypeRevision version)