summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2023-04-21 12:48:45 +0200
committerChristian Kandeler <christian.kandeler@qt.io>2023-04-24 07:19:28 +0000
commit4c188eff3d63f60a891a609993390fedb8e9f639 (patch)
treef82f410fceb8735bba534f41b55f1612a7d344b6
parent0024c63ae5206e8d04245e764d8560dd165706aa (diff)
downloadqbs-4c188eff3d63f60a891a609993390fedb8e9f639.tar.gz
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 <ABBAPOH@gmail.com>
-rw-r--r--src/lib/corelib/language/moduleloader.cpp257
-rw-r--r--src/lib/corelib/language/moduleloader.h33
-rw-r--r--src/lib/corelib/language/moduleproviderloader.h2
-rw-r--r--src/lib/corelib/language/projecttreebuilder.cpp193
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 <api/languageinfo.h>
#include <logging/categories.h>
#include <logging/translator.h>
#include <tools/error.h>
+#include <tools/fileinfo.h>
#include <tools/hostosinfo.h>
+#include <tools/profiling.h>
#include <tools/setupprojectparameters.h>
#include <tools/stringconstants.h>
+#include <QDirIterator>
+#include <QHash>
+
#include <unordered_map>
#include <utility>
@@ -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<Item *, bool> loadModuleFile(const ProductContext &product,
+ const QString &moduleName, const QString &filePath);
std::pair<Item *, bool> 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<const Item *, std::vector<ErrorInfo>> unknownProfilePropertyErrors;
std::unordered_map<const Item *, Item::PropertyDeclarationMap> parameterDeclarations;
+ std::unordered_map<const Item *, std::optional<QVariantMap>> providerConfigsPerProduct;
+ QHash<std::pair<QString, QualifiedId>, std::optional<QString>> existingModulePathCache;
+ std::map<QString, QStringList> 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<Item *, bool> 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<PrioritizedItem> &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<QVariantMap> &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<PrioritizedItem> 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<Item *, bool> 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<Item *, bool> 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<Item *, bool> 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<Item *, bool> 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<const Item *, Item::PropertyDeclarationMap> &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 <QString>
#include <QVariantMap>
+#include <vector>
+
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<Item *, bool> loadModuleFile(const ProductContext &product,
- const QString &moduleName, const QString &filePath);
+ struct Result {
+ Item *moduleItem = nullptr;
+ std::vector<ProbeConstPtr> 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<QVariantMap> 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<ProductContext::ResolvedDependsItem>
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::pair<QString, QualifiedId>, std::optional<QString>> existingModulePathCache;
- QMap<QString, QStringList> 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<PrioritizedItem> &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<PrioritizedItem> 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<ProductContext::ResolvedDependsItem>
ProjectTreeBuilder::Private::resolveDependsItem(const ProductContext &product, Item *dependsItem)
{