From 249c5ab3b4c3d1c68b294ff93ec8089f85728051 Mon Sep 17 00:00:00 2001 From: Raphael Cotty Date: Fri, 21 Jan 2022 16:05:36 +0100 Subject: Factorize out probe code from module loader Change-Id: I349589aa5d92f221e43c61722e53a80d59f183b0 Reviewed-by: Ivan Komissarov --- src/lib/corelib/CMakeLists.txt | 2 + src/lib/corelib/corelib.qbs | 2 + src/lib/corelib/language/language.pri | 2 + src/lib/corelib/language/moduleloader.cpp | 220 +------------------- src/lib/corelib/language/moduleloader.h | 25 +-- src/lib/corelib/language/probesresolver.cpp | 299 ++++++++++++++++++++++++++++ src/lib/corelib/language/probesresolver.h | 92 +++++++++ 7 files changed, 410 insertions(+), 232 deletions(-) create mode 100644 src/lib/corelib/language/probesresolver.cpp create mode 100644 src/lib/corelib/language/probesresolver.h (limited to 'src') 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 {}; -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(logger)) , m_evaluator(evaluator) + , m_probesResolver(std::make_unique(m_evaluator, m_logger)) , m_moduleProviderLoader( std::make_unique(m_reader.get(), m_evaluator, m_logger)) { @@ -277,14 +261,12 @@ void ModuleLoader::setSearchPaths(const QStringList &searchPaths) void ModuleLoader::setOldProjectProbes(const std::vector &oldProbes) { - m_oldProjectProbes.clear(); - for (const ProbeConstPtr& probe : oldProbes) - m_oldProjectProbes[probe->globalId()] << probe; + m_probesResolver->setOldProjectProbes(oldProbes); } void ModuleLoader::setOldProductProbes(const QHash> &oldProbes) { - m_oldProductProbes = oldProbes; + m_probesResolver->setOldProductProbes(oldProbes); } void ModuleLoader::setStoredProfiles(const QVariantMap &profiles) @@ -310,12 +292,11 @@ ModuleLoaderResult ModuleLoader::load(const SetupProjectParameters ¶meters) 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(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 &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; - std::vector 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 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 *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 m_reader; Evaluator *m_evaluator; + const std::unique_ptr m_probesResolver; const std::unique_ptr m_moduleProviderLoader; QMap m_moduleDirListCache; QHash, std::optional> m_existingModulePathCache; @@ -431,10 +419,7 @@ private: class DependsChainManager; std::vector m_dependsChain; - QHash> m_oldProjectProbes; - QHash> m_oldProductProbes; FileTime m_lastResolveTime; - QHash> m_currentProbes; QVariantMap m_storedProfiles; QVariantMap m_localProfiles; std::multimap 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 m_projectNamesUsedInOverrides; Set m_productNamesUsedInOverrides; Set 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 +** 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 +#include +#include +#include +#include +#include +#include + +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 &oldProbes) +{ + m_oldProjectProbes.clear(); + for (const ProbeConstPtr& probe : oldProbes) + m_oldProjectProbes[probe->globalId()] << probe; +} + +void ProbesResolver::setOldProductProbes( + const QHash> &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; + std::vector 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 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 &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 +** 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 &oldProbes); + void setOldProductProbes(const QHash> &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> m_oldProjectProbes; + QHash> m_oldProductProbes; + FileTime m_lastResolveTime; + QHash> m_currentProbes; +}; + +} // namespace Internal +} // namespace qbs + +#endif // PROBESRESOLVER_H -- cgit v1.2.3