From 70cacb48b0bdbfe6ca58b7472cbeadb18d4dfc35 Mon Sep 17 00:00:00 2001 From: mae Date: Tue, 8 Feb 2011 12:33:03 +0100 Subject: Support seperate versions of installed modules QML supports versioned types in modules. There's a version major and a version minor. This makes it possible to have a module com.organisation.fancycomponents with version 1.0, and later you could ship a new module com.organisation.fancycomponents which contains a more recent version 1.1 or 2.0 AND also the old versions to keep old code running. This is good. The problem is that this is difficult with certain QA procedures. It's hard to verify that a new module is indeed 100% compatible with the previous versions. The change extends the import mechanism by adding optional versioning to the component patch. With the patch, you can add a new module com.organisation.fancycomponents.2.0 which will be loaded when the QML file specifies "import com.organisation.fancycomponents 2.0". The patch works as follows: if you try to load com.organisation.fancycomponents in version 2.0, the engine first looks for com/organisation/fancycomponents.2.0, then for com/organisation/fancycomponents.2 then for com.organisation/fancycomponents. Reviewed-by: Aaron Kennedy Task-number: QTBUG-16455 (cherry picked from commit dd49b322b327fe87d8420abcce0e6cee877a88d7) --- doc/src/declarative/modules.qdoc | 9 +++ src/declarative/qml/qdeclarativeimport.cpp | 76 ++++++++++++++++---- .../qdeclarativemoduleplugin/data/works2.qml | 3 + .../qdeclarativemoduleplugin/data/works21.qml | 3 + .../com/nokia/AutoTestQmlPluginType.2.1/qmldir | 1 + .../com/nokia/AutoTestQmlPluginType.2/qmldir | 1 + .../plugin.2.1/plugin.2.1.pro | 9 +++ .../qdeclarativemoduleplugin/plugin.2.1/plugin.cpp | 84 ++++++++++++++++++++++ .../qdeclarativemoduleplugin/plugin.2/plugin.2.pro | 9 +++ .../qdeclarativemoduleplugin/plugin.2/plugin.cpp | 84 ++++++++++++++++++++++ .../qdeclarativemoduleplugin.pro | 2 +- .../tst_qdeclarativemoduleplugin.cpp | 34 +++++++++ 12 files changed, 302 insertions(+), 13 deletions(-) create mode 100644 tests/auto/declarative/qdeclarativemoduleplugin/data/works2.qml create mode 100644 tests/auto/declarative/qdeclarativemoduleplugin/data/works21.qml create mode 100644 tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/AutoTestQmlPluginType.2.1/qmldir create mode 100644 tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/AutoTestQmlPluginType.2/qmldir create mode 100644 tests/auto/declarative/qdeclarativemoduleplugin/plugin.2.1/plugin.2.1.pro create mode 100644 tests/auto/declarative/qdeclarativemoduleplugin/plugin.2.1/plugin.cpp create mode 100644 tests/auto/declarative/qdeclarativemoduleplugin/plugin.2/plugin.2.pro create mode 100644 tests/auto/declarative/qdeclarativemoduleplugin/plugin.2/plugin.cpp diff --git a/doc/src/declarative/modules.qdoc b/doc/src/declarative/modules.qdoc index 1dca28c..4b2b33a 100644 --- a/doc/src/declarative/modules.qdoc +++ b/doc/src/declarative/modules.qdoc @@ -134,6 +134,15 @@ Installed modules that are installed into the import path or created as a \l{QDeclarativeExtensionPlugin}{QML C++ plugin} must define a \l{Writing a qmldir file}{qmldir file}. +Modules that are installed into the import path translate the URI into +directory names. For example, the qmldir file of the module \c com.nokia.qml.mymodule +must be located in the subpath \c com/nokia/qml/mymodule/qmldir somewhere in the +QML import path. In addition it is possible to store different versions of the +module in subdirectories of its own. For example, a version 2.1 of the +module could be located under \c com/nokia/qml/mymodule.2/qmldir or +\c com/nokia/qml/mymodule.2.1/qmldir. The engine will automatically load +the module which matches best. + \section2 The QML import path diff --git a/src/declarative/qml/qdeclarativeimport.cpp b/src/declarative/qml/qdeclarativeimport.cpp index c89666d..22dcb44 100644 --- a/src/declarative/qml/qdeclarativeimport.cpp +++ b/src/declarative/qml/qdeclarativeimport.cpp @@ -425,8 +425,18 @@ QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclara break; } } + + stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/')); + + // remove optional versioning in dot notation from uri + int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/')); + if (lastSlash >= 0) { + int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash); + if (versionDot >= 0) + stableRelativePath = stableRelativePath.left(versionDot); + } + stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.')); - stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('.')); return stableRelativePath; } @@ -453,20 +463,62 @@ bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomp QString dir; - foreach (const QString &p, database->fileImportPath) { - dir = p+QLatin1Char('/')+url; + // step 1: search for extension with fully encoded version number + if (vmaj >= 0 && vmin >= 0) { + foreach (const QString &p, database->fileImportPath) { + dir = p+QLatin1Char('/')+url; - QFileInfo fi(dir+QLatin1String("/qmldir")); - const QString absoluteFilePath = fi.absoluteFilePath(); + QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir")); + const QString absoluteFilePath = fi.absoluteFilePath(); - if (fi.isFile()) { - found = true; + if (fi.isFile()) { + found = true; - url = QUrl::fromLocalFile(fi.absolutePath()).toString(); - uri = resolvedUri(dir, database); - if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString)) - return false; - break; + url = QUrl::fromLocalFile(fi.absolutePath()).toString(); + uri = resolvedUri(dir, database); + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString)) + return false; + break; + } + } + } + // step 2: search for extension with encoded version major + if (vmaj >= 0 && vmin >= 0) { + foreach (const QString &p, database->fileImportPath) { + dir = p+QLatin1Char('/')+url; + + QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir")); + const QString absoluteFilePath = fi.absoluteFilePath(); + + if (fi.isFile()) { + found = true; + + url = QUrl::fromLocalFile(fi.absolutePath()).toString(); + uri = resolvedUri(dir, database); + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString)) + return false; + break; + } + } + } + if (!found) { + // step 3: search for extension without version number + + foreach (const QString &p, database->fileImportPath) { + dir = p+QLatin1Char('/')+url; + + QFileInfo fi(dir+QLatin1String("/qmldir")); + const QString absoluteFilePath = fi.absoluteFilePath(); + + if (fi.isFile()) { + found = true; + + url = QUrl::fromLocalFile(fi.absolutePath()).toString(); + uri = resolvedUri(dir, database); + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString)) + return false; + break; + } } } diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/data/works2.qml b/tests/auto/declarative/qdeclarativemoduleplugin/data/works2.qml new file mode 100644 index 0000000..cc322bf --- /dev/null +++ b/tests/auto/declarative/qdeclarativemoduleplugin/data/works2.qml @@ -0,0 +1,3 @@ +import com.nokia.AutoTestQmlPluginType 2.0 + +MyPluginType { valueOnlyIn2: 123 } diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/data/works21.qml b/tests/auto/declarative/qdeclarativemoduleplugin/data/works21.qml new file mode 100644 index 0000000..c08160a --- /dev/null +++ b/tests/auto/declarative/qdeclarativemoduleplugin/data/works21.qml @@ -0,0 +1,3 @@ +import com.nokia.AutoTestQmlPluginType 2.1 + +MyPluginType { valueOnlyIn2: 123 } diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/AutoTestQmlPluginType.2.1/qmldir b/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/AutoTestQmlPluginType.2.1/qmldir new file mode 100644 index 0000000..0a8b5d4 --- /dev/null +++ b/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/AutoTestQmlPluginType.2.1/qmldir @@ -0,0 +1 @@ +plugin plugin diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/AutoTestQmlPluginType.2/qmldir b/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/AutoTestQmlPluginType.2/qmldir new file mode 100644 index 0000000..0a8b5d4 --- /dev/null +++ b/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/AutoTestQmlPluginType.2/qmldir @@ -0,0 +1 @@ +plugin plugin diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/plugin.2.1/plugin.2.1.pro b/tests/auto/declarative/qdeclarativemoduleplugin/plugin.2.1/plugin.2.1.pro new file mode 100644 index 0000000..661675a --- /dev/null +++ b/tests/auto/declarative/qdeclarativemoduleplugin/plugin.2.1/plugin.2.1.pro @@ -0,0 +1,9 @@ +TEMPLATE = lib +CONFIG += plugin +SOURCES = plugin.cpp +QT = core declarative +DESTDIR = ../imports/com/nokia/AutoTestQmlPluginType.2.1 + +symbian: { + TARGET.EPOCALLOWDLLDATA=1 +} diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/plugin.2.1/plugin.cpp b/tests/auto/declarative/qdeclarativemoduleplugin/plugin.2.1/plugin.cpp new file mode 100644 index 0000000..1ae2bd4 --- /dev/null +++ b/tests/auto/declarative/qdeclarativemoduleplugin/plugin.2.1/plugin.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include + +class MyPluginType : public QObject +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue) + Q_PROPERTY(int valueOnlyIn2 READ value WRITE setValue) + +public: + MyPluginType(QObject *parent=0) : QObject(parent) + { + qWarning("import2.1 worked"); + } + + int value() const { return v; } + void setValue(int i) { v = i; } + +private: + int v; +}; + + +class MyPlugin : public QDeclarativeExtensionPlugin +{ + Q_OBJECT +public: + MyPlugin() + { + qWarning("plugin2.1 created"); + } + + void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == "com.nokia.AutoTestQmlPluginType"); + qmlRegisterType(uri, 2, 1, "MyPluginType"); + } +}; + +#include "plugin.moc" + +Q_EXPORT_PLUGIN2(plugin, MyPlugin); diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/plugin.2/plugin.2.pro b/tests/auto/declarative/qdeclarativemoduleplugin/plugin.2/plugin.2.pro new file mode 100644 index 0000000..d254642 --- /dev/null +++ b/tests/auto/declarative/qdeclarativemoduleplugin/plugin.2/plugin.2.pro @@ -0,0 +1,9 @@ +TEMPLATE = lib +CONFIG += plugin +SOURCES = plugin.cpp +QT = core declarative +DESTDIR = ../imports/com/nokia/AutoTestQmlPluginType.2 + +symbian: { + TARGET.EPOCALLOWDLLDATA=1 +} diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/plugin.2/plugin.cpp b/tests/auto/declarative/qdeclarativemoduleplugin/plugin.2/plugin.cpp new file mode 100644 index 0000000..6bd8542 --- /dev/null +++ b/tests/auto/declarative/qdeclarativemoduleplugin/plugin.2/plugin.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include + +class MyPluginType : public QObject +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue) + Q_PROPERTY(int valueOnlyIn2 READ value WRITE setValue) + +public: + MyPluginType(QObject *parent=0) : QObject(parent) + { + qWarning("import2 worked"); + } + + int value() const { return v; } + void setValue(int i) { v = i; } + +private: + int v; +}; + + +class MyPlugin : public QDeclarativeExtensionPlugin +{ + Q_OBJECT +public: + MyPlugin() + { + qWarning("plugin2 created"); + } + + void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == "com.nokia.AutoTestQmlPluginType"); + qmlRegisterType(uri, 2, 0, "MyPluginType"); + } +}; + +#include "plugin.moc" + +Q_EXPORT_PLUGIN2(plugin, MyPlugin); diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/qdeclarativemoduleplugin.pro b/tests/auto/declarative/qdeclarativemoduleplugin/qdeclarativemoduleplugin.pro index 221e465..10864df 100644 --- a/tests/auto/declarative/qdeclarativemoduleplugin/qdeclarativemoduleplugin.pro +++ b/tests/auto/declarative/qdeclarativemoduleplugin/qdeclarativemoduleplugin.pro @@ -1,6 +1,6 @@ QT = core TEMPLATE = subdirs -SUBDIRS = plugin pluginWrongCase +SUBDIRS = plugin plugin.2 plugin.2.1 pluginWrongCase tst_qdeclarativemoduleplugin_pro.depends += plugin SUBDIRS += tst_qdeclarativemoduleplugin.pro diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.cpp b/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.cpp index 96ec21b..7e381ee 100644 --- a/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.cpp +++ b/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.cpp @@ -54,6 +54,8 @@ public: private slots: void importsPlugin(); + void importsPlugin2(); + void importsPlugin21(); void incorrectPluginCase(); }; @@ -121,6 +123,38 @@ void tst_qdeclarativemoduleplugin::importsPlugin() delete object; } +void tst_qdeclarativemoduleplugin::importsPlugin2() +{ + QDeclarativeEngine engine; + engine.addImportPath(QLatin1String(SRCDIR) + QDir::separator() + QLatin1String("imports")); + QTest::ignoreMessage(QtWarningMsg, "plugin2 created"); + QTest::ignoreMessage(QtWarningMsg, "import2 worked"); + QDeclarativeComponent component(&engine, TEST_FILE("data/works2.qml")); + foreach (QDeclarativeError err, component.errors()) + qWarning() << err; + VERIFY_ERRORS(0); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("value").toInt(),123); + delete object; +} + +void tst_qdeclarativemoduleplugin::importsPlugin21() +{ + QDeclarativeEngine engine; + engine.addImportPath(QLatin1String(SRCDIR) + QDir::separator() + QLatin1String("imports")); + QTest::ignoreMessage(QtWarningMsg, "plugin2.1 created"); + QTest::ignoreMessage(QtWarningMsg, "import2.1 worked"); + QDeclarativeComponent component(&engine, TEST_FILE("data/works21.qml")); + foreach (QDeclarativeError err, component.errors()) + qWarning() << err; + VERIFY_ERRORS(0); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("value").toInt(),123); + delete object; +} + void tst_qdeclarativemoduleplugin::incorrectPluginCase() { QDeclarativeEngine engine; -- cgit v0.12