diff options
Diffstat (limited to 'src/lib/corelib/language')
27 files changed, 445 insertions, 384 deletions
diff --git a/src/lib/corelib/language/artifactproperties.cpp b/src/lib/corelib/language/artifactproperties.cpp index dd61bf1a2..011e58d88 100644 --- a/src/lib/corelib/language/artifactproperties.cpp +++ b/src/lib/corelib/language/artifactproperties.cpp @@ -48,9 +48,7 @@ ArtifactPropertiesPtr ArtifactProperties::create() return ArtifactPropertiesPtr(new ArtifactProperties); } -ArtifactProperties::ArtifactProperties() -{ -} +ArtifactProperties::ArtifactProperties() = default; FileTags ArtifactProperties::extraFileTags() const { @@ -66,6 +64,7 @@ bool operator==(const ArtifactProperties &ap1, const ArtifactProperties &ap2) { return ap1.fileTagsFilter() == ap2.fileTagsFilter() && ap1.extraFileTags() == ap2.extraFileTags() + && !ap1.propertyMap() == !ap2.propertyMap() && *ap1.propertyMap() == *ap2.propertyMap(); } diff --git a/src/lib/corelib/language/asttools.cpp b/src/lib/corelib/language/asttools.cpp index 617c8b95b..1b6abac7f 100644 --- a/src/lib/corelib/language/asttools.cpp +++ b/src/lib/corelib/language/asttools.cpp @@ -61,13 +61,13 @@ QString textOf(const QString &source, QbsQmlJS::AST::Node *node) if (!node) return {}; return source.mid(node->firstSourceLocation().begin(), - node->lastSourceLocation().end() - node->firstSourceLocation().begin()); + int(node->lastSourceLocation().end() - node->firstSourceLocation().begin())); } QStringRef textRefOf(const QString &source, QbsQmlJS::AST::Node *node) { const quint32 firstBegin = node->firstSourceLocation().begin(); - return source.midRef(firstBegin, node->lastSourceLocation().end() - firstBegin); + return source.midRef(firstBegin, int(node->lastSourceLocation().end() - firstBegin)); } } // namespace Internal diff --git a/src/lib/corelib/language/builtindeclarations.cpp b/src/lib/corelib/language/builtindeclarations.cpp index 68355df51..13783d3b9 100644 --- a/src/lib/corelib/language/builtindeclarations.cpp +++ b/src/lib/corelib/language/builtindeclarations.cpp @@ -125,7 +125,7 @@ ItemDeclaration BuiltinDeclarations::declarationsForType(ItemType type) const } ItemType BuiltinDeclarations::typeForName(const QString &typeName, - const CodeLocation location) const + const CodeLocation &location) const { const auto it = m_typeMap.constFind(typeName); if (it == m_typeMap.constEnd()) diff --git a/src/lib/corelib/language/builtindeclarations.h b/src/lib/corelib/language/builtindeclarations.h index 988f9ab81..9d7aee982 100644 --- a/src/lib/corelib/language/builtindeclarations.h +++ b/src/lib/corelib/language/builtindeclarations.h @@ -62,7 +62,7 @@ public: QStringList allTypeNames() const; ItemDeclaration declarationsForType(ItemType type) const; ItemType typeForName(const QString &typeName, - const CodeLocation location = CodeLocation()) const; + const CodeLocation &location = CodeLocation()) const; QString nameForType(ItemType itemType) const; QStringList argumentNamesForScriptFunction(ItemType itemType, const QString &scriptName) const; diff --git a/src/lib/corelib/language/deprecationinfo.h b/src/lib/corelib/language/deprecationinfo.h index 502715b84..89cd07f4a 100644 --- a/src/lib/corelib/language/deprecationinfo.h +++ b/src/lib/corelib/language/deprecationinfo.h @@ -50,11 +50,11 @@ class DeprecationInfo { public: explicit DeprecationInfo(const Version &removalVersion, - const QString &additionalUserInfo = QString()) + QString additionalUserInfo = QString()) : m_removalVersion(removalVersion) - , m_additionalUserInfo(additionalUserInfo) + , m_additionalUserInfo(std::move(additionalUserInfo)) {} - DeprecationInfo() {} + DeprecationInfo() = default; bool isValid() const { return m_removalVersion.isValid(); } Version removalVersion() const { return m_removalVersion; } diff --git a/src/lib/corelib/language/evaluator.h b/src/lib/corelib/language/evaluator.h index d8931a37e..f8535d0d7 100644 --- a/src/lib/corelib/language/evaluator.h +++ b/src/lib/corelib/language/evaluator.h @@ -64,7 +64,7 @@ class QBS_AUTOTEST_EXPORT Evaluator : private ItemObserver public: Evaluator(ScriptEngine *scriptEngine); - virtual ~Evaluator(); + ~Evaluator() override; ScriptEngine *engine() const { return m_scriptEngine; } QScriptValue property(const Item *item, const QString &name); diff --git a/src/lib/corelib/language/identifiersearch.cpp b/src/lib/corelib/language/identifiersearch.cpp index 973aae6a8..49ceab36c 100644 --- a/src/lib/corelib/language/identifiersearch.cpp +++ b/src/lib/corelib/language/identifiersearch.cpp @@ -43,9 +43,7 @@ namespace qbs { namespace Internal { -IdentifierSearch::IdentifierSearch() -{ -} +IdentifierSearch::IdentifierSearch() = default; void IdentifierSearch::start(QbsQmlJS::AST::Node *node) { diff --git a/src/lib/corelib/language/item.cpp b/src/lib/corelib/language/item.cpp index 9f754bdd7..a86cfeac1 100644 --- a/src/lib/corelib/language/item.cpp +++ b/src/lib/corelib/language/item.cpp @@ -203,15 +203,15 @@ bool Item::isOfTypeOrhasParentOfType(ItemType type) const PropertyDeclaration Item::propertyDeclaration(const QString &name, bool allowExpired) const { - PropertyDeclaration decl = m_propertyDeclarations.value(name); - if (decl.isValid()) - return decl; + auto it = m_propertyDeclarations.find(name); + if (it != m_propertyDeclarations.end()) + return it.value(); if (allowExpired) { - decl = m_expiredPropertyDeclarations.value(name); - if (decl.isValid()) - return decl; + it = m_expiredPropertyDeclarations.find(name); + if (it != m_expiredPropertyDeclarations.end()) + return it.value(); } - return m_prototype ? m_prototype->propertyDeclaration(name) : decl; + return m_prototype ? m_prototype->propertyDeclaration(name) : PropertyDeclaration(); } void Item::addModule(const Item::Module &module) diff --git a/src/lib/corelib/language/itempool.cpp b/src/lib/corelib/language/itempool.cpp index 3da8b947b..ccd22fe2e 100644 --- a/src/lib/corelib/language/itempool.cpp +++ b/src/lib/corelib/language/itempool.cpp @@ -43,9 +43,7 @@ namespace qbs { namespace Internal { -ItemPool::ItemPool() -{ -} +ItemPool::ItemPool() = default; ItemPool::~ItemPool() { diff --git a/src/lib/corelib/language/itemreadervisitorstate.cpp b/src/lib/corelib/language/itemreadervisitorstate.cpp index ca6ba2e12..20ddb5cfb 100644 --- a/src/lib/corelib/language/itemreadervisitorstate.cpp +++ b/src/lib/corelib/language/itemreadervisitorstate.cpp @@ -81,10 +81,7 @@ public: { } - ASTCacheValue(const ASTCacheValue &other) - : d(other.d) - { - } + ASTCacheValue(const ASTCacheValue &other) = default; void setProcessingFlag(bool b) { d->processing = b; } bool isProcessing() const { return d->processing; } diff --git a/src/lib/corelib/language/language.cpp b/src/lib/corelib/language/language.cpp index 40549b836..3b3e7401e 100644 --- a/src/lib/corelib/language/language.cpp +++ b/src/lib/corelib/language/language.cpp @@ -188,15 +188,9 @@ void ResolvedGroup::store(PersistentPool &pool) * \sa Rule */ -ScriptFunction::ScriptFunction() -{ - -} - -ScriptFunction::~ScriptFunction() -{ +ScriptFunction::ScriptFunction() = default; -} +ScriptFunction::~ScriptFunction() = default; /*! * \variable ScriptFunction::script @@ -282,7 +276,7 @@ QString Rule::toString() const FileTags Rule::staticOutputFileTags() const { FileTags result; - for (const RuleArtifactConstPtr &artifact : artifacts) + for (const auto &artifact : artifacts) result.unite(artifact->fileTags); return result; } @@ -312,9 +306,7 @@ ResolvedProduct::ResolvedProduct() { } -ResolvedProduct::~ResolvedProduct() -{ -} +ResolvedProduct::~ResolvedProduct() = default; void ResolvedProduct::accept(BuildGraphVisitor *visitor) const { @@ -331,7 +323,7 @@ void ResolvedProduct::accept(BuildGraphVisitor *visitor) const std::vector<SourceArtifactPtr> ResolvedProduct::allFiles() const { std::vector<SourceArtifactPtr> lst; - for (const GroupConstPtr &group : groups) + for (const auto &group : groups) lst << group->allFiles(); return lst; } @@ -343,7 +335,7 @@ std::vector<SourceArtifactPtr> ResolvedProduct::allFiles() const std::vector<SourceArtifactPtr> ResolvedProduct::allEnabledFiles() const { std::vector<SourceArtifactPtr> lst; - for (const GroupConstPtr &group : groups) { + for (const auto &group : groups) { if (group->enabled) lst << group->allFiles(); } @@ -364,7 +356,7 @@ FileTags ResolvedProduct::fileTagsForFileName(const QString &fileName) const return result; } } else { - priority.reset(new int(tagger->priority())); + priority = std::make_unique<int>(tagger->priority()); } result.unite(tagger->fileTags()); break; @@ -524,9 +516,7 @@ ResolvedProject::ResolvedProject() : enabled(true), m_topLevelProject(nullptr) { } -ResolvedProject::~ResolvedProject() -{ -} +ResolvedProject::~ResolvedProject() = default; void ResolvedProject::accept(BuildGraphVisitor *visitor) const { @@ -551,7 +541,7 @@ TopLevelProject *ResolvedProject::topLevelProject() std::vector<ResolvedProjectPtr> ResolvedProject::allSubProjects() const { std::vector<ResolvedProjectPtr> projectList = subProjects; - for (const ResolvedProjectConstPtr &subProject : subProjects) + for (const auto &subProject : subProjects) projectList << subProject->allSubProjects(); return projectList; } @@ -559,7 +549,7 @@ std::vector<ResolvedProjectPtr> ResolvedProject::allSubProjects() const std::vector<ResolvedProductPtr> ResolvedProject::allProducts() const { std::vector<ResolvedProductPtr> productList = products; - for (const ResolvedProjectConstPtr &subProject : qAsConst(subProjects)) + for (const auto &subProject : qAsConst(subProjects)) productList << subProject->allProducts(); return productList; } @@ -765,7 +755,7 @@ void SourceWildCards::expandPatterns(Set<QString> &result, const GroupConstPtr & if (baseDir.startsWith(buildDir)) return; - dirTimeStamps.push_back({ baseDir, FileInfo(baseDir).lastModified() }); + dirTimeStamps.emplace_back(baseDir, FileInfo(baseDir).lastModified()); QStringList changed_parts = parts; bool recursive = false; @@ -810,7 +800,7 @@ void SourceWildCards::expandPatterns(Set<QString> &result, const GroupConstPtr & expandPatterns(result, group, changed_parts, filePath, buildDir); } else { if (parentDir != baseDir) - dirTimeStamps.push_back({parentDir, FileInfo(baseDir).lastModified()}); + dirTimeStamps.emplace_back(parentDir, FileInfo(baseDir).lastModified()); result += QDir::cleanPath(filePath); } } @@ -867,6 +857,7 @@ bool operator==(const SourceArtifactInternal &sa1, const SourceArtifactInternal && sa1.fileTags == sa2.fileTags && sa1.overrideFileTags == sa2.overrideFileTags && sa1.targetOfModule == sa2.targetOfModule + && !sa1.properties == !sa2.properties && *sa1.properties == *sa2.properties; } diff --git a/src/lib/corelib/language/language.h b/src/lib/corelib/language/language.h index 65879dd56..bbd851333 100644 --- a/src/lib/corelib/language/language.h +++ b/src/lib/corelib/language/language.h @@ -99,7 +99,7 @@ public: private: FileTagger(const QStringList &patterns, FileTags fileTags, int priority); - FileTagger() {} + FileTagger() = default; void setPatterns(const QStringList &patterns); @@ -139,20 +139,20 @@ public: } private: - Probe() {} - Probe(const QString &globalId, + Probe() = default; + Probe(QString globalId, const CodeLocation &location, bool condition, - const QString &configureScript, - const QVariantMap &properties, - const QVariantMap &initialProperties, - const std::vector<QString> &importedFilesUsed) - : m_globalId(globalId) + QString configureScript, + QVariantMap properties, + QVariantMap initialProperties, + std::vector<QString> importedFilesUsed) + : m_globalId(std::move(globalId)) , m_location(location) - , m_configureScript(configureScript) - , m_properties(properties) - , m_initialProperties(initialProperties) - , m_importedFilesUsed(importedFilesUsed) + , m_configureScript(std::move(configureScript)) + , m_properties(std::move(properties)) + , m_initialProperties(std::move(initialProperties)) + , m_importedFilesUsed(std::move(importedFilesUsed)) , m_condition(condition) {} @@ -378,7 +378,7 @@ public: } private: - ResolvedModule() {} + ResolvedModule() = default; }; bool operator==(const ResolvedModule &m1, const ResolvedModule &m2); inline bool operator!=(const ResolvedModule &m1, const ResolvedModule &m2) { return !(m1 == m2); } diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp index 9c8f9da1d..56fbc198e 100644 --- a/src/lib/corelib/language/moduleloader.cpp +++ b/src/lib/corelib/language/moduleloader.cpp @@ -71,20 +71,24 @@ #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> +#include <memory> #include <utility> namespace qbs { namespace Internal { -static QString shadowProductPrefix() { return QStringLiteral("__shadow__"); } +using MultiplexConfigurationByIdTable = QThreadStorage<QHash<QString, QVariantMap> >; +Q_GLOBAL_STATIC(MultiplexConfigurationByIdTable, multiplexConfigurationsById); static void handlePropertyError(const ErrorInfo &error, const SetupProjectParameters ¶ms, Logger &logger) @@ -94,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) @@ -292,7 +310,7 @@ ModuleLoaderResult ModuleLoader::load(const SetupProjectParameters ¶meters) = m_elapsedTimePropertyChecking = 0; m_elapsedTimeProbes = 0; m_probesEncountered = m_probesRun = m_probesCachedCurrent = m_probesCachedOld = 0; - m_settings.reset(new Settings(parameters.settingsDirectory())); + m_settings = std::make_unique<Settings>(parameters.settingsDirectory()); const auto keys = m_parameters.overriddenValues().keys(); for (const QString &key : keys) { @@ -381,9 +399,9 @@ class PropertyDeclarationCheck : public ValueHandler Logger &m_logger; public: PropertyDeclarationCheck(const Set<Item *> &disabledItems, - const SetupProjectParameters ¶ms, Logger &logger) + SetupProjectParameters params, Logger &logger) : m_disabledItems(disabledItems) - , m_params(params) + , m_params(std::move(params)) , m_logger(logger) { } @@ -616,7 +634,7 @@ void ModuleLoader::handleTopLevelProject(ModuleLoaderResult *loadResult, Item *p for (ProductContext * const p : productSorter.sortedProducts()) { try { handleProduct(p); - if (p->name.startsWith(shadowProductPrefix())) + if (p->name.startsWith(StringConstants::shadowProductPrefix())) tlp.probes << p->info.probes; } catch (const ErrorInfo &err) { handleProductError(err, p); @@ -688,6 +706,9 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, m_qbsVersion.toString())); } + for (Item * const child : projectItem->children()) + child->setScope(projectContext.scope); + resolveProbes(&dummyProductContext, projectItem); projectContext.topLevelProject->probes << dummyProductContext.info.probes; @@ -695,7 +716,6 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, QList<Item *> multiplexedProducts; for (Item * const child : projectItem->children()) { - child->setScope(projectContext.scope); if (child->type() == ItemType::Product) multiplexedProducts << multiplexProductItem(&dummyProductContext, child); } @@ -762,9 +782,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) @@ -1085,66 +1120,111 @@ void ModuleLoader::adjustDependenciesForMultiplexing(const ProductContext &produ StringConstants::profilesProperty(), &profilesPropertyIsSet); const auto productRange = m_productsByName.equal_range(name); - std::vector<const ProductContext *> dependencies; + if (productRange.first == productRange.second) { + // Dependency is a module. Nothing to adjust. + return; + } + + std::vector<const ProductContext *> multiplexedDependencies; bool hasNonMultiplexedDependency = false; for (auto it = productRange.first; it != productRange.second; ++it) { - if (!it->second->multiplexConfigurationId.isEmpty()) { - dependencies.push_back(it->second); - if (productIsMultiplexed && !profilesPropertyIsSet) - break; - } else { + if (!it->second->multiplexConfigurationId.isEmpty()) + multiplexedDependencies.push_back(it->second); + else hasNonMultiplexedDependency = true; - break; - } } + bool hasMultiplexedDependencies = !multiplexedDependencies.empty(); // 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 best + // matching variant. + // (2b) The profiles property is set, we want to depend on all variants + // with a matching profile. // (3) The product is not multiplexed, but the dependency is. - // (3a) The dependency has an aggregator. We want to depend on the aggregator. - // (3b) The dependency does not have an aggregator. We want to depend on all the - // multiplexed variants. - // (4) The product is multiplexed, but the dependency is not. This case is implicitly - // handled, because we don't have to adapt any Depends items. + // (3a) The profiles property is not set, the dependency has an aggregator. + // We want to depend on the aggregator. + // (3b) The profiles property is not set, the dependency does not have an + // aggregator. We want to depend on all the multiplexed variants. + // (3c) The profiles property is set, we want to depend on all variants + // with a matching profile regardless of whether an aggregator exists or not. + // (4) The product is multiplexed, but the dependency is not. We don't have to adapt + // any Depends items. // (5) The product is a "shadow product". In that case, we know which product // it should have a dependency on, and we make sure we depend on that. - // (1) and (3a) - if (!productIsMultiplexed && hasNonMultiplexedDependency) + // (1) and (4) + if (!hasMultiplexedDependencies) + return; + + // (3a) + if (!productIsMultiplexed && hasNonMultiplexedDependency && !profilesPropertyIsSet) return; QStringList multiplexIds; const ShadowProductInfo shadowProductInfo = getShadowProductInfo(product); const bool isShadowProduct = shadowProductInfo.first && shadowProductInfo.second == name; - for (const ProductContext *dependency : dependencies) { + const auto productMultiplexConfig = + MultiplexInfo::multiplexIdToVariantMap(product.multiplexConfigurationId); + + for (const ProductContext *dependency : multiplexedDependencies) { const bool depMatchesShadowProduct = isShadowProduct && dependency->item == product.item->parent(); const QString depMultiplexId = dependency->multiplexConfigurationId; if (depMatchesShadowProduct) { // (5) dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(), VariantValue::create(depMultiplexId)); - multiplexIds.clear(); - break; - } - if (productIsMultiplexed && !profilesPropertyIsSet) { // (2) - const ValuePtr &multiplexId = product.item->property( - StringConstants::multiplexConfigurationIdProperty()); - dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(), - multiplexId); - break; + return; } + if (productIsMultiplexed && !profilesPropertyIsSet) { // 2a + if (dependency->multiplexConfigurationId == product.multiplexConfigurationId) { + const ValuePtr &multiplexId = product.item->property( + StringConstants::multiplexConfigurationIdProperty()); + dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(), + multiplexId); + return; - // (3b) (or (2) if Depends.profiles is set). - 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 { + // Otherwise collect partial matches and decide later + const auto dependencyMultiplexConfig = + MultiplexInfo::multiplexIdToVariantMap(dependency->multiplexConfigurationId); + + 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()) { + const QString productName = ResolvedProduct::fullDisplayName( + product.name, product.multiplexConfigurationId); + throw ErrorInfo(Tr::tr("Dependency from product '%1' to product '%2' not fulfilled. " + "There are no eligible multiplex candidates.").arg(productName, + 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) @@ -1216,7 +1296,8 @@ void ModuleLoader::prepareProduct(ProjectContext *projectContext, Item *productI // evaluate the product's exported properties in isolation in the project resolver. Item * const importer = Item::create(productItem->pool(), ItemType::Product); importer->setProperty(QStringLiteral("name"), - VariantValue::create(shadowProductPrefix() + productContext.name)); + VariantValue::create(StringConstants::shadowProductPrefix() + + productContext.name)); importer->setFile(productItem->file()); importer->setLocation(productItem->location()); importer->setScope(projectContext->scope); @@ -1281,7 +1362,6 @@ void ModuleLoader::createSortedModuleList(const Item::Module &parentModule, Item for (const Item::Module &dep : parentModule.item->modules()) createSortedModuleList(dep, modules); modules.push_back(parentModule); - return; } Item::Modules ModuleLoader::modulesSortedByDependency(const Item *productItem) @@ -1350,8 +1430,7 @@ void ModuleLoader::handleProduct(ModuleLoader::ProductContext *productContext) // set by the dependency module's merger (namely, scopes of defining items; see // ModuleMerger::replaceItemInScopes()). Item::Modules topSortedModules = modulesSortedByDependency(item); - for (Item::Module &module : topSortedModules) - ModuleMerger(m_logger, item, module).start(); + ModuleMerger::merge(m_logger, item, productContext->name, &topSortedModules); // Re-sort the modules by name. This is more stable; see QBS-818. // The list of modules in the product now has the same order as before, @@ -1585,7 +1664,7 @@ void ModuleLoader::handleSubProject(ModuleLoader::ProjectContext *projectContext const Item::PropertyMap &overriddenProperties = propertiesItem->properties(); for (Item::PropertyMap::ConstIterator it = overriddenProperties.constBegin(); it != overriddenProperties.constEnd(); ++it) { - loadedItem->setProperty(it.key(), overriddenProperties.value(it.key())); + loadedItem->setProperty(it.key(), it.value()); } } @@ -1806,7 +1885,7 @@ ProbeConstPtr ModuleLoader::findCurrentProbe( bool condition, const QVariantMap &initialProperties) const { - const QList<ProbeConstPtr> &cachedProbes = m_currentProbes.value(location); + const std::vector<ProbeConstPtr> &cachedProbes = m_currentProbes.value(location); for (const ProbeConstPtr &probe : cachedProbes) { if (probeMatches(probe, condition, initialProperties, QString(), CompareScript::No)) return probe; @@ -1964,7 +2043,7 @@ bool ModuleLoader::mergeExportItems(const ProductContext &productContext) productContext.project->topLevelProject->productModules.insert(productContext.name, pmi); if (hasDependenciesOnProductType) m_exportsWithDeferredDependsItems.insert(merged); - return exportItems.size() > 0; + return !exportItems.empty(); } Item *ModuleLoader::loadItemFromFile(const QString &filePath, @@ -2171,9 +2250,10 @@ void ModuleLoader::setSearchPathsForProduct(ModuleLoader::ProductContext *produc ModuleLoader::ShadowProductInfo ModuleLoader::getShadowProductInfo( const ModuleLoader::ProductContext &product) const { - const bool isShadowProduct = product.name.startsWith(shadowProductPrefix()); + const bool isShadowProduct = product.name.startsWith(StringConstants::shadowProductPrefix()); return std::make_pair(isShadowProduct, isShadowProduct - ? product.name.mid(shadowProductPrefix().size()) : QString()); + ? product.name.mid(StringConstants::shadowProductPrefix().size()) + : QString()); } void ModuleLoader::collectProductsByName(const TopLevelProjectContext &topLevelProject) @@ -2705,7 +2785,7 @@ void ModuleLoader::resolveParameterDeclarations(const Item *module) for (Item *param : moduleChildren) { if (param->type() != ItemType::Parameter) continue; - const auto paramDecls = param->propertyDeclarations(); + const auto ¶mDecls = param->propertyDeclarations(); for (auto it = paramDecls.begin(); it != paramDecls.end(); ++it) decls.insert(it.key(), it.value()); } @@ -2756,7 +2836,8 @@ QVariantMap ModuleLoader::extractParameters(Item *dependsItem) const QScriptValue sv = m_evaluator->scriptValue(dependsItem); try { result = safeToVariant(sv); - } catch (ErrorInfo ei) { + } catch (const ErrorInfo &exception) { + auto ei = exception; ei.prepend(Tr::tr("Error in dependency parameter."), dependsItem->location()); throw ei; } @@ -3024,72 +3105,67 @@ Item *ModuleLoader::searchAndLoadModuleFile(ProductContext *productContext, const CodeLocation &dependsItemLocation, const QualifiedId &moduleName, FallbackMode fallbackMode, bool isRequired, Item *moduleInstance) { - bool triedToLoadModule = false; + auto existingPaths = findExistingModulePaths(m_reader->allSearchPaths(), moduleName); + + if (existingPaths.isEmpty()) { // no suitable names found, try to use providers + bool moduleAlreadyKnown = false; + ModuleProviderResult result; + for (QualifiedId providerName = moduleName; !providerName.empty(); + providerName.pop_back()) { + if (!productContext->knownModuleProviders.insert(providerName).second) { + moduleAlreadyKnown = true; + break; + } + qCDebug(lcModuleLoader) << "Module" << moduleName.toString() + << "not found, checking for module providers"; + result = findModuleProvider(providerName, *productContext, + ModuleProviderLookup::Regular, dependsItemLocation); + if (result.providerFound) + break; + } + if (fallbackMode == FallbackMode::Enabled && !result.providerFound + && !moduleAlreadyKnown) { + qCDebug(lcModuleLoader) << "Specific module provider not found for" + << moduleName.toString() << ", setting up fallback."; + result = findModuleProvider(moduleName, *productContext, + ModuleProviderLookup::Fallback, dependsItemLocation); + } + if (result.providerAddedSearchPaths) { + qCDebug(lcModuleLoader) << "Re-checking for module" << moduleName.toString() + << "with newly added search paths from module provider"; + existingPaths = findExistingModulePaths(m_reader->allSearchPaths(), moduleName); + } + } + const QString fullName = moduleName.toString(); + bool triedToLoadModule = false; std::vector<PrioritizedItem> candidates; - const QStringList &searchPaths = m_reader->allSearchPaths(); - bool matchingDirectoryFound = false; - for (int i = 0; i < searchPaths.size(); ++i) { - const QString &path = searchPaths.at(i); - const QString dirPath = findExistingModulePath(path, moduleName); - if (dirPath.isEmpty()) - continue; - matchingDirectoryFound = true; - QStringList moduleFileNames = m_moduleDirListCache.value(dirPath); - if (moduleFileNames.empty()) { - QDirIterator dirIter(dirPath, StringConstants::qbsFileWildcards()); - while (dirIter.hasNext()) - moduleFileNames += dirIter.next(); - - m_moduleDirListCache.insert(dirPath, moduleFileNames); - } - for (const QString &filePath : qAsConst(moduleFileNames)) { - triedToLoadModule = true; + candidates.reserve(size_t(existingPaths.size())); + for (int i = 0; i < existingPaths.size(); ++i) { + const QString &dirPath = existingPaths.at(i); + QStringList &moduleFileNames = getModuleFileNames(dirPath); + for (auto it = moduleFileNames.begin(); it != moduleFileNames.end(); ) { + const QString &filePath = *it; + bool triedToLoad = true; Item *module = loadModuleFile(productContext, fullName, isBaseModule(moduleName), - filePath, &triedToLoadModule, moduleInstance); + filePath, &triedToLoad, moduleInstance); if (module) candidates.emplace_back(module, 0, i); - if (!triedToLoadModule) - m_moduleDirListCache[dirPath].removeOne(filePath); + if (!triedToLoad) + it = moduleFileNames.erase(it); + else + ++it; + triedToLoadModule = triedToLoadModule || triedToLoad; } } if (candidates.empty()) { - if (!matchingDirectoryFound) { - bool moduleAlreadyKnown = false; - ModuleProviderResult result; - for (QualifiedId providerName = moduleName; !providerName.empty(); - providerName.pop_back()) { - if (!productContext->knownModuleProviders.insert(providerName).second) { - moduleAlreadyKnown = true; - break; - } - qCDebug(lcModuleLoader) << "Module" << moduleName.toString() - << "not found, checking for module providers"; - result = findModuleProvider(providerName, *productContext, - ModuleProviderLookup::Regular, dependsItemLocation); - if (result.providerFound) - break; - } - if (fallbackMode == FallbackMode::Enabled && !result.providerFound - && !moduleAlreadyKnown) { - qCDebug(lcModuleLoader) << "Specific module provider not found for" - << moduleName.toString() << ", setting up fallback."; - result = findModuleProvider(moduleName, *productContext, - ModuleProviderLookup::Fallback, dependsItemLocation); - } - if (result.providerAddedSearchPaths) { - qCDebug(lcModuleLoader) << "Re-checking for module" << moduleName.toString() - << "with newly added search paths from module provider"; - return searchAndLoadModuleFile(productContext, dependsItemLocation, moduleName, - fallbackMode, isRequired, moduleInstance); - } - } if (!isRequired) return createNonPresentModule(fullName, QStringLiteral("not found"), nullptr); - if (Q_UNLIKELY(triedToLoadModule)) + if (Q_UNLIKELY(triedToLoadModule)) { throw ErrorInfo(Tr::tr("Module %1 could not be loaded.").arg(fullName), dependsItemLocation); + } return nullptr; } @@ -3119,6 +3195,17 @@ Item *ModuleLoader::searchAndLoadModuleFile(ProductContext *productContext, return moduleItem; } +QStringList &ModuleLoader::getModuleFileNames(const QString &dirPath) +{ + QStringList &moduleFileNames = m_moduleDirListCache[dirPath]; + if (moduleFileNames.empty()) { + QDirIterator dirIter(dirPath, StringConstants::qbsFileWildcards()); + while (dirIter.hasNext()) + moduleFileNames += dirIter.next(); + } + return moduleFileNames; +} + // returns QVariant::Invalid for types that do not need conversion static QVariant::Type variantType(PropertyDeclaration::Type t) { @@ -3227,7 +3314,7 @@ Item *ModuleLoader::getModulePrototype(ProductContext *productContext, } } Item * const module = loadItemFromFile(filePath, CodeLocation()); - prototypeList.push_back(std::make_pair(module, productContext->profileName)); + prototypeList.emplace_back(module, productContext->profileName); if (module->type() != ItemType::Module) { qCDebug(lcModuleLoader).nospace() << "Alleged module " << fullModuleName << " has type '" @@ -3285,6 +3372,9 @@ void ModuleLoader::setupBaseModulePrototype(Item *prototype) prototype->setProperty(QStringLiteral("hostPlatform"), VariantValue::create(QString::fromStdString( HostOsInfo::hostOSIdentifier()))); + prototype->setProperty(QStringLiteral("hostArchitecture"), + VariantValue::create(QString::fromStdString( + HostOsInfo::hostOSArchitecture()))); prototype->setProperty(QStringLiteral("libexecPath"), VariantValue::create(m_parameters.libexecPath())); @@ -3327,7 +3417,7 @@ static std::vector<std::pair<QualifiedId, ItemValuePtr>> instanceItemProperties( if (itemValue->item()->type() == ItemType::ModulePrefix) f(itemValue->item()); else - result.push_back(std::make_pair(name, itemValue)); + result.emplace_back(name, itemValue); name.removeLast(); } }; @@ -3496,7 +3586,7 @@ void ModuleLoader::resolveProbe(ProductContext *productContext, Item *parent, It if (Q_UNLIKELY(configureScript->sourceCode() == StringConstants::undefinedValue())) throw ErrorInfo(Tr::tr("Probe.configure must be set."), probe->location()); using ProbeProperty = std::pair<QString, QScriptValue>; - QList<ProbeProperty> probeBindings; + std::vector<ProbeProperty> probeBindings; QVariantMap initialProperties; for (Item *obj = probe; obj; obj = obj->prototype()) { const Item::PropertyMap &props = obj->properties(); @@ -3505,7 +3595,7 @@ void ModuleLoader::resolveProbe(ProductContext *productContext, Item *parent, It if (name == StringConstants::configureProperty()) continue; const QScriptValue value = m_evaluator->value(probe, name); - probeBindings += ProbeProperty(name, value); + probeBindings << ProbeProperty(name, value); if (name != StringConstants::conditionProperty()) initialProperties.insert(name, value.toVariant()); } @@ -3516,7 +3606,7 @@ void ModuleLoader::resolveProbe(ProductContext *productContext, Item *parent, It const QString &sourceCode = configureScript->sourceCode().toString(); ProbeConstPtr resolvedProbe; if (parent->type() == ItemType::Project - || productContext->name.startsWith(shadowProductPrefix())) { + || productContext->name.startsWith(StringConstants::shadowProductPrefix())) { resolvedProbe = findOldProjectProbe(probeId, condition, initialProperties, sourceCode); } else { const QString &uniqueProductName = productContext->uniqueName(); @@ -3544,7 +3634,7 @@ void ModuleLoader::resolveProbe(ProductContext *productContext, Item *parent, It engine->currentContext()->pushScope(fileCtxScopes.fileScope); engine->currentContext()->pushScope(fileCtxScopes.importScope); configureScope = engine->newObject(); - for (const ProbeProperty &b : qAsConst(probeBindings)) + for (const ProbeProperty &b : probeBindings) configureScope.setProperty(b.first, b.second); engine->currentContext()->pushScope(configureScope); engine->clearRequestedProperties(); @@ -3560,7 +3650,7 @@ void ModuleLoader::resolveProbe(ProductContext *productContext, Item *parent, It importedFilesUsedInConfigure = resolvedProbe->importedFilesUsed(); } QVariantMap properties; - for (const ProbeProperty &b : qAsConst(probeBindings)) { + for (const ProbeProperty &b : probeBindings) { QVariant newValue; if (resolvedProbe) { newValue = resolvedProbe->properties().value(b.first); @@ -3678,12 +3768,36 @@ QString ModuleLoader::findExistingModulePath(const QString &searchPath, const QualifiedId &moduleName) { QString dirPath = searchPath + QStringLiteral("/modules"); + + // isFileCaseCorrect is a very expensive call on macOS, so we cache the value for the + // modules and search paths we've already processed + auto &moduleInfo = m_existingModulePathCache[{searchPath, moduleName}]; + if (moduleInfo.first) // poor man's std::optional<QString> + return moduleInfo.second; + for (const QString &moduleNamePart : moduleName) { dirPath = FileInfo::resolvePath(dirPath, moduleNamePart); - if (!FileInfo::exists(dirPath) || !FileInfo::isFileCaseCorrect(dirPath)) - return {}; + if (!FileInfo::exists(dirPath) || !FileInfo::isFileCaseCorrect(dirPath)) { + moduleInfo.first = true; + return moduleInfo.second = QString(); + } } - return dirPath; + + moduleInfo.first = true; + return moduleInfo.second = dirPath; +} + +QStringList ModuleLoader::findExistingModulePaths( + const QStringList &searchPaths, const QualifiedId &moduleName) +{ + QStringList result; + result.reserve(searchPaths.size()); + for (const auto &path: searchPaths) { + const QString dirPath = findExistingModulePath(path, moduleName); + if (!dirPath.isEmpty()) + result.append(dirPath); + } + return result; } QVariantMap ModuleLoader::moduleProviderConfig(ModuleLoader::ProductContext &product) @@ -3769,6 +3883,7 @@ ModuleLoader::ModuleProviderResult ModuleLoader::findModuleProvider(const Qualif const QString searchPathBaseDir = ModuleProviderInfo::outputDirPath(projectBuildDir, name); const QVariant moduleConfig = moduleProviderConfig(product).value(name.toString()); QTextStream stream(&dummyItemFile); + using Qt::endl; stream.setCodec("UTF-8"); stream << "import qbs.FileInfo" << endl; stream << "import qbs.Utilities" << endl; diff --git a/src/lib/corelib/language/moduleloader.h b/src/lib/corelib/language/moduleloader.h index 85a2467f2..942f93c83 100644 --- a/src/lib/corelib/language/moduleloader.h +++ b/src/lib/corelib/language/moduleloader.h @@ -218,7 +218,7 @@ private: { Q_DISABLE_COPY(TopLevelProjectContext) public: - TopLevelProjectContext() {} + TopLevelProjectContext() = default; ~TopLevelProjectContext() { qDeleteAll(projects); } std::vector<ProjectContext *> projects; @@ -252,6 +252,7 @@ private: VariantValuePtr multiplexedType; QString toIdString(size_t row) const; + static QVariantMap multiplexIdToVariantMap(const QString &multiplexId); }; void dump(const MultiplexInfo &mpi); @@ -317,6 +318,7 @@ private: Item *searchAndLoadModuleFile(ProductContext *productContext, const CodeLocation &dependsItemLocation, const QualifiedId &moduleName, FallbackMode fallbackMode, bool isRequired, Item *moduleInstance); + QStringList &getModuleFileNames(const QString &dirPath); Item *loadModuleFile(ProductContext *productContext, const QString &fullModuleName, bool isBaseModule, const QString &filePath, bool *triedToLoad, Item *moduleInstance); Item *getModulePrototype(ProductContext *productContext, const QString &fullModuleName, @@ -337,8 +339,9 @@ private: QStringList readExtraSearchPaths(Item *item, bool *wasSet = nullptr); void copyProperties(const Item *sourceProject, Item *targetProject); Item *wrapInProjectIfNecessary(Item *item); - static QString findExistingModulePath(const QString &searchPath, - const QualifiedId &moduleName); + QString findExistingModulePath(const QString &searchPath, const QualifiedId &moduleName); + QStringList findExistingModulePaths( + const QStringList &searchPaths, const QualifiedId &moduleName); enum class ModuleProviderLookup { Regular, Fallback }; struct ModuleProviderResult @@ -411,6 +414,7 @@ private: ItemReader *m_reader; Evaluator *m_evaluator; QMap<QString, QStringList> m_moduleDirListCache; + QHash<std::pair<QString, QualifiedId>, std::pair<bool, QString>> m_existingModulePathCache; // The keys are file paths, the values are module prototype items accompanied by a profile. std::unordered_map<QString, std::vector<std::pair<Item *, QString>>> m_modulePrototypes; @@ -425,8 +429,8 @@ private: struct DependsChainEntry { - DependsChainEntry(const QualifiedId &name, const CodeLocation &location) - : name(name), location(location) + DependsChainEntry(QualifiedId name, const CodeLocation &location) + : name(std::move(name)), location(location) { } @@ -437,10 +441,10 @@ private: class DependsChainManager; std::vector<DependsChainEntry> m_dependsChain; - QHash<QString, QList<ProbeConstPtr>> m_oldProjectProbes; + QHash<QString, std::vector<ProbeConstPtr>> m_oldProjectProbes; QHash<QString, std::vector<ProbeConstPtr>> m_oldProductProbes; FileTime m_lastResolveTime; - QHash<CodeLocation, QList<ProbeConstPtr>> m_currentProbes; + QHash<CodeLocation, std::vector<ProbeConstPtr>> m_currentProbes; QVariantMap m_storedProfiles; QVariantMap m_localProfiles; std::multimap<QString, const ProductContext *> m_productsByName; diff --git a/src/lib/corelib/language/modulemerger.cpp b/src/lib/corelib/language/modulemerger.cpp index 053e90d53..c5deaae04 100644 --- a/src/lib/corelib/language/modulemerger.cpp +++ b/src/lib/corelib/language/modulemerger.cpp @@ -50,15 +50,18 @@ namespace qbs { namespace Internal { -ModuleMerger::ModuleMerger(Logger &logger, Item *root, Item::Module &moduleToMerge) +ModuleMerger::ModuleMerger(Logger &logger, Item *productItem, const QString &productName, + const Item::Modules::iterator &modulesBegin, + const Item::Modules::iterator &modulesEnd) : m_logger(logger) - , m_rootItem(root) - , m_mergedModule(moduleToMerge) - , m_required(moduleToMerge.required) - , m_isBaseModule(moduleToMerge.name.first() == StringConstants::qbsModule()) - , m_versionRange(moduleToMerge.versionRange) + , m_productItem(productItem) + , m_mergedModule(*modulesBegin) + , m_isBaseModule(m_mergedModule.name.first() == StringConstants::qbsModule()) + , m_isShadowProduct(productName.startsWith(StringConstants::shadowProductPrefix())) + , m_modulesBegin(std::next(modulesBegin)) + , m_modulesEnd(modulesEnd) { - QBS_CHECK(moduleToMerge.item->type() == ItemType::ModuleInstance); + QBS_CHECK(modulesBegin->item->type() == ItemType::ModuleInstance); } void ModuleMerger::replaceItemInValues(QualifiedId moduleName, Item *containerItem, Item *toReplace) @@ -83,48 +86,39 @@ void ModuleMerger::replaceItemInValues(QualifiedId moduleName, Item *containerIt } } -void ModuleMerger::replaceItemInScopes(Item *toReplace) -{ - // In insertProperties(), we potentially call setDefiningItem() with the "wrong" - // (to-be-replaced) module instance as an argument. If such module instances - // are dependencies of other modules, they have the depending module's instance - // as their "instance scope", which is the scope of their scope. This function takes - // care that the "wrong" definingItem of values in sub-modules still has the "right" - // instance scope, namely our merged module instead of some other instance. - for (const Item::Module &module : toReplace->modules()) { - for (const ValuePtr &property : module.item->properties()) { - ValuePtr v = property; - do { - if (v->definingItem() && v->definingItem()->scope() - && v->definingItem()->scope()->scope() == toReplace) { - v->definingItem()->scope()->setScope(m_mergedModule.item); - } - v = v->next(); - } while (v); - } - } -} - void ModuleMerger::start() { + // Iterate over any module that our product depends on. These modules + // may depend on m_mergedModule and contribute property assignments. + Item::PropertyMap props; + for (auto module = m_modulesBegin; module != m_modulesEnd; module++) + mergeModule(&props, *module); + + // Module property assignments in the product have the highest priority + // and are thus prepended. Item::Module m; - m.item = m_rootItem; - 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(); + m.item = m_productItem; + mergeModule(&props, m); + // The module's prototype is the essential unmodified module as loaded + // from the cache. Item *moduleProto = m_mergedModule.item->prototype(); while (moduleProto->prototype()) moduleProto = moduleProto->prototype(); + // The prototype item might contain default values which get appended in + // case of list properties. Scalar properties will only be set if not + // already specified above. + Item::PropertyMap mergedProps = m_mergedModule.item->properties(); for (auto it = props.constBegin(); it != props.constEnd(); ++it) { appendPrototypeValueToNextChain(moduleProto, it.key(), it.value()); mergedProps[it.key()] = it.value(); } + m_mergedModule.item->setProperties(mergedProps); + // Update all sibling instances of the to-be-merged module to behave identical + // to the merged module. for (Item *moduleInstanceContainer : qAsConst(m_moduleInstanceContainers)) { Item::Modules modules; for (const Item::Module &dep : moduleInstanceContainer->modules()) { @@ -133,11 +127,9 @@ void ModuleMerger::start() if (isTheModule && m.item != m_mergedModule.item) { QBS_CHECK(m.item->type() == ItemType::ModuleInstance); replaceItemInValues(m.name, moduleInstanceContainer, m.item); - replaceItemInScopes(m.item); m.item = m_mergedModule.item; - if (m_required) - m.required = true; - m.versionRange.narrowDown(m_versionRange); + m.required = m_mergedModule.required; + m.versionRange = m_mergedModule.versionRange; } modules << m; } @@ -145,94 +137,26 @@ void ModuleMerger::start() } } -Item::PropertyMap ModuleMerger::dfs(const Item::Module &m, Item::PropertyMap props) +void ModuleMerger::mergeModule(Item::PropertyMap *dstProps, const Item::Module &module) { - Item *moduleInstance = nullptr; - size_t numberOfOutprops = m.item->modules().size(); - for (const Item::Module &dep : m.item->modules()) { - if (dep.name == m_mergedModule.name) { - --numberOfOutprops; - moduleInstance = dep.item; - insertProperties(&props, moduleInstance, ScalarProperties); - m_moduleInstanceContainers << m.item; - if (dep.required) - m_required = true; - m_versionRange.narrowDown(dep.versionRange); - break; - } - } - - std::vector<Item::PropertyMap> outprops; - outprops.reserve(numberOfOutprops); - for (const Item::Module &dep : m.item->modules()) { - if (dep.item != moduleInstance) - outprops.push_back(dfs(dep, props)); - } - - if (!outprops.empty()) { - props = outprops.front(); - for (size_t i = 1; i < outprops.size(); ++i) - mergeOutProps(&props, outprops.at(i)); - } - - if (moduleInstance) - insertProperties(&props, moduleInstance, ListProperties); - - const bool isNonPresentModule = m.item->type() != ItemType::Product - && !m.item->isPresentModule(); - return isNonPresentModule ? Item::PropertyMap() : props; -} - -void ModuleMerger::mergeOutProps(Item::PropertyMap *dst, const Item::PropertyMap &src) -{ - for (auto it = src.constBegin(); it != src.constEnd(); ++it) { - ValuePtr &v = (*dst)[it.key()]; - if (!v) { - v = it.value(); - QBS_ASSERT(it.value(), continue); - continue; - } - if (v->type() != Value::JSSourceValueType) - continue; - if (it.value()->type() != Value::JSSourceValueType) - continue; - // possible conflict - const JSSourceValuePtr dstVal = std::static_pointer_cast<JSSourceValue>(v); - JSSourceValuePtr srcVal = std::static_pointer_cast<JSSourceValue>(it.value()); - - const PropertyDeclaration pd = m_decls.value(srcVal); - QBS_CHECK(pd.isValid()); - - if (pd.isScalar()) { - if (dstVal->sourceCode() != srcVal->sourceCode()) { - m_logger.qbsWarning() << Tr::tr("Conflicting scalar values at %1 and %2.").arg( - dstVal->location().toString(), - srcVal->location().toString()); - // TODO: yield error with a hint how to solve the conflict. - } - v = it.value(); - } else { - lastInNextChain(dstVal)->setNext(srcVal); - } - } -} + const Item::Module *dep = findModule(module.item, m_mergedModule.name); + if (!dep) + return; -void ModuleMerger::insertProperties(Item::PropertyMap *dst, Item *srcItem, PropertiesType type) -{ - Set<const Item *> &seenInstances = type == ScalarProperties - ? m_seenInstancesTopDown : m_seenInstancesBottomUp; + const bool mergingProductItem = (module.item == m_productItem); + Item *srcItem = dep->item; Item *origSrcItem = srcItem; do { - if (seenInstances.insert(srcItem).second) { - for (Item::PropertyMap::const_iterator it = srcItem->properties().constBegin(); - it != srcItem->properties().constEnd(); ++it) { + if (m_seenInstances.insert(srcItem).second) { + for (auto it = srcItem->properties().constBegin(); + it != srcItem->properties().constEnd(); ++it) { const ValuePtr &srcVal = it.value(); if (srcVal->type() == Value::ItemValueType) continue; if (it.key() == StringConstants::qbsSourceDirPropertyInternal()) continue; const PropertyDeclaration srcDecl = srcItem->propertyDeclaration(it.key()); - if (!srcDecl.isValid() || srcDecl.isScalar() != (type == ScalarProperties)) + if (!srcDecl.isValid()) continue; // Scalar variant values could stem from product multiplexing, in which case @@ -242,21 +166,51 @@ void ModuleMerger::insertProperties(Item::PropertyMap *dst, Item *srcItem, Prope continue; } - ValuePtr &v = (*dst)[it.key()]; - if (v && type == ScalarProperties) - continue; - ValuePtr clonedVal = srcVal->clone(); - m_decls[clonedVal] = srcDecl; - clonedVal->setDefiningItem(origSrcItem); - if (v) { - QBS_CHECK(!clonedVal->next()); - clonedVal->setNext(v); + ValuePtr clonedSrcVal = srcVal->clone(); + clonedSrcVal->setDefiningItem(origSrcItem); + + ValuePtr &dstVal = (*dstProps)[it.key()]; + if (dstVal) { + if (srcDecl.isScalar()) { + // Scalar properties get replaced. + if ((dstVal->type() == Value::JSSourceValueType) + && (srcVal->type() == Value::JSSourceValueType)) { + // Warn only about conflicting source code values + const JSSourceValuePtr dstJsVal = + std::static_pointer_cast<JSSourceValue>(dstVal); + const JSSourceValuePtr srcJsVal = + std::static_pointer_cast<JSSourceValue>(srcVal); + const bool overriddenInProduct = + m_mergedModule.item->properties().contains(it.key()); + + if (dstJsVal->sourceCode() != srcJsVal->sourceCode() + && !mergingProductItem && !overriddenInProduct + && !m_isShadowProduct) { + m_logger.qbsWarning() + << Tr::tr("Conflicting scalar values at %1 and %2.").arg( + dstJsVal->location().toString(), + srcJsVal->location().toString()); + } + } + } else { + // List properties get prepended + QBS_CHECK(!clonedSrcVal->next()); + clonedSrcVal->setNext(dstVal); + } } - v = clonedVal; + dstVal = clonedSrcVal; } } srcItem = srcItem->prototype(); } while (srcItem && srcItem->type() == ItemType::ModuleInstance); + + // Update dependency constraints + if (dep->required) + m_mergedModule.required = true; + m_mergedModule.versionRange.narrowDown(dep->versionRange); + + // We need to touch the unmerged module instances later once more + m_moduleInstanceContainers << module.item; } void ModuleMerger::appendPrototypeValueToNextChain(Item *moduleProto, const QString &propertyName, @@ -288,5 +242,23 @@ ValuePtr ModuleMerger::lastInNextChain(const ValuePtr &v) return n; } +const Item::Module *ModuleMerger::findModule(const Item *item, const QualifiedId &name) +{ + for (const auto &module : item->modules()) { + if (module.name == name) + return &module; + } + return nullptr; +} + +void ModuleMerger::merge(Logger &logger, Item *product, const QString &productName, + Item::Modules *topSortedModules) +{ + for (auto it = topSortedModules->begin(); it != topSortedModules->end(); ++it) + ModuleMerger(logger, product, productName, it, topSortedModules->end()).start(); +} + + + } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/language/modulemerger.h b/src/lib/corelib/language/modulemerger.h index 3cc3ba08a..469dc86c4 100644 --- a/src/lib/corelib/language/modulemerger.h +++ b/src/lib/corelib/language/modulemerger.h @@ -54,32 +54,33 @@ namespace Internal { class ModuleMerger { public: - ModuleMerger(Logger &logger, Item *root, Item::Module &moduleToMerge); - void start(); + static void merge(Logger &logger, Item *productItem, const QString &productName, + Item::Modules *topSortedModules); private: - Item::PropertyMap dfs(const Item::Module &m, Item::PropertyMap props); - void mergeOutProps(Item::PropertyMap *dst, const Item::PropertyMap &src); + ModuleMerger(Logger &logger, Item *productItem, const QString &productName, + const Item::Modules::iterator &modulesBegin, + const Item::Modules::iterator &modulesEnd); + void appendPrototypeValueToNextChain(Item *moduleProto, const QString &propertyName, const ValuePtr &sv); - static ValuePtr lastInNextChain(const ValuePtr &v); - - enum PropertiesType { ScalarProperties, ListProperties }; - void insertProperties(Item::PropertyMap *dst, Item *srcItem, PropertiesType type); + void mergeModule(Item::PropertyMap *props, const Item::Module &m); void replaceItemInValues(QualifiedId moduleName, Item *containerItem, Item *toReplace); - void replaceItemInScopes(Item *toReplace); + void start(); + + static ValuePtr lastInNextChain(const ValuePtr &v); + static const Item::Module *findModule(const Item *item, const QualifiedId &name); Logger &m_logger; - Item * const m_rootItem; + Item * const m_productItem; Item::Module &m_mergedModule; Item *m_clonedModulePrototype = nullptr; - QHash<ValuePtr, PropertyDeclaration> m_decls; - Set<const Item *> m_seenInstancesTopDown; - Set<const Item *> m_seenInstancesBottomUp; + Set<const Item *> m_seenInstances; Set<Item *> m_moduleInstanceContainers; - bool m_required; const bool m_isBaseModule; - VersionRange m_versionRange; + const bool m_isShadowProduct; + const Item::Modules::iterator m_modulesBegin; + const Item::Modules::iterator m_modulesEnd; }; } // namespace Internal diff --git a/src/lib/corelib/language/moduleproviderinfo.h b/src/lib/corelib/language/moduleproviderinfo.h index fef9d9765..4f757d3d9 100644 --- a/src/lib/corelib/language/moduleproviderinfo.h +++ b/src/lib/corelib/language/moduleproviderinfo.h @@ -55,9 +55,12 @@ class ModuleProviderInfo { public: ModuleProviderInfo() = default; - ModuleProviderInfo(const QualifiedId &name, const QVariantMap &config, - const QStringList &searchPaths, bool transientOutput) - : name(name), config(config), searchPaths(searchPaths), transientOutput(transientOutput) + ModuleProviderInfo(QualifiedId name, QVariantMap config, + QStringList searchPaths, bool transientOutput) + : name(std::move(name)) + , config(std::move(config)) + , searchPaths(std::move(searchPaths)) + , transientOutput(transientOutput) {} static QString outputBaseDirName() { return QStringLiteral("genmodules"); } diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp index 049472310..fd6063381 100644 --- a/src/lib/corelib/language/projectresolver.cpp +++ b/src/lib/corelib/language/projectresolver.cpp @@ -71,6 +71,7 @@ #include <QtCore/qregexp.h> #include <algorithm> +#include <memory> #include <queue> namespace qbs { @@ -126,9 +127,7 @@ ProjectResolver::ProjectResolver(Evaluator *evaluator, ModuleLoaderResult loadRe QBS_CHECK(FileInfo::isAbsolute(m_setupParams.buildRoot())); } -ProjectResolver::~ProjectResolver() -{ -} +ProjectResolver::~ProjectResolver() = default; void ProjectResolver::setProgressObserver(ProgressObserver *observer) { @@ -812,7 +811,7 @@ void ProjectResolver::resolveGroupFully(Item *item, ProjectResolver::ProjectCont group->targetOfModule = moduleProp->value().toString(); ErrorInfo fileError; if (!patterns.empty()) { - group->wildcards = std::unique_ptr<SourceWildCards>(new SourceWildCards); + group->wildcards = std::make_unique<SourceWildCards>(); SourceWildCards *wildcards = group->wildcards.get(); wildcards->group = group.get(); wildcards->excludePatterns = m_evaluator->stringListValue( @@ -988,7 +987,7 @@ void ProjectResolver::resolveShadowProduct(Item *item, ProjectResolver::ProjectC try { adaptExportedPropertyValues(item); } catch (const ErrorInfo &) {} - m_productExportInfo.push_back(std::make_pair(m_productContext->product, item)); + m_productExportInfo.emplace_back(m_productContext->product, item); } void ProjectResolver::setupExportedProperties(const Item *item, const QString &namePrefix, @@ -1460,7 +1459,7 @@ void ProjectResolver::matchArtifactProperties(const ResolvedProductPtr &product, const std::vector<SourceArtifactPtr> &artifacts) { for (const SourceArtifactPtr &artifact : artifacts) { - for (const ArtifactPropertiesConstPtr &artifactProperties : product->artifactProperties) { + for (const auto &artifactProperties : product->artifactProperties) { if (!artifact->isTargetOfModule() && artifact->fileTags.intersects(artifactProperties->fileTagsFilter())) { artifact->properties = artifactProperties->propertyMap(); diff --git a/src/lib/corelib/language/projectresolver.h b/src/lib/corelib/language/projectresolver.h index 428ba144d..a1e24a555 100644 --- a/src/lib/corelib/language/projectresolver.h +++ b/src/lib/corelib/language/projectresolver.h @@ -139,9 +139,9 @@ private: struct ProductDependencyInfo { - ProductDependencyInfo(const ResolvedProductPtr &product, - const QVariantMap ¶meters = QVariantMap()) - : product(product), parameters(parameters) + ProductDependencyInfo(ResolvedProductPtr product, + QVariantMap parameters = QVariantMap()) + : product(std::move(product)), parameters(std::move(parameters)) { } diff --git a/src/lib/corelib/language/property.h b/src/lib/corelib/language/property.h index 204704672..78061bf6f 100644 --- a/src/lib/corelib/language/property.h +++ b/src/lib/corelib/language/property.h @@ -65,9 +65,13 @@ public: { } - Property(const QString &product, const QString &module, const QString &property, - const QVariant &v, Kind k) - : productName(product), moduleName(module), propertyName(property), value(v), kind(k) + Property(QString product, QString module, QString property, + QVariant v, Kind k) + : productName(std::move(product)) + , moduleName(std::move(module)) + , propertyName(std::move(property)) + , value(std::move(v)) + , kind(k) { } diff --git a/src/lib/corelib/language/propertydeclaration.cpp b/src/lib/corelib/language/propertydeclaration.cpp index 5ea6a3d88..abe6a1626 100644 --- a/src/lib/corelib/language/propertydeclaration.cpp +++ b/src/lib/corelib/language/propertydeclaration.cpp @@ -84,20 +84,11 @@ PropertyDeclaration::PropertyDeclaration(const QString &name, Type type, d->flags = flags; } -PropertyDeclaration::PropertyDeclaration(const PropertyDeclaration &other) - : d(other.d) -{ -} +PropertyDeclaration::PropertyDeclaration(const PropertyDeclaration &other) = default; -PropertyDeclaration::~PropertyDeclaration() -{ -} +PropertyDeclaration::~PropertyDeclaration() = default; -PropertyDeclaration &PropertyDeclaration::operator=(const PropertyDeclaration &other) -{ - d = other.d; - return *this; -} +PropertyDeclaration &PropertyDeclaration::operator=(const PropertyDeclaration &other) = default; bool PropertyDeclaration::isValid() const { diff --git a/src/lib/corelib/language/propertymapinternal.cpp b/src/lib/corelib/language/propertymapinternal.cpp index 2a35e2a6a..fe0f672c5 100644 --- a/src/lib/corelib/language/propertymapinternal.cpp +++ b/src/lib/corelib/language/propertymapinternal.cpp @@ -58,13 +58,9 @@ namespace Internal { * \sa ResolvedProduct * \sa SourceArtifact */ -PropertyMapInternal::PropertyMapInternal() -{ -} +PropertyMapInternal::PropertyMapInternal() = default; -PropertyMapInternal::PropertyMapInternal(const PropertyMapInternal &other) : m_value(other.m_value) -{ -} +PropertyMapInternal::PropertyMapInternal(const PropertyMapInternal &other) = default; QVariant PropertyMapInternal::moduleProperty(const QString &moduleName, const QString &key, bool *isPresent) const diff --git a/src/lib/corelib/language/qualifiedid.cpp b/src/lib/corelib/language/qualifiedid.cpp index 5cc315bb1..9eb0e9463 100644 --- a/src/lib/corelib/language/qualifiedid.cpp +++ b/src/lib/corelib/language/qualifiedid.cpp @@ -44,9 +44,7 @@ namespace qbs { namespace Internal { -QualifiedId::QualifiedId() -{ -} +QualifiedId::QualifiedId() = default; QualifiedId::QualifiedId(const QString &singlePartName) : QStringList(singlePartName) diff --git a/src/lib/corelib/language/resolvedfilecontext.h b/src/lib/corelib/language/resolvedfilecontext.h index 81a3f7472..d783cf725 100644 --- a/src/lib/corelib/language/resolvedfilecontext.h +++ b/src/lib/corelib/language/resolvedfilecontext.h @@ -65,7 +65,7 @@ public: pool.serializationOp<opType>(m_filePath, m_jsExtensions, m_searchPaths, m_jsImports); } private: - ResolvedFileContext() {} + ResolvedFileContext() = default; ResolvedFileContext(const FileContextBase &ctx); }; diff --git a/src/lib/corelib/language/scriptengine.cpp b/src/lib/corelib/language/scriptengine.cpp index 7c531e764..e79ec54d7 100644 --- a/src/lib/corelib/language/scriptengine.cpp +++ b/src/lib/corelib/language/scriptengine.cpp @@ -478,19 +478,18 @@ QScriptValue ScriptEngine::js_require(QScriptContext *context, QScriptEngine *qt return context->throwError( ScriptEngine::tr("require: internal error. No search paths.")); - const QString uri = moduleName; if (engine->m_logger.debugEnabled()) { engine->m_logger.qbsDebug() - << "[require] loading extension " << uri; + << "[require] loading extension " << moduleName; } - QString uriAsPath = uri; - uriAsPath.replace(QLatin1Char('.'), QLatin1Char('/')); + QString moduleNameAsPath = moduleName; + moduleNameAsPath.replace(QLatin1Char('.'), QLatin1Char('/')); const QStringList searchPaths = engine->m_extensionSearchPathsStack.top(); - const QString dirPath = findExtensionDir(searchPaths, uriAsPath); + const QString dirPath = findExtensionDir(searchPaths, moduleNameAsPath); if (dirPath.isEmpty()) { - if (uri.startsWith(QStringLiteral("qbs."))) - return loadInternalExtension(context, engine, uri); + if (moduleName.startsWith(QStringLiteral("qbs."))) + return loadInternalExtension(context, engine, moduleName); } else { QDirIterator dit(dirPath, StringConstants::jsFileWildcards(), QDir::Files | QDir::Readable); diff --git a/src/lib/corelib/language/value.cpp b/src/lib/corelib/language/value.cpp index 656f38874..342fbd89a 100644 --- a/src/lib/corelib/language/value.cpp +++ b/src/lib/corelib/language/value.cpp @@ -61,9 +61,7 @@ Value::Value(const Value &other) { } -Value::~Value() -{ -} +Value::~Value() = default; Item *Value::definingItem() const { @@ -115,9 +113,7 @@ JSSourceValuePtr JSSourceValue::create(bool createdByPropertiesBlock) return JSSourceValuePtr(new JSSourceValue(createdByPropertiesBlock)); } -JSSourceValue::~JSSourceValue() -{ -} +JSSourceValue::~JSSourceValue() = default; ValuePtr JSSourceValue::clone() const { @@ -200,7 +196,7 @@ VariantValuePtr VariantValue::create(const QVariant &v) ValuePtr VariantValue::clone() const { - return VariantValuePtr(new VariantValue(*this)); + return std::make_shared<VariantValue>(*this); } const VariantValuePtr &VariantValue::falseValue() diff --git a/src/lib/corelib/language/value.h b/src/lib/corelib/language/value.h index f27406f2d..d3a748d92 100644 --- a/src/lib/corelib/language/value.h +++ b/src/lib/corelib/language/value.h @@ -151,14 +151,14 @@ public: struct PropertyData { PropertyData() = default; - PropertyData(const QString &v, const CodeLocation &l) : value(v), location(l) {} + PropertyData(QString v, const CodeLocation &l) : value(std::move(v)), location(l) {} QString value; CodeLocation location; }; - Alternative() { } - Alternative(const PropertyData &c, const PropertyData &o, const JSSourceValuePtr &v) - : condition(c), overrideListProperties(o), value(v) {} + Alternative() = default; + Alternative(PropertyData c, PropertyData o, JSSourceValuePtr v) + : condition(std::move(c)), overrideListProperties(std::move(o)), value(std::move(v)) {} Alternative clone() const { return Alternative(condition, overrideListProperties, |