aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/corelib/language/moduleloader.cpp
diff options
context:
space:
mode:
authorRichard Weickelt <richard@weickelt.de>2019-12-28 14:48:35 +0100
committerRichard Weickelt <richard@weickelt.de>2020-01-29 19:13:12 +0000
commit139ea745c07b7f2157a1d79e4c824c8b0db0ed08 (patch)
treec196a879b5af7d5665bd249f59c06c1e958583c3 /src/lib/corelib/language/moduleloader.cpp
parentf4a8824164820c428ca9c686337f462a4a9ff43d (diff)
Allow relaxed matching of multiplexed dependencies
If a multiplexed product depends on another multiplexed product, Qbs previously forced all multiplexed properties to match exactly. Only if Depends.profiles was specified, the comparison was limited to the qbs.profile property and all other properties were ignored. While strict matching is usually the natural choice, there are corner cases where it limits the applicability of Qbs. For example when multiplexing product A over buildVariant, but product B over buildVariant and architecture. In such cases a relaxed matching would be desirable, where only the common properties of A and B are considered. As long as there is only a single resulting dependency, the dependency is unambiguous and safe. Task-number: QBS-1515 Change-Id: I4ae6b413229bf1577311b4198d0596447e650816 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Diffstat (limited to 'src/lib/corelib/language/moduleloader.cpp')
-rw-r--r--src/lib/corelib/language/moduleloader.cpp98
1 files changed, 79 insertions, 19 deletions
diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp
index f8a651e45..55b671022 100644
--- a/src/lib/corelib/language/moduleloader.cpp
+++ b/src/lib/corelib/language/moduleloader.cpp
@@ -71,11 +71,13 @@
#include <QtCore/qdebug.h>
#include <QtCore/qdir.h>
+#include <QtCore/qglobalstatic.h>
#include <QtCore/qdiriterator.h>
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qtemporaryfile.h>
#include <QtCore/qtextstream.h>
+#include <QtCore/qthreadstorage.h>
#include <QtScript/qscriptvalueiterator.h>
#include <algorithm>
@@ -85,6 +87,9 @@
namespace qbs {
namespace Internal {
+using MultiplexConfigurationByIdTable = QThreadStorage<QHash<QString, QVariantMap> >;
+Q_GLOBAL_STATIC(MultiplexConfigurationByIdTable, multiplexConfigurationsById);
+
static void handlePropertyError(const ErrorInfo &error, const SetupProjectParameters &params,
Logger &logger)
{
@@ -93,6 +98,20 @@ static void handlePropertyError(const ErrorInfo &error, const SetupProjectParame
logger.printWarning(error);
}
+static bool multiplexConfigurationIntersects(const QVariantMap &lhs, const QVariantMap &rhs)
+{
+ QBS_CHECK(!lhs.isEmpty() && !rhs.isEmpty());
+
+ for (auto lhsProperty = lhs.constBegin(); lhsProperty != lhs.constEnd(); lhsProperty++) {
+ const auto rhsProperty = rhs.find(lhsProperty.key());
+ const bool isCommonProperty = rhsProperty != rhs.constEnd();
+ if (isCommonProperty && lhsProperty.value() != rhsProperty.value())
+ return false;
+ }
+
+ return true;
+}
+
class ModuleLoader::ItemModuleList : public QList<Item::Module> {};
static QString probeGlobalId(Item *probe)
@@ -761,9 +780,24 @@ QString ModuleLoader::MultiplexInfo::toIdString(size_t row) const
const VariantValuePtr &mpvalue = mprow.at(column);
multiplexConfiguration.insert(propertyName, mpvalue->value());
}
- return QString::fromUtf8(QJsonDocument::fromVariant(multiplexConfiguration)
- .toJson(QJsonDocument::Compact)
- .toBase64());
+ QString id = QString::fromUtf8(QJsonDocument::fromVariant(multiplexConfiguration)
+ .toJson(QJsonDocument::Compact)
+ .toBase64());
+ // Cache for later use in:multiplexIdToVariantMap()
+ multiplexConfigurationsById->localData().insert(id, multiplexConfiguration);
+ return id;
+}
+
+QVariantMap ModuleLoader::MultiplexInfo::multiplexIdToVariantMap(const QString &multiplexId)
+{
+ if (multiplexId.isEmpty())
+ return QVariantMap();
+
+ QVariantMap result = multiplexConfigurationsById->localData().value(multiplexId);
+ // We assume that MultiplexInfo::toIdString() has been called for this
+ // particular multiplex configuration.
+ QBS_CHECK(!result.isEmpty());
+ return result;
}
void qbs::Internal::ModuleLoader::ModuleLoader::dump(const ModuleLoader::MultiplexInfo &mpi)
@@ -1102,7 +1136,7 @@ void ModuleLoader::adjustDependenciesForMultiplexing(const ProductContext &produ
// These are the allowed cases:
// (1) Normal dependency with no multiplexing whatsoever.
// (2) Both product and dependency are multiplexed.
- // (2a) The profiles property is not set, we want to depend on the
+ // (2a) The profiles property is not set, we want to depend on the best
// matching variant.
// (2b) The profiles property is set, we want to depend on all variants
// with a matching profile.
@@ -1129,6 +1163,9 @@ void ModuleLoader::adjustDependenciesForMultiplexing(const ProductContext &produ
QStringList multiplexIds;
const ShadowProductInfo shadowProductInfo = getShadowProductInfo(product);
const bool isShadowProduct = shadowProductInfo.first && shadowProductInfo.second == name;
+ const auto productMultiplexConfig =
+ MultiplexInfo::multiplexIdToVariantMap(product.multiplexConfigurationId);
+
for (const ProductContext *dependency : multiplexedDependencies) {
const bool depMatchesShadowProduct = isShadowProduct
&& dependency->item == product.item->parent();
@@ -1139,23 +1176,30 @@ void ModuleLoader::adjustDependenciesForMultiplexing(const ProductContext &produ
return;
}
if (productIsMultiplexed && !profilesPropertyIsSet) { // 2a
- const ValuePtr &multiplexId = product.item->property(
- StringConstants::multiplexConfigurationIdProperty());
- dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(),
- multiplexId);
- return;
- }
+ if (dependency->multiplexConfigurationId == product.multiplexConfigurationId) {
+ const ValuePtr &multiplexId = product.item->property(
+ StringConstants::multiplexConfigurationIdProperty());
+ dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(),
+ multiplexId);
+ return;
+
+ } else {
+ // Otherwise collect partial matches and decide later
+ const auto dependencyMultiplexConfig =
+ MultiplexInfo::multiplexIdToVariantMap(dependency->multiplexConfigurationId);
- // (2b), (3b) or (3c)
- const bool profileMatch = !profilesPropertyIsSet || profiles.empty()
- || profiles.contains(dependency->profileName);
- if (profileMatch)
- multiplexIds << depMultiplexId;
+ if (multiplexConfigurationIntersects(dependencyMultiplexConfig, productMultiplexConfig))
+ multiplexIds << dependency->multiplexConfigurationId;
+ }
+ } else {
+ // (2b), (3b) or (3c)
+ const bool profileMatch = !profilesPropertyIsSet || profiles.empty()
+ || profiles.contains(dependency->profileName);
+ if (profileMatch)
+ multiplexIds << depMultiplexId;
+ }
}
- if (!multiplexIds.empty()) {
- dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(),
- VariantValue::create(multiplexIds));
- } else {
+ if (multiplexIds.empty()) {
const QString productName = ResolvedProduct::fullDisplayName(
product.name, product.multiplexConfigurationId);
throw ErrorInfo(Tr::tr("Dependency from product '%1' to product '%2' not fulfilled. "
@@ -1163,6 +1207,22 @@ void ModuleLoader::adjustDependenciesForMultiplexing(const ProductContext &produ
name),
dependsItem->location());
}
+
+ // In case of (2a), at most 1 match is allowed
+ if (productIsMultiplexed && !profilesPropertyIsSet && multiplexIds.size() > 1) {
+ const QString productName = ResolvedProduct::fullDisplayName(
+ product.name, product.multiplexConfigurationId);
+ QStringList candidateNames;
+ for (const auto &id : qAsConst(multiplexIds))
+ candidateNames << ResolvedProduct::fullDisplayName(name, id);
+ throw ErrorInfo(Tr::tr("Dependency from product '%1' to product '%2' is ambiguous. "
+ "Eligible multiplex candidates: %3.").arg(
+ productName, name, candidateNames.join(QLatin1String(", "))),
+ dependsItem->location());
+ }
+
+ dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(),
+ VariantValue::create(multiplexIds));
}
void ModuleLoader::prepareProduct(ProjectContext *projectContext, Item *productItem)