From 4c188eff3d63f60a891a609993390fedb8e9f639 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 21 Apr 2023 12:48:45 +0200 Subject: Move module file loading code into ModuleLoader That's where it belongs, and now it is possible to put it there. Change-Id: I2bd041dcf2e1e8e1be804f4b31c72c2859615017 Reviewed-by: Ivan Komissarov --- src/lib/corelib/language/moduleloader.cpp | 257 ++++++++++++++++++++---- src/lib/corelib/language/moduleloader.h | 33 ++- src/lib/corelib/language/moduleproviderloader.h | 2 +- src/lib/corelib/language/projecttreebuilder.cpp | 193 ++---------------- 4 files changed, 259 insertions(+), 226 deletions(-) diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp index 6efb2ab4d..27ce2cd77 100644 --- a/src/lib/corelib/language/moduleloader.cpp +++ b/src/lib/corelib/language/moduleloader.cpp @@ -41,16 +41,23 @@ #include "evaluator.h" #include "itemreader.h" +#include "moduleproviderloader.h" +#include "productitemmultiplexer.h" #include "value.h" #include #include #include #include +#include #include +#include #include #include +#include +#include + #include #include @@ -59,11 +66,13 @@ namespace qbs::Internal { class ModuleLoader::Private { public: - Private(const SetupProjectParameters &setupParameters, ItemReader &itemReader, - Evaluator &evaluator, Logger &logger) - : setupParameters(setupParameters), itemReader(itemReader), evaluator(evaluator), - logger(logger) {} + Private(const SetupProjectParameters &setupParameters, ModuleProviderLoader &providerLoader, + ItemReader &itemReader, Evaluator &evaluator, Logger &logger) + : setupParameters(setupParameters), providerLoader(providerLoader), + itemReader(itemReader), evaluator(evaluator), logger(logger) {} + std::pair loadModuleFile(const ProductContext &product, + const QString &moduleName, const QString &filePath); std::pair getModulePrototype(const ModuleLoader::ProductContext &product, const QString &moduleName, const QString &filePath); bool evaluateModuleCondition(const ModuleLoader::ProductContext &product, Item *module, @@ -72,6 +81,7 @@ public: const Item::Modules &modules); const SetupProjectParameters &setupParameters; + ModuleProviderLoader &providerLoader; ItemReader &itemReader; Evaluator &evaluator; Logger &logger; @@ -85,34 +95,213 @@ public: std::unordered_map> unknownProfilePropertyErrors; std::unordered_map parameterDeclarations; + std::unordered_map> providerConfigsPerProduct; + QHash, std::optional> existingModulePathCache; + std::map moduleDirListCache; + + qint64 elapsedTimeModuleProviders = 0; }; -ModuleLoader::ModuleLoader(const SetupProjectParameters &setupParameters, ItemReader &itemReader, - Evaluator &evaluator, Logger &logger) - : d(new Private(setupParameters, itemReader, evaluator, logger)) { } +ModuleLoader::ModuleLoader( + const SetupProjectParameters &setupParameters, ModuleProviderLoader &providerLoader, + ItemReader &itemReader, Evaluator &evaluator, Logger &logger) + : d(new Private(setupParameters, providerLoader, itemReader, evaluator, logger)) { } ModuleLoader::~ModuleLoader() { delete d; } -std::pair ModuleLoader::loadModuleFile( +struct PrioritizedItem +{ + PrioritizedItem(Item *item, int priority, int searchPathIndex) + : item(item), priority(priority), searchPathIndex(searchPathIndex) { } + + Item * const item; + int priority = 0; + const int searchPathIndex; +}; + +static Item *chooseModuleCandidate(const std::vector &candidates, + const QString &moduleName) +{ + // TODO: This should also consider the version requirement. + + auto maxIt = std::max_element( + candidates.begin(), candidates.end(), + [] (const PrioritizedItem &a, const PrioritizedItem &b) { + if (a.priority < b.priority) + return true; + if (a.priority > b.priority) + return false; + return a.searchPathIndex > b.searchPathIndex; + }); + + size_t nmax = std::count_if( + candidates.begin(), candidates.end(), + [maxIt] (const PrioritizedItem &i) { + return i.priority == maxIt->priority && i.searchPathIndex == maxIt->searchPathIndex; + }); + + if (nmax > 1) { + ErrorInfo e(Tr::tr("There is more than one equally prioritized candidate for module '%1'.") + .arg(moduleName)); + for (size_t i = 0; i < candidates.size(); ++i) { + const auto candidate = candidates.at(i); + if (candidate.priority == maxIt->priority) { + //: The %1 denotes the number of the candidate. + e.append(Tr::tr("candidate %1").arg(i + 1), candidates.at(i).item->location()); + } + } + throw e; + } + + return maxIt->item; +} + +ModuleLoader::Result ModuleLoader::searchAndLoadModuleFile( + const ProductContext &productContext, const CodeLocation &dependsItemLocation, + const QualifiedId &moduleName, FallbackMode fallbackMode, bool isRequired) +{ + const auto findExistingModulePath = [&](const QString &searchPath) { + // 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 = d->existingModulePathCache[{searchPath, moduleName}]; + if (moduleInfo) + return *moduleInfo; + + QString dirPath = searchPath + QStringLiteral("/modules"); + for (const QString &moduleNamePart : moduleName) { + dirPath = FileInfo::resolvePath(dirPath, moduleNamePart); + if (!FileInfo::exists(dirPath) || !FileInfo::isFileCaseCorrect(dirPath)) { + return *(moduleInfo = QString()); + } + } + + return *(moduleInfo = dirPath); + }; + const auto findExistingModulePaths = [&] { + const QStringList &searchPaths = d->itemReader.allSearchPaths(); + QStringList result; + result.reserve(searchPaths.size()); + for (const auto &path: searchPaths) { + const QString dirPath = findExistingModulePath(path); + if (!dirPath.isEmpty()) + result.append(dirPath); + } + return result; + }; + + Result loadResult; + auto existingPaths = findExistingModulePaths(); + if (existingPaths.isEmpty()) { // no suitable names found, try to use providers + AccumulatingTimer providersTimer( + d->setupParameters.logElapsedTime() ? &d->elapsedTimeModuleProviders : nullptr); + std::optional &providerConfig + = d->providerConfigsPerProduct[productContext.productItem]; + auto result = d->providerLoader.executeModuleProviders( + {productContext.productItem, productContext.projectItem, productContext.name, + productContext.uniqueName, productContext.moduleProperties, providerConfig}, + dependsItemLocation, + moduleName, + fallbackMode); + loadResult.providerProbes << result.probes; + if (!providerConfig) + providerConfig = result.providerConfig; + if (result.providerAddedSearchPaths) { + qCDebug(lcModuleLoader) << "Re-checking for module" << moduleName.toString() + << "with newly added search paths from module provider"; + existingPaths = findExistingModulePaths(); + } + } + + const auto getModuleFileNames = [&](const QString &dirPath) -> QStringList & { + QStringList &moduleFileNames = d->moduleDirListCache[dirPath]; + if (moduleFileNames.empty()) { + QDirIterator dirIter(dirPath, StringConstants::qbsFileWildcards()); + while (dirIter.hasNext()) + moduleFileNames += dirIter.next(); + } + return moduleFileNames; + }; + + const QString fullName = moduleName.toString(); + bool triedToLoadModule = false; + std::vector candidates; + 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; + const auto [module, triedToLoad] = d->loadModuleFile(productContext, fullName, + filePath); + if (module) + candidates.emplace_back(module, 0, i); + if (!triedToLoad) + it = moduleFileNames.erase(it); + else + ++it; + triedToLoadModule = triedToLoadModule || triedToLoad; + } + } + + if (candidates.empty()) { + if (!isRequired) { + loadResult.moduleItem = createNonPresentModule( + *productContext.projectItem->pool(), fullName, QStringLiteral("not found"), + nullptr); + return loadResult; + } + if (Q_UNLIKELY(triedToLoadModule)) { + throw ErrorInfo(Tr::tr("Module %1 could not be loaded.").arg(fullName), + dependsItemLocation); + } + return loadResult; + } + + if (candidates.size() == 1) { + loadResult.moduleItem = candidates.at(0).item; + } else { + for (auto &candidate : candidates) { + candidate.priority = d->evaluator.intValue(candidate.item, + StringConstants::priorityProperty(), + candidate.priority); + } + loadResult.moduleItem = chooseModuleCandidate(candidates, fullName); + } + + const QString fullProductName = ProductItemMultiplexer::fullProductDisplayName( + productContext.name, productContext.multiplexId); + const auto it = d->unknownProfilePropertyErrors.find(loadResult.moduleItem); + if (it != d->unknownProfilePropertyErrors.cend()) { + ErrorInfo error(Tr::tr("Loading module '%1' for product '%2' failed due to invalid values " + "in profile '%3':") + .arg(moduleName.toString(), fullProductName, productContext.profile)); + for (const ErrorInfo &e : it->second) + error.append(e.toString()); + handlePropertyError(error, d->setupParameters, d->logger); + } + + return loadResult; +} + +std::pair ModuleLoader::Private::loadModuleFile( const ProductContext &product, const QString &moduleName, const QString &filePath) { qCDebug(lcModuleLoader) << "loadModuleFile" << moduleName << "from" << filePath; - const auto [module, triedToLoad] = - d->getModulePrototype(product, moduleName, filePath); + const auto [module, triedToLoad] = getModulePrototype(product, moduleName, filePath); if (!module) return {nullptr, triedToLoad}; - const auto key = std::make_pair(module, product.item); - const auto it = d->modulePrototypeEnabledInfo.find(key); - if (it != d->modulePrototypeEnabledInfo.end()) { + const auto key = std::make_pair(module, product.productItem); + const auto it = modulePrototypeEnabledInfo.find(key); + if (it != modulePrototypeEnabledInfo.end()) { qCDebug(lcModuleLoader) << "prototype cache hit (level 2)"; return {it->second ? module : nullptr, triedToLoad}; } - if (!d->evaluateModuleCondition(product, module, moduleName)) { + if (!evaluateModuleCondition(product, module, moduleName)) { qCDebug(lcModuleLoader) << "condition of module" << moduleName << "is false"; - d->modulePrototypeEnabledInfo.insert({key, false}); + modulePrototypeEnabledInfo.insert({key, false}); return {nullptr, triedToLoad}; } @@ -122,7 +311,7 @@ std::pair ModuleLoader::loadModuleFile( module->setProperty(QStringLiteral("hostArchitecture"), VariantValue::create(HostOsInfo::hostOSArchitecture())); module->setProperty(QStringLiteral("libexecPath"), - VariantValue::create(d->setupParameters.libexecPath())); + VariantValue::create(setupParameters.libexecPath())); const Version qbsVersion = LanguageInfo::qbsVersion(); module->setProperty(QStringLiteral("versionMajor"), @@ -141,26 +330,13 @@ std::pair ModuleLoader::loadModuleFile( for (auto it = paramDecls.begin(); it != paramDecls.end(); ++it) decls.insert(it.key(), it.value()); } - d->parameterDeclarations.insert({module, decls}); + parameterDeclarations.insert({module, decls}); } - d->modulePrototypeEnabledInfo.insert({key, true}); + modulePrototypeEnabledInfo.insert({key, true}); return {module, triedToLoad}; } -void ModuleLoader::checkProfileErrorsForModule( - Item *module, const QString &moduleName, const QString &productName, const QString &profileName) -{ - const auto it = d->unknownProfilePropertyErrors.find(module); - if (it != d->unknownProfilePropertyErrors.cend()) { - ErrorInfo error(Tr::tr("Loading module '%1' for product '%2' failed due to invalid values " - "in profile '%3':").arg(moduleName, productName, profileName)); - for (const ErrorInfo &e : it->second) - error.append(e.toString()); - handlePropertyError(error, d->setupParameters, d->logger); - } -} - std::pair ModuleLoader::Private::getModulePrototype( const ProductContext &product, const QString &moduleName, const QString &filePath) { @@ -219,7 +395,7 @@ bool ModuleLoader::Private::evaluateModuleCondition(const ProductContext &produc if (m_needsQbsItem) { m_prevQbsItemValue = module->property(StringConstants::qbsModule()); module->setProperty(StringConstants::qbsModule(), - product.item->property(StringConstants::qbsModule())); + product.productItem->property(StringConstants::qbsModule())); } } ~TempQbsModuleProvider() @@ -296,10 +472,11 @@ private: const std::unordered_map &m_parameterDeclarations; }; -void ModuleLoader::checkDependencyParameterDeclarations(const ProductContext &product) const +void ModuleLoader::checkDependencyParameterDeclarations(const Item *productItem, + const QString &productName) const { - DependencyParameterDeclarationCheck dpdc(product.name, product.item, d->parameterDeclarations); - for (const Item::Module &dep : product.item->modules()) { + DependencyParameterDeclarationCheck dpdc(productName, productItem, d->parameterDeclarations); + for (const Item::Module &dep : productItem->modules()) { if (!dep.parameters.empty()) dpdc(dep.parameters); } @@ -336,4 +513,14 @@ void ModuleLoader::Private::forwardParameterDeclarations(const QualifiedId &modu } } +void ModuleLoader::printProfilingInfo(int indent) +{ + if (!d->setupParameters.logElapsedTime()) + return; + d->logger.qbsLog(LoggerInfo, true) + << QByteArray(indent, ' ') + << Tr::tr("Running module providers took %1.") + .arg(elapsedTimeString(d->elapsedTimeModuleProviders)); +} + } // namespace qbs::Internal diff --git a/src/lib/corelib/language/moduleloader.h b/src/lib/corelib/language/moduleloader.h index 2e1140726..37ed34c08 100644 --- a/src/lib/corelib/language/moduleloader.h +++ b/src/lib/corelib/language/moduleloader.h @@ -39,41 +39,56 @@ #pragma once +#include "forward_decls.h" #include "item.h" #include #include +#include + namespace qbs { +class CodeLocation; class SetupProjectParameters; namespace Internal { class Evaluator; +enum class FallbackMode; class ItemReader; class Logger; +class ModuleProviderLoader; class ModuleLoader { public: - ModuleLoader(const SetupProjectParameters &setupParameters, ItemReader &itemReader, + ModuleLoader(const SetupProjectParameters &setupParameters, + ModuleProviderLoader &providerLoader, ItemReader &itemReader, Evaluator &evaluator, Logger &logger); ~ModuleLoader(); struct ProductContext { - const Item *item = nullptr; + Item * const productItem; + const Item * const projectItem; const QString &name; + const QString &uniqueName; const QString &profile; + const QString &multiplexId; + const QVariantMap &moduleProperties; const QVariantMap &profileModuleProperties; }; - // TODO: Entry point should be searchAndLoadModuleFile(). Needs ProviderLoader clean-up first. - std::pair loadModuleFile(const ProductContext &product, - const QString &moduleName, const QString &filePath); + struct Result { + Item *moduleItem = nullptr; + std::vector providerProbes; + }; + Result searchAndLoadModuleFile(const ProductContext &productContext, + const CodeLocation &dependsItemLocation, + const QualifiedId &moduleName, + FallbackMode fallbackMode, bool isRequired); - void checkDependencyParameterDeclarations(const ProductContext &product) const; + void checkDependencyParameterDeclarations(const Item *productItem, + const QString &productName) const; void forwardParameterDeclarations(const Item *dependsItem, const Item::Modules &modules); + void printProfilingInfo(int indent); - // TODO: Remove once entry point is changed. - void checkProfileErrorsForModule(Item *module, const QString &moduleName, - const QString &productName, const QString &profileName); private: class Private; Private * const d; diff --git a/src/lib/corelib/language/moduleproviderloader.h b/src/lib/corelib/language/moduleproviderloader.h index 4216b4430..8fb0d6f45 100644 --- a/src/lib/corelib/language/moduleproviderloader.h +++ b/src/lib/corelib/language/moduleproviderloader.h @@ -102,7 +102,7 @@ public: struct ProductContext { Item * const productItem; - Item * const projectItem; + const Item * const projectItem; const QString &name; const QString &uniqueName; const QVariantMap &moduleProperties; diff --git a/src/lib/corelib/language/projecttreebuilder.cpp b/src/lib/corelib/language/projecttreebuilder.cpp index 42fc59717..3786517ad 100644 --- a/src/lib/corelib/language/projecttreebuilder.cpp +++ b/src/lib/corelib/language/projecttreebuilder.cpp @@ -108,8 +108,6 @@ public: QVariantMap defaultParameters; // In Export item. QStringList searchPaths; - std::optional theModuleProviderConfig; - struct ResolvedDependsItem { Item *item = nullptr; QualifiedId name; @@ -223,7 +221,6 @@ class TimingData { public: qint64 prepareProducts = 0; qint64 productDependencies = 0; - qint64 moduleProviders = 0; qint64 handleProducts = 0; qint64 propertyChecking = 0; }; @@ -285,10 +282,6 @@ public: LoadModuleResult loadModule(ProductContext &product, Item *loadingItem, const ProductContext::ResolvedAndMultiplexedDependsItem &dependency, Deferral deferral); - Item *searchAndLoadModuleFile(ProductContext *productContext, - const CodeLocation &dependsItemLocation, - const QualifiedId &moduleName, - FallbackMode fallbackMode, bool isRequired); std::optional resolveDependsItem(const ProductContext &product, Item *dependsItem); @@ -307,7 +300,7 @@ public: ItemReader reader{logger}; ProbesResolver probesResolver{parameters, evaluator, logger}; ModuleProviderLoader moduleProviderLoader{parameters, reader, evaluator, probesResolver, logger}; - ModuleLoader moduleLoader{parameters, reader, evaluator, logger}; + ModuleLoader moduleLoader{parameters, moduleProviderLoader, reader, evaluator, logger}; ModulePropertyMerger propertyMerger{parameters, evaluator, logger}; ModuleInstantiator moduleInstantiator{parameters, itemPool, propertyMerger, logger}; ProductItemMultiplexer multiplexer{parameters, evaluator, logger, [this](Item *productItem) { @@ -332,8 +325,6 @@ public: Version qbsVersion; Item *tempScopeItem = nullptr; - QHash, std::optional> existingModulePathCache; - QMap moduleDirListCache; private: class TempBaseModuleAttacher { @@ -919,7 +910,7 @@ void ProjectTreeBuilder::Private::handleProduct(ProductContext &product, Deferra propertyMerger.doFinalMerge(product.item); const bool enabled = checkItemCondition(product.item); - moduleLoader.checkDependencyParameterDeclarations({product.item, product.name, {}, {}}); + moduleLoader.checkDependencyParameterDeclarations(product.item, product.name); groupsHandler.setupGroups(product.item, product.scope); product.info.modulePropertiesSetInGroups = groupsHandler.modulePropertiesSetInGroups(); @@ -1171,9 +1162,7 @@ void ProjectTreeBuilder::Private::printProfilingInfo() logger.qbsLog(LoggerInfo, true) << " " << Tr::tr("Setting up product dependencies took %1.") .arg(elapsedTimeString(timingData.productDependencies)); - logger.qbsLog(LoggerInfo, true) << " " - << Tr::tr("Running module providers took %1.") - .arg(elapsedTimeString(timingData.moduleProviders)); + moduleLoader.printProfilingInfo(6); moduleInstantiator.printProfilingInfo(6); propertyMerger.printProfilingInfo(6); groupsHandler.printProfilingInfo(4); @@ -1830,9 +1819,15 @@ ProjectTreeBuilder::Private::loadModule(ProductContext &product, Item *loadingIt } } else { // No matching product found, look for a "real" module. - moduleItem = searchAndLoadModuleFile(&product, dependency.location(), - dependency.name, dependency.fallbackMode, - dependency.requiredGlobally); + const ModuleLoader::ProductContext loaderContext{ + product.item, product.project->item, product.name, product.uniqueName(), + product.profileName, product.multiplexConfigurationId, product.moduleProperties, + product.profileModuleProperties}; + const ModuleLoader::Result loaderResult = moduleLoader.searchAndLoadModuleFile( + loaderContext, dependency.location(), dependency.name, dependency.fallbackMode, + dependency.requiredGlobally); + moduleItem = loaderResult.moduleItem; + product.info.probes << loaderResult.providerProbes; if (moduleItem) { Item * const proto = moduleItem; @@ -2055,170 +2050,6 @@ QVariantMap ProjectTreeBuilder::Private::extractParameters(Item *dependsItem) co return result; } -// TODO Move the search & load stuff into ModuleLoader once we untangled the data types -struct PrioritizedItem -{ - PrioritizedItem(Item *item, int priority, int searchPathIndex) - : item(item), priority(priority), searchPathIndex(searchPathIndex) { } - - Item * const item; - int priority = 0; - const int searchPathIndex; -}; - -static Item *chooseModuleCandidate(const std::vector &candidates, - const QString &moduleName) -{ - // TODO: This should also consider the version requirement. - - auto maxIt = std::max_element( - candidates.begin(), candidates.end(), - [] (const PrioritizedItem &a, const PrioritizedItem &b) { - if (a.priority < b.priority) - return true; - if (a.priority > b.priority) - return false; - return a.searchPathIndex > b.searchPathIndex; - }); - - size_t nmax = std::count_if( - candidates.begin(), candidates.end(), - [maxIt] (const PrioritizedItem &i) { - return i.priority == maxIt->priority && i.searchPathIndex == maxIt->searchPathIndex; - }); - - if (nmax > 1) { - ErrorInfo e(Tr::tr("There is more than one equally prioritized candidate for module '%1'.") - .arg(moduleName)); - for (size_t i = 0; i < candidates.size(); ++i) { - const auto candidate = candidates.at(i); - if (candidate.priority == maxIt->priority) { - //: The %1 denotes the number of the candidate. - e.append(Tr::tr("candidate %1").arg(i + 1), candidates.at(i).item->location()); - } - } - throw e; - } - - return maxIt->item; -} - -Item *ProjectTreeBuilder::Private::searchAndLoadModuleFile( - ProductContext *productContext, const CodeLocation &dependsItemLocation, - const QualifiedId &moduleName, FallbackMode fallbackMode, bool isRequired) -{ - const auto findExistingModulePath = [&](const QString &searchPath) { - // 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 = existingModulePathCache[{searchPath, moduleName}]; - if (moduleInfo) - return *moduleInfo; - - QString dirPath = searchPath + QStringLiteral("/modules"); - for (const QString &moduleNamePart : moduleName) { - dirPath = FileInfo::resolvePath(dirPath, moduleNamePart); - if (!FileInfo::exists(dirPath) || !FileInfo::isFileCaseCorrect(dirPath)) { - return *(moduleInfo = QString()); - } - } - - return *(moduleInfo = dirPath); - }; - const auto findExistingModulePaths = [&] { - const QStringList &searchPaths = reader.allSearchPaths(); - QStringList result; - result.reserve(searchPaths.size()); - for (const auto &path: searchPaths) { - const QString dirPath = findExistingModulePath(path); - if (!dirPath.isEmpty()) - result.append(dirPath); - } - return result; - }; - - auto existingPaths = findExistingModulePaths(); - if (existingPaths.isEmpty()) { // no suitable names found, try to use providers - AccumulatingTimer providersTimer( - parameters.logElapsedTime() ? &timingData.moduleProviders : nullptr); - auto result = moduleProviderLoader.executeModuleProviders( - {productContext->item, productContext->project->item, productContext->name, - productContext->uniqueName(), productContext->moduleProperties, - productContext->theModuleProviderConfig}, - dependsItemLocation, - moduleName, - fallbackMode); - productContext->info.probes << result.probes; - if (!productContext->theModuleProviderConfig) - productContext->theModuleProviderConfig = result.providerConfig; - if (result.providerAddedSearchPaths) { - qCDebug(lcModuleLoader) << "Re-checking for module" << moduleName.toString() - << "with newly added search paths from module provider"; - existingPaths = findExistingModulePaths(); - } - } - - const auto getModuleFileNames = [&](const QString &dirPath) -> QStringList & { - QStringList &moduleFileNames = moduleDirListCache[dirPath]; - if (moduleFileNames.empty()) { - QDirIterator dirIter(dirPath, StringConstants::qbsFileWildcards()); - while (dirIter.hasNext()) - moduleFileNames += dirIter.next(); - } - return moduleFileNames; - }; - - const QString fullName = moduleName.toString(); - bool triedToLoadModule = false; - std::vector candidates; - 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; - const auto [module, triedToLoad] = moduleLoader.loadModuleFile( - {productContext->item, productContext->name, productContext->profileName, - productContext->profileModuleProperties}, - fullName, filePath); - if (module) - candidates.emplace_back(module, 0, i); - if (!triedToLoad) - it = moduleFileNames.erase(it); - else - ++it; - triedToLoadModule = triedToLoadModule || triedToLoad; - } - } - - if (candidates.empty()) { - if (!isRequired) - return createNonPresentModule(itemPool, fullName, QStringLiteral("not found"), nullptr); - if (Q_UNLIKELY(triedToLoadModule)) { - throw ErrorInfo(Tr::tr("Module %1 could not be loaded.").arg(fullName), - dependsItemLocation); - } - return nullptr; - } - - Item *moduleItem; - if (candidates.size() == 1) { - moduleItem = candidates.at(0).item; - } else { - for (auto &candidate : candidates) { - candidate.priority = evaluator.intValue(candidate.item, - StringConstants::priorityProperty(), - candidate.priority); - } - moduleItem = chooseModuleCandidate(candidates, fullName); - } - - const QString fullProductName = ProductItemMultiplexer::fullProductDisplayName( - productContext->name, productContext->multiplexConfigurationId); - moduleLoader.checkProfileErrorsForModule(moduleItem, fullName, fullProductName, - productContext->profileName); - return moduleItem; -} - std::optional ProjectTreeBuilder::Private::resolveDependsItem(const ProductContext &product, Item *dependsItem) { -- cgit v1.2.1