aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2023-05-23 17:27:39 +0200
committerChristian Kandeler <christian.kandeler@qt.io>2023-05-25 10:45:10 +0000
commit85603c2879f7f28a6bcbec863f6d80b79f280cfe (patch)
tree061d9cf6690622c6cb96f9b68451ac93aa76eb47
parentecd73e94f57ea3c98d32dd08c0902593d55785f3 (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.txt2
-rw-r--r--src/lib/corelib/corelib.qbs2
-rw-r--r--src/lib/corelib/loader/productshandler.cpp345
-rw-r--r--src/lib/corelib/loader/productshandler.h80
-rw-r--r--src/lib/corelib/loader/projecttreebuilder.cpp231
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 &parameters;
+ 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 &parameters, 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 &parameters,
+ 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 &parameters;
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 &parameters, 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