summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/reference/items/depends.qdoc15
-rw-r--r--doc/reference/items/module.qdoc6
-rw-r--r--src/lib/corelib/language/builtindeclarations.cpp3
-rw-r--r--src/lib/corelib/language/item.h2
-rw-r--r--src/lib/corelib/language/moduleloader.cpp35
-rw-r--r--src/lib/corelib/language/modulemerger.cpp4
-rw-r--r--src/lib/corelib/language/modulemerger.h2
-rw-r--r--src/lib/corelib/tools/version.cpp14
-rw-r--r--src/lib/corelib/tools/version.h12
-rw-r--r--tests/auto/blackbox/testdata/versioncheck/modules/higher/higher.qbs10
-rw-r--r--tests/auto/blackbox/testdata/versioncheck/modules/lower/lower.qbs3
-rw-r--r--tests/auto/blackbox/testdata/versioncheck/versioncheck.qbs12
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp37
-rw-r--r--tests/auto/blackbox/tst_blackbox.h2
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();