aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRaphael Cotty <raphael.cotty@gmail.com>2022-01-21 16:05:36 +0100
committerRaphaël Cotty <raphael.cotty@gmail.com>2022-01-22 11:53:33 +0000
commit249c5ab3b4c3d1c68b294ff93ec8089f85728051 (patch)
tree0faa6b7e8f7a0dc7d59282a239b932863bf323b3 /src
parent6529f1c7eecd22095f71c2a553c9f88c92cef6ff (diff)
Factorize out probe code from module loader
Change-Id: I349589aa5d92f221e43c61722e53a80d59f183b0 Reviewed-by: Ivan Komissarov <ABBAPOH@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/lib/corelib/CMakeLists.txt2
-rw-r--r--src/lib/corelib/corelib.qbs2
-rw-r--r--src/lib/corelib/language/language.pri2
-rw-r--r--src/lib/corelib/language/moduleloader.cpp220
-rw-r--r--src/lib/corelib/language/moduleloader.h25
-rw-r--r--src/lib/corelib/language/probesresolver.cpp299
-rw-r--r--src/lib/corelib/language/probesresolver.h92
7 files changed, 410 insertions, 232 deletions
diff --git a/src/lib/corelib/CMakeLists.txt b/src/lib/corelib/CMakeLists.txt
index 3a658db98..28f4f4fdf 100644
--- a/src/lib/corelib/CMakeLists.txt
+++ b/src/lib/corelib/CMakeLists.txt
@@ -234,6 +234,8 @@ set(LANGUAGE_SOURCES
moduleproviderloader.h
preparescriptobserver.cpp
preparescriptobserver.h
+ probesresolver.cpp
+ probesresolver.h
projectresolver.cpp
projectresolver.h
property.cpp
diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs
index b50b1e18b..89e8fb40b 100644
--- a/src/lib/corelib/corelib.qbs
+++ b/src/lib/corelib/corelib.qbs
@@ -323,6 +323,8 @@ QbsLibrary {
"moduleproviderloader.h",
"preparescriptobserver.cpp",
"preparescriptobserver.h",
+ "probesresolver.cpp",
+ "probesresolver.h",
"projectresolver.cpp",
"projectresolver.h",
"property.cpp",
diff --git a/src/lib/corelib/language/language.pri b/src/lib/corelib/language/language.pri
index 249093e61..0b4cfbd08 100644
--- a/src/lib/corelib/language/language.pri
+++ b/src/lib/corelib/language/language.pri
@@ -31,6 +31,7 @@ HEADERS += \
$$PWD/moduleproviderinfo.h \
$$PWD/moduleproviderloader.h \
$$PWD/preparescriptobserver.h \
+ $$PWD/probesresolver.h \
$$PWD/projectresolver.h \
$$PWD/property.h \
$$PWD/propertydeclaration.h \
@@ -67,6 +68,7 @@ SOURCES += \
$$PWD/moduleproviderloader.cpp \
$$PWD/preparescriptobserver.cpp \
$$PWD/scriptpropertyobserver.cpp \
+ $$PWD/probesresolver.cpp \
$$PWD/projectresolver.cpp \
$$PWD/property.cpp \
$$PWD/propertydeclaration.cpp \
diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp
index b09749deb..57578ddb0 100644
--- a/src/lib/corelib/language/moduleloader.cpp
+++ b/src/lib/corelib/language/moduleloader.cpp
@@ -47,6 +47,7 @@
#include "language.h"
#include "modulemerger.h"
#include "moduleproviderloader.h"
+#include "probesresolver.h"
#include "qualifiedid.h"
#include "scriptengine.h"
#include "value.h"
@@ -105,24 +106,6 @@ static bool multiplexConfigurationIntersects(const QVariantMap &lhs, const QVari
class ModuleLoader::ItemModuleList : public QList<Item::Module> {};
-static QString probeGlobalId(Item *probe)
-{
- QString id;
-
- for (Item *obj = probe; obj; obj = obj->prototype()) {
- if (!obj->id().isEmpty()) {
- id = obj->id();
- break;
- }
- }
-
- if (id.isEmpty())
- return {};
-
- QBS_CHECK(probe->file());
- return id + QLatin1Char('_') + probe->file()->filePath();
-}
-
class ModuleLoader::ProductSortByDependencies
{
public:
@@ -257,6 +240,7 @@ ModuleLoader::ModuleLoader(Evaluator *evaluator, Logger &logger)
, m_progressObserver(nullptr)
, m_reader(std::make_unique<ItemReader>(logger))
, m_evaluator(evaluator)
+ , m_probesResolver(std::make_unique<ProbesResolver>(m_evaluator, m_logger))
, m_moduleProviderLoader(
std::make_unique<ModuleProviderLoader>(m_reader.get(), m_evaluator, m_logger))
{
@@ -277,14 +261,12 @@ void ModuleLoader::setSearchPaths(const QStringList &searchPaths)
void ModuleLoader::setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes)
{
- m_oldProjectProbes.clear();
- for (const ProbeConstPtr& probe : oldProbes)
- m_oldProjectProbes[probe->globalId()] << probe;
+ m_probesResolver->setOldProjectProbes(oldProbes);
}
void ModuleLoader::setOldProductProbes(const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes)
{
- m_oldProductProbes = oldProbes;
+ m_probesResolver->setOldProductProbes(oldProbes);
}
void ModuleLoader::setStoredProfiles(const QVariantMap &profiles)
@@ -310,12 +292,11 @@ ModuleLoaderResult ModuleLoader::load(const SetupProjectParameters &parameters)
m_reader->clearExtraSearchPathsStack();
m_reader->setEnableTiming(parameters.logElapsedTime());
m_moduleProviderLoader->setProjectParameters(m_parameters);
- m_elapsedTimeProbes = m_elapsedTimePrepareProducts = m_elapsedTimeHandleProducts
+ m_probesResolver->setProjectParameters(m_parameters);
+ m_elapsedTimePrepareProducts = m_elapsedTimeHandleProducts
= m_elapsedTimeProductDependencies = m_elapsedTimeTransitiveDependencies
= m_elapsedTimePropertyChecking = 0;
m_elapsedTimeModuleProviders = 0;
- m_elapsedTimeProbes = 0;
- m_probesEncountered = m_probesRun = m_probesCachedCurrent = m_probesCachedOld = 0;
m_settings = std::make_unique<Settings>(parameters.settingsDirectory());
const auto keys = m_parameters.overriddenValues().keys();
@@ -714,7 +695,7 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult,
for (Item * const child : projectItem->children())
child->setScope(projectContext.scope);
- resolveProbes(&dummyProductContext, projectItem);
+ m_probesResolver->resolveProbes(&dummyProductContext, projectItem);
projectContext.topLevelProject->probes << dummyProductContext.info.probes;
handleProfileItems(projectItem, &projectContext);
@@ -1453,7 +1434,7 @@ void ModuleLoader::handleProduct(ModuleLoader::ProductContext *productContext)
if (!module.item->isPresentModule())
continue;
try {
- resolveProbes(productContext, module.item);
+ m_probesResolver->resolveProbes(productContext, module.item);
if (module.versionRange.minimum.isValid()
|| module.versionRange.maximum.isValid()) {
if (module.versionRange.maximum.isValid()
@@ -1487,7 +1468,7 @@ void ModuleLoader::handleProduct(ModuleLoader::ProductContext *productContext)
}
}
- resolveProbes(productContext, item);
+ m_probesResolver->resolveProbes(productContext, item);
// Module validation must happen in an extra pass, after all Probes have been resolved.
EvalCacheEnabler cacheEnabler(m_evaluator);
@@ -1867,64 +1848,6 @@ bool ModuleLoader::checkExportItemCondition(Item *exportItem, const ProductConte
return checkItemCondition(exportItem);
}
-ProbeConstPtr ModuleLoader::findOldProjectProbe(
- const QString &globalId,
- bool condition,
- const QVariantMap &initialProperties,
- const QString &sourceCode) const
-{
- if (m_parameters.forceProbeExecution())
- return {};
-
- for (const ProbeConstPtr &oldProbe : m_oldProjectProbes.value(globalId)) {
- if (probeMatches(oldProbe, condition, initialProperties, sourceCode, CompareScript::Yes))
- return oldProbe;
- }
-
- return {};
-}
-
-ProbeConstPtr ModuleLoader::findOldProductProbe(
- const QString &productName,
- bool condition,
- const QVariantMap &initialProperties,
- const QString &sourceCode) const
-{
- if (m_parameters.forceProbeExecution())
- return {};
-
- for (const ProbeConstPtr &oldProbe : m_oldProductProbes.value(productName)) {
- if (probeMatches(oldProbe, condition, initialProperties, sourceCode, CompareScript::Yes))
- return oldProbe;
- }
-
- return {};
-}
-
-ProbeConstPtr ModuleLoader::findCurrentProbe(
- const CodeLocation &location,
- bool condition,
- const QVariantMap &initialProperties) const
-{
- const std::vector<ProbeConstPtr> &cachedProbes = m_currentProbes.value(location);
- for (const ProbeConstPtr &probe : cachedProbes) {
- if (probeMatches(probe, condition, initialProperties, QString(), CompareScript::No))
- return probe;
- }
- return {};
-}
-
-bool ModuleLoader::probeMatches(const ProbeConstPtr &probe, bool condition,
- const QVariantMap &initialProperties, const QString &configureScript,
- CompareScript compareScript) const
-{
- return probe->condition() == condition
- && probe->initialProperties() == initialProperties
- && (compareScript == CompareScript::No
- || (probe->configureScript() == configureScript
- && !probe->needsReconfigure(m_lastResolveTime)));
-}
-
void ModuleLoader::printProfilingInfo()
{
if (!m_parameters.logElapsedTime())
@@ -1947,14 +1870,7 @@ void ModuleLoader::printProfilingInfo()
m_logger.qbsLog(LoggerInfo, true) << "\t"
<< Tr::tr("Handling products took %1.")
.arg(elapsedTimeString(m_elapsedTimeHandleProducts));
- m_logger.qbsLog(LoggerInfo, true) << "\t\t"
- << Tr::tr("Running Probes took %1.")
- .arg(elapsedTimeString(m_elapsedTimeProbes));
- m_logger.qbsLog(LoggerInfo, true) << "\t\t"
- << Tr::tr("%1 probes encountered, %2 configure scripts executed, "
- "%3 re-used from current run, %4 re-used from earlier run.")
- .arg(m_probesEncountered).arg(m_probesRun).arg(m_probesCachedCurrent)
- .arg(m_probesCachedOld);
+ m_probesResolver->printProfilingInfo();
m_logger.qbsLog(LoggerInfo, true) << "\t"
<< Tr::tr("Property checking took %1.")
.arg(elapsedTimeString(m_elapsedTimePropertyChecking));
@@ -3506,122 +3422,6 @@ void ModuleLoader::createChildInstances(Item *instance, Item *prototype,
}
}
-void ModuleLoader::resolveProbes(ProductContext *productContext, Item *item)
-{
- AccumulatingTimer probesTimer(m_parameters.logElapsedTime() ? &m_elapsedTimeProbes : nullptr);
- EvalContextSwitcher evalContextSwitcher(m_evaluator->engine(), EvalContext::ProbeExecution);
- for (Item * const child : item->children())
- if (child->type() == ItemType::Probe)
- resolveProbe(productContext, item, child);
-}
-
-void ModuleLoader::resolveProbe(ProductContext *productContext, Item *parent, Item *probe)
-{
- qCDebug(lcModuleLoader) << "Resolving Probe at " << probe->location().toString();
- ++m_probesEncountered;
- const QString &probeId = probeGlobalId(probe);
- if (Q_UNLIKELY(probeId.isEmpty()))
- throw ErrorInfo(Tr::tr("Probe.id must be set."), probe->location());
- const JSSourceValueConstPtr configureScript
- = probe->sourceProperty(StringConstants::configureProperty());
- QBS_CHECK(configureScript);
- if (Q_UNLIKELY(configureScript->sourceCode() == StringConstants::undefinedValue()))
- throw ErrorInfo(Tr::tr("Probe.configure must be set."), probe->location());
- using ProbeProperty = std::pair<QString, QScriptValue>;
- std::vector<ProbeProperty> probeBindings;
- QVariantMap initialProperties;
- for (Item *obj = probe; obj; obj = obj->prototype()) {
- const Item::PropertyMap &props = obj->properties();
- for (auto it = props.cbegin(); it != props.cend(); ++it) {
- const QString &name = it.key();
- if (name == StringConstants::configureProperty())
- continue;
- const QScriptValue value = m_evaluator->value(probe, name);
- probeBindings << ProbeProperty(name, value);
- if (name != StringConstants::conditionProperty())
- initialProperties.insert(name, value.toVariant());
- }
- }
- ScriptEngine * const engine = m_evaluator->engine();
- QScriptValue configureScope;
- const bool condition = m_evaluator->boolValue(probe, StringConstants::conditionProperty());
- const QString &sourceCode = configureScript->sourceCode().toString();
- ProbeConstPtr resolvedProbe;
- if (parent->type() == ItemType::Project
- || productContext->name.startsWith(StringConstants::shadowProductPrefix())) {
- resolvedProbe = findOldProjectProbe(probeId, condition, initialProperties, sourceCode);
- } else {
- const QString &uniqueProductName = productContext->uniqueName();
- resolvedProbe
- = findOldProductProbe(uniqueProductName, condition, initialProperties, sourceCode);
- }
- if (!resolvedProbe) {
- resolvedProbe = findCurrentProbe(probe->location(), condition, initialProperties);
- if (resolvedProbe) {
- qCDebug(lcModuleLoader) << "probe results cached from current run";
- ++m_probesCachedCurrent;
- }
- } else {
- qCDebug(lcModuleLoader) << "probe results cached from earlier run";
- ++m_probesCachedOld;
- }
- std::vector<QString> importedFilesUsedInConfigure;
- if (!condition) {
- qCDebug(lcModuleLoader) << "Probe disabled; skipping";
- } else if (!resolvedProbe) {
- ++m_probesRun;
- qCDebug(lcModuleLoader) << "configure script needs to run";
- const Evaluator::FileContextScopes fileCtxScopes
- = m_evaluator->fileContextScopes(configureScript->file());
- engine->currentContext()->pushScope(fileCtxScopes.fileScope);
- engine->currentContext()->pushScope(fileCtxScopes.importScope);
- configureScope = engine->newObject();
- for (const ProbeProperty &b : probeBindings)
- configureScope.setProperty(b.first, b.second);
- engine->currentContext()->pushScope(configureScope);
- engine->clearRequestedProperties();
- QScriptValue sv = engine->evaluate(configureScript->sourceCodeForEvaluation());
- engine->currentContext()->popScope();
- engine->currentContext()->popScope();
- engine->currentContext()->popScope();
- engine->releaseResourcesOfScriptObjects();
- if (Q_UNLIKELY(engine->hasErrorOrException(sv)))
- throw ErrorInfo(engine->lastErrorString(sv), configureScript->location());
- importedFilesUsedInConfigure = engine->importedFilesUsedInScript();
- } else {
- importedFilesUsedInConfigure = resolvedProbe->importedFilesUsed();
- }
- QVariantMap properties;
- for (const ProbeProperty &b : probeBindings) {
- QVariant newValue;
- if (resolvedProbe) {
- newValue = resolvedProbe->properties().value(b.first);
- } else {
- if (condition) {
- QScriptValue v = configureScope.property(b.first);
- m_evaluator->convertToPropertyType(probe->propertyDeclaration(
- b.first), probe->location(), v);
- if (Q_UNLIKELY(engine->hasErrorOrException(v)))
- throw ErrorInfo(engine->lastError(v));
- newValue = v.toVariant();
- } else {
- newValue = initialProperties.value(b.first);
- }
- }
- if (newValue != b.second.toVariant())
- probe->setProperty(b.first, VariantValue::create(newValue));
- if (!resolvedProbe)
- properties.insert(b.first, newValue);
- }
- if (!resolvedProbe) {
- resolvedProbe = Probe::create(probeId, probe->location(), condition,
- sourceCode, properties, initialProperties,
- importedFilesUsedInConfigure);
- m_currentProbes[probe->location()] << resolvedProbe;
- }
- productContext->info.probes << resolvedProbe;
-}
-
void ModuleLoader::checkCancelation() const
{
if (m_progressObserver && m_progressObserver->canceled()) {
diff --git a/src/lib/corelib/language/moduleloader.h b/src/lib/corelib/language/moduleloader.h
index cea3be518..db0b51cae 100644
--- a/src/lib/corelib/language/moduleloader.h
+++ b/src/lib/corelib/language/moduleloader.h
@@ -74,6 +74,7 @@ class Evaluator;
class Item;
class ItemReader;
class ModuleProviderLoader;
+class ProbesResolver;
class ProgressObserver;
class QualifiedId;
class SearchPathsManager;
@@ -140,6 +141,7 @@ public:
private:
friend class ModuleProviderLoader;
+ friend class ProbesResolver;
class ProductSortByDependencies;
class ContextBase
@@ -334,8 +336,6 @@ private:
const QualifiedId &moduleName, ProductModuleInfo *productModuleInfo);
void createChildInstances(Item *instance, Item *prototype,
QHash<Item *, Item *> *prototypeInstanceMap) const;
- void resolveProbes(ProductContext *productContext, Item *item);
- void resolveProbe(ProductContext *productContext, Item *parent, Item *probe);
void checkCancelation() const;
bool checkItemCondition(Item *item, Item *itemToDisable = nullptr);
QStringList readExtraSearchPaths(Item *item, bool *wasSet = nullptr);
@@ -357,19 +357,6 @@ private:
void copyGroupsFromModulesToProduct(const ProductContext &productContext);
void markModuleTargetGroups(Item *group, const Item::Module &module);
bool checkExportItemCondition(Item *exportItem, const ProductContext &productContext);
- ProbeConstPtr findOldProjectProbe(const QString &globalId, bool condition,
- const QVariantMap &initialProperties,
- const QString &sourceCode) const;
- ProbeConstPtr findOldProductProbe(const QString &productName, bool condition,
- const QVariantMap &initialProperties,
- const QString &sourceCode) const;
- ProbeConstPtr findCurrentProbe(const CodeLocation &location, bool condition,
- const QVariantMap &initialProperties) const;
-
- enum class CompareScript { No, Yes };
- bool probeMatches(const ProbeConstPtr &probe, bool condition,
- const QVariantMap &initialProperties, const QString &configureScript,
- CompareScript compareScript) const;
void printProfilingInfo();
void handleProductError(const ErrorInfo &error, ProductContext *productContext);
@@ -402,6 +389,7 @@ private:
ProgressObserver *m_progressObserver;
const std::unique_ptr<ItemReader> m_reader;
Evaluator *m_evaluator;
+ const std::unique_ptr<ProbesResolver> m_probesResolver;
const std::unique_ptr<ModuleProviderLoader> m_moduleProviderLoader;
QMap<QString, QStringList> m_moduleDirListCache;
QHash<std::pair<QString, QualifiedId>, std::optional<QString>> m_existingModulePathCache;
@@ -431,10 +419,7 @@ private:
class DependsChainManager;
std::vector<DependsChainEntry> m_dependsChain;
- QHash<QString, std::vector<ProbeConstPtr>> m_oldProjectProbes;
- QHash<QString, std::vector<ProbeConstPtr>> m_oldProductProbes;
FileTime m_lastResolveTime;
- QHash<CodeLocation, std::vector<ProbeConstPtr>> m_currentProbes;
QVariantMap m_storedProfiles;
QVariantMap m_localProfiles;
std::multimap<QString, const ProductContext *> m_productsByName;
@@ -455,10 +440,6 @@ private:
qint64 m_elapsedTimeTransitiveDependencies = 0;
qint64 m_elapsedTimeHandleProducts = 0;
qint64 m_elapsedTimePropertyChecking = 0;
- quint64 m_probesEncountered = 0;
- quint64 m_probesRun = 0;
- quint64 m_probesCachedCurrent = 0;
- quint64 m_probesCachedOld = 0;
Set<QString> m_projectNamesUsedInOverrides;
Set<QString> m_productNamesUsedInOverrides;
Set<QString> m_disabledProjects;
diff --git a/src/lib/corelib/language/probesresolver.cpp b/src/lib/corelib/language/probesresolver.cpp
new file mode 100644
index 000000000..059080155
--- /dev/null
+++ b/src/lib/corelib/language/probesresolver.cpp
@@ -0,0 +1,299 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2022 Raphaël Cotty <raphael.cotty@gmail.com>
+** 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 "probesresolver.h"
+
+#include "builtindeclarations.h"
+#include "evaluator.h"
+#include "filecontext.h"
+#include "item.h"
+#include "itemreader.h"
+#include "language.h"
+#include "modulemerger.h"
+#include "qualifiedid.h"
+#include "scriptengine.h"
+#include "value.h"
+
+#include <api/languageinfo.h>
+#include <language/language.h>
+#include <logging/categories.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/profiling.h>
+#include <tools/stringconstants.h>
+
+namespace qbs {
+namespace Internal {
+
+static QString probeGlobalId(Item *probe)
+{
+ QString id;
+
+ for (Item *obj = probe; obj; obj = obj->prototype()) {
+ if (!obj->id().isEmpty()) {
+ id = obj->id();
+ break;
+ }
+ }
+
+ if (id.isEmpty())
+ return {};
+
+ QBS_CHECK(probe->file());
+ return id + QLatin1Char('_') + probe->file()->filePath();
+}
+
+ProbesResolver::ProbesResolver(Evaluator *evaluator, Logger &logger)
+ : m_evaluator(evaluator)
+ , m_logger(logger)
+{
+}
+
+void ProbesResolver::setProjectParameters(SetupProjectParameters parameters)
+{
+ m_parameters = std::move(parameters);
+ m_elapsedTimeProbes = m_probesEncountered = m_probesRun = m_probesCachedCurrent
+ = m_probesCachedOld = 0;
+}
+
+void ProbesResolver::setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes)
+{
+ m_oldProjectProbes.clear();
+ for (const ProbeConstPtr& probe : oldProbes)
+ m_oldProjectProbes[probe->globalId()] << probe;
+}
+
+void ProbesResolver::setOldProductProbes(
+ const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes)
+{
+ m_oldProductProbes = oldProbes;
+}
+
+void ProbesResolver::resolveProbes(ModuleLoader::ProductContext *productContext, Item *item)
+{
+ AccumulatingTimer probesTimer(m_parameters.logElapsedTime() ? &m_elapsedTimeProbes : nullptr);
+ EvalContextSwitcher evalContextSwitcher(m_evaluator->engine(), EvalContext::ProbeExecution);
+ for (Item * const child : item->children())
+ if (child->type() == ItemType::Probe)
+ resolveProbe(productContext, item, child);
+}
+
+void ProbesResolver::resolveProbe(ModuleLoader::ProductContext *productContext, Item *parent,
+ Item *probe)
+{
+ qCDebug(lcModuleLoader) << "Resolving Probe at " << probe->location().toString();
+ ++m_probesEncountered;
+ const QString &probeId = probeGlobalId(probe);
+ if (Q_UNLIKELY(probeId.isEmpty()))
+ throw ErrorInfo(Tr::tr("Probe.id must be set."), probe->location());
+ const JSSourceValueConstPtr configureScript
+ = probe->sourceProperty(StringConstants::configureProperty());
+ QBS_CHECK(configureScript);
+ if (Q_UNLIKELY(configureScript->sourceCode() == StringConstants::undefinedValue()))
+ throw ErrorInfo(Tr::tr("Probe.configure must be set."), probe->location());
+ using ProbeProperty = std::pair<QString, QScriptValue>;
+ std::vector<ProbeProperty> probeBindings;
+ QVariantMap initialProperties;
+ for (Item *obj = probe; obj; obj = obj->prototype()) {
+ const Item::PropertyMap &props = obj->properties();
+ for (auto it = props.cbegin(); it != props.cend(); ++it) {
+ const QString &name = it.key();
+ if (name == StringConstants::configureProperty())
+ continue;
+ const QScriptValue value = m_evaluator->value(probe, name);
+ probeBindings << ProbeProperty(name, value);
+ if (name != StringConstants::conditionProperty())
+ initialProperties.insert(name, value.toVariant());
+ }
+ }
+ ScriptEngine * const engine = m_evaluator->engine();
+ QScriptValue configureScope;
+ const bool condition = m_evaluator->boolValue(probe, StringConstants::conditionProperty());
+ const QString &sourceCode = configureScript->sourceCode().toString();
+ ProbeConstPtr resolvedProbe;
+ if (parent->type() == ItemType::Project
+ || productContext->name.startsWith(StringConstants::shadowProductPrefix())) {
+ resolvedProbe = findOldProjectProbe(probeId, condition, initialProperties, sourceCode);
+ } else {
+ const QString &uniqueProductName = productContext->uniqueName();
+ resolvedProbe
+ = findOldProductProbe(uniqueProductName, condition, initialProperties, sourceCode);
+ }
+ if (!resolvedProbe) {
+ resolvedProbe = findCurrentProbe(probe->location(), condition, initialProperties);
+ if (resolvedProbe) {
+ qCDebug(lcModuleLoader) << "probe results cached from current run";
+ ++m_probesCachedCurrent;
+ }
+ } else {
+ qCDebug(lcModuleLoader) << "probe results cached from earlier run";
+ ++m_probesCachedOld;
+ }
+ std::vector<QString> importedFilesUsedInConfigure;
+ if (!condition) {
+ qCDebug(lcModuleLoader) << "Probe disabled; skipping";
+ } else if (!resolvedProbe) {
+ ++m_probesRun;
+ qCDebug(lcModuleLoader) << "configure script needs to run";
+ const Evaluator::FileContextScopes fileCtxScopes
+ = m_evaluator->fileContextScopes(configureScript->file());
+ engine->currentContext()->pushScope(fileCtxScopes.fileScope);
+ engine->currentContext()->pushScope(fileCtxScopes.importScope);
+ configureScope = engine->newObject();
+ for (const ProbeProperty &b : probeBindings)
+ configureScope.setProperty(b.first, b.second);
+ engine->currentContext()->pushScope(configureScope);
+ engine->clearRequestedProperties();
+ QScriptValue sv = engine->evaluate(configureScript->sourceCodeForEvaluation());
+ engine->currentContext()->popScope();
+ engine->currentContext()->popScope();
+ engine->currentContext()->popScope();
+ engine->releaseResourcesOfScriptObjects();
+ if (Q_UNLIKELY(engine->hasErrorOrException(sv)))
+ throw ErrorInfo(engine->lastErrorString(sv), configureScript->location());
+ importedFilesUsedInConfigure = engine->importedFilesUsedInScript();
+ } else {
+ importedFilesUsedInConfigure = resolvedProbe->importedFilesUsed();
+ }
+ QVariantMap properties;
+ for (const ProbeProperty &b : probeBindings) {
+ QVariant newValue;
+ if (resolvedProbe) {
+ newValue = resolvedProbe->properties().value(b.first);
+ } else {
+ if (condition) {
+ QScriptValue v = configureScope.property(b.first);
+ m_evaluator->convertToPropertyType(probe->propertyDeclaration(
+ b.first), probe->location(), v);
+ if (Q_UNLIKELY(engine->hasErrorOrException(v)))
+ throw ErrorInfo(engine->lastError(v));
+ newValue = v.toVariant();
+ } else {
+ newValue = initialProperties.value(b.first);
+ }
+ }
+ if (newValue != b.second.toVariant())
+ probe->setProperty(b.first, VariantValue::create(newValue));
+ if (!resolvedProbe)
+ properties.insert(b.first, newValue);
+ }
+ if (!resolvedProbe) {
+ resolvedProbe = Probe::create(probeId, probe->location(), condition,
+ sourceCode, properties, initialProperties,
+ importedFilesUsedInConfigure);
+ m_currentProbes[probe->location()] << resolvedProbe;
+ }
+ productContext->info.probes << resolvedProbe;
+}
+
+ProbeConstPtr ProbesResolver::findOldProjectProbe(
+ const QString &globalId,
+ bool condition,
+ const QVariantMap &initialProperties,
+ const QString &sourceCode) const
+{
+ if (m_parameters.forceProbeExecution())
+ return {};
+
+ for (const ProbeConstPtr &oldProbe : m_oldProjectProbes.value(globalId)) {
+ if (probeMatches(oldProbe, condition, initialProperties, sourceCode, CompareScript::Yes))
+ return oldProbe;
+ }
+
+ return {};
+}
+
+ProbeConstPtr ProbesResolver::findOldProductProbe(
+ const QString &productName,
+ bool condition,
+ const QVariantMap &initialProperties,
+ const QString &sourceCode) const
+{
+ if (m_parameters.forceProbeExecution())
+ return {};
+
+ for (const ProbeConstPtr &oldProbe : m_oldProductProbes.value(productName)) {
+ if (probeMatches(oldProbe, condition, initialProperties, sourceCode, CompareScript::Yes))
+ return oldProbe;
+ }
+
+ return {};
+}
+
+ProbeConstPtr ProbesResolver::findCurrentProbe(
+ const CodeLocation &location,
+ bool condition,
+ const QVariantMap &initialProperties) const
+{
+ const std::vector<ProbeConstPtr> &cachedProbes = m_currentProbes.value(location);
+ for (const ProbeConstPtr &probe : cachedProbes) {
+ if (probeMatches(probe, condition, initialProperties, QString(), CompareScript::No))
+ return probe;
+ }
+ return {};
+}
+
+bool ProbesResolver::probeMatches(const ProbeConstPtr &probe, bool condition,
+ const QVariantMap &initialProperties, const QString &configureScript,
+ CompareScript compareScript) const
+{
+ return probe->condition() == condition
+ && probe->initialProperties() == initialProperties
+ && (compareScript == CompareScript::No
+ || (probe->configureScript() == configureScript
+ && !probe->needsReconfigure(m_lastResolveTime)));
+}
+
+void ProbesResolver::printProfilingInfo()
+{
+ if (!m_parameters.logElapsedTime())
+ return;
+ m_logger.qbsLog(LoggerInfo, true) << "\t\t"
+ << Tr::tr("Running Probes took %1.")
+ .arg(elapsedTimeString(m_elapsedTimeProbes));
+ m_logger.qbsLog(LoggerInfo, true) << "\t\t"
+ << Tr::tr("%1 probes encountered, %2 configure scripts executed, "
+ "%3 re-used from current run, %4 re-used from earlier run.")
+ .arg(m_probesEncountered).arg(m_probesRun).arg(m_probesCachedCurrent)
+ .arg(m_probesCachedOld);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/probesresolver.h b/src/lib/corelib/language/probesresolver.h
new file mode 100644
index 000000000..1aeec27ce
--- /dev/null
+++ b/src/lib/corelib/language/probesresolver.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2022 Raphaël Cotty <raphael.cotty@gmail.com>
+** 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$
+**
+****************************************************************************/
+
+#ifndef PROBESRESOLVER_H
+#define PROBESRESOLVER_H
+
+#include "moduleloader.h"
+
+namespace qbs {
+namespace Internal {
+
+class ProbesResolver
+{
+public:
+ explicit ProbesResolver(Evaluator *evaluator, Logger &logger);
+ void setProjectParameters(SetupProjectParameters parameters);
+ void setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes);
+ void setOldProductProbes(const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes);
+ void resolveProbes(ModuleLoader::ProductContext *productContext, Item *item);
+ void resolveProbe(ModuleLoader::ProductContext *productContext, Item *parent, Item *probe);
+ void printProfilingInfo();
+
+private:
+ ProbeConstPtr findOldProjectProbe(const QString &globalId, bool condition,
+ const QVariantMap &initialProperties,
+ const QString &sourceCode) const;
+ ProbeConstPtr findOldProductProbe(const QString &productName, bool condition,
+ const QVariantMap &initialProperties,
+ const QString &sourceCode) const;
+ ProbeConstPtr findCurrentProbe(const CodeLocation &location, bool condition,
+ const QVariantMap &initialProperties) const;
+ enum class CompareScript { No, Yes };
+ bool probeMatches(const ProbeConstPtr &probe, bool condition,
+ const QVariantMap &initialProperties, const QString &configureScript,
+ CompareScript compareScript) const;
+
+ qint64 m_elapsedTimeProbes = 0;
+ quint64 m_probesEncountered = 0;
+ quint64 m_probesRun = 0;
+ quint64 m_probesCachedCurrent = 0;
+ quint64 m_probesCachedOld = 0;
+
+ SetupProjectParameters m_parameters;
+ Evaluator *m_evaluator = nullptr;
+ Logger &m_logger;
+ QHash<QString, std::vector<ProbeConstPtr>> m_oldProjectProbes;
+ QHash<QString, std::vector<ProbeConstPtr>> m_oldProductProbes;
+ FileTime m_lastResolveTime;
+ QHash<CodeLocation, std::vector<ProbeConstPtr>> m_currentProbes;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // PROBESRESOLVER_H