1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qqmlimport_p.h"
44 #include <QtCore/qdebug.h>
45 #include <QtCore/qdir.h>
46 #include <QtQml/qqmlfile.h>
47 #include <QtCore/qfileinfo.h>
48 #include <QtCore/qpluginloader.h>
49 #include <QtCore/qlibraryinfo.h>
50 #include <QtCore/qreadwritelock.h>
51 #include <QtQml/qqmlextensioninterface.h>
52 #include <private/qqmlglobal_p.h>
53 #include <private/qqmltypenamecache_p.h>
54 #include <private/qqmlengine_p.h>
55 #include <private/qfieldlist_p.h>
59 DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
60 DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
62 static const QLatin1Char Dot('.');
63 static const QLatin1Char Slash('/');
64 static const QLatin1Char Backslash('\\');
65 static const QLatin1Char Colon(':');
66 static const QLatin1String Slash_qmldir("/qmldir");
67 static const QLatin1String String_qmldir("qmldir");
68 static const QString dotqml_string(QLatin1String(".qml"));
72 QString resolveLocalUrl(const QString &url, const QString &relative)
74 if (relative.contains(Colon)) {
75 // contains a host name
76 return QUrl(url).resolved(QUrl(relative)).toString();
77 } else if (relative.isEmpty()) {
79 } else if (relative.at(0) == Slash || !url.contains(Slash)) {
82 QString base(url.left(url.lastIndexOf(Slash) + 1));
84 if (relative == QLatin1String("."))
87 base.append(relative);
89 // Remove any relative directory elements in the path
90 int length = base.length();
92 while ((index = base.indexOf(QLatin1String("/."), index)) != -1) {
93 if ((length > (index + 2)) && (base.at(index + 2) == Dot) &&
94 (length == (index + 3) || (base.at(index + 3) == Slash))) {
95 // Either "/../" or "/..<END>"
96 int previous = base.lastIndexOf(Slash, index - 1);
100 int removeLength = (index - previous) + 3;
101 base.remove(previous + 1, removeLength);
102 length -= removeLength;
104 } else if ((length == (index + 2)) || (base.at(index + 2) == Slash)) {
105 // Either "/./" or "/.<END>"
106 base.remove(index, 2);
117 bool isPathAbsolute(const QString &path)
119 #if defined(Q_OS_UNIX)
120 return (path.at(0) == Slash);
123 return fi.isAbsolute();
129 typedef QMap<QString, QString> StringStringMap;
130 Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
132 class QQmlImportNamespace
135 QQmlImportNamespace() : nextNamespace(0) {}
136 ~QQmlImportNamespace() { qDeleteAll(imports); }
144 QQmlDirComponents qmlDirComponents;
145 QQmlDirScripts qmlDirScripts;
147 bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoader::QmldirContent *qmldir,
148 QQmlImportNamespace *nameSpace, QList<QQmlError> *errors);
150 static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin);
152 bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type,
153 int *vmajor, int *vminor,
154 QQmlType** type_return, QString* url_return,
155 QString *base = 0, bool *typeRecursionDetected = 0) const;
157 QList<Import *> imports;
159 Import *findImport(const QString &uri);
161 bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type,
162 int *vmajor, int *vminor,
163 QQmlType** type_return, QString* url_return,
164 QString *base = 0, QList<QQmlError> *errors = 0);
166 // Prefix when used as a qualified import. Otherwise empty.
167 QHashedString prefix;
169 // Used by QQmlImportsPrivate::qualifiedSets
170 QQmlImportNamespace *nextNamespace;
173 class QQmlImportsPrivate
176 QQmlImportsPrivate(QQmlTypeLoader *loader);
177 ~QQmlImportsPrivate();
179 QQmlImportNamespace *importNamespace(const QString &prefix) const;
181 bool addLibraryImport(const QString& uri, const QString &prefix,
182 int vmaj, int vmin, const QString &qmldirIdentifier, const QString &qmldirUrl, bool incomplete,
183 QQmlImportDatabase *database,
184 QList<QQmlError> *errors);
186 bool addFileImport(const QString &uri, const QString &prefix,
188 bool isImplicitImport, bool incomplete, QQmlImportDatabase *database,
189 QList<QQmlError> *errors);
191 bool updateQmldirContent(const QString &uri, const QString &prefix,
192 const QString &qmldirIdentifier, const QString& qmldirUrl,
193 QQmlImportDatabase *database,
194 QList<QQmlError> *errors);
196 bool resolveType(const QHashedStringRef &type, int *vmajor, int *vminor,
197 QQmlType** type_return, QString* url_return,
198 QList<QQmlError> *errors);
204 mutable QQmlImportNamespace unqualifiedset;
206 QQmlImportNamespace *findQualifiedNamespace(const QHashedStringRef &) const;
207 mutable QFieldList<QQmlImportNamespace, &QQmlImportNamespace::nextNamespace> qualifiedSets;
209 QQmlTypeLoader *typeLoader;
211 static inline QString tr(const char *str) {
212 return QQmlImportDatabase::tr(str);
215 static bool locateQmldir(const QString &uri, int vmaj, int vmin,
216 QQmlImportDatabase *database,
217 QString *outQmldirFilePath, QString *outUrl);
219 static bool validateQmldirVersion(const QQmlTypeLoader::QmldirContent *qmldir, const QString &uri, int vmaj, int vmin,
220 QList<QQmlError> *errors);
222 bool importExtension(const QString &absoluteFilePath, const QString &uri,
223 QQmlImportDatabase *database,
224 const QQmlTypeLoader::QmldirContent *qmldir,
225 QList<QQmlError> *errors);
227 bool getQmldirContent(const QString &qmldirIdentifier, const QString &uri,
228 const QQmlTypeLoader::QmldirContent **qmldir, QList<QQmlError> *errors);
230 QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database);
232 QQmlImportNamespace::Import *addImportToNamespace(QQmlImportNamespace *nameSpace,
233 const QString &uri, const QString &url,
234 int vmaj, int vmin, QQmlScript::Import::Type type,
235 QList<QQmlError> *errors);
240 \brief The QQmlImports class encapsulates one QML document's import statements.
243 QQmlImports::QQmlImports(const QQmlImports ©)
250 QQmlImports::operator =(const QQmlImports ©)
259 QQmlImports::QQmlImports(QQmlTypeLoader *typeLoader)
260 : d(new QQmlImportsPrivate(typeLoader))
264 QQmlImports::~QQmlImports()
271 Sets the base URL to be used for all relative file imports added.
273 void QQmlImports::setBaseUrl(const QUrl& url, const QString &urlString)
277 if (urlString.isEmpty()) {
278 d->base = url.toString();
280 //Q_ASSERT(url.toString() == urlString);
286 Returns the base URL to be used for all relative file imports added.
288 QUrl QQmlImports::baseUrl() const
293 void QQmlImports::populateCache(QQmlTypeNameCache *cache, QQmlEngine *engine) const
295 const QQmlImportNamespace &set = d->unqualifiedset;
297 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
298 const QQmlImportNamespace::Import *import = set.imports.at(ii);
299 QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion);
301 cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, import->minversion));
303 QQmlMetaType::SingletonType singletonType = QQmlMetaType::singletonType(import->uri, import->majversion,
305 if (singletonType.script || singletonType.qobject) {
306 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
307 QQmlMetaType::SingletonInstance *apiInstance = ep->singletonTypeInstance(singletonType);
309 cache->addSingletonType(singletonType.typeName, apiInstance);
313 for (QQmlImportNamespace *ns = d->qualifiedSets.first(); ns; ns = d->qualifiedSets.next(ns)) {
315 const QQmlImportNamespace &set = *ns;
317 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
318 const QQmlImportNamespace::Import *import = set.imports.at(ii);
319 QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion);
321 QQmlTypeNameCache::Import &typeimport = cache->m_namedImports[set.prefix];
322 typeimport.modules.append(QQmlTypeModuleVersion(module, import->minversion));
325 QQmlMetaType::SingletonType singletonType = QQmlMetaType::singletonType(import->uri, import->majversion,
327 if (singletonType.script || singletonType.qobject) {
328 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
329 QQmlMetaType::SingletonInstance *apiInstance = ep->singletonTypeInstance(singletonType);
331 cache->add(set.prefix);
332 cache->addSingletonType(singletonType.typeName, apiInstance, set.prefix);
338 QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const
340 QList<QQmlImports::ScriptReference> scripts;
342 const QQmlImportNamespace &set = d->unqualifiedset;
344 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
345 const QQmlImportNamespace::Import *import = set.imports.at(ii);
347 foreach (const QQmlDirParser::Script &script, import->qmlDirScripts) {
349 ref.nameSpace = script.nameSpace;
350 ref.location = QUrl(import->url).resolved(QUrl(script.fileName));
355 for (QQmlImportNamespace *ns = d->qualifiedSets.first(); ns; ns = d->qualifiedSets.next(ns)) {
356 const QQmlImportNamespace &set = *ns;
358 for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
359 const QQmlImportNamespace::Import *import = set.imports.at(ii);
361 foreach (const QQmlDirParser::Script &script, import->qmlDirScripts) {
363 ref.nameSpace = script.nameSpace;
364 ref.qualifier = set.prefix;
365 ref.location = QUrl(import->url).resolved(QUrl(script.fileName));
375 Form a complete path to a qmldir file, from a base URL, a module URI and version specification.
377 QString QQmlImports::completeQmldirPath(const QString &uri, const QString &base, int vmaj, int vmin,
378 ImportVersion version)
381 url.replace(Dot, Slash);
384 if (!dir.endsWith(Slash) && !dir.endsWith(Backslash))
388 if (version == QQmlImports::FullyVersioned) {
389 // extension with fully encoded version number (eg. MyModule.3.2)
390 dir += QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin);
391 } else if (version == QQmlImports::PartiallyVersioned) {
392 // extension with encoded version major (eg. MyModule.3)
393 dir += QString(QLatin1String(".%1")).arg(vmaj);
394 } // else extension without version number (eg. MyModule)
396 return dir + Slash_qmldir;
402 The given (namespace qualified) \a type is resolved to either
404 \li a QQmlImportNamespace stored at \a ns_return,
405 \li a QQmlType stored at \a type_return, or
406 \li a component located at \a url_return.
409 If any return pointer is 0, the corresponding search is not done.
411 \sa addFileImport(), addLibraryImport
413 bool QQmlImports::resolveType(const QHashedStringRef &type,
414 QQmlType** type_return, QString* url_return, int *vmaj, int *vmin,
415 QQmlImportNamespace** ns_return, QList<QQmlError> *errors) const
417 QQmlImportNamespace* ns = d->findQualifiedNamespace(type);
423 if (type_return || url_return) {
424 if (d->resolveType(type,vmaj,vmin,type_return,url_return, errors)) {
425 if (qmlImportTrace()) {
426 #define RESOLVE_TYPE_DEBUG qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) \
427 << ')' << "::resolveType: " << type.toString() << " => "
429 if (type_return && *type_return && url_return && !url_return->isEmpty())
430 RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << ' ' << *url_return << " TYPE/URL";
431 if (type_return && *type_return)
432 RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << " TYPE";
433 if (url_return && !url_return->isEmpty())
434 RESOLVE_TYPE_DEBUG << *url_return << " URL";
436 #undef RESOLVE_TYPE_DEBUG
444 bool QQmlImportNamespace::Import::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoader::QmldirContent *qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors)
446 Q_ASSERT(resolvedUrl.endsWith(Slash));
449 qmlDirComponents = qmldir->components();
451 const QQmlDirScripts &scripts = qmldir->scripts();
452 if (!scripts.isEmpty()) {
453 // Verify that we haven't imported these scripts already
454 for (QList<QQmlImportNamespace::Import *>::const_iterator it = nameSpace->imports.constBegin();
455 it != nameSpace->imports.constEnd(); ++it) {
456 if ((*it != this) && ((*it)->uri == uri)) {
458 error.setDescription(QQmlImportDatabase::tr("\"%1\" is ambiguous. Found in %2 and in %3").arg(uri).arg(url).arg((*it)->url));
459 errors->prepend(error);
464 qmlDirScripts = getVersionedScripts(scripts, majversion, minversion);
470 QQmlDirScripts QQmlImportNamespace::Import::getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin)
472 QMap<QString, QQmlDirParser::Script> versioned;
474 for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin();
475 sit != qmldirscripts.constEnd(); ++sit) {
476 // Only include scripts that match our requested version
477 if (((vmaj == -1) || (sit->majorVersion == vmaj)) &&
478 ((vmin == -1) || (sit->minorVersion <= vmin))) {
479 // Load the highest version that matches
480 QMap<QString, QQmlDirParser::Script>::iterator vit = versioned.find(sit->nameSpace);
481 if (vit == versioned.end() || (vit->minorVersion < sit->minorVersion)) {
482 versioned.insert(sit->nameSpace, *sit);
487 return versioned.values();
493 Searching \e only in the namespace \a ns (previously returned in a call to
494 resolveType(), \a type is found and returned to either
495 a QQmlType stored at \a type_return, or
496 a component located at \a url_return.
498 If either return pointer is 0, the corresponding search is not done.
500 bool QQmlImports::resolveType(QQmlImportNamespace* ns, const QHashedStringRef &type,
501 QQmlType** type_return, QString* url_return,
502 int *vmaj, int *vmin) const
504 return ns->resolveType(d->typeLoader,type,vmaj,vmin,type_return,url_return);
507 bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader,
508 const QHashedStringRef& type, int *vmajor, int *vminor,
509 QQmlType** type_return, QString* url_return,
510 QString *base, bool *typeRecursionDetected) const
512 if (majversion >= 0 && minversion >= 0) {
513 QQmlType *t = QQmlMetaType::qmlType(type, uri, majversion, minversion);
515 if (vmajor) *vmajor = majversion;
516 if (vminor) *vminor = minversion;
523 QQmlDirComponents::ConstIterator it = qmlDirComponents.find(type), end = qmlDirComponents.end();
525 QString componentUrl;
526 QQmlDirComponents::ConstIterator candidate = end;
527 for ( ; it != end && it.key() == type; ++it) {
528 const QQmlDirParser::Component &c = *it;
530 // importing version -1 means import ALL versions
531 if ((majversion == -1) ||
532 (c.majorVersion == majversion && c.minorVersion <= minversion)) {
533 // Is this better than the previous candidate?
534 if ((candidate == end) ||
535 (c.majorVersion > candidate->majorVersion) ||
536 ((c.majorVersion == candidate->majorVersion) && (c.minorVersion > candidate->minorVersion))) {
537 componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName);
538 if (c.internal && base) {
539 if (resolveLocalUrl(*base, c.fileName) != componentUrl)
540 continue; // failed attempt to access an internal type
542 if (base && (*base == componentUrl)) {
543 if (typeRecursionDetected)
544 *typeRecursionDetected = true;
545 continue; // no recursion
548 // This is our best candidate so far
554 if (candidate != end) {
556 *url_return = componentUrl;
559 } else if (!isLibrary) {
560 QString qmlUrl = url + QString::fromRawData(type.constData(), type.length()) + dotqml_string;
564 if (QQmlFile::isBundle(qmlUrl)) {
565 exists = QQmlFile::bundleFileExists(qmlUrl, typeLoader->engine());
567 exists = !typeLoader->absoluteFilePath(QQmlFile::urlToLocalFileOrQrc(qmlUrl)).isEmpty();
571 if (base && (*base == qmlUrl)) { // no recursion
572 if (typeRecursionDetected)
573 *typeRecursionDetected = true;
576 *url_return = qmlUrl;
585 bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, int *vminor,
586 QQmlType** type_return, QString* url_return,
587 QList<QQmlError> *errors)
589 QQmlImportNamespace *s = 0;
590 int dot = type.indexOf(Dot);
592 QHashedStringRef namespaceName(type.constData(), dot);
593 s = findQualifiedNamespace(namespaceName);
597 error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(namespaceName.toString()));
598 errors->prepend(error);
602 int ndot = type.indexOf(Dot,dot+1);
606 error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
607 errors->prepend(error);
614 QHashedStringRef unqualifiedtype = dot < 0 ? type : QHashedStringRef(type.constData()+dot+1, type.length()-dot-1);
616 if (s->resolveType(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
618 if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && url_return && s != &unqualifiedset) {
619 // qualified, and only 1 url
620 *url_return = resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml"));
628 QQmlImportNamespace::Import *QQmlImportNamespace::findImport(const QString &uri)
630 for (QList<Import *>::iterator it = imports.begin(), end = imports.end(); it != end; ++it) {
631 if ((*it)->uri == uri)
638 bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type,
639 int *vmajor, int *vminor, QQmlType** type_return,
640 QString* url_return, QString *base, QList<QQmlError> *errors)
642 bool typeRecursionDetected = false;
643 for (int i=0; i<imports.count(); ++i) {
644 const Import *import = imports.at(i);
645 if (import->resolveType(typeLoader, type, vmajor, vminor, type_return, url_return,
646 base, &typeRecursionDetected)) {
647 if (qmlCheckTypes()) {
648 // check for type clashes
649 for (int j = i+1; j<imports.count(); ++j) {
650 const Import *import2 = imports.at(j);
651 if (import2->resolveType(typeLoader, type, vmajor, vminor, 0, 0, base)) {
653 QString u1 = import->url;
654 QString u2 = import2->url;
657 int dot = b.lastIndexOf(Dot);
660 QString l = b.left(dot);
661 if (u1.startsWith(b))
662 u1 = u1.mid(b.count());
664 u1 = QQmlImportDatabase::tr("local directory");
665 if (u2.startsWith(b))
666 u2 = u2.mid(b.count());
668 u2 = QQmlImportDatabase::tr("local directory");
674 error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
676 error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
678 .arg(import->majversion).arg(import->minversion)
679 .arg(import2->majversion).arg(import2->minversion));
681 errors->prepend(error);
692 if (typeRecursionDetected)
693 error.setDescription(QQmlImportDatabase::tr("is instantiated recursively"));
695 error.setDescription(QQmlImportDatabase::tr("is not a type"));
696 errors->prepend(error);
701 QQmlImportsPrivate::QQmlImportsPrivate(QQmlTypeLoader *loader)
702 : ref(1), typeLoader(loader) {
705 QQmlImportsPrivate::~QQmlImportsPrivate()
707 while (QQmlImportNamespace *ns = qualifiedSets.takeFirst())
711 QQmlImportNamespace *QQmlImportsPrivate::findQualifiedNamespace(const QHashedStringRef &prefix) const
713 for (QQmlImportNamespace *ns = qualifiedSets.first(); ns; ns = qualifiedSets.next(ns)) {
714 if (prefix == ns->prefix)
721 Import an extension defined by a qmldir file.
723 \a qmldirFilePath is either a raw file path, or a bundle url.
725 bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
727 QQmlImportDatabase *database,
728 const QQmlTypeLoader::QmldirContent *qmldir,
729 QList<QQmlError> *errors)
733 if (qmlImportTrace())
734 qDebug().nospace() << "QQmlImports(" << qPrintable(base) << ")::importExtension: "
735 << "loaded " << qmldirFilePath;
737 if (!database->qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(qmldirFilePath)) {
739 QString qmldirPath = qmldirFilePath;
741 int slash = qmldirPath.lastIndexOf(Slash);
743 qmldirPath.truncate(slash);
745 foreach (const QQmlDirParser::Plugin &plugin, qmldir->plugins()) {
746 QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath,
747 plugin.path, plugin.name);
748 if (!resolvedFilePath.isEmpty()) {
749 if (!database->importPlugin(resolvedFilePath, uri, qmldir->typeNamespace(), errors)) {
751 // XXX TODO: should we leave the import plugin error alone?
752 // Here, we pop it off the top and coalesce it into this error's message.
753 // The reason is that the lower level may add url and line/column numbering information.
754 QQmlError poppedError = errors->takeFirst();
756 error.setDescription(tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
757 error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
758 errors->prepend(error);
765 error.setDescription(tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
766 error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
767 errors->prepend(error);
773 database->qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(qmldirFilePath);
779 bool QQmlImportsPrivate::getQmldirContent(const QString &qmldirIdentifier, const QString &uri,
780 const QQmlTypeLoader::QmldirContent **qmldir, QList<QQmlError> *errors)
785 *qmldir = typeLoader->qmldirContent(qmldirIdentifier, uri);
787 // Ensure that parsing was successful
788 if ((*qmldir)->hasError()) {
791 if (QQmlFile::isBundle(qmldirIdentifier))
792 url = QUrl(qmldirIdentifier);
794 url = QUrl::fromLocalFile(qmldirIdentifier);
796 const QList<QQmlError> qmldirErrors = (*qmldir)->errors(uri);
797 for (int i = 0; i < qmldirErrors.size(); ++i) {
798 QQmlError error = qmldirErrors.at(i);
800 errors->append(error);
809 QString QQmlImportsPrivate::resolvedUri(const QString &dir_arg, QQmlImportDatabase *database)
811 struct I { static bool greaterThan(const QString &s1, const QString &s2) {
815 QString dir = dir_arg;
816 if (dir.endsWith(Slash) || dir.endsWith(Backslash))
819 QStringList paths = database->fileImportPath;
820 qSort(paths.begin(), paths.end(), I::greaterThan); // Ensure subdirs preceed their parents.
822 QString stableRelativePath = dir;
823 foreach(const QString &path, paths) {
824 if (dir.startsWith(path)) {
825 stableRelativePath = dir.mid(path.length()+1);
830 stableRelativePath.replace(Backslash, Slash);
832 // remove optional versioning in dot notation from uri
833 int lastSlash = stableRelativePath.lastIndexOf(Slash);
834 if (lastSlash >= 0) {
835 int versionDot = stableRelativePath.indexOf(Dot, lastSlash);
837 stableRelativePath = stableRelativePath.left(versionDot);
840 stableRelativePath.replace(Slash, Dot);
842 return stableRelativePath;
846 Locates the qmldir file for \a uri version \a vmaj.vmin. Returns true if found,
847 and fills in outQmldirFilePath and outQmldirUrl appropriately. Otherwise returns
850 bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database,
851 QString *outQmldirFilePath, QString *outQmldirPathUrl)
853 Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
857 QQmlImportDatabase::QmldirCache *cacheHead = 0;
859 QQmlImportDatabase::QmldirCache **cachePtr = database->qmldirCache.value(uri);
861 cacheHead = *cachePtr;
862 QQmlImportDatabase::QmldirCache *cache = cacheHead;
864 if (cache->versionMajor == vmaj && cache->versionMinor == vmin) {
865 *outQmldirFilePath = cache->qmldirFilePath;
866 *outQmldirPathUrl = cache->qmldirPathUrl;
867 return !cache->qmldirFilePath.isEmpty();
874 QQmlTypeLoader &typeLoader = QQmlEnginePrivate::get(database->engine)->typeLoader;
876 QStringList localImportPaths = database->importPathList(QQmlImportDatabase::Local);
878 // Search local import paths for a matching version
879 for (int version = QQmlImports::FullyVersioned; version <= QQmlImports::Unversioned; ++version) {
880 foreach (const QString &path, localImportPaths) {
881 QString qmldirPath = QQmlImports::completeQmldirPath(uri, path, vmaj, vmin, static_cast<QQmlImports::ImportVersion>(version));
883 QString absoluteFilePath = typeLoader.absoluteFilePath(qmldirPath);
884 if (!absoluteFilePath.isEmpty()) {
886 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
887 if (absolutePath.at(0) == Colon)
888 url = QLatin1String("qrc://") + absolutePath.mid(1);
890 url = QUrl::fromLocalFile(absolutePath).toString();
892 QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
893 cache->versionMajor = vmaj;
894 cache->versionMinor = vmin;
895 cache->qmldirFilePath = absoluteFilePath;
896 cache->qmldirPathUrl = url;
897 cache->next = cacheHead;
898 database->qmldirCache.insert(uri, cache);
900 *outQmldirFilePath = absoluteFilePath;
901 *outQmldirPathUrl = url;
908 QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
909 cache->versionMajor = vmaj;
910 cache->versionMinor = vmin;
911 cache->next = cacheHead;
912 database->qmldirCache.insert(uri, cache);
917 bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoader::QmldirContent *qmldir, const QString &uri, int vmaj, int vmin,
918 QList<QQmlError> *errors)
920 int lowest_min = INT_MAX;
921 int highest_min = INT_MIN;
923 typedef QQmlDirComponents::const_iterator ConstIterator;
924 const QQmlDirComponents &components = qmldir->components();
926 ConstIterator cend = components.constEnd();
927 for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
928 for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
929 if ((cit2->typeName == cit->typeName) &&
930 (cit2->majorVersion == cit->majorVersion) &&
931 (cit2->minorVersion == cit->minorVersion)) {
932 // This entry clashes with a predecessor
934 error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
935 .arg(cit->typeName).arg(cit->majorVersion).arg(cit->minorVersion).arg(uri));
936 errors->prepend(error);
941 if (cit->majorVersion == vmaj) {
942 lowest_min = qMin(lowest_min, cit->minorVersion);
943 highest_min = qMax(highest_min, cit->minorVersion);
947 typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
948 const QQmlDirScripts &scripts = qmldir->scripts();
950 SConstIterator send = scripts.constEnd();
951 for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) {
952 for (SConstIterator sit2 = scripts.constBegin(); sit2 != sit; ++sit2) {
953 if ((sit2->nameSpace == sit->nameSpace) &&
954 (sit2->majorVersion == sit->majorVersion) &&
955 (sit2->minorVersion == sit->minorVersion)) {
956 // This entry clashes with a predecessor
958 error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
959 .arg(sit->nameSpace).arg(sit->majorVersion).arg(sit->minorVersion).arg(uri));
960 errors->prepend(error);
965 if (sit->majorVersion == vmaj) {
966 lowest_min = qMin(lowest_min, sit->minorVersion);
967 highest_min = qMax(highest_min, sit->minorVersion);
971 if (lowest_min > vmin || highest_min < vmin) {
973 error.setDescription(QQmlImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri).arg(vmaj).arg(vmin));
974 errors->prepend(error);
981 QQmlImportNamespace *QQmlImportsPrivate::importNamespace(const QString &prefix) const
983 QQmlImportNamespace *nameSpace = 0;
985 if (prefix.isEmpty()) {
986 nameSpace = &unqualifiedset;
988 nameSpace = findQualifiedNamespace(prefix);
991 nameSpace = new QQmlImportNamespace;
992 nameSpace->prefix = prefix;
993 qualifiedSets.append(nameSpace);
1000 QQmlImportNamespace::Import *QQmlImportsPrivate::addImportToNamespace(QQmlImportNamespace *nameSpace,
1001 const QString &uri, const QString &url, int vmaj, int vmin,
1002 QQmlScript::Import::Type type,
1003 QList<QQmlError> *errors)
1005 Q_ASSERT(nameSpace);
1007 Q_ASSERT(url.isEmpty() || url.endsWith(Slash));
1009 QQmlImportNamespace::Import *import = new QQmlImportNamespace::Import;
1012 import->majversion = vmaj;
1013 import->minversion = vmin;
1014 import->isLibrary = (type == QQmlScript::Import::Library);
1016 nameSpace->imports.prepend(import);
1020 bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &prefix,
1021 int vmaj, int vmin, const QString &qmldirIdentifier, const QString &qmldirUrl, bool incomplete,
1022 QQmlImportDatabase *database,
1023 QList<QQmlError> *errors)
1028 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1029 Q_ASSERT(nameSpace);
1031 QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, uri, qmldirUrl, vmaj, vmin, QQmlScript::Import::Library, errors);
1035 const QQmlTypeLoader::QmldirContent *qmldir = 0;
1037 if (!qmldirIdentifier.isEmpty()) {
1038 if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
1042 if (!importExtension(qmldir->pluginLocation(), uri, database, qmldir, errors))
1045 if (!inserted->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors))
1050 // Ensure that we are actually providing something
1051 if ((vmaj < 0) || (vmin < 0) || !QQmlMetaType::isModule(uri, vmaj, vmin)) {
1052 if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
1054 if (QQmlMetaType::isAnyModule(uri))
1055 error.setDescription(tr("module \"%1\" version %2.%3 is not installed").arg(uri).arg(vmaj).arg(vmin));
1057 error.setDescription(tr("module \"%1\" is not installed").arg(uri));
1058 errors->prepend(error);
1060 } else if ((vmaj >= 0) && (vmin >= 0) && qmldir) {
1061 // Verify that the qmldir content is valid for this version
1062 if (!validateQmldirVersion(qmldir, uri, vmaj, vmin, errors))
1071 bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix,
1073 bool isImplicitImport, bool incomplete, QQmlImportDatabase *database,
1074 QList<QQmlError> *errors)
1078 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1079 Q_ASSERT(nameSpace);
1081 // The uri for this import. For library imports this is the same as uri
1082 // specified by the user, but it may be different in the case of file imports.
1083 QString importUri = uri;
1085 QString qmldirPath = importUri;
1086 if (importUri.endsWith(Slash))
1087 qmldirPath += String_qmldir;
1089 qmldirPath += Slash_qmldir;
1091 QString qmldirUrl = resolveLocalUrl(base, qmldirPath);
1093 QString qmldirIdentifier;
1095 if (QQmlFile::isBundle(qmldirUrl)) {
1097 QString dir = resolveLocalUrl(base, importUri);
1098 Q_ASSERT(QQmlFile::isBundle(dir));
1099 if (!QQmlFile::bundleDirectoryExists(dir, typeLoader->engine())) {
1100 if (!isImplicitImport) {
1102 error.setDescription(tr("\"%1\": no such directory").arg(uri));
1103 error.setUrl(QUrl(qmldirUrl));
1104 errors->prepend(error);
1109 // Transforms the (possible relative) uri into our best guess relative to the
1111 importUri = resolvedUri(dir, database);
1112 if (importUri.endsWith(Slash))
1115 if (QQmlFile::bundleFileExists(qmldirUrl, typeLoader->engine()))
1116 qmldirIdentifier = qmldirUrl;
1118 } else if (QQmlFile::isLocalFile(qmldirUrl)) {
1120 QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
1121 Q_ASSERT(!localFileOrQrc.isEmpty());
1123 QString dir = QQmlFile::urlToLocalFileOrQrc(resolveLocalUrl(base, importUri));
1124 if (!typeLoader->directoryExists(dir)) {
1125 if (!isImplicitImport) {
1127 error.setDescription(tr("\"%1\": no such directory").arg(uri));
1128 error.setUrl(QUrl(qmldirUrl));
1129 errors->prepend(error);
1134 // Transforms the (possible relative) uri into our best guess relative to the
1136 importUri = resolvedUri(dir, database);
1137 if (importUri.endsWith(Slash))
1140 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty())
1141 qmldirIdentifier = localFileOrQrc;
1143 } else if (nameSpace->prefix.isEmpty() && !incomplete) {
1145 if (!isImplicitImport) {
1147 error.setDescription(tr("import \"%1\" has no qmldir and no namespace").arg(importUri));
1148 error.setUrl(QUrl(qmldirUrl));
1149 errors->prepend(error);
1156 // The url for the path containing files for this import
1157 QString url = resolveLocalUrl(base, uri);
1158 if (!url.endsWith(Slash) && !url.endsWith(Backslash))
1161 QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, importUri, url, vmaj, vmin, QQmlScript::Import::File, errors);
1164 if (!incomplete && !qmldirIdentifier.isEmpty()) {
1165 const QQmlTypeLoader::QmldirContent *qmldir = 0;
1166 if (!getQmldirContent(qmldirIdentifier, importUri, &qmldir, errors))
1170 if (!importExtension(qmldir->pluginLocation(), importUri, database, qmldir, errors))
1173 if (!inserted->setQmldirContent(url, qmldir, nameSpace, errors))
1181 bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString &prefix,
1182 const QString &qmldirIdentifier, const QString& qmldirUrl,
1183 QQmlImportDatabase *database, QList<QQmlError> *errors)
1185 QQmlImportNamespace *nameSpace = importNamespace(prefix);
1186 Q_ASSERT(nameSpace);
1188 if (QQmlImportNamespace::Import *import = nameSpace->findImport(uri)) {
1189 const QQmlTypeLoader::QmldirContent *qmldir = 0;
1190 if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
1194 if (!importExtension(qmldir->pluginLocation(), uri, database, qmldir, errors))
1197 if (import->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) {
1198 // Ensure that we are actually providing something
1199 int vmaj = import->majversion;
1200 int vmin = import->minversion;
1202 if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
1203 // The implicit import qmldir can be empty
1204 if (uri != QLatin1String(".")) {
1206 if (QQmlMetaType::isAnyModule(uri))
1207 error.setDescription(tr("module \"%1\" version %2.%3 is not installed").arg(uri).arg(vmaj).arg(vmin));
1209 error.setDescription(tr("module \"%1\" is not installed").arg(uri));
1210 errors->prepend(error);
1213 } else if ((vmaj >= 0) && (vmin >= 0)) {
1214 // Verify that the qmldir content is valid for this version
1215 if (!validateQmldirVersion(qmldir, uri, vmaj, vmin, errors))
1223 if (errors->isEmpty()) {
1225 error.setDescription(QQmlTypeLoader::tr("Cannot update qmldir content for '%1'").arg(uri));
1226 errors->prepend(error);
1235 Adds an implicit "." file import. This is equivalent to calling addFileImport(), but error
1236 messages related to the path or qmldir file not existing are suppressed.
1238 bool QQmlImports::addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlError> *errors)
1242 if (qmlImportTrace())
1243 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString())
1244 << ")::addImplicitImport";
1246 bool incomplete = !isLocal(baseUrl());
1247 return d->addFileImport(QLatin1String("."), QString(), -1, -1, true, incomplete, importDb, errors);
1253 Adds information to \a imports such that subsequent calls to resolveType()
1254 will resolve types qualified by \a prefix by considering types found at the given \a uri.
1256 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
1257 added via addImportPath() (if importType is LibraryImport).
1259 The \a prefix may be empty, in which case the import location is considered for
1262 The base URL must already have been set with Import::setBaseUrl().
1264 Optionally, the url the import resolved to can be returned by providing the url parameter.
1265 Not all imports will result in an output url being generated, in which case the url will
1266 be set to an empty string.
1268 Returns true on success, and false on failure. In case of failure, the errors array will
1269 filled appropriately.
1271 bool QQmlImports::addFileImport(QQmlImportDatabase *importDb,
1272 const QString& uri, const QString& prefix, int vmaj, int vmin,
1273 bool incomplete, QList<QQmlError> *errors)
1278 if (qmlImportTrace())
1279 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::addFileImport: "
1280 << uri << ' ' << vmaj << '.' << vmin << " as " << prefix;
1282 return d->addFileImport(uri, prefix, vmaj, vmin, false, incomplete, importDb, errors);
1285 bool QQmlImports::addLibraryImport(QQmlImportDatabase *importDb,
1286 const QString &uri, const QString &prefix, int vmaj, int vmin,
1287 const QString &qmldirIdentifier, const QString& qmldirUrl, bool incomplete, QList<QQmlError> *errors)
1292 if (qmlImportTrace())
1293 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::addLibraryImport: "
1294 << uri << ' ' << vmaj << '.' << vmin << " as " << prefix;
1296 return d->addLibraryImport(uri, prefix, vmaj, vmin, qmldirIdentifier, qmldirUrl, incomplete, importDb, errors);
1299 bool QQmlImports::updateQmldirContent(QQmlImportDatabase *importDb,
1300 const QString &uri, const QString &prefix,
1301 const QString &qmldirIdentifier, const QString& qmldirUrl, QList<QQmlError> *errors)
1306 if (qmlImportTrace())
1307 qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ')' << "::updateQmldirContent: "
1308 << uri << " to " << qmldirUrl << " as " << prefix;
1310 return d->updateQmldirContent(uri, prefix, qmldirIdentifier, qmldirUrl, importDb, errors);
1313 bool QQmlImports::locateQmldir(QQmlImportDatabase *importDb,
1314 const QString& uri, int vmaj, int vmin,
1315 QString *qmldirFilePath, QString *url)
1317 return d->locateQmldir(uri, vmaj, vmin, importDb, qmldirFilePath, url);
1320 bool QQmlImports::isLocal(const QString &url)
1322 return QQmlFile::isBundle(url) || !QQmlFile::urlToLocalFileOrQrc(url).isEmpty();
1325 bool QQmlImports::isLocal(const QUrl &url)
1327 return QQmlFile::isBundle(url) || !QQmlFile::urlToLocalFileOrQrc(url).isEmpty();
1332 \class QQmlImportDatabase
1333 \brief The QQmlImportDatabase class manages the QML imports for a QQmlEngine.
1336 QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
1339 filePluginPath << QLatin1String(".");
1341 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
1343 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
1344 addImportPath(installImportsPath);
1347 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
1348 if (!envImportPath.isEmpty()) {
1349 #if defined(Q_OS_WIN)
1350 QLatin1Char pathSep(';');
1352 QLatin1Char pathSep(':');
1354 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
1355 for (int ii = paths.count() - 1; ii >= 0; --ii)
1356 addImportPath(paths.at(ii));
1359 addImportPath(QCoreApplication::applicationDirPath());
1362 QQmlImportDatabase::~QQmlImportDatabase()
1364 qmldirCache.clear();
1370 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
1371 The \a prefix must contain the dot.
1373 \a qmldirPath is the location of the qmldir file.
1375 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
1376 const QString &qmldirPath,
1377 const QString &qmldirPluginPath,
1378 const QString &baseName, const QStringList &suffixes,
1379 const QString &prefix)
1381 QStringList searchPaths = filePluginPath;
1382 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
1383 if (!qmldirPluginPathIsRelative)
1384 searchPaths.prepend(qmldirPluginPath);
1386 foreach (const QString &pluginPath, searchPaths) {
1388 QString resolvedPath;
1389 if (pluginPath == QLatin1String(".")) {
1390 if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String("."))
1391 resolvedPath = QDir::cleanPath(qmldirPath + Slash + qmldirPluginPath);
1393 resolvedPath = qmldirPath;
1395 if (QDir::isRelativePath(pluginPath))
1396 resolvedPath = QDir::cleanPath(qmldirPath + Slash + pluginPath);
1398 resolvedPath = pluginPath;
1401 // hack for resources, should probably go away
1402 if (resolvedPath.startsWith(Colon))
1403 resolvedPath = QCoreApplication::applicationDirPath();
1405 if (!resolvedPath.endsWith(Slash))
1406 resolvedPath += Slash;
1408 foreach (const QString &suffix, suffixes) {
1409 QString pluginFileName = prefix;
1411 pluginFileName += baseName;
1412 pluginFileName += suffix;
1414 QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
1415 if (!absolutePath.isEmpty())
1416 return absolutePath;
1420 if (qmlImportTrace())
1421 qDebug() << "QQmlImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
1422 << "in" << qmldirPath;
1430 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
1433 \header \li Platform \li Valid suffixes
1434 \row \li Windows \li \c .dll
1435 \row \li Unix/Linux \li \c .so
1436 \row \li AIX \li \c .a
1437 \row \li HP-UX \li \c .sl, \c .so (HP-UXi)
1438 \row \li Mac OS X \li \c .dylib, \c .bundle, \c .so
1441 Version number on unix are ignored.
1443 QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
1444 const QString &qmldirPath, const QString &qmldirPluginPath,
1445 const QString &baseName)
1447 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
1448 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
1451 << QLatin1String("d.dll") // try a qmake-style debug build first
1453 << QLatin1String(".dll"));
1456 # if defined(Q_OS_DARWIN)
1458 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
1461 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
1462 << QLatin1String(".dylib")
1464 << QLatin1String(".dylib")
1465 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
1467 << QLatin1String(".so")
1468 << QLatin1String(".bundle"),
1469 QLatin1String("lib"));
1470 # else // Generic Unix
1471 QStringList validSuffixList;
1473 # if defined(Q_OS_HPUX)
1475 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
1476 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
1477 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
1479 validSuffixList << QLatin1String(".sl");
1481 validSuffixList << QLatin1String(".so");
1483 # elif defined(Q_OS_AIX)
1484 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
1485 # elif defined(Q_OS_UNIX)
1486 validSuffixList << QLatin1String(".so");
1489 // Examples of valid library names:
1492 return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
1501 QStringList QQmlImportDatabase::pluginPathList() const
1503 return filePluginPath;
1509 void QQmlImportDatabase::setPluginPathList(const QStringList &paths)
1511 if (qmlImportTrace())
1512 qDebug().nospace() << "QQmlImportDatabase::setPluginPathList: " << paths;
1514 filePluginPath = paths;
1520 void QQmlImportDatabase::addPluginPath(const QString& path)
1522 if (qmlImportTrace())
1523 qDebug().nospace() << "QQmlImportDatabase::addPluginPath: " << path;
1525 QUrl url = QUrl(path);
1526 if (url.isRelative() || url.scheme() == QLatin1String("file")
1527 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1528 QDir dir = QDir(path);
1529 filePluginPath.prepend(dir.canonicalPath());
1531 filePluginPath.prepend(path);
1538 void QQmlImportDatabase::addImportPath(const QString& path)
1540 if (qmlImportTrace())
1541 qDebug().nospace() << "QQmlImportDatabase::addImportPath: " << path;
1546 QUrl url = QUrl(path);
1549 if (url.scheme() == QLatin1String("file")) {
1550 cPath = QQmlFile::urlToLocalFileOrQrc(url);
1551 } else if (path.startsWith(QLatin1Char(':'))) {
1552 // qrc directory, e.g. :/foo
1553 // need to convert to a qrc url, e.g. qrc:/foo
1554 cPath = QStringLiteral("qrc") + path;
1555 cPath.replace(Backslash, Slash);
1556 } else if (url.isRelative() ||
1557 (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1558 QDir dir = QDir(path);
1559 cPath = dir.canonicalPath();
1562 cPath.replace(Backslash, Slash);
1565 if (!cPath.isEmpty()
1566 && !fileImportPath.contains(cPath))
1567 fileImportPath.prepend(cPath);
1573 QStringList QQmlImportDatabase::importPathList(PathType type) const
1575 if (type == LocalOrRemote)
1576 return fileImportPath;
1579 foreach (const QString &path, fileImportPath) {
1580 bool localPath = isPathAbsolute(path) || QQmlFile::isLocalFile(path);
1581 if (localPath == (type == Local))
1591 void QQmlImportDatabase::setImportPathList(const QStringList &paths)
1593 if (qmlImportTrace())
1594 qDebug().nospace() << "QQmlImportDatabase::setImportPathList: " << paths;
1596 fileImportPath = paths;
1598 // Our existing cached paths may have been invalidated
1599 qmldirCache.clear();
1605 bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &uri, const QString &typeNamespace, QList<QQmlError> *errors)
1607 if (qmlImportTrace())
1608 qDebug().nospace() << "QQmlImportDatabase::importPlugin: " << uri << " from " << filePath;
1610 #ifndef QT_NO_LIBRARY
1611 QFileInfo fileInfo(filePath);
1612 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1614 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1615 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1617 if (typesRegistered) {
1618 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1619 "QQmlImportDatabase::importPlugin",
1620 "Internal error: Plugin imported previously with different uri");
1623 if (!engineInitialized || !typesRegistered) {
1624 if (!QQml_isFileCaseCorrect(absoluteFilePath)) {
1627 error.setDescription(tr("File name case mismatch for \"%1\"").arg(absoluteFilePath));
1628 errors->prepend(error);
1632 QPluginLoader loader(absoluteFilePath);
1634 if (!loader.load()) {
1637 error.setDescription(loader.errorString());
1638 errors->prepend(error);
1643 QObject *instance = loader.instance();
1644 if (QQmlTypesExtensionInterface *iface = qobject_cast<QQmlExtensionInterface *>(instance)) {
1646 const QByteArray bytes = uri.toUtf8();
1647 const char *moduleId = bytes.constData();
1648 if (!typesRegistered) {
1650 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1652 QStringList registrationFailures;
1655 QWriteLocker lock(QQmlMetaType::typeRegistrationLock());
1657 if (!typeNamespace.isEmpty()) {
1658 // This is an 'identified' module
1659 if (typeNamespace != uri) {
1660 // The namespace for type registrations must match the URI for locating the module
1662 error.setDescription(tr("Module namespace '%1' does not match import URI '%2'").arg(typeNamespace).arg(uri));
1663 errors->prepend(error);
1667 if (QQmlMetaType::namespaceContainsRegistrations(typeNamespace)) {
1668 // Other modules have already installed to this namespace
1670 error.setDescription(tr("Namespace '%1' has already been used for type registration").arg(typeNamespace));
1671 errors->prepend(error);
1674 QQmlMetaType::protectNamespace(typeNamespace);
1677 // This is not an identified module - provide a warning
1678 qWarning().nospace() << qPrintable(tr("Module '%1' does not contain a module identifier directive - it cannot be protected from external registrations.").arg(uri));
1681 QQmlMetaType::setTypeRegistrationNamespace(typeNamespace);
1683 iface->registerTypes(moduleId);
1685 registrationFailures = QQmlMetaType::typeRegistrationFailures();
1686 QQmlMetaType::setTypeRegistrationNamespace(QString());
1689 if (!registrationFailures.isEmpty()) {
1690 foreach (const QString &failure, registrationFailures) {
1692 error.setDescription(failure);
1693 errors->prepend(error);
1698 if (!engineInitialized) {
1699 // things on the engine (eg. adding new global objects) have to be done for every
1701 // XXX protect against double initialization
1702 initializedPlugins.insert(absoluteFilePath);
1704 QQmlExtensionInterface *eiface =
1705 qobject_cast<QQmlExtensionInterface *>(instance);
1707 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1708 ep->typeLoader.initializeEngine(eiface, moduleId);
1714 error.setDescription(loader.errorString());
1715 errors->prepend(error);