diff options
-rw-r--r-- | doc/reference/items/depends.qdoc | 15 | ||||
-rw-r--r-- | doc/reference/items/module.qdoc | 6 | ||||
-rw-r--r-- | src/lib/corelib/language/builtindeclarations.cpp | 3 | ||||
-rw-r--r-- | src/lib/corelib/language/item.h | 2 | ||||
-rw-r--r-- | src/lib/corelib/language/moduleloader.cpp | 35 | ||||
-rw-r--r-- | src/lib/corelib/language/modulemerger.cpp | 4 | ||||
-rw-r--r-- | src/lib/corelib/language/modulemerger.h | 2 | ||||
-rw-r--r-- | src/lib/corelib/tools/version.cpp | 14 | ||||
-rw-r--r-- | src/lib/corelib/tools/version.h | 12 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata/versioncheck/modules/higher/higher.qbs | 10 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata/versioncheck/modules/lower/lower.qbs | 3 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata/versioncheck/versioncheck.qbs | 12 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackbox.cpp | 37 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackbox.h | 2 |
14 files changed, 157 insertions, 0 deletions
diff --git a/doc/reference/items/depends.qdoc b/doc/reference/items/depends.qdoc index cdc45dd8c..f3941f8d4 100644 --- a/doc/reference/items/depends.qdoc +++ b/doc/reference/items/depends.qdoc @@ -46,6 +46,7 @@ Depends { name: "cpp" } Depends { name: "awesome_module" + versionAtLeast: "2.0" required: false } Depends { @@ -77,6 +78,20 @@ \li true \li Determines whether the dependency will actually be applied. \row + \li versionAtLeast + \li string + \li undefined + \li The minimum value that the dependency's \c version property needs to have. If the + actual version is lower than that, loading the dependency will fail. + The value consists of integers separated by dots. + \row + \li versionBelow + \li string + \li undefined + \li A value that the dependency's \c version property must be lower than. If the + actual version is equal to or higher than that, loading the dependency will fail. + The value consists of integers separated by dots. + \row \li productTypes \li stringList \li undefined diff --git a/doc/reference/items/module.qdoc b/doc/reference/items/module.qdoc index 28a80e9e3..aa1e7d1bc 100644 --- a/doc/reference/items/module.qdoc +++ b/doc/reference/items/module.qdoc @@ -193,5 +193,11 @@ \li \c undefined \li Script that is run after the module is loaded. It can be used to check property values and throw errors in unexpected cases. The return value is ignored. + \row + \li version + \li string + \li \c undefined + \li The module's version. It consists of integer values separated by dots. You can check + for specific values of this property in a \l{Depends item}{Depends} item. \endtable */ diff --git a/src/lib/corelib/language/builtindeclarations.cpp b/src/lib/corelib/language/builtindeclarations.cpp index 7c35cc163..1abeb65a9 100644 --- a/src/lib/corelib/language/builtindeclarations.cpp +++ b/src/lib/corelib/language/builtindeclarations.cpp @@ -197,6 +197,8 @@ void BuiltinDeclarations::addDependsItem() PropertyDeclaration requiredDecl(QLatin1String("required"), PropertyDeclaration::Boolean); requiredDecl.setInitialValueSource(QLatin1String("true")); item << requiredDecl; + item << PropertyDeclaration(QLatin1String("versionAtLeast"), PropertyDeclaration::String); + item << PropertyDeclaration(QLatin1String("versionBelow"), PropertyDeclaration::String); PropertyDeclaration profileDecl(QLatin1String("profiles"), PropertyDeclaration::StringList); profileDecl.setInitialValueSource(QLatin1String("[product.profile]")); item << profileDecl; @@ -275,6 +277,7 @@ void BuiltinDeclarations::addModuleLikeItem(ItemType type) PropertyDeclaration::PropertyNotAvailableInConfig); item << PropertyDeclaration(QLatin1String("additionalProductTypes"), PropertyDeclaration::StringList); + item << PropertyDeclaration(QLatin1String("version"), PropertyDeclaration::String); PropertyDeclaration presentDecl(QLatin1String("present"), PropertyDeclaration::Boolean); presentDecl.setInitialValueSource(QLatin1String("true")); item << presentDecl; diff --git a/src/lib/corelib/language/item.h b/src/lib/corelib/language/item.h index d89156c2c..3c4883c88 100644 --- a/src/lib/corelib/language/item.h +++ b/src/lib/corelib/language/item.h @@ -39,6 +39,7 @@ #include <parser/qmljsmemorypool_p.h> #include <tools/codelocation.h> #include <tools/error.h> +#include <tools/version.h> #include <QList> #include <QMap> @@ -70,6 +71,7 @@ public: Item *item; bool isProduct; bool required; + VersionRange versionRange; }; typedef QList<Module> Modules; typedef QMap<QString, PropertyDeclaration> PropertyDeclarationMap; diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp index 7dd945feb..baf3ddde1 100644 --- a/src/lib/corelib/language/moduleloader.cpp +++ b/src/lib/corelib/language/moduleloader.cpp @@ -685,6 +685,31 @@ void ModuleLoader::handleProduct(ModuleLoader::ProductContext *productContext) continue; try { resolveProbes(productContext, module.item); + if (module.versionRange.minimum.isValid() + || module.versionRange.maximum.isValid()) { + if (module.versionRange.maximum.isValid() + && module.versionRange.minimum >= module.versionRange.maximum) { + throw ErrorInfo(Tr::tr("Impossible version constraint [%1,%2) set for module " + "'%3'").arg(module.versionRange.minimum.toString(), + module.versionRange.maximum.toString(), + module.name.toString())); + } + const Version moduleVersion = Version::fromString( + m_evaluator->stringValue(module.item, QLatin1String("version"))); + if (moduleVersion < module.versionRange.minimum) { + throw ErrorInfo(Tr::tr("Module '%1' has version %2, but it needs to be " + "at least %3.").arg(module.name.toString(), + moduleVersion.toString(), + module.versionRange.minimum.toString())); + } + if (module.versionRange.maximum.isValid() + && moduleVersion >= module.versionRange.maximum) { + throw ErrorInfo(Tr::tr("Module '%1' has version %2, but it needs to be " + "lower than %3.").arg(module.name.toString(), + moduleVersion.toString(), + module.versionRange.maximum.toString())); + } + } m_evaluator->boolValue(module.item, QLatin1String("validate")); } catch (const ErrorInfo &error) { if (module.required) { // Error will be thrown for enabled products only @@ -1016,6 +1041,12 @@ void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *pare if (!m_requiredChain.at(i)) isRequired = false; } + const Version minVersion = Version::fromString( + m_evaluator->stringValue(dependsItem, QLatin1String("versionAtLeast"))); + const Version maxVersion = Version::fromString( + m_evaluator->stringValue(dependsItem, QLatin1String("versionBelow"))); + const VersionRange versionRange(minVersion, maxVersion); + // Don't load the same module twice. Duplicate Depends statements can easily // happen due to inheritance. const auto it = std::find_if(moduleResults->begin(), moduleResults->end(), @@ -1023,6 +1054,7 @@ void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *pare if (it != moduleResults->end()) { if (isRequired) it->required = true; + it->versionRange.narrowDown(versionRange); continue; } @@ -1040,6 +1072,7 @@ void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *pare result.name = moduleName; result.item = moduleItem; result.required = isRequired; + result.versionRange = versionRange; moduleResults->append(result); if (result.isProduct) { if (m_logger.traceEnabled()) @@ -1867,6 +1900,7 @@ static void collectAllModules(Item *item, QVector<Item::Module> *modules) // If a module is required somewhere, it is required in the top-level item. if (m.required) it->required = true; + it->versionRange.narrowDown(m.versionRange); continue; } modules->append(m); @@ -1916,6 +1950,7 @@ void ModuleLoader::addTransitiveDependencies(ProductContext *ctx) } dep.name = module.name; dep.required = module.required; + dep.versionRange = module.versionRange; ctx->item->addModule(dep); } } diff --git a/src/lib/corelib/language/modulemerger.cpp b/src/lib/corelib/language/modulemerger.cpp index 8c47e2fe7..3ac498dd9 100644 --- a/src/lib/corelib/language/modulemerger.cpp +++ b/src/lib/corelib/language/modulemerger.cpp @@ -43,6 +43,7 @@ ModuleMerger::ModuleMerger(const Logger &logger, Item *root, Item::Module &modul , m_rootItem(root) , m_mergedModule(moduleToMerge) , m_required(moduleToMerge.required) + , m_versionRange(moduleToMerge.versionRange) { QBS_CHECK(moduleToMerge.item->type() == ItemType::ModuleInstance); } @@ -94,6 +95,7 @@ void ModuleMerger::start() const Item::PropertyMap props = dfs(m, Item::PropertyMap()); if (m_required) m_mergedModule.required = true; + m_mergedModule.versionRange.narrowDown(m_versionRange); Item::PropertyMap mergedProps = m_mergedModule.item->properties(); Item *moduleProto = m_mergedModule.item->prototype(); @@ -118,6 +120,7 @@ void ModuleMerger::start() m.item = m_mergedModule.item; if (m_required) m.required = true; + m.versionRange.narrowDown(m_versionRange); } modules << m; } @@ -137,6 +140,7 @@ Item::PropertyMap ModuleMerger::dfs(const Item::Module &m, Item::PropertyMap pro m_moduleInstanceContainers << m.item; if (dep.required) m_required = true; + m_versionRange.narrowDown(dep.versionRange); break; } } diff --git a/src/lib/corelib/language/modulemerger.h b/src/lib/corelib/language/modulemerger.h index f39c58216..1eca4d6af 100644 --- a/src/lib/corelib/language/modulemerger.h +++ b/src/lib/corelib/language/modulemerger.h @@ -35,6 +35,7 @@ #include "qualifiedid.h" #include <logging/logger.h> +#include <tools/version.h> #include <QHash> #include <QSet> @@ -68,6 +69,7 @@ private: QSet<const Item *> m_seenInstancesBottomUp; QSet<Item *> m_moduleInstanceContainers; bool m_required; + VersionRange m_versionRange; }; } // namespace Internal diff --git a/src/lib/corelib/tools/version.cpp b/src/lib/corelib/tools/version.cpp index 8468d44e5..229b05b68 100644 --- a/src/lib/corelib/tools/version.cpp +++ b/src/lib/corelib/tools/version.cpp @@ -134,5 +134,19 @@ int compare(const Version &lhs, const Version &rhs) return 0; } +VersionRange::VersionRange(const Version &minVersion, const Version &maxVersion) + : minimum(minVersion), maximum(maxVersion) +{ +} + +VersionRange &VersionRange::narrowDown(const VersionRange &other) +{ + if (other.minimum > minimum) + minimum = other.minimum; + if (other.maximum.isValid() && other.maximum < maximum) + maximum = other.maximum; + return *this; +} + } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/tools/version.h b/src/lib/corelib/tools/version.h index e037777d7..be203ec5c 100644 --- a/src/lib/corelib/tools/version.h +++ b/src/lib/corelib/tools/version.h @@ -72,6 +72,18 @@ private: int m_build; }; +class VersionRange +{ +public: + VersionRange() = default; + VersionRange(const Version &minVersion, const Version &maxVersion); + + Version minimum; + Version maximum; // exclusive + + VersionRange &narrowDown(const VersionRange &other); +}; + QBS_EXPORT int compare(const Version &lhs, const Version &rhs); inline bool operator==(const Version &lhs, const Version &rhs) { return compare(lhs, rhs) == 0; } inline bool operator!=(const Version &lhs, const Version &rhs) { return !operator==(lhs, rhs); } diff --git a/tests/auto/blackbox/testdata/versioncheck/modules/higher/higher.qbs b/tests/auto/blackbox/testdata/versioncheck/modules/higher/higher.qbs new file mode 100644 index 000000000..4a9c85ceb --- /dev/null +++ b/tests/auto/blackbox/testdata/versioncheck/modules/higher/higher.qbs @@ -0,0 +1,10 @@ +import qbs + +Module { + Depends { + name: "lower" + required: false + versionAtLeast: "1.0" + versionBelow: "10.0" + } +} diff --git a/tests/auto/blackbox/testdata/versioncheck/modules/lower/lower.qbs b/tests/auto/blackbox/testdata/versioncheck/modules/lower/lower.qbs new file mode 100644 index 000000000..9322b53b1 --- /dev/null +++ b/tests/auto/blackbox/testdata/versioncheck/modules/lower/lower.qbs @@ -0,0 +1,3 @@ +import qbs + +Module { } diff --git a/tests/auto/blackbox/testdata/versioncheck/versioncheck.qbs b/tests/auto/blackbox/testdata/versioncheck/versioncheck.qbs new file mode 100644 index 000000000..8214428f1 --- /dev/null +++ b/tests/auto/blackbox/testdata/versioncheck/versioncheck.qbs @@ -0,0 +1,12 @@ +import qbs + +Product { + property string requestedMinVersion + property string requestedMaxVersion + Depends { name: "higher" } + Depends { + name: "lower" + versionAtLeast: requestedMinVersion + versionBelow: requestedMaxVersion + } +} diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 3a5f98c2a..6cbca8077 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -983,6 +983,43 @@ void TestBlackbox::usingsAsSoleInputsNonMultiplexed() QVERIFY(regularFileExists(p3BuildDir + "/custom2.out.plus")); } +void TestBlackbox::versionCheck() +{ + QDir::setCurrent(testDataDir + "/versioncheck"); + QFETCH(QString, requestedMinVersion); + QFETCH(QString, requestedMaxVersion); + QFETCH(QString, actualVersion); + QFETCH(QString, errorMessage); + QbsRunParameters params; + params.expectFailure = !errorMessage.isEmpty(); + params.arguments << "-n" << ("versioncheck.requestedMinVersion:" + requestedMinVersion) + << ("versioncheck.requestedMaxVersion:" + requestedMaxVersion) + << ("lower.version:" + actualVersion); + QCOMPARE(runQbs(params) == 0, errorMessage.isEmpty()); + if (params.expectFailure) + QVERIFY2(QString(m_qbsStderr).contains(errorMessage), m_qbsStderr.constData()); +} + +void TestBlackbox::versionCheck_data() +{ + QTest::addColumn<QString>("requestedMinVersion"); + QTest::addColumn<QString>("requestedMaxVersion"); + QTest::addColumn<QString>("actualVersion"); + QTest::addColumn<QString>("errorMessage"); + + QTest::newRow("ok1") << "1.0" << "1.1" << "1.0" << QString(); + QTest::newRow("ok2") << "1.0" << "2.0.1" << "2.0" << QString(); + QTest::newRow("ok3") << "1.0" << "2.5" << "1.5" << QString(); + QTest::newRow("ok3") << "1.0" << "2.0" << "1.99" << QString(); + QTest::newRow("bad1") << "2.0" << "2.1" << "1.5" << "needs to be at least"; + QTest::newRow("bad2") << "2.0" << "3.0" << "1.5" << "needs to be at least"; + QTest::newRow("bad3") << "2.0" << "3.0" << "3.5" << "needs to be lower than"; + QTest::newRow("bad4") << "2.0" << "3.0" << "3.0" << "needs to be lower than"; + + // "bad" because the "higer" module has stronger requirements. + QTest::newRow("bad5") << "0.1" << "0.9" << "0.5" << "Impossible version constraint"; +} + void TestBlackbox::versionScript() { Settings settings((QString())); diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h index ef65c1db4..7ce148aa2 100644 --- a/tests/auto/blackbox/tst_blackbox.h +++ b/tests/auto/blackbox/tst_blackbox.h @@ -226,6 +226,8 @@ private slots: void transitiveOptionalDependencies(); void typescript(); void usingsAsSoleInputsNonMultiplexed(); + void versionCheck(); + void versionCheck_data(); void versionScript(); void wildCardsAndRules(); void wildcardRenaming(); |