diff options
author | Christian Kandeler <christian.kandeler@qt.io> | 2023-05-23 17:27:39 +0200 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@qt.io> | 2023-05-25 10:45:10 +0000 |
commit | 85603c2879f7f28a6bcbec863f6d80b79f280cfe (patch) | |
tree | 061d9cf6690622c6cb96f9b68451ac93aa76eb47 | |
parent | ecd73e94f57ea3c98d32dd08c0902593d55785f3 (diff) |
Loader: Move product handling into its own class
And split up the large handleProduct() function into sensible chunks.
Change-Id: I39d086547087729d6db4980f4b0a2d991a584646
Reviewed-by: Ivan Komissarov <ABBAPOH@gmail.com>
-rw-r--r-- | src/lib/corelib/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/lib/corelib/corelib.qbs | 2 | ||||
-rw-r--r-- | src/lib/corelib/loader/productshandler.cpp | 345 | ||||
-rw-r--r-- | src/lib/corelib/loader/productshandler.h | 80 | ||||
-rw-r--r-- | src/lib/corelib/loader/projecttreebuilder.cpp | 231 |
5 files changed, 437 insertions, 223 deletions
diff --git a/src/lib/corelib/CMakeLists.txt b/src/lib/corelib/CMakeLists.txt index d8c67af9b..4bf7575c3 100644 --- a/src/lib/corelib/CMakeLists.txt +++ b/src/lib/corelib/CMakeLists.txt @@ -268,6 +268,8 @@ set(LOADER_SOURCES productitemmultiplexer.h productscollector.cpp productscollector.h + productshandler.cpp + productshandler.h projectresolver.cpp projectresolver.h projecttreebuilder.cpp diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs index 6904ec3ee..5e837168e 100644 --- a/src/lib/corelib/corelib.qbs +++ b/src/lib/corelib/corelib.qbs @@ -357,6 +357,8 @@ QbsLibrary { "productitemmultiplexer.h", "productscollector.cpp", "productscollector.h", + "productshandler.cpp", + "productshandler.h", "projectresolver.cpp", "projectresolver.h", "projecttreebuilder.cpp", diff --git a/src/lib/corelib/loader/productshandler.cpp b/src/lib/corelib/loader/productshandler.cpp new file mode 100644 index 000000000..759ec7496 --- /dev/null +++ b/src/lib/corelib/loader/productshandler.cpp @@ -0,0 +1,345 @@ +/**************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "productshandler.h" + +#include "dependenciesresolver.h" +#include "groupshandler.h" +#include "itemreader.h" +#include "loaderutils.h" +#include "modulepropertymerger.h" +#include "probesresolver.h" + +#include <language/evaluator.h> +#include <language/item.h> +#include <language/value.h> +#include <logging/categories.h> +#include <logging/translator.h> +#include <tools/profiling.h> +#include <tools/setupprojectparameters.h> +#include <tools/stringconstants.h> + +namespace qbs::Internal { + +class ProductsHandler::Private +{ +public: + Private(const SetupProjectParameters &paremeters, TopLevelProjectContext &topLevelProject, + ItemReader &itemReader, DependenciesResolver &dependenciesResolver, + ProbesResolver &probesResolver, ModulePropertyMerger &propertyMerger, + ModuleInstantiator &instantiator, Evaluator &evaluator, Logger &logger) + : parameters(paremeters), topLevelProject(topLevelProject), itemReader(itemReader), + dependenciesResolver(dependenciesResolver),probesResolver(probesResolver), + propertyMerger(propertyMerger), instantiator(instantiator), + evaluator(evaluator), logger(logger) {} + + void handleNextProduct(); + void handleProduct(ProductContext &product, Deferral deferral); + void resolveProbes(ProductContext &product); + void resolveProbes(ProductContext &product, Item *item); + void handleModuleSetupError(ProductContext &product, const Item::Module &module, + const ErrorInfo &error); + void runModuleProbes(ProductContext &product, const Item::Module &module); + bool validateModule(ProductContext &product, const Item::Module &module); + void updateModulePresentState(ProductContext &product, const Item::Module &module); + void handleGroups(ProductContext &product); + + const SetupProjectParameters ¶meters; + TopLevelProjectContext &topLevelProject; + ItemReader &itemReader; + DependenciesResolver &dependenciesResolver; + ProbesResolver &probesResolver; + ModulePropertyMerger &propertyMerger; + ModuleInstantiator &instantiator; + Evaluator &evaluator; + Logger &logger; + GroupsHandler groupsHandler{parameters, instantiator, evaluator, logger}; + qint64 elapsedTime = 0; +}; + +ProductsHandler::ProductsHandler( + const SetupProjectParameters ¶meters, TopLevelProjectContext &topLevelProject, + ItemReader &itemReader, DependenciesResolver &dependenciesResolver, + ProbesResolver &probesResolver, ModulePropertyMerger &propertyMerger, + ModuleInstantiator &instantiator, Evaluator &evaluator, Logger &logger) + : d(makePimpl<Private>(parameters, topLevelProject, itemReader, dependenciesResolver, + probesResolver, propertyMerger, instantiator, evaluator, logger)) +{} + +void ProductsHandler::run() +{ + AccumulatingTimer timer(d->parameters.logElapsedTime() ? &d->elapsedTime : nullptr); + + for (ProjectContext * const projectContext : d->topLevelProject.projects) { + for (ProductContext &productContext : projectContext->products) + d->topLevelProject.productsToHandle.emplace_back(&productContext, -1); + } + while (!d->topLevelProject.productsToHandle.empty()) + d->handleNextProduct(); +} + +void ProductsHandler::printProfilingInfo(int indent) +{ + if (!d->parameters.logElapsedTime()) + return; + d->logger.qbsLog(LoggerInfo, true) << " " + << Tr::tr("Handling products took %1.") + .arg(elapsedTimeString(d->elapsedTime)); + const QByteArray prefix(indent, ' '); + d->groupsHandler.printProfilingInfo(indent + 2); +} + +ProductsHandler::~ProductsHandler() = default; + +void ProductsHandler::Private::handleNextProduct() +{ + auto [product, queueSizeOnInsert] = topLevelProject.productsToHandle.front(); + topLevelProject.productsToHandle.pop_front(); + + // If the queue of in-progress products has shrunk since the last time we tried handling + // this product, there has been forward progress and we can allow a deferral. + const Deferral deferral = queueSizeOnInsert == -1 + || queueSizeOnInsert > int(topLevelProject.productsToHandle.size()) + ? Deferral::Allowed : Deferral::NotAllowed; + + itemReader.setExtraSearchPathsStack(product->project->searchPathsStack); + try { + handleProduct(*product, deferral); + if (product->name.startsWith(StringConstants::shadowProductPrefix())) + topLevelProject.probes << product->info.probes; + } catch (const ErrorInfo &err) { + product->handleError(err); + } + + // The search paths stack can change during dependency resolution (due to module providers); + // check that we've rolled back all the changes + QBS_CHECK(itemReader.extraSearchPathsStack() == product->project->searchPathsStack); + + // If we encountered a dependency to an in-progress product or to a bulk dependency, + // we defer handling this product if it hasn't failed yet and there is still forward progress. + if (!product->info.delayedError.hasError() && !product->dependenciesResolved) { + topLevelProject.productsToHandle.emplace_back( + product, int(topLevelProject.productsToHandle.size())); + } +} + +void ProductsHandler::Private::handleProduct(ProductContext &product, Deferral deferral) +{ + topLevelProject.checkCancelation(parameters); + + if (product.info.delayedError.hasError()) + return; + + product.dependenciesResolved = dependenciesResolver.resolveDependencies(product, deferral); + if (!product.dependenciesResolved) + return; + + // Run probes for modules and product. + resolveProbes(product); + + // After the probes have run, we can switch on the evaluator cache. + FileTags fileTags = evaluator.fileTagsValue(product.item, StringConstants::typeProperty()); + EvalCacheEnabler cacheEnabler(&evaluator, evaluator.stringValue( + product.item, + StringConstants::sourceDirectoryProperty())); + + // Run module validation scripts. + for (const Item::Module &module : product.item->modules()) { + if (!validateModule(product, module)) + return; + fileTags += evaluator.fileTagsValue( + module.item, StringConstants::additionalProductTypesProperty()); + } + + // Disable modules that have been pulled in only by now-disabled modules. + // Note that this has to happen in the reverse order compared to the other loops, + // with the leaves checked last. + for (auto it = product.item->modules().rbegin(); it != product.item->modules().rend(); ++it) + updateModulePresentState(product, *it); + + // Now do the canonical module property values merge. Note that this will remove + // previously attached values from modules that failed validation. + // Evaluator cache entries that could potentially change due to this will be purged. + propertyMerger.doFinalMerge(product.item); + + const bool enabled = topLevelProject.checkItemCondition(product.item, evaluator); + dependenciesResolver.checkDependencyParameterDeclarations(product.item, product.name); + + handleGroups(product); + + // Collect the full list of fileTags, including the values contributed by modules. + if (!product.info.delayedError.hasError() && enabled + && !product.name.startsWith(StringConstants::shadowProductPrefix())) { + for (const FileTag &tag : fileTags) + topLevelProject.productsByType.insert({tag, &product}); + product.item->setProperty(StringConstants::typeProperty(), + VariantValue::create(sorted(fileTags.toStringList()))); + } + topLevelProject.productInfos[product.item] = product.info; +} + +void ProductsHandler::Private::resolveProbes(ProductContext &product) +{ + for (const Item::Module &module : product.item->modules()) { + runModuleProbes(product, module); + if (product.info.delayedError.hasError()) + return; + } + resolveProbes(product, product.item); +} + +void ProductsHandler::Private::resolveProbes(ProductContext &product, Item *item) +{ + product.info.probes << probesResolver.resolveProbes({product.name, product.uniqueName()}, item); +} + +void ProductsHandler::Private::handleModuleSetupError( + ProductContext &product, const Item::Module &module, const ErrorInfo &error) +{ + if (module.required) { + product.handleError(error); + } else { + qCDebug(lcModuleLoader()) << "non-required module" << module.name.toString() + << "found, but not usable in product" << product.name + << error.toString(); + createNonPresentModule(*module.item->pool(), module.name.toString(), + QStringLiteral("failed validation"), module.item); + } +} + +void ProductsHandler::Private::runModuleProbes(ProductContext &product, const Item::Module &module) +{ + if (!module.item->isPresentModule()) + return; + if (module.productInfo && topLevelProject.disabledItems.contains(module.productInfo->item)) { + createNonPresentModule(*module.item->pool(), module.name.toString(), + QLatin1String("module's exporting product is disabled"), + module.item); + return; + } + try { + resolveProbes(product, module.item); + if (module.versionRange.minimum.isValid() + || module.versionRange.maximum.isValid()) { + if (module.versionRange.maximum.isValid() + && module.versionRange.minimum >= module.versionRange.maximum) { + throw ErrorInfo(Tr::tr("Impossible version constraint [%1,%2) set for module " + "'%3'").arg(module.versionRange.minimum.toString(), + module.versionRange.maximum.toString(), + module.name.toString())); + } + const Version moduleVersion = Version::fromString( + evaluator.stringValue(module.item, + StringConstants::versionProperty())); + if (moduleVersion < module.versionRange.minimum) { + throw ErrorInfo(Tr::tr("Module '%1' has version %2, but it needs to be " + "at least %3.").arg(module.name.toString(), + moduleVersion.toString(), + module.versionRange.minimum.toString())); + } + if (module.versionRange.maximum.isValid() + && moduleVersion >= module.versionRange.maximum) { + throw ErrorInfo(Tr::tr("Module '%1' has version %2, but it needs to be " + "lower than %3.").arg(module.name.toString(), + moduleVersion.toString(), + module.versionRange.maximum.toString())); + } + } + } catch (const ErrorInfo &error) { + handleModuleSetupError(product, module, error); + } +} + +bool ProductsHandler::Private::validateModule(ProductContext &product, const Item::Module &module) +{ + if (!module.item->isPresentModule()) + return true; + try { + evaluator.boolValue(module.item, StringConstants::validateProperty()); + for (const auto &dep : module.item->modules()) { + if (dep.required && !dep.item->isPresentModule()) { + throw ErrorInfo(Tr::tr("Module '%1' depends on module '%2', which was not " + "loaded successfully") + .arg(module.name.toString(), dep.name.toString())); + } + } + } catch (const ErrorInfo &error) { + handleModuleSetupError(product, module, error); + if (product.info.delayedError.hasError()) + return false; + } + return true; +} + +void ProductsHandler::Private::updateModulePresentState(ProductContext &product, + const Item::Module &module) +{ + if (!module.item->isPresentModule()) + return; + bool hasPresentLoadingItem = false; + for (const Item * const loadingItem : module.loadingItems) { + if (loadingItem == product.item) { + hasPresentLoadingItem = true; + break; + } + if (!loadingItem->isPresentModule()) + continue; + if (loadingItem->prototype() && loadingItem->prototype()->type() == ItemType::Export) { + QBS_CHECK(loadingItem->prototype()->parent()->type() == ItemType::Product); + if (topLevelProject.disabledItems.contains(loadingItem->prototype()->parent())) + continue; + } + hasPresentLoadingItem = true; + break; + } + if (!hasPresentLoadingItem) { + createNonPresentModule(*module.item->pool(), module.name.toString(), + QLatin1String("imported only by disabled module(s)"), + module.item); + } +} + +void ProductsHandler::Private::handleGroups(ProductContext &product) +{ + groupsHandler.setupGroups(product.item, product.scope); + product.info.modulePropertiesSetInGroups = groupsHandler.modulePropertiesSetInGroups(); + topLevelProject.disabledItems.unite(groupsHandler.disabledGroups()); +} + +} // namespace qbs::Internal diff --git a/src/lib/corelib/loader/productshandler.h b/src/lib/corelib/loader/productshandler.h new file mode 100644 index 000000000..51ddba332 --- /dev/null +++ b/src/lib/corelib/loader/productshandler.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include <tools/pimpl.h> + +namespace qbs { +class SetupProjectParameters; +namespace Internal { +class DependenciesResolver; +class Evaluator; +class ItemReader; +class Logger; +class ModuleInstantiator; +class ModulePropertyMerger; +class ProbesResolver; +class TopLevelProjectContext; + +// Responsibilities: +// - Resolving dependencies to modules and other products (via DependenciesResolver). +// - Module validation. +// - Running probes (via ProbesResolver) in Product and Module items. +// - Preparing Group items for property evaluation. +class ProductsHandler +{ +public: + ProductsHandler(const SetupProjectParameters ¶meters, + TopLevelProjectContext &topLevelProject, ItemReader &itemReader, + DependenciesResolver &dependenciesResolver, ProbesResolver &probesResolver, + ModulePropertyMerger &propertyMerger, ModuleInstantiator &instantiator, + Evaluator &evaluator, Logger &logger); + ~ProductsHandler(); + + void run(); + void printProfilingInfo(int indent); + +private: + class Private; + Pimpl<Private> d; +}; + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/loader/projecttreebuilder.cpp b/src/lib/corelib/loader/projecttreebuilder.cpp index 83288ba5e..36f315513 100644 --- a/src/lib/corelib/loader/projecttreebuilder.cpp +++ b/src/lib/corelib/loader/projecttreebuilder.cpp @@ -40,7 +40,6 @@ #include "projecttreebuilder.h" #include "dependenciesresolver.h" -#include "groupshandler.h" #include "itemreader.h" #include "localprofiles.h" #include "moduleinstantiator.h" @@ -48,6 +47,7 @@ #include "probesresolver.h" #include "productitemmultiplexer.h" #include "productscollector.h" +#include "productshandler.h" #include <language/builtindeclarations.h> #include <language/evaluator.h> @@ -85,16 +85,6 @@ namespace qbs::Internal { -namespace { - -class TimingData { -public: - qint64 handleProducts = 0; - qint64 propertyChecking = 0; -}; - -} // namespace - class ProjectTreeBuilder::Private { public: @@ -106,18 +96,12 @@ public: void checkOverriddenValues(); void collectNameFromOverride(const QString &overrideString); void handleTopLevelProject(Item *projectItem); - void handleNextProduct(); - void handleProduct(ProductContext &productContext, Deferral deferral); void printProfilingInfo(); - void handleModuleSetupError(ProductContext &product, const Item::Module &module, - const ErrorInfo &error); - void resolveProbes(ProductContext &product, Item *item); const SetupProjectParameters ¶meters; ItemPool &itemPool; Evaluator &evaluator; Logger &logger; - TimingData timingData; ItemReader reader{parameters, logger}; ProbesResolver probesResolver{parameters, evaluator, logger}; ModulePropertyMerger propertyMerger{parameters, evaluator, logger}; @@ -125,16 +109,18 @@ public: ProductItemMultiplexer multiplexer{parameters, evaluator, logger, [this](Item *productItem) { return moduleInstantiator.retrieveQbsItem(productItem); }}; - GroupsHandler groupsHandler{parameters, moduleInstantiator, evaluator, logger}; LocalProfiles localProfiles{parameters, evaluator, logger}; DependenciesResolver dependenciesResolver{parameters, itemPool, evaluator, reader, probesResolver, moduleInstantiator, logger}; ProductsCollector productsCollector{parameters, topLevelProject, dependenciesResolver, evaluator, reader, probesResolver, localProfiles, multiplexer, logger}; + ProductsHandler productsHandler{parameters, topLevelProject, reader, dependenciesResolver, + probesResolver, propertyMerger, moduleInstantiator, evaluator, logger}; FileTime lastResolveTime; QVariantMap storedProfiles; TopLevelProjectContext topLevelProject; + qint64 elapsedTimePropertyChecking = 0; }; ProjectTreeBuilder::ProjectTreeBuilder(const SetupProjectParameters ¶meters, ItemPool &itemPool, @@ -293,192 +279,14 @@ void ProjectTreeBuilder::Private::handleTopLevelProject(Item *projectItem) projectItem->setProperty(StringConstants::profileProperty(), VariantValue::create(parameters.topLevelProfile())); productsCollector.run(projectItem); + productsHandler.run(); - for (ProjectContext * const projectContext : topLevelProject.projects) { - for (ProductContext &productContext : projectContext->products) - topLevelProject.productsToHandle.emplace_back(&productContext, -1); - } - while (!topLevelProject.productsToHandle.empty()) - handleNextProduct(); - - reader.clearExtraSearchPathsStack(); + reader.clearExtraSearchPathsStack(); // TODO: Unneeded? AccumulatingTimer timer(parameters.logElapsedTime() - ? &timingData.propertyChecking : nullptr); + ? &elapsedTimePropertyChecking : nullptr); checkPropertyDeclarations(projectItem, topLevelProject.disabledItems, parameters, logger); } -void ProjectTreeBuilder::Private::handleNextProduct() -{ - auto [product, queueSizeOnInsert] = topLevelProject.productsToHandle.front(); - topLevelProject.productsToHandle.pop_front(); - - // If the queue of in-progress products has shrunk since the last time we tried handling - // this product, there has been forward progress and we can allow a deferral. - const Deferral deferral = queueSizeOnInsert == -1 - || queueSizeOnInsert > int(topLevelProject.productsToHandle.size()) - ? Deferral::Allowed : Deferral::NotAllowed; - - reader.setExtraSearchPathsStack(product->project->searchPathsStack); - try { - handleProduct(*product, deferral); - if (product->name.startsWith(StringConstants::shadowProductPrefix())) - topLevelProject.probes << product->info.probes; - } catch (const ErrorInfo &err) { - product->handleError(err); - } - - // The search paths stack can change during dependency resolution (due to module providers); - // check that we've rolled back all the changes - QBS_CHECK(reader.extraSearchPathsStack() == product->project->searchPathsStack); - - // If we encountered a dependency to an in-progress product or to a bulk dependency, - // we defer handling this product if it hasn't failed yet and there is still forward progress. - if (!product->info.delayedError.hasError() && !product->dependenciesResolved) { - topLevelProject.productsToHandle.emplace_back( - product, int(topLevelProject.productsToHandle.size())); - } -} - -void ProjectTreeBuilder::Private::handleProduct(ProductContext &product, Deferral deferral) -{ - topLevelProject.checkCancelation(parameters); - - AccumulatingTimer timer(parameters.logElapsedTime() ? &timingData.handleProducts : nullptr); - if (product.info.delayedError.hasError()) - return; - - product.dependenciesResolved = dependenciesResolver.resolveDependencies(product, deferral); - if (!product.dependenciesResolved) - return; - - // Run probes for modules and product. - for (const Item::Module &module : product.item->modules()) { - if (!module.item->isPresentModule()) - continue; - if (module.productInfo && topLevelProject.disabledItems.contains(module.productInfo->item)) { - createNonPresentModule(itemPool, module.name.toString(), - QLatin1String("module's exporting product is disabled"), - module.item); - continue; - } - try { - resolveProbes(product, module.item); - if (module.versionRange.minimum.isValid() - || module.versionRange.maximum.isValid()) { - if (module.versionRange.maximum.isValid() - && module.versionRange.minimum >= module.versionRange.maximum) { - throw ErrorInfo(Tr::tr("Impossible version constraint [%1,%2) set for module " - "'%3'").arg(module.versionRange.minimum.toString(), - module.versionRange.maximum.toString(), - module.name.toString())); - } - const Version moduleVersion = Version::fromString( - evaluator.stringValue(module.item, - StringConstants::versionProperty())); - if (moduleVersion < module.versionRange.minimum) { - throw ErrorInfo(Tr::tr("Module '%1' has version %2, but it needs to be " - "at least %3.").arg(module.name.toString(), - moduleVersion.toString(), - module.versionRange.minimum.toString())); - } - if (module.versionRange.maximum.isValid() - && moduleVersion >= module.versionRange.maximum) { - throw ErrorInfo(Tr::tr("Module '%1' has version %2, but it needs to be " - "lower than %3.").arg(module.name.toString(), - moduleVersion.toString(), - module.versionRange.maximum.toString())); - } - } - } catch (const ErrorInfo &error) { - handleModuleSetupError(product, module, error); - if (product.info.delayedError.hasError()) - return; - } - } - resolveProbes(product, product.item); - - // After the probes have run, we can switch on the evaluator cache. - FileTags fileTags = evaluator.fileTagsValue(product.item, StringConstants::typeProperty()); - EvalCacheEnabler cacheEnabler(&evaluator, evaluator.stringValue( - product.item, - StringConstants::sourceDirectoryProperty())); - - // Run module validation scripts. - for (const Item::Module &module : product.item->modules()) { - if (!module.item->isPresentModule()) - continue; - try { - evaluator.boolValue(module.item, StringConstants::validateProperty()); - for (const auto &dep : module.item->modules()) { - if (dep.required && !dep.item->isPresentModule()) { - throw ErrorInfo(Tr::tr("Module '%1' depends on module '%2', which was not " - "loaded successfully") - .arg(module.name.toString(), dep.name.toString())); - } - } - fileTags += evaluator.fileTagsValue( - module.item, StringConstants::additionalProductTypesProperty()); - } catch (const ErrorInfo &error) { - handleModuleSetupError(product, module, error); - if (product.info.delayedError.hasError()) - return; - } - } - - // Disable modules that have been pulled in only by now-disabled modules. - // Note that this has to happen in the reverse order compared to the other loops, - // with the leaves checked last. - for (auto it = product.item->modules().rbegin(); it != product.item->modules().rend(); ++it) { - const Item::Module &module = *it; - if (!module.item->isPresentModule()) - continue; - bool hasPresentLoadingItem = false; - for (const Item * const loadingItem : module.loadingItems) { - if (loadingItem == product.item) { - hasPresentLoadingItem = true; - break; - } - if (!loadingItem->isPresentModule()) - continue; - if (loadingItem->prototype() && loadingItem->prototype()->type() == ItemType::Export) { - QBS_CHECK(loadingItem->prototype()->parent()->type() == ItemType::Product); - if (topLevelProject.disabledItems.contains(loadingItem->prototype()->parent())) - continue; - } - hasPresentLoadingItem = true; - break; - } - if (!hasPresentLoadingItem) { - createNonPresentModule(itemPool, module.name.toString(), - QLatin1String("imported only by disabled module(s)"), - module.item); - continue; - } - } - - // Now do the canonical module property values merge. Note that this will remove - // previously attached values from modules that failed validation. - // Evaluator cache entries that could potentially change due to this will be purged. - propertyMerger.doFinalMerge(product.item); - - const bool enabled = topLevelProject.checkItemCondition(product.item, evaluator); - dependenciesResolver.checkDependencyParameterDeclarations(product.item, product.name); - - groupsHandler.setupGroups(product.item, product.scope); - product.info.modulePropertiesSetInGroups = groupsHandler.modulePropertiesSetInGroups(); - topLevelProject.disabledItems.unite(groupsHandler.disabledGroups()); - - // Collect the full list of fileTags, including the values contributed by modules. - if (!product.info.delayedError.hasError() && enabled - && !product.name.startsWith(StringConstants::shadowProductPrefix())) { - for (const FileTag &tag : fileTags) - topLevelProject.productsByType.insert({tag, &product}); - product.item->setProperty(StringConstants::typeProperty(), - VariantValue::create(sorted(fileTags.toStringList()))); - } - topLevelProject.productInfos[product.item] = product.info; -} - void ProjectTreeBuilder::Private::printProfilingInfo() { if (!parameters.logElapsedTime()) @@ -487,36 +295,13 @@ void ProjectTreeBuilder::Private::printProfilingInfo() << Tr::tr("Project file loading and parsing took %1.") .arg(elapsedTimeString(reader.elapsedTime())); productsCollector.printProfilingInfo(2); - logger.qbsLog(LoggerInfo, true) << " " - << Tr::tr("Handling products took %1.") - .arg(elapsedTimeString(timingData.handleProducts)); dependenciesResolver.printProfilingInfo(4); moduleInstantiator.printProfilingInfo(6); propertyMerger.printProfilingInfo(6); - groupsHandler.printProfilingInfo(4); probesResolver.printProfilingInfo(4); logger.qbsLog(LoggerInfo, true) << " " << Tr::tr("Property checking took %1.") - .arg(elapsedTimeString(timingData.propertyChecking)); -} - -void ProjectTreeBuilder::Private::handleModuleSetupError( - ProductContext &product, const Item::Module &module, const ErrorInfo &error) -{ - if (module.required) { - product.handleError(error); - } else { - qCDebug(lcModuleLoader()) << "non-required module" << module.name.toString() - << "found, but not usable in product" << product.name - << error.toString(); - createNonPresentModule(itemPool, module.name.toString(), - QStringLiteral("failed validation"), module.item); - } -} - -void ProjectTreeBuilder::Private::resolveProbes(ProductContext &product, Item *item) -{ - product.info.probes << probesResolver.resolveProbes({product.name, product.uniqueName()}, item); + .arg(elapsedTimeString(elapsedTimePropertyChecking)); } } // namespace qbs::Internal |