aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/corelib/language/moduleloader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/corelib/language/moduleloader.cpp')
-rw-r--r--src/lib/corelib/language/moduleloader.cpp3840
1 files changed, 0 insertions, 3840 deletions
diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp
deleted file mode 100644
index 574c03bfb..000000000
--- a/src/lib/corelib/language/moduleloader.cpp
+++ /dev/null
@@ -1,3840 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 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 "moduleloader.h"
-
-#include "builtindeclarations.h"
-#include "evaluator.h"
-#include "filecontext.h"
-#include "item.h"
-#include "itemreader.h"
-#include "language.h"
-#include "modulemerger.h"
-#include "moduleproviderloader.h"
-#include "probesresolver.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/error.h>
-#include <tools/fileinfo.h>
-#include <tools/preferences.h>
-#include <tools/profile.h>
-#include <tools/profiling.h>
-#include <tools/progressobserver.h>
-#include <tools/qbsassert.h>
-#include <tools/qttools.h>
-#include <tools/scripttools.h>
-#include <tools/settings.h>
-#include <tools/stlutils.h>
-#include <tools/stringconstants.h>
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qdir.h>
-#include <QtCore/qglobalstatic.h>
-#include <QtCore/qdiriterator.h>
-#include <QtCore/qjsondocument.h>
-#include <QtCore/qjsonobject.h>
-#include <QtCore/qtextstream.h>
-#include <QtCore/qthreadstorage.h>
-#include <QtScript/qscriptvalueiterator.h>
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-
-namespace qbs {
-namespace Internal {
-
-using MultiplexConfigurationByIdTable = QThreadStorage<QHash<QString, QVariantMap> >;
-Q_GLOBAL_STATIC(MultiplexConfigurationByIdTable, multiplexConfigurationsById);
-
-static bool multiplexConfigurationIntersects(const QVariantMap &lhs, const QVariantMap &rhs)
-{
- QBS_CHECK(!lhs.isEmpty() && !rhs.isEmpty());
-
- for (auto lhsProperty = lhs.constBegin(); lhsProperty != lhs.constEnd(); lhsProperty++) {
- const auto rhsProperty = rhs.find(lhsProperty.key());
- const bool isCommonProperty = rhsProperty != rhs.constEnd();
- if (isCommonProperty && lhsProperty.value() != rhsProperty.value())
- return false;
- }
-
- return true;
-}
-
-class ModuleLoader::ItemModuleList : public QList<Item::Module> {};
-
-class ModuleLoader::ProductSortByDependencies
-{
-public:
- ProductSortByDependencies(TopLevelProjectContext &tlp) : m_tlp(tlp)
- {
- }
-
- void apply()
- {
- QHash<QString, std::vector<ProductContext *>> productsMap;
- QList<ProductContext *> allProducts;
- for (ProjectContext * const projectContext : qAsConst(m_tlp.projects)) {
- for (auto &product : projectContext->products) {
- allProducts.push_back(&product);
- productsMap[product.name].push_back(&product);
- }
- }
- Set<ProductContext *> allDependencies;
- for (auto productContext : qAsConst(allProducts)) {
- auto &productDependencies = m_dependencyMap[productContext];
- for (const auto &dep : qAsConst(productContext->info.usedProducts)) {
- QBS_CHECK(!dep.name.isEmpty());
- const auto &deps = productsMap.value(dep.name);
- if (dep.profile == StringConstants::star()) {
- QBS_CHECK(!deps.empty());
- for (ProductContext *depProduct : deps) {
- if (depProduct == productContext)
- continue;
- productDependencies.push_back(depProduct);
- allDependencies << depProduct;
- }
- } else {
- auto it = std::find_if(deps.begin(), deps.end(), [&dep] (ProductContext *p) {
- return p->multiplexConfigurationId == dep.multiplexConfigurationId;
- });
- if (it == deps.end()) {
- QBS_CHECK(!productContext->multiplexConfigurationId.isEmpty());
- const QString productName = ResolvedProduct::fullDisplayName(
- productContext->name, productContext->multiplexConfigurationId);
- const QString depName = ResolvedProduct::fullDisplayName(
- dep.name, dep.multiplexConfigurationId);
- throw ErrorInfo(Tr::tr("Dependency from product '%1' to product '%2' not "
- "fulfilled.").arg(productName, depName),
- productContext->item->location());
- }
- productDependencies.push_back(*it);
- allDependencies << *it;
- }
- }
- }
- const Set<ProductContext *> rootProducts
- = rangeTo<Set<ProductContext *>>(allProducts) - allDependencies;
- for (ProductContext * const rootProduct : rootProducts)
- traverse(rootProduct);
- if (m_sortedProducts.size() < allProducts.size()) {
- for (auto const product : qAsConst(allProducts)) {
- QList<ModuleLoader::ProductContext *> path;
- findCycle(product, path);
- }
- }
- QBS_CHECK(m_sortedProducts.size() == allProducts.size());
- }
-
- // No product at position i has dependencies to a product at position j > i.
- const QList<ProductContext *> &sortedProducts() const
- {
- return m_sortedProducts;
- }
-
-private:
- void traverse(ModuleLoader::ProductContext *product)
- {
- if (!m_seenProducts.insert(product).second)
- return;
- for (const auto &dependency : m_dependencyMap.value(product))
- traverse(dependency);
- m_sortedProducts << product;
- }
-
- void findCycle(ModuleLoader::ProductContext *product,
- QList<ModuleLoader::ProductContext *> &path)
- {
- if (path.contains(product)) {
- ErrorInfo error(Tr::tr("Cyclic dependencies detected."));
- for (const auto * const p : path)
- error.append(p->name, p->item->location());
- error.append(product->name, product->item->location());
- throw error;
- }
- path << product;
- for (auto const dep : m_dependencyMap.value(product))
- findCycle(dep, path);
- path.removeLast();
- }
-
- TopLevelProjectContext &m_tlp;
- QHash<ProductContext *, std::vector<ProductContext *>> m_dependencyMap;
- Set<ProductContext *> m_seenProducts;
- QList<ProductContext *> m_sortedProducts;
-};
-
-class SearchPathsManager {
-public:
- explicit SearchPathsManager(ItemReader *itemReader)
- : m_itemReader(itemReader)
- , m_oldSize(itemReader->extraSearchPathsStack().size())
- {
- }
- SearchPathsManager(ItemReader *itemReader, const QStringList &extraSearchPaths)
- : SearchPathsManager(itemReader)
- {
- m_itemReader->pushExtraSearchPaths(extraSearchPaths);
- }
- ~SearchPathsManager()
- {
- reset();
- }
- void reset()
- {
- while (m_itemReader->extraSearchPathsStack().size() > m_oldSize)
- m_itemReader->popExtraSearchPaths();
- }
-
-private:
- ItemReader * const m_itemReader;
- size_t m_oldSize{0};
-};
-
-ModuleLoader::ModuleLoader(Evaluator *evaluator, Logger &logger)
- : m_pool(nullptr)
- , m_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_probesResolver.get(),
- m_logger))
-{
-}
-
-ModuleLoader::~ModuleLoader() = default;
-
-void ModuleLoader::setProgressObserver(ProgressObserver *progressObserver)
-{
- m_progressObserver = progressObserver;
-}
-
-void ModuleLoader::setSearchPaths(const QStringList &searchPaths)
-{
- m_reader->setSearchPaths(searchPaths);
- qCDebug(lcModuleLoader) << "initial search paths:" << searchPaths;
-}
-
-void ModuleLoader::setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes)
-{
- m_probesResolver->setOldProjectProbes(oldProbes);
-}
-
-void ModuleLoader::setOldProductProbes(const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes)
-{
- m_probesResolver->setOldProductProbes(oldProbes);
-}
-
-void ModuleLoader::setStoredProfiles(const QVariantMap &profiles)
-{
- m_storedProfiles = profiles;
-}
-
-void ModuleLoader::setStoredModuleProviderInfo(const StoredModuleProviderInfo &moduleProviderInfo)
-{
- m_moduleProviderLoader->setStoredModuleProviderInfo(moduleProviderInfo);
-}
-
-ModuleLoaderResult ModuleLoader::load(const SetupProjectParameters &parameters)
-{
- TimedActivityLogger moduleLoaderTimer(m_logger, Tr::tr("ModuleLoader"),
- parameters.logElapsedTime());
- qCDebug(lcModuleLoader) << "load" << parameters.projectFilePath();
- m_parameters = parameters;
- m_modulePrototypes.clear();
- m_modulePrototypeEnabledInfo.clear();
- m_parameterDeclarations.clear();
- m_disabledItems.clear();
- m_reader->clearExtraSearchPathsStack();
- m_reader->setEnableTiming(parameters.logElapsedTime());
- m_moduleProviderLoader->setProjectParameters(m_parameters);
- m_probesResolver->setProjectParameters(m_parameters);
- m_elapsedTimePrepareProducts = m_elapsedTimeHandleProducts
- = m_elapsedTimeProductDependencies = m_elapsedTimeTransitiveDependencies
- = m_elapsedTimePropertyChecking = 0;
- m_elapsedTimeModuleProviders = 0;
- m_settings = std::make_unique<Settings>(parameters.settingsDirectory());
-
- const auto keys = m_parameters.overriddenValues().keys();
- for (const QString &key : keys) {
- static const QStringList prefixes({ StringConstants::projectPrefix(),
- QStringLiteral("projects"),
- QStringLiteral("products"), QStringLiteral("modules"),
- StringConstants::moduleProviders(),
- StringConstants::qbsModule()});
- bool ok = false;
- for (const auto &prefix : prefixes) {
- if (key.startsWith(prefix + QLatin1Char('.'))) {
- ok = true;
- break;
- }
- }
- if (ok) {
- collectNameFromOverride(key);
- continue;
- }
- ErrorInfo e(Tr::tr("Property override key '%1' not understood.").arg(key));
- e.append(Tr::tr("Please use one of the following:"));
- e.append(QLatin1Char('\t') + Tr::tr("projects.<project-name>.<property-name>:value"));
- e.append(QLatin1Char('\t') + Tr::tr("products.<product-name>.<property-name>:value"));
- e.append(QLatin1Char('\t') + Tr::tr("modules.<module-name>.<property-name>:value"));
- e.append(QLatin1Char('\t') + Tr::tr("products.<product-name>.<module-name>."
- "<property-name>:value"));
- e.append(QLatin1Char('\t') + Tr::tr("moduleProviders.<provider-name>."
- "<property-name>:value"));
- handlePropertyError(e, m_parameters, m_logger);
- }
-
- ModuleLoaderResult result;
- result.profileConfigs = m_storedProfiles;
- m_pool = result.itemPool.get();
- m_reader->setPool(m_pool);
-
- const QStringList topLevelSearchPaths = parameters.finalBuildConfigurationTree()
- .value(StringConstants::projectPrefix()).toMap()
- .value(StringConstants::qbsSearchPathsProperty()).toStringList();
- Item *root;
- {
- SearchPathsManager searchPathsManager(m_reader.get(), topLevelSearchPaths);
- root = loadItemFromFile(parameters.projectFilePath(), CodeLocation());
- if (!root)
- return ModuleLoaderResult();
- }
-
- switch (root->type()) {
- case ItemType::Product:
- root = wrapInProjectIfNecessary(root);
- break;
- case ItemType::Project:
- break;
- default:
- throw ErrorInfo(Tr::tr("The top-level item must be of type 'Project' or 'Product', but it"
- " is of type '%1'.").arg(root->typeName()), root->location());
- }
-
- const QString buildDirectory = TopLevelProject::deriveBuildDirectory(parameters.buildRoot(),
- TopLevelProject::deriveId(parameters.finalBuildConfigurationTree()));
- root->setProperty(StringConstants::sourceDirectoryProperty(),
- VariantValue::create(QFileInfo(root->file()->filePath()).absolutePath()));
- root->setProperty(StringConstants::buildDirectoryProperty(),
- VariantValue::create(buildDirectory));
- root->setProperty(StringConstants::profileProperty(),
- VariantValue::create(m_parameters.topLevelProfile()));
- handleTopLevelProject(&result, root, buildDirectory,
- Set<QString>() << QDir::cleanPath(parameters.projectFilePath()));
- result.root = root;
- result.qbsFiles = m_reader->filesRead() - m_moduleProviderLoader->tempQbsFiles();
- for (auto it = m_localProfiles.cbegin(); it != m_localProfiles.cend(); ++it)
- result.profileConfigs.remove(it.key());
- printProfilingInfo();
- return result;
-}
-
-class PropertyDeclarationCheck : public ValueHandler
-{
- const Set<Item *> &m_disabledItems;
- Set<Item *> m_handledItems;
- std::vector<Item *> m_parentItems;
- Item *m_currentModuleInstance = nullptr;
- QualifiedId m_currentModuleName;
- QString m_currentName;
- SetupProjectParameters m_params;
- Logger &m_logger;
-public:
- PropertyDeclarationCheck(const Set<Item *> &disabledItems,
- SetupProjectParameters params, Logger &logger)
- : m_disabledItems(disabledItems)
- , m_params(std::move(params))
- , m_logger(logger)
- {
- }
-
- void operator()(Item *item)
- {
- handleItem(item);
- }
-
-private:
- void handle(JSSourceValue *value) override
- {
- if (!value->createdByPropertiesBlock()) {
- const ErrorInfo error(Tr::tr("Property '%1' is not declared.")
- .arg(m_currentName), value->location());
- handlePropertyError(error, m_params, m_logger);
- }
- }
-
- void handle(ItemValue *value) override
- {
- if (checkItemValue(value))
- handleItem(value->item());
- }
-
- bool checkItemValue(ItemValue *value)
- {
- // TODO: Remove once QBS-1030 is fixed.
- if (parentItem()->type() == ItemType::Artifact)
- return false;
-
- if (parentItem()->type() == ItemType::Properties)
- return false;
-
- if (parentItem()->isOfTypeOrhasParentOfType(ItemType::Export)) {
- // Export item prototypes do not have instantiated modules.
- // The module instances are where the Export is used.
- QBS_ASSERT(m_currentModuleInstance, return false);
- auto hasCurrentModuleName = [this](const Item::Module &m) {
- return m.name == m_currentModuleName;
- };
- if (any_of(m_currentModuleInstance->modules(), hasCurrentModuleName))
- return true;
- }
-
- // TODO: We really should have a dedicated item type for "pre-instantiated" item values
- // and only use ModuleInstance for actual module instances.
- const bool itemIsModuleInstance = value->item()->type() == ItemType::ModuleInstance
- && value->item()->hasProperty(StringConstants::presentProperty());
-
- if (!itemIsModuleInstance
- && value->item()->type() != ItemType::ModulePrefix
- && (!parentItem()->file() || !parentItem()->file()->idScope()
- || !parentItem()->file()->idScope()->hasProperty(m_currentName))
- && !value->createdByPropertiesBlock()) {
- CodeLocation location = value->location();
- for (int i = int(m_parentItems.size() - 1); !location.isValid() && i >= 0; --i)
- location = m_parentItems.at(i)->location();
- const ErrorInfo error(Tr::tr("Item '%1' is not declared. "
- "Did you forget to add a Depends item?")
- .arg(m_currentModuleName.toString()), location);
- handlePropertyError(error, m_params, m_logger);
- return false;
- }
-
- return true;
- }
-
- void handleItem(Item *item)
- {
- if (!m_handledItems.insert(item).second)
- return;
- if (m_disabledItems.contains(item)
- || (item->type() == ItemType::ModuleInstance && !item->isPresentModule())
- || item->type() == ItemType::Properties
-
- // The Properties child of a SubProject item is not a regular item.
- || item->type() == ItemType::PropertiesInSubProject) {
- return;
- }
-
- // If a module was found but its validate script failed, only the canonical
- // module instance will have the "non-present" flag set, so we need to locate it.
- if (item->type() == ItemType::ModuleInstance) {
- const Item *productItem = nullptr;
- for (auto it = m_parentItems.rbegin(); it != m_parentItems.rend(); ++it) {
- if ((*it)->type() == ItemType::Product) {
- productItem = *it;
- break;
- }
- }
- if (productItem) {
- for (const Item::Module &m : productItem->modules()) {
- if (m.name == m_currentModuleName) {
- if (!m.item->isPresentModule())
- return;
- break;
- }
- }
- }
- }
-
- m_parentItems.push_back(item);
- for (Item::PropertyMap::const_iterator it = item->properties().constBegin();
- it != item->properties().constEnd(); ++it) {
- if (item->type() == ItemType::Product && it.key() == StringConstants::moduleProviders()
- && it.value()->type() == Value::ItemValueType)
- continue;
- const PropertyDeclaration decl = item->propertyDeclaration(it.key());
- if (decl.isValid()) {
- if (!decl.isDeprecated())
- continue;
- const DeprecationInfo &di = decl.deprecationInfo();
- QString message;
- bool warningOnly;
- if (decl.isExpired()) {
- message = Tr::tr("The property '%1' can no longer be used. "
- "It was removed in Qbs %2.")
- .arg(decl.name(), di.removalVersion().toString());
- warningOnly = false;
- } else {
- message = Tr::tr("The property '%1' is deprecated and will be removed "
- "in Qbs %2.").arg(decl.name(), di.removalVersion().toString());
- warningOnly = true;
- }
- ErrorInfo error(message, it.value()->location());
- if (!di.additionalUserInfo().isEmpty())
- error.append(di.additionalUserInfo());
- if (warningOnly)
- m_logger.printWarning(error);
- else
- handlePropertyError(error, m_params, m_logger);
- continue;
- }
- m_currentName = it.key();
- const QualifiedId oldModuleName = m_currentModuleName;
- if (parentItem()->type() != ItemType::ModulePrefix)
- m_currentModuleName.clear();
- m_currentModuleName.push_back(m_currentName);
- it.value()->apply(this);
- m_currentModuleName = oldModuleName;
- }
- m_parentItems.pop_back();
- for (Item * const child : item->children()) {
- switch (child->type()) {
- case ItemType::Export:
- case ItemType::Depends:
- case ItemType::Parameter:
- case ItemType::Parameters:
- break;
- case ItemType::Group:
- if (item->type() == ItemType::Module || item->type() == ItemType::ModuleInstance)
- break;
- Q_FALLTHROUGH();
- default:
- handleItem(child);
- }
- }
-
- // Properties that don't refer to an existing module with a matching Depends item
- // only exist in the prototype of an Export item, not in the instance.
- // Example 1 - setting a property of an unknown module: Export { abc.def: true }
- // Example 2 - setting a non-existing Export property: Export { blubb: true }
- if (item->type() == ItemType::ModuleInstance && item->prototype()) {
- Item *oldInstance = m_currentModuleInstance;
- m_currentModuleInstance = item;
- handleItem(item->prototype());
- m_currentModuleInstance = oldInstance;
- }
- }
-
- void handle(VariantValue *) override { /* only created internally - no need to check */ }
-
- Item *parentItem() const { return m_parentItems.back(); }
-};
-
-void ModuleLoader::handleTopLevelProject(ModuleLoaderResult *loadResult, Item *projectItem,
- const QString &buildDirectory, const Set<QString> &referencedFilePaths)
-{
- TopLevelProjectContext tlp;
- tlp.buildDirectory = buildDirectory;
- handleProject(loadResult, &tlp, projectItem, referencedFilePaths);
- checkProjectNamesInOverrides(tlp);
- collectProductsByName(tlp);
- checkProductNamesInOverrides();
-
- adjustDependenciesForMultiplexing(tlp);
-
- m_dependencyResolvingPass = 1;
- for (ProjectContext * const projectContext : qAsConst(tlp.projects)) {
- m_reader->setExtraSearchPathsStack(projectContext->searchPathsStack);
- for (ProductContext &productContext : projectContext->products) {
- try {
- setupProductDependencies(&productContext, Set<DeferredDependsContext>());
- } catch (const ErrorInfo &err) {
- if (productContext.name.isEmpty())
- throw err;
- handleProductError(err, &productContext);
- }
- // extraSearchPathsStack is changed during dependency resolution, check
- // that we've rolled back all the changes
- QBS_CHECK(m_reader->extraSearchPathsStack() == projectContext->searchPathsStack);
- }
- }
- if (!m_productsWithDeferredDependsItems.empty() || !m_exportsWithDeferredDependsItems.empty()) {
- collectProductsByType(tlp);
- m_dependencyResolvingPass = 2;
-
- // Doing the normalization for the Export items themselves (as opposed to doing it only
- // for the corresponding module instances) serves two purposes:
- // (1) It makes recursive use of Depends.productTypes via Export items work; otherwise,
- // we'd need an additional dependency resolving pass for every export level.
- // (2) The "expanded" Depends items are available to the Exporter.qbs module.
- for (Item * const exportItem : m_exportsWithDeferredDependsItems)
- normalizeDependencies(nullptr, DeferredDependsContext(nullptr, exportItem));
-
- for (const auto &deferredDependsData : m_productsWithDeferredDependsItems) {
- ProductContext * const productContext = deferredDependsData.first;
- m_reader->setExtraSearchPathsStack(productContext->project->searchPathsStack);
- try {
- setupProductDependencies(productContext, deferredDependsData.second);
- } catch (const ErrorInfo &err) {
- handleProductError(err, productContext);
- }
- }
- }
-
- ProductSortByDependencies productSorter(tlp);
- productSorter.apply();
- for (ProductContext * const p : productSorter.sortedProducts()) {
- try {
- handleProduct(p);
- if (p->name.startsWith(StringConstants::shadowProductPrefix()))
- tlp.probes << p->info.probes;
- } catch (const ErrorInfo &err) {
- handleProductError(err, p);
- }
- }
-
- loadResult->projectProbes = tlp.probes;
- loadResult->storedModuleProviderInfo = m_moduleProviderLoader->storedModuleProviderInfo();
-
- m_reader->clearExtraSearchPathsStack();
- AccumulatingTimer timer(m_parameters.logElapsedTime()
- ? &m_elapsedTimePropertyChecking : nullptr);
- PropertyDeclarationCheck check(m_disabledItems, m_parameters, m_logger);
- check(projectItem);
-}
-
-void ModuleLoader::handleProject(ModuleLoaderResult *loadResult,
- TopLevelProjectContext *topLevelProjectContext, Item *projectItem,
- const Set<QString> &referencedFilePaths)
-{
- auto p = std::make_unique<ProjectContext>();
- auto &projectContext = *p;
- projectContext.topLevelProject = topLevelProjectContext;
- projectContext.result = loadResult;
- ItemValuePtr itemValue = ItemValue::create(projectItem);
- projectContext.scope = Item::create(m_pool, ItemType::Scope);
- projectContext.scope->setFile(projectItem->file());
- projectContext.scope->setProperty(StringConstants::projectVar(), itemValue);
- ProductContext dummyProductContext;
- dummyProductContext.project = &projectContext;
- dummyProductContext.moduleProperties = m_parameters.finalBuildConfigurationTree();
- projectItem->addModule(loadBaseModule(&dummyProductContext, projectItem));
- overrideItemProperties(projectItem, StringConstants::projectPrefix(),
- m_parameters.overriddenValuesTree());
- projectContext.name = m_evaluator->stringValue(projectItem,
- StringConstants::nameProperty());
- if (projectContext.name.isEmpty()) {
- projectContext.name = FileInfo::baseName(projectItem->location().filePath());
- projectItem->setProperty(StringConstants::nameProperty(),
- VariantValue::create(projectContext.name));
- }
- overrideItemProperties(projectItem,
- StringConstants::projectsOverridePrefix() + projectContext.name,
- m_parameters.overriddenValuesTree());
- if (!checkItemCondition(projectItem)) {
- m_disabledProjects.insert(projectContext.name);
- return;
- }
- topLevelProjectContext->projects.push_back(p.release());
- SearchPathsManager searchPathsManager(m_reader.get(), readExtraSearchPaths(projectItem)
- << projectItem->file()->dirPath());
- projectContext.searchPathsStack = m_reader->extraSearchPathsStack();
- projectContext.item = projectItem;
-
- const QString minVersionStr
- = m_evaluator->stringValue(projectItem, StringConstants::minimumQbsVersionProperty(),
- QStringLiteral("1.3.0"));
- const Version minVersion = Version::fromString(minVersionStr);
- if (!minVersion.isValid()) {
- throw ErrorInfo(Tr::tr("The value '%1' of Project.minimumQbsVersion "
- "is not a valid version string.").arg(minVersionStr), projectItem->location());
- }
- if (!m_qbsVersion.isValid())
- m_qbsVersion = Version::fromString(QLatin1String(QBS_VERSION));
- if (m_qbsVersion < minVersion) {
- throw ErrorInfo(Tr::tr("The project requires at least qbs version %1, but "
- "this is qbs version %2.").arg(minVersion.toString(),
- m_qbsVersion.toString()));
- }
-
- for (Item * const child : projectItem->children())
- child->setScope(projectContext.scope);
-
- m_probesResolver->resolveProbes(&dummyProductContext, projectItem);
- projectContext.topLevelProject->probes << dummyProductContext.info.probes;
-
- handleProfileItems(projectItem, &projectContext);
-
- QList<Item *> multiplexedProducts;
- for (Item * const child : projectItem->children()) {
- if (child->type() == ItemType::Product)
- multiplexedProducts << multiplexProductItem(&dummyProductContext, child);
- }
- for (Item * const additionalProductItem : qAsConst(multiplexedProducts))
- Item::addChild(projectItem, additionalProductItem);
-
- const QList<Item *> originalChildren = projectItem->children();
- for (Item * const child : originalChildren) {
- switch (child->type()) {
- case ItemType::Product:
- prepareProduct(&projectContext, child);
- break;
- case ItemType::SubProject:
- handleSubProject(&projectContext, child, referencedFilePaths);
- break;
- case ItemType::Project:
- copyProperties(projectItem, child);
- handleProject(loadResult, topLevelProjectContext, child, referencedFilePaths);
- break;
- default:
- break;
- }
- }
-
- const QStringList refs = m_evaluator->stringListValue(
- projectItem, StringConstants::referencesProperty());
- const CodeLocation referencingLocation
- = projectItem->property(StringConstants::referencesProperty())->location();
- QList<Item *> additionalProjectChildren;
- for (const QString &filePath : refs) {
- try {
- additionalProjectChildren << loadReferencedFile(filePath, referencingLocation,
- referencedFilePaths, dummyProductContext);
- } catch (const ErrorInfo &error) {
- if (m_parameters.productErrorMode() == ErrorHandlingMode::Strict)
- throw;
- m_logger.printWarning(error);
- }
- }
- for (Item * const subItem : qAsConst(additionalProjectChildren)) {
- Item::addChild(projectContext.item, subItem);
- switch (subItem->type()) {
- case ItemType::Product:
- prepareProduct(&projectContext, subItem);
- break;
- case ItemType::Project:
- copyProperties(projectItem, subItem);
- handleProject(loadResult, topLevelProjectContext, subItem,
- Set<QString>(referencedFilePaths) << subItem->file()->filePath());
- break;
- default:
- break;
- }
- }
-}
-
-QString ModuleLoader::MultiplexInfo::toIdString(size_t row) const
-{
- const auto &mprow = table.at(row);
- QVariantMap multiplexConfiguration;
- for (size_t column = 0; column < mprow.size(); ++column) {
- const QString &propertyName = properties.at(column);
- const VariantValuePtr &mpvalue = mprow.at(column);
- multiplexConfiguration.insert(propertyName, mpvalue->value());
- }
- QString id = QString::fromUtf8(QJsonDocument::fromVariant(multiplexConfiguration)
- .toJson(QJsonDocument::Compact)
- .toBase64());
- // Cache for later use in:multiplexIdToVariantMap()
- multiplexConfigurationsById->localData().insert(id, multiplexConfiguration);
- return id;
-}
-
-QVariantMap ModuleLoader::MultiplexInfo::multiplexIdToVariantMap(const QString &multiplexId)
-{
- if (multiplexId.isEmpty())
- return QVariantMap();
-
- QVariantMap result = multiplexConfigurationsById->localData().value(multiplexId);
- // We assume that MultiplexInfo::toIdString() has been called for this
- // particular multiplex configuration.
- QBS_CHECK(!result.isEmpty());
- return result;
-}
-
-void qbs::Internal::ModuleLoader::ModuleLoader::dump(const ModuleLoader::MultiplexInfo &mpi)
-{
- QStringList header;
- for (const auto &str : mpi.properties)
- header << str;
- qDebug() << header;
-
- for (const auto &row : mpi.table) {
- QVariantList values;
- for (const auto &elem : row) {
- values << elem->value();
- }
- qDebug() << values;
- }
-}
-
-ModuleLoader::MultiplexTable ModuleLoader::combine(const MultiplexTable &table,
- const MultiplexRow &values)
-{
- MultiplexTable result;
- if (table.empty()) {
- result.resize(values.size());
- for (size_t i = 0; i < values.size(); ++i) {
- MultiplexRow row;
- row.resize(1);
- row[0] = values.at(i);
- result[i] = row;
- }
- } else {
- for (const auto &row : table) {
- for (const auto &value : values) {
- MultiplexRow newRow = row;
- newRow.push_back(value);
- result.push_back(newRow);
- }
- }
- }
- return result;
-}
-
-ModuleLoader::MultiplexInfo ModuleLoader::extractMultiplexInfo(Item *productItem,
- Item *qbsModuleItem)
-{
- static const QString mpmKey = QStringLiteral("multiplexMap");
-
- const QScriptValue multiplexMap = m_evaluator->value(qbsModuleItem, mpmKey);
- const QStringList multiplexByQbsProperties = m_evaluator->stringListValue(
- productItem, StringConstants::multiplexByQbsPropertiesProperty());
-
- MultiplexInfo multiplexInfo;
- multiplexInfo.aggregate = m_evaluator->boolValue(
- productItem, StringConstants::aggregateProperty());
-
- const QString multiplexedType = m_evaluator->stringValue(
- productItem, StringConstants::multiplexedTypeProperty());
- if (!multiplexedType.isEmpty())
- multiplexInfo.multiplexedType = VariantValue::create(multiplexedType);
-
- Set<QString> uniqueMultiplexByQbsProperties;
- for (const QString &key : multiplexByQbsProperties) {
- const QString mappedKey = multiplexMap.property(key).toString();
- if (mappedKey.isEmpty())
- throw ErrorInfo(Tr::tr("There is no entry for '%1' in 'qbs.multiplexMap'.").arg(key));
-
- if (!uniqueMultiplexByQbsProperties.insert(mappedKey).second) {
- throw ErrorInfo(Tr::tr("Duplicate entry '%1' in Product.%2.")
- .arg(mappedKey, StringConstants::multiplexByQbsPropertiesProperty()),
- productItem->location());
- }
-
- const QScriptValue arr = m_evaluator->value(qbsModuleItem, key);
- if (arr.isUndefined())
- continue;
- if (!arr.isArray())
- throw ErrorInfo(Tr::tr("Property '%1' must be an array.").arg(key));
-
- const quint32 arrlen = arr.property(StringConstants::lengthProperty()).toUInt32();
- if (arrlen == 0)
- continue;
-
- MultiplexRow mprow;
- mprow.resize(arrlen);
- QVariantList entriesForKey;
- for (quint32 i = 0; i < arrlen; ++i) {
- const QVariant value = arr.property(i).toVariant();
- if (entriesForKey.contains(value)) {
- throw ErrorInfo(Tr::tr("Duplicate entry '%1' in qbs.%2.")
- .arg(value.toString(), key), productItem->location());
- }
- entriesForKey << value;
- mprow[i] = VariantValue::create(value);
- }
- multiplexInfo.table = combine(multiplexInfo.table, mprow);
- multiplexInfo.properties.push_back(mappedKey);
- }
- return multiplexInfo;
-}
-
-template <typename T, typename F>
-T ModuleLoader::callWithTemporaryBaseModule(ProductContext *productContext, const F &func)
-{
- // Temporarily attach the qbs module here, in case we need to access one of its properties
- // to evaluate properties.
- const QString &qbsKey = StringConstants::qbsModule();
- Item *productItem = productContext->item;
- ValuePtr qbsValue = productItem->property(qbsKey); // Retrieve now to restore later.
- if (qbsValue)
- qbsValue = qbsValue->clone();
- const Item::Module qbsModule = loadBaseModule(productContext, productItem);
- productItem->addModule(qbsModule);
-
- auto &&result = func(qbsModule);
-
- // "Unload" the qbs module again.
- if (qbsValue)
- productItem->setProperty(qbsKey, qbsValue);
- else
- productItem->removeProperty(qbsKey);
- productItem->removeModules();
-
- return std::forward<T>(result);
-}
-
-QList<Item *> ModuleLoader::multiplexProductItem(ProductContext *dummyContext, Item *productItem)
-{
- QString productName;
- dummyContext->item = productItem;
- auto extractMultiplexInfoFromProduct
- = [this, productItem, &productName](const Item::Module &qbsModule) {
- // Overriding the product item properties must be done here already, because multiplexing
- // properties might depend on product properties.
- const QString &nameKey = StringConstants::nameProperty();
- productName = m_evaluator->stringValue(productItem, nameKey);
- if (productName.isEmpty()) {
- productName = FileInfo::completeBaseName(productItem->file()->filePath());
- productItem->setProperty(nameKey, VariantValue::create(productName));
- }
- overrideItemProperties(productItem, StringConstants::productsOverridePrefix() + productName,
- m_parameters.overriddenValuesTree());
-
- return extractMultiplexInfo(productItem, qbsModule.item);
- };
- const auto multiplexInfo
- = callWithTemporaryBaseModule<const MultiplexInfo>(dummyContext,
- extractMultiplexInfoFromProduct);
-
- if (multiplexInfo.table.size() > 1)
- productItem->setProperty(StringConstants::multiplexedProperty(), VariantValue::trueValue());
-
- VariantValuePtr productNameValue = VariantValue::create(productName);
-
- Item *aggregator = multiplexInfo.aggregate ? productItem->clone() : nullptr;
- QList<Item *> additionalProductItems;
- std::vector<VariantValuePtr> multiplexConfigurationIdValues;
- for (size_t row = 0; row < multiplexInfo.table.size(); ++row) {
- Item *item = productItem;
- const auto &mprow = multiplexInfo.table.at(row);
- QBS_CHECK(mprow.size() == multiplexInfo.properties.size());
- if (row > 0) {
- item = productItem->clone();
- additionalProductItems.push_back(item);
- }
- const QString multiplexConfigurationId = multiplexInfo.toIdString(row);
- const VariantValuePtr multiplexConfigurationIdValue
- = VariantValue::create(multiplexConfigurationId);
- if (multiplexInfo.table.size() > 1 || aggregator) {
- multiplexConfigurationIdValues.push_back(multiplexConfigurationIdValue);
- item->setProperty(StringConstants::multiplexConfigurationIdProperty(),
- multiplexConfigurationIdValue);
- }
- if (multiplexInfo.multiplexedType)
- item->setProperty(StringConstants::typeProperty(), multiplexInfo.multiplexedType);
- for (size_t column = 0; column < mprow.size(); ++column) {
- Item *qbsItem = moduleInstanceItem(item, StringConstants::qbsModule());
- const QString &propertyName = multiplexInfo.properties.at(column);
- const VariantValuePtr &mpvalue = mprow.at(column);
- qbsItem->setProperty(propertyName, mpvalue);
- }
- }
-
- if (aggregator) {
- additionalProductItems << aggregator;
-
- // Add dependencies to all multiplexed instances.
- for (const auto &v : multiplexConfigurationIdValues) {
- Item *dependsItem = Item::create(aggregator->pool(), ItemType::Depends);
- dependsItem->setProperty(StringConstants::nameProperty(), productNameValue);
- dependsItem->setProperty(StringConstants::multiplexConfigurationIdProperty(), v);
- dependsItem->setProperty(StringConstants::profilesProperty(),
- VariantValue::create(QStringList()));
- dependsItem->setFile(aggregator->file());
- dependsItem->setupForBuiltinType(m_logger);
- Item::addChild(aggregator, dependsItem);
- }
- }
-
- return additionalProductItems;
-}
-
-void ModuleLoader::normalizeDependencies(ProductContext *product,
- const DeferredDependsContext &dependsContext)
-{
- std::vector<Item *> dependsItemsToAdd;
- std::vector<Item *> dependsItemsToRemove;
- std::vector<Item *> deferredDependsItems;
- for (Item *dependsItem : dependsContext.parentItem->children()) {
- if (dependsItem->type() != ItemType::Depends)
- continue;
- bool productTypesIsSet;
- const FileTags productTypes = m_evaluator->fileTagsValue(dependsItem,
- StringConstants::productTypesProperty(), &productTypesIsSet);
- if (productTypesIsSet) {
- bool nameIsSet;
- m_evaluator->stringValue(dependsItem, StringConstants::nameProperty(), QString(),
- &nameIsSet);
-
- // The second condition is for the case where the dependency comes from an Export item
- // that has itself been normalized in the mean time.
- if (nameIsSet && !dependsItem->variantProperty(StringConstants::nameProperty())) {
- throw ErrorInfo(Tr::tr("The 'productTypes' and 'name' properties are mutually "
- "exclusive."), dependsItem->location());
- }
-
- bool submodulesPropertySet;
- m_evaluator->stringListValue( dependsItem, StringConstants::submodulesProperty(),
- &submodulesPropertySet);
- if (submodulesPropertySet) {
- throw ErrorInfo(Tr::tr("The 'productTypes' and 'subModules' properties are "
- "mutually exclusive."), dependsItem->location());
- }
-
- // We ignore the "limitToSubProject" property for dependencies from Export items,
- // because we cannot make it work consistently, as the importing product is not
- // yet known when normalizing via an Export item.
- const bool limitToSubProject = dependsContext.parentItem->type() == ItemType::Product
- && m_evaluator->boolValue(dependsItem,
- StringConstants::limitToSubProjectProperty());
- static const auto hasSameSubProject
- = [](const ProductContext &product, const ProductContext &other) {
- for (const Item *otherParent = other.item->parent(); otherParent;
- otherParent = otherParent->parent()) {
- if (otherParent == product.item->parent())
- return true;
- }
- return false;
- };
- std::vector<const ProductContext *> matchingProducts;
- for (const FileTag &typeTag : productTypes) {
- const auto range = m_productsByType.equal_range(typeTag);
- for (auto it = range.first; it != range.second; ++it) {
- if (it->second != product
- && (!product || it->second->name != product->name)
- && (!limitToSubProject || hasSameSubProject(*product, *it->second))) {
- matchingProducts.push_back(it->second);
- }
- }
- }
- if (matchingProducts.empty()) {
- qCDebug(lcModuleLoader) << "Depends.productTypes does not match anything."
- << dependsItem->location();
- dependsItemsToRemove.push_back(dependsItem);
- continue;
- }
- if (dependsContext.parentItem->type() != ItemType::Export)
- deferredDependsItems.push_back(dependsItem);
- for (std::size_t i = 1; i < matchingProducts.size(); ++i) {
- Item * const dependsClone = dependsItem->clone();
- dependsClone->setProperty(StringConstants::nameProperty(),
- VariantValue::create(matchingProducts.at(i)->name));
- dependsItemsToAdd.push_back(dependsClone);
- if (dependsContext.parentItem->type() != ItemType::Export)
- deferredDependsItems.push_back(dependsClone);
-
- }
- dependsItem->setProperty(StringConstants::nameProperty(),
- VariantValue::create(matchingProducts.front()->name));
- }
- }
- for (Item * const newDependsItem : dependsItemsToAdd)
- Item::addChild(dependsContext.parentItem, newDependsItem);
- for (Item * const dependsItem : dependsItemsToRemove)
- Item::removeChild(dependsContext.parentItem, dependsItem);
- if (!deferredDependsItems.empty()) {
- auto &allDeferredDependsItems
- = product->deferredDependsItems[dependsContext.exportingProductItem];
- allDeferredDependsItems.insert(allDeferredDependsItems.end(), deferredDependsItems.cbegin(),
- deferredDependsItems.cend());
- }
-}
-
-void ModuleLoader::adjustDependenciesForMultiplexing(const TopLevelProjectContext &tlp)
-{
- for (const ProjectContext * const project : tlp.projects) {
- for (const ProductContext &product : project->products)
- adjustDependenciesForMultiplexing(product);
- }
-}
-
-void ModuleLoader::adjustDependenciesForMultiplexing(const ModuleLoader::ProductContext &product)
-{
- for (Item *dependsItem : product.item->children()) {
- if (dependsItem->type() == ItemType::Depends)
- adjustDependenciesForMultiplexing(product, dependsItem);
- }
-}
-
-void ModuleLoader::adjustDependenciesForMultiplexing(const ProductContext &product,
- Item *dependsItem)
-{
- const QString name = m_evaluator->stringValue(dependsItem, StringConstants::nameProperty());
- const bool productIsMultiplexed = !product.multiplexConfigurationId.isEmpty();
- if (name == product.name) {
- QBS_CHECK(!productIsMultiplexed); // This product must be an aggregator.
- return;
- }
-
- bool profilesPropertyIsSet;
- const QStringList profiles = m_evaluator->stringListValue(dependsItem,
- StringConstants::profilesProperty(), &profilesPropertyIsSet);
-
- const auto productRange = m_productsByName.equal_range(name);
- if (productRange.first == productRange.second) {
- // Dependency is a module. Nothing to adjust.
- return;
- }
-
- std::vector<const ProductContext *> multiplexedDependencies;
- bool hasNonMultiplexedDependency = false;
- for (auto it = productRange.first; it != productRange.second; ++it) {
- if (!it->second->multiplexConfigurationId.isEmpty())
- multiplexedDependencies.push_back(it->second);
- else
- hasNonMultiplexedDependency = true;
- }
- bool hasMultiplexedDependencies = !multiplexedDependencies.empty();
-
- // These are the allowed cases:
- // (1) Normal dependency with no multiplexing whatsoever.
- // (2) Both product and dependency are multiplexed.
- // (2a) The profiles property is not set, we want to depend on the best
- // matching variant.
- // (2b) The profiles property is set, we want to depend on all variants
- // with a matching profile.
- // (3) The product is not multiplexed, but the dependency is.
- // (3a) The profiles property is not set, the dependency has an aggregator.
- // We want to depend on the aggregator.
- // (3b) The profiles property is not set, the dependency does not have an
- // aggregator. We want to depend on all the multiplexed variants.
- // (3c) The profiles property is set, we want to depend on all variants
- // with a matching profile regardless of whether an aggregator exists or not.
- // (4) The product is multiplexed, but the dependency is not. We don't have to adapt
- // any Depends items.
- // (5) The product is a "shadow product". In that case, we know which product
- // it should have a dependency on, and we make sure we depend on that.
-
- // (1) and (4)
- if (!hasMultiplexedDependencies)
- return;
-
- // (3a)
- if (!productIsMultiplexed && hasNonMultiplexedDependency && !profilesPropertyIsSet)
- return;
-
- QStringList multiplexIds;
- const ShadowProductInfo shadowProductInfo = getShadowProductInfo(product);
- const bool isShadowProduct = shadowProductInfo.first && shadowProductInfo.second == name;
- const auto productMultiplexConfig =
- MultiplexInfo::multiplexIdToVariantMap(product.multiplexConfigurationId);
-
- for (const ProductContext *dependency : multiplexedDependencies) {
- const bool depMatchesShadowProduct = isShadowProduct
- && dependency->item == product.item->parent();
- const QString depMultiplexId = dependency->multiplexConfigurationId;
- if (depMatchesShadowProduct) { // (5)
- dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(),
- VariantValue::create(depMultiplexId));
- return;
- }
- if (productIsMultiplexed && !profilesPropertyIsSet) { // 2a
- if (dependency->multiplexConfigurationId == product.multiplexConfigurationId) {
- const ValuePtr &multiplexId = product.item->property(
- StringConstants::multiplexConfigurationIdProperty());
- dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(),
- multiplexId);
- return;
-
- }
- // Otherwise collect partial matches and decide later
- const auto dependencyMultiplexConfig = MultiplexInfo::multiplexIdToVariantMap(
- dependency->multiplexConfigurationId);
-
- if (multiplexConfigurationIntersects(dependencyMultiplexConfig, productMultiplexConfig))
- multiplexIds << dependency->multiplexConfigurationId;
- } else {
- // (2b), (3b) or (3c)
- const bool profileMatch = !profilesPropertyIsSet || profiles.empty()
- || profiles.contains(dependency->profileName);
- if (profileMatch)
- multiplexIds << depMultiplexId;
- }
- }
- if (multiplexIds.empty()) {
- const QString productName = ResolvedProduct::fullDisplayName(
- product.name, product.multiplexConfigurationId);
- throw ErrorInfo(Tr::tr("Dependency from product '%1' to product '%2' not fulfilled. "
- "There are no eligible multiplex candidates.").arg(productName,
- name),
- dependsItem->location());
- }
-
- // In case of (2a), at most 1 match is allowed
- if (productIsMultiplexed && !profilesPropertyIsSet && multiplexIds.size() > 1) {
- const QString productName = ResolvedProduct::fullDisplayName(
- product.name, product.multiplexConfigurationId);
- QStringList candidateNames;
- for (const auto &id : qAsConst(multiplexIds))
- candidateNames << ResolvedProduct::fullDisplayName(name, id);
- throw ErrorInfo(Tr::tr("Dependency from product '%1' to product '%2' is ambiguous. "
- "Eligible multiplex candidates: %3.").arg(
- productName, name, candidateNames.join(QLatin1String(", "))),
- dependsItem->location());
- }
-
- dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(),
- VariantValue::create(multiplexIds));
-}
-
-void ModuleLoader::prepareProduct(ProjectContext *projectContext, Item *productItem)
-{
- AccumulatingTimer timer(m_parameters.logElapsedTime()
- ? &m_elapsedTimePrepareProducts : nullptr);
- checkCancelation();
- qCDebug(lcModuleLoader) << "prepareProduct" << productItem->file()->filePath();
-
- ProductContext productContext;
- productContext.item = productItem;
- productContext.project = projectContext;
- productContext.name = m_evaluator->stringValue(productItem, StringConstants::nameProperty());
- QBS_CHECK(!productContext.name.isEmpty());
- const ItemValueConstPtr qbsItemValue = productItem->itemProperty(StringConstants::qbsModule());
- if (!!qbsItemValue && qbsItemValue->item()->hasProperty(StringConstants::profileProperty())) {
- qbsItemValue->item()->setProperty(StringConstants::nameProperty(),
- VariantValue::create(StringConstants::nameProperty()));
- auto evaluateQbsProfileProperty = [this](const Item::Module &qbsModule) {
- return m_evaluator->stringValue(qbsModule.item,
- StringConstants::profileProperty(), QString());
- };
- productContext.profileName
- = callWithTemporaryBaseModule<QString>(&productContext,
- evaluateQbsProfileProperty);
- } else {
- productContext.profileName = m_parameters.topLevelProfile();
- }
- productContext.multiplexConfigurationId = m_evaluator->stringValue(
- productItem, StringConstants::multiplexConfigurationIdProperty());
- QBS_CHECK(!productContext.profileName.isEmpty());
- const auto it = projectContext->result->profileConfigs.constFind(productContext.profileName);
- if (it == projectContext->result->profileConfigs.constEnd()) {
- const Profile profile(productContext.profileName, m_settings.get(), m_localProfiles);
- if (!profile.exists()) {
- ErrorInfo error(Tr::tr("Profile '%1' does not exist.").arg(profile.name()),
- productItem->location());
- handleProductError(error, &productContext);
- return;
- }
- const QVariantMap buildConfig = SetupProjectParameters::expandedBuildConfiguration(
- profile, m_parameters.configurationName());
- productContext.moduleProperties = SetupProjectParameters::finalBuildConfigurationTree(
- buildConfig, m_parameters.overriddenValues());
- projectContext->result->profileConfigs.insert(productContext.profileName,
- productContext.moduleProperties);
- } else {
- productContext.moduleProperties = it.value().toMap();
- }
- initProductProperties(productContext);
-
- ItemValuePtr itemValue = ItemValue::create(productItem);
- productContext.scope = Item::create(m_pool, ItemType::Scope);
- productContext.scope->setProperty(StringConstants::productVar(), itemValue);
- productContext.scope->setFile(productItem->file());
- productContext.scope->setScope(productContext.project->scope);
-
- const bool hasExportItems = mergeExportItems(productContext);
-
- setScopeForDescendants(productItem, productContext.scope);
-
- projectContext->products.push_back(productContext);
-
- if (!hasExportItems || getShadowProductInfo(productContext).first)
- return;
-
- // This "shadow product" exists only to pull in a dependency on the actual product
- // and nothing else, thus providing us with the pure environment that we need to
- // evaluate the product's exported properties in isolation in the project resolver.
- Item * const importer = Item::create(productItem->pool(), ItemType::Product);
- importer->setProperty(QStringLiteral("name"),
- VariantValue::create(StringConstants::shadowProductPrefix()
- + productContext.name));
- importer->setFile(productItem->file());
- importer->setLocation(productItem->location());
- importer->setScope(projectContext->scope);
- importer->setupForBuiltinType(m_logger);
- Item * const dependsItem = Item::create(productItem->pool(), ItemType::Depends);
- dependsItem->setProperty(QStringLiteral("name"), VariantValue::create(productContext.name));
- dependsItem->setProperty(QStringLiteral("required"), VariantValue::create(false));
- dependsItem->setFile(importer->file());
- dependsItem->setLocation(importer->location());
- dependsItem->setupForBuiltinType(m_logger);
- Item::addChild(importer, dependsItem);
- Item::addChild(productItem, importer);
- prepareProduct(projectContext, importer);
-}
-
-void ModuleLoader::setupProductDependencies(ProductContext *productContext,
- const Set<DeferredDependsContext> &deferredDependsContext)
-{
- if (m_dependencyResolvingPass == 2) {
- for (const DeferredDependsContext &ctx : deferredDependsContext)
- normalizeDependencies(productContext, ctx);
- for (const auto &deferralData : productContext->deferredDependsItems) {
- for (Item * const deferredDependsItem : deferralData.second) {
-
- // Dependencies from Export items are handled in addProductModuleDependencies().
- if (deferredDependsItem->parent() == productContext->item)
- adjustDependenciesForMultiplexing(*productContext, deferredDependsItem);
- }
- }
- }
- AccumulatingTimer timer(m_parameters.logElapsedTime()
- ? &m_elapsedTimeProductDependencies : nullptr);
- checkCancelation();
- Item *item = productContext->item;
- qCDebug(lcModuleLoader) << "setupProductDependencies" << productContext->name
- << productContext->item->location();
-
- if (m_dependencyResolvingPass == 1)
- setSearchPathsForProduct(productContext);
-
- // Module providers may push some extra search paths which we will be cleared
- // by this SearchPathsManager. However, they will be also added to productContext->searchPaths
- // so second pass will take that into account
- SearchPathsManager searchPathsManager(m_reader.get(), productContext->searchPaths);
-
- DependsContext dependsContext;
- dependsContext.product = productContext;
- dependsContext.productDependencies = &productContext->info.usedProducts;
- resolveDependencies(&dependsContext, item, productContext);
- if (m_dependencyResolvingPass == 2
- || !containsKey(m_productsWithDeferredDependsItems, productContext)) {
- addProductModuleDependencies(productContext);
- }
- productContext->project->result->productInfos[item] = productContext->info;
-}
-
-// Leaf modules first.
-// TODO: Can this be merged with addTransitiveDependencies? Looks suspiciously similar.
-void ModuleLoader::createSortedModuleList(const Item::Module &parentModule, Item::Modules &modules)
-{
- if (std::find_if(modules.cbegin(), modules.cend(),
- [parentModule](const Item::Module &m) { return m.name == parentModule.name;})
- != modules.cend()) {
- return;
- }
- for (const Item::Module &dep : parentModule.item->modules())
- createSortedModuleList(dep, modules);
- modules.push_back(parentModule);
-}
-
-Item::Modules ModuleLoader::modulesSortedByDependency(const Item *productItem)
-{
- QBS_CHECK(productItem->type() == ItemType::Product);
- Item::Modules sortedModules;
- const Item::Modules &unsortedModules = productItem->modules();
- for (const Item::Module &module : unsortedModules)
- createSortedModuleList(module, sortedModules);
- QBS_CHECK(sortedModules.size() == unsortedModules.size());
-
- // Make sure the top-level items stay the same.
- for (Item::Module &s : sortedModules) {
- for (const Item::Module &u : unsortedModules) {
- if (s.name == u.name) {
- s.item = u.item;
- break;
- }
- }
- }
- return sortedModules;
-}
-
-
-template<typename T> bool insertIntoSet(Set<T> &set, const T &value)
-{
- const auto insertionResult = set.insert(value);
- return insertionResult.second;
-}
-
-void ModuleLoader::setupReverseModuleDependencies(const Item::Module &module,
- ModuleDependencies &deps,
- QualifiedIdSet &seenModules)
-{
- if (!insertIntoSet(seenModules, module.name))
- return;
- for (const Item::Module &m : module.item->modules()) {
- deps[m.name].insert(module.name);
- setupReverseModuleDependencies(m, deps, seenModules);
- }
-}
-
-ModuleLoader::ModuleDependencies ModuleLoader::setupReverseModuleDependencies(const Item *product)
-{
- ModuleDependencies deps;
- QualifiedIdSet seenModules;
- for (const Item::Module &m : product->modules())
- setupReverseModuleDependencies(m, deps, seenModules);
- return deps;
-}
-
-void ModuleLoader::handleProduct(ModuleLoader::ProductContext *productContext)
-{
- AccumulatingTimer timer(m_parameters.logElapsedTime() ? &m_elapsedTimeHandleProducts : nullptr);
- if (productContext->info.delayedError.hasError())
- return;
-
- Item * const item = productContext->item;
-
- m_reader->setExtraSearchPathsStack(productContext->project->searchPathsStack);
- SearchPathsManager searchPathsManager(m_reader.get(), productContext->searchPaths);
- addTransitiveDependencies(productContext);
-
- // It is important that dependent modules are merged after their dependency, because
- // the dependent module's merger potentially needs to replace module items that were
- // set by the dependency module's merger (namely, scopes of defining items; see
- // ModuleMerger::replaceItemInScopes()).
- Item::Modules topSortedModules = modulesSortedByDependency(item);
- ModuleMerger::merge(m_logger, item, productContext->name, &topSortedModules);
-
- // Re-sort the modules by name. This is more stable; see QBS-818.
- // The list of modules in the product now has the same order as before,
- // only the items have been replaced by their merged counterparts.
- Item::Modules lexicographicallySortedModules = topSortedModules;
- std::sort(lexicographicallySortedModules.begin(), lexicographicallySortedModules.end());
- item->setModules(lexicographicallySortedModules);
-
- for (const Item::Module &module : topSortedModules) {
- if (!module.item->isPresentModule())
- continue;
- try {
- m_probesResolver->resolveProbes(productContext, 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(
- m_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(productContext, module, error);
- if (productContext->info.delayedError.hasError())
- return;
- }
- }
-
- m_probesResolver->resolveProbes(productContext, item);
-
- // Module validation must happen in an extra pass, after all Probes have been resolved.
- EvalCacheEnabler cacheEnabler(m_evaluator);
- for (const Item::Module &module : topSortedModules) {
- if (!module.item->isPresentModule())
- continue;
- try {
- m_evaluator->boolValue(module.item, StringConstants::validateProperty());
- for (const auto &dep : module.item->modules()) {
- if (dep.requiredValue && !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(productContext, module, error);
- if (productContext->info.delayedError.hasError())
- return;
- }
- }
-
- if (!checkItemCondition(item)) {
- const auto &exportsData = productContext->project->topLevelProject->productModules;
- for (auto it = exportsData.find(productContext->name);
- it != exportsData.end() && it.key() == productContext->name; ++it) {
- if (it.value().multiplexId == productContext->multiplexConfigurationId) {
- createNonPresentModule(productContext->name, QStringLiteral("disabled"),
- it.value().exportItem);
- break;
- }
- }
- }
-
- checkDependencyParameterDeclarations(productContext);
- copyGroupsFromModulesToProduct(*productContext);
-
- ModuleDependencies reverseModuleDeps;
- for (Item * const child : item->children()) {
- if (child->type() == ItemType::Group) {
- if (reverseModuleDeps.empty())
- reverseModuleDeps = setupReverseModuleDependencies(item);
- handleGroup(productContext, child, reverseModuleDeps);
- }
- }
- productContext->project->result->productInfos[item] = productContext->info;
-}
-
-static Item *rootPrototype(Item *item)
-{
- Item *modulePrototype = item;
- while (modulePrototype->prototype())
- modulePrototype = modulePrototype->prototype();
- return modulePrototype;
-}
-
-class DependencyParameterDeclarationCheck
-{
-public:
- DependencyParameterDeclarationCheck(const QString &productName, const Item *productItem,
- const QHash<const Item *, Item::PropertyDeclarationMap> &decls)
- : m_productName(productName), m_productItem(productItem), m_parameterDeclarations(decls)
- {
- }
-
- void operator()(const QVariantMap &parameters) const
- {
- check(parameters, QualifiedId());
- }
-
-private:
- void check(const QVariantMap &parameters, const QualifiedId &moduleName) const
- {
- for (auto it = parameters.begin(); it != parameters.end(); ++it) {
- if (it.value().userType() == QMetaType::QVariantMap) {
- check(it.value().toMap(), QualifiedId(moduleName) << it.key());
- } else {
- const auto &deps = m_productItem->modules();
- auto m = std::find_if(deps.begin(), deps.end(),
- [&moduleName] (const Item::Module &module) {
- return module.name == moduleName;
- });
-
- if (m == deps.end()) {
- const QualifiedId fullName = QualifiedId(moduleName) << it.key();
- throw ErrorInfo(Tr::tr("Cannot set parameter '%1', "
- "because '%2' does not have a dependency on '%3'.")
- .arg(fullName.toString(), m_productName, moduleName.toString()),
- m_productItem->location());
- }
-
- auto decls = m_parameterDeclarations.value(rootPrototype(m->item));
-
- if (!decls.contains(it.key())) {
- const QualifiedId fullName = QualifiedId(moduleName) << it.key();
- throw ErrorInfo(Tr::tr("Parameter '%1' is not declared.")
- .arg(fullName.toString()), m_productItem->location());
- }
- }
- }
- }
-
- bool moduleExists(const QualifiedId &name) const
- {
- const auto &deps = m_productItem->modules();
- return any_of(deps, [&name](const Item::Module &module) {
- return module.name == name;
- });
- }
-
- const QString &m_productName;
- const Item *m_productItem;
- const QHash<const Item *, Item::PropertyDeclarationMap> &m_parameterDeclarations;
-};
-
-void ModuleLoader::checkDependencyParameterDeclarations(const ProductContext *productContext) const
-{
- DependencyParameterDeclarationCheck dpdc(productContext->name, productContext->item,
- m_parameterDeclarations);
- for (const Item::Module &dep : productContext->item->modules()) {
- if (!dep.parameters.empty())
- dpdc(dep.parameters);
- }
-}
-
-void ModuleLoader::handleModuleSetupError(ModuleLoader::ProductContext *productContext,
- const Item::Module &module, const ErrorInfo &error)
-{
- if (module.required) {
- handleProductError(error, productContext);
- } else {
- qCDebug(lcModuleLoader()) << "non-required module" << module.name.toString()
- << "found, but not usable in product" << productContext->name
- << error.toString();
- createNonPresentModule(module.name.toString(), QStringLiteral("failed validation"),
- module.item);
- }
-}
-
-void ModuleLoader::initProductProperties(const ProductContext &product)
-{
- QString buildDir = ResolvedProduct::deriveBuildDirectoryName(product.name,
- product.multiplexConfigurationId);
- buildDir = FileInfo::resolvePath(product.project->topLevelProject->buildDirectory, buildDir);
- product.item->setProperty(StringConstants::buildDirectoryProperty(),
- VariantValue::create(buildDir));
- const QString sourceDir = QFileInfo(product.item->file()->filePath()).absolutePath();
- product.item->setProperty(StringConstants::sourceDirectoryProperty(),
- VariantValue::create(sourceDir));
-}
-
-void ModuleLoader::handleSubProject(ModuleLoader::ProjectContext *projectContext, Item *projectItem,
- const Set<QString> &referencedFilePaths)
-{
- qCDebug(lcModuleLoader) << "handleSubProject" << projectItem->file()->filePath();
-
- Item * const propertiesItem = projectItem->child(ItemType::PropertiesInSubProject);
- if (!checkItemCondition(projectItem))
- return;
- if (propertiesItem) {
- propertiesItem->setScope(projectItem);
- if (!checkItemCondition(propertiesItem))
- return;
- }
-
- Item *loadedItem;
- QString subProjectFilePath;
- try {
- const QString projectFileDirPath = FileInfo::path(projectItem->file()->filePath());
- const QString relativeFilePath
- = m_evaluator->stringValue(projectItem, StringConstants::filePathProperty());
- subProjectFilePath = FileInfo::resolvePath(projectFileDirPath, relativeFilePath);
- if (referencedFilePaths.contains(subProjectFilePath))
- throw ErrorInfo(Tr::tr("Cycle detected while loading subproject file '%1'.")
- .arg(relativeFilePath), projectItem->location());
- loadedItem = loadItemFromFile(subProjectFilePath, projectItem->location());
- } catch (const ErrorInfo &error) {
- if (m_parameters.productErrorMode() == ErrorHandlingMode::Strict)
- throw;
- m_logger.printWarning(error);
- return;
- }
-
- loadedItem = wrapInProjectIfNecessary(loadedItem);
- const bool inheritProperties = m_evaluator->boolValue(
- projectItem, StringConstants::inheritPropertiesProperty());
-
- if (inheritProperties)
- copyProperties(projectItem->parent(), loadedItem);
- if (propertiesItem) {
- const Item::PropertyMap &overriddenProperties = propertiesItem->properties();
- for (Item::PropertyMap::ConstIterator it = overriddenProperties.constBegin();
- it != overriddenProperties.constEnd(); ++it) {
- loadedItem->setProperty(it.key(), it.value());
- }
- }
-
- Item::addChild(projectItem, loadedItem);
- projectItem->setScope(projectContext->scope);
- handleProject(projectContext->result, projectContext->topLevelProject, loadedItem,
- Set<QString>(referencedFilePaths) << subProjectFilePath);
-}
-
-QList<Item *> ModuleLoader::loadReferencedFile(const QString &relativePath,
- const CodeLocation &referencingLocation,
- const Set<QString> &referencedFilePaths,
- ModuleLoader::ProductContext &dummyContext)
-{
- QString absReferencePath = FileInfo::resolvePath(FileInfo::path(referencingLocation.filePath()),
- relativePath);
- if (FileInfo(absReferencePath).isDir()) {
- QString qbsFilePath;
-
- QDirIterator dit(absReferencePath, StringConstants::qbsFileWildcards());
- while (dit.hasNext()) {
- if (!qbsFilePath.isEmpty()) {
- throw ErrorInfo(Tr::tr("Referenced directory '%1' contains more than one "
- "qbs file.").arg(absReferencePath), referencingLocation);
- }
- qbsFilePath = dit.next();
- }
- if (qbsFilePath.isEmpty()) {
- throw ErrorInfo(Tr::tr("Referenced directory '%1' does not contain a qbs file.")
- .arg(absReferencePath), referencingLocation);
- }
- absReferencePath = qbsFilePath;
- }
- if (referencedFilePaths.contains(absReferencePath))
- throw ErrorInfo(Tr::tr("Cycle detected while referencing file '%1'.").arg(relativePath),
- referencingLocation);
- Item * const subItem = loadItemFromFile(absReferencePath, referencingLocation);
- if (subItem->type() != ItemType::Project && subItem->type() != ItemType::Product) {
- ErrorInfo error(Tr::tr("Item type should be 'Product' or 'Project', but is '%1'.")
- .arg(subItem->typeName()));
- error.append(Tr::tr("Item is defined here."), subItem->location());
- error.append(Tr::tr("File is referenced here."), referencingLocation);
- throw error;
- }
- subItem->setScope(dummyContext.project->scope);
- subItem->setParent(dummyContext.project->item);
- QList<Item *> loadedItems;
- loadedItems << subItem;
- if (subItem->type() == ItemType::Product) {
- handleProfileItems(subItem, dummyContext.project);
- loadedItems << multiplexProductItem(&dummyContext, subItem);
- }
- return loadedItems;
-}
-
-void ModuleLoader::handleGroup(ProductContext *productContext, Item *groupItem,
- const ModuleDependencies &reverseDepencencies)
-{
- checkCancelation();
- propagateModulesFromParent(productContext, groupItem, reverseDepencencies);
- checkItemCondition(groupItem);
- for (Item * const child : groupItem->children()) {
- if (child->type() == ItemType::Group)
- handleGroup(productContext, child, reverseDepencencies);
- }
-}
-
-void ModuleLoader::handleAllPropertyOptionsItems(Item *item)
-{
- QList<Item *> childItems = item->children();
- auto childIt = childItems.begin();
- while (childIt != childItems.end()) {
- Item * const child = *childIt;
- if (child->type() == ItemType::PropertyOptions) {
- handlePropertyOptions(child);
- childIt = childItems.erase(childIt);
- } else {
- handleAllPropertyOptionsItems(child);
- ++childIt;
- }
- }
- item->setChildren(childItems);
-}
-
-void ModuleLoader::handlePropertyOptions(Item *optionsItem)
-{
- const QString name = m_evaluator->stringValue(optionsItem, StringConstants::nameProperty());
- if (name.isEmpty()) {
- throw ErrorInfo(Tr::tr("PropertyOptions item needs a name property"),
- optionsItem->location());
- }
- const QString description = m_evaluator->stringValue(
- optionsItem, StringConstants::descriptionProperty());
- const auto removalVersion = Version::fromString(m_evaluator->stringValue(optionsItem,
- StringConstants::removalVersionProperty()));
- const auto allowedValues = m_evaluator->stringListValue(
- optionsItem, StringConstants::allowedValuesProperty());
-
- PropertyDeclaration decl = optionsItem->parent()->propertyDeclaration(name);
- if (!decl.isValid()) {
- decl.setName(name);
- decl.setType(PropertyDeclaration::Variant);
- }
- decl.setDescription(description);
- if (removalVersion.isValid()) {
- DeprecationInfo di(removalVersion, description);
- decl.setDeprecationInfo(di);
- }
- decl.setAllowedValues(allowedValues);
- const ValuePtr property = optionsItem->parent()->property(name);
- if (!property && !decl.isExpired()) {
- throw ErrorInfo(Tr::tr("PropertyOptions item refers to non-existing property '%1'")
- .arg(name), optionsItem->location());
- }
- if (property && decl.isExpired()) {
- ErrorInfo e(Tr::tr("Property '%1' was scheduled for removal in version %2, but "
- "is still present.")
- .arg(name, removalVersion.toString()),
- property->location());
- e.append(Tr::tr("Removal version for '%1' specified here.").arg(name),
- optionsItem->location());
- m_logger.printWarning(e);
- }
- optionsItem->parent()->setPropertyDeclaration(name, decl);
-}
-
-static void mergeProperty(Item *dst, const QString &name, const ValuePtr &value)
-{
- if (value->type() == Value::ItemValueType) {
- const ItemValueConstPtr itemValue = std::static_pointer_cast<ItemValue>(value);
- const Item * const valueItem = itemValue->item();
- Item * const subItem = dst->itemProperty(name, itemValue)->item();
- for (QMap<QString, ValuePtr>::const_iterator it = valueItem->properties().constBegin();
- it != valueItem->properties().constEnd(); ++it)
- mergeProperty(subItem, it.key(), it.value());
- } else {
- // If the property already exists set up the base value.
- if (value->type() == Value::JSSourceValueType) {
- const auto jsValue = static_cast<JSSourceValue *>(value.get());
- if (jsValue->isBuiltinDefaultValue())
- return;
- const ValuePtr baseValue = dst->property(name);
- if (baseValue) {
- QBS_CHECK(baseValue->type() == Value::JSSourceValueType);
- const JSSourceValuePtr jsBaseValue = std::static_pointer_cast<JSSourceValue>(
- baseValue->clone());
- jsValue->setBaseValue(jsBaseValue);
- std::vector<JSSourceValue::Alternative> alternatives = jsValue->alternatives();
- jsValue->clearAlternatives();
- for (JSSourceValue::Alternative &a : alternatives) {
- a.value->setBaseValue(jsBaseValue);
- jsValue->addAlternative(a);
- }
- }
- }
- dst->setProperty(name, value);
- }
-}
-
-bool ModuleLoader::checkExportItemCondition(Item *exportItem, const ProductContext &productContext)
-{
- class ScopeHandler {
- public:
- ScopeHandler(Item *exportItem, const ProductContext &productContext, Item **cachedScopeItem)
- : m_exportItem(exportItem)
- {
- if (!*cachedScopeItem)
- *cachedScopeItem = Item::create(exportItem->pool(), ItemType::Scope);
- Item * const scope = *cachedScopeItem;
- QBS_CHECK(productContext.item->file());
- scope->setFile(productContext.item->file());
- scope->setScope(productContext.item);
- productContext.project->scope->copyProperty(StringConstants::projectVar(), scope);
- productContext.scope->copyProperty(StringConstants::productVar(), scope);
- QBS_CHECK(!exportItem->scope());
- exportItem->setScope(scope);
- }
- ~ScopeHandler() { m_exportItem->setScope(nullptr); }
-
- private:
- Item * const m_exportItem;
- } scopeHandler(exportItem, productContext, &m_tempScopeItem);
- return checkItemCondition(exportItem);
-}
-
-void ModuleLoader::printProfilingInfo()
-{
- if (!m_parameters.logElapsedTime())
- return;
- m_logger.qbsLog(LoggerInfo, true) << "\t"
- << Tr::tr("Project file loading and parsing took %1.")
- .arg(elapsedTimeString(m_reader->elapsedTime()));
- m_logger.qbsLog(LoggerInfo, true) << "\t"
- << Tr::tr("Preparing products took %1.")
- .arg(elapsedTimeString(m_elapsedTimePrepareProducts));
- m_logger.qbsLog(LoggerInfo, true) << "\t"
- << Tr::tr("Setting up product dependencies took %1.")
- .arg(elapsedTimeString(m_elapsedTimeProductDependencies));
- m_logger.qbsLog(LoggerInfo, true) << "\t\t"
- << Tr::tr("Running module providers took %1.")
- .arg(elapsedTimeString(m_elapsedTimeModuleProviders));
- m_logger.qbsLog(LoggerInfo, true) << "\t\t"
- << Tr::tr("Setting up transitive product dependencies took %1.")
- .arg(elapsedTimeString(m_elapsedTimeTransitiveDependencies));
- m_logger.qbsLog(LoggerInfo, true) << "\t"
- << Tr::tr("Handling products took %1.")
- .arg(elapsedTimeString(m_elapsedTimeHandleProducts));
- m_probesResolver->printProfilingInfo();
- m_logger.qbsLog(LoggerInfo, true) << "\t"
- << Tr::tr("Property checking took %1.")
- .arg(elapsedTimeString(m_elapsedTimePropertyChecking));
-}
-
-static void mergeParameters(QVariantMap &dst, const QVariantMap &src)
-{
- for (auto it = src.begin(); it != src.end(); ++it) {
- if (it.value().userType() == QMetaType::QVariantMap) {
- QVariant &vdst = dst[it.key()];
- QVariantMap mdst = vdst.toMap();
- mergeParameters(mdst, it.value().toMap());
- vdst = mdst;
- } else {
- dst[it.key()] = it.value();
- }
- }
-}
-
-static void adjustParametersScopes(Item *item, Item *scope)
-{
- if (item->type() == ItemType::ModuleParameters) {
- item->setScope(scope);
- return;
- }
-
- for (const auto &value : item->properties()) {
- if (value->type() != Value::ItemValueType)
- continue;
- adjustParametersScopes(std::static_pointer_cast<ItemValue>(value)->item(), scope);
- }
-}
-
-bool ModuleLoader::mergeExportItems(const ProductContext &productContext)
-{
- std::vector<Item *> exportItems;
- QList<Item *> children = productContext.item->children();
-
- auto isExport = [](Item *item) { return item->type() == ItemType::Export; };
- std::copy_if(children.cbegin(), children.cend(), std::back_inserter(exportItems), isExport);
- qbs::Internal::removeIf(children, isExport);
-
- // Note that we do not return if there are no Export items: The "merged" item becomes the
- // "product module", which always needs to exist, regardless of whether the product sources
- // actually contain an Export item or not.
- if (!exportItems.empty())
- productContext.item->setChildren(children);
-
- Item *merged = Item::create(productContext.item->pool(), ItemType::Export);
- const QString &nameKey = StringConstants::nameProperty();
- const ValuePtr nameValue = VariantValue::create(productContext.name);
- merged->setProperty(nameKey, nameValue);
- Set<FileContextConstPtr> filesWithExportItem;
- ProductModuleInfo pmi;
- bool hasDependenciesOnProductType = false;
- for (Item * const exportItem : qAsConst(exportItems)) {
- checkCancelation();
- if (Q_UNLIKELY(filesWithExportItem.contains(exportItem->file())))
- throw ErrorInfo(Tr::tr("Multiple Export items in one product are prohibited."),
- exportItem->location());
- exportItem->setProperty(nameKey, nameValue);
- if (!checkExportItemCondition(exportItem, productContext))
- continue;
- filesWithExportItem += exportItem->file();
- for (Item * const child : exportItem->children()) {
- if (child->type() == ItemType::Parameters) {
- adjustParametersScopes(child, child);
- mergeParameters(pmi.defaultParameters,
- m_evaluator->scriptValue(child).toVariant().toMap());
- } else {
- if (child->type() == ItemType::Depends) {
- bool productTypesIsSet;
- m_evaluator->stringValue(child, StringConstants::productTypesProperty(),
- QString(), &productTypesIsSet);
- if (productTypesIsSet)
- hasDependenciesOnProductType = true;
- }
- Item::addChild(merged, child);
- }
- }
- const Item::PropertyDeclarationMap &decls = exportItem->propertyDeclarations();
- for (auto it = decls.constBegin(); it != decls.constEnd(); ++it) {
- const PropertyDeclaration &newDecl = it.value();
- const PropertyDeclaration &existingDecl = merged->propertyDeclaration(it.key());
- if (existingDecl.isValid() && existingDecl.type() != newDecl.type()) {
- ErrorInfo error(Tr::tr("Export item in inherited item redeclares property "
- "'%1' with different type.").arg(it.key()), exportItem->location());
- handlePropertyError(error, m_parameters, m_logger);
- }
- merged->setPropertyDeclaration(newDecl.name(), newDecl);
- }
- for (QMap<QString, ValuePtr>::const_iterator it = exportItem->properties().constBegin();
- it != exportItem->properties().constEnd(); ++it) {
- mergeProperty(merged, it.key(), it.value());
- }
- }
- merged->setFile(exportItems.empty()
- ? productContext.item->file() : exportItems.back()->file());
- merged->setLocation(exportItems.empty()
- ? productContext.item->location() : exportItems.back()->location());
- Item::addChild(productContext.item, merged);
- merged->setupForBuiltinType(m_logger);
- pmi.exportItem = merged;
- pmi.multiplexId = productContext.multiplexConfigurationId;
- productContext.project->topLevelProject->productModules.insert(productContext.name, pmi);
- if (hasDependenciesOnProductType)
- m_exportsWithDeferredDependsItems.insert(merged);
- return !exportItems.empty();
-}
-
-Item *ModuleLoader::loadItemFromFile(const QString &filePath,
- const CodeLocation &referencingLocation)
-{
- Item *item = m_reader->readFile(filePath, referencingLocation);
- handleAllPropertyOptionsItems(item);
- return item;
-}
-
-void ModuleLoader::handleProfileItems(Item *item, ProjectContext *projectContext)
-{
- const std::vector<Item *> profileItems = collectProfileItems(item, projectContext);
- for (Item * const profileItem : profileItems) {
- try {
- handleProfile(profileItem);
- } catch (const ErrorInfo &e) {
- handlePropertyError(e, m_parameters, m_logger);
- }
- }
-}
-
-std::vector<Item *> ModuleLoader::collectProfileItems(Item *item, ProjectContext *projectContext)
-{
- QList<Item *> childItems = item->children();
- std::vector<Item *> profileItems;
- Item * scope = item->type() == ItemType::Project ? projectContext->scope : nullptr;
- for (auto it = childItems.begin(); it != childItems.end();) {
- Item * const childItem = *it;
- if (childItem->type() == ItemType::Profile) {
- if (!scope) {
- const ItemValuePtr itemValue = ItemValue::create(item);
- scope = Item::create(m_pool, ItemType::Scope);
- scope->setProperty(StringConstants::productVar(), itemValue);
- scope->setFile(item->file());
- scope->setScope(projectContext->scope);
- }
- childItem->setScope(scope);
- profileItems.push_back(childItem);
- it = childItems.erase(it);
- } else {
- if (childItem->type() == ItemType::Product) {
- for (Item * const profileItem : collectProfileItems(childItem, projectContext))
- profileItems.push_back(profileItem);
- }
- ++it;
- }
- }
- if (!profileItems.empty())
- item->setChildren(childItems);
- return profileItems;
-}
-
-void ModuleLoader::evaluateProfileValues(const QualifiedId &namePrefix, Item *item,
- Item *profileItem, QVariantMap &values)
-{
- const Item::PropertyMap &props = item->properties();
- for (auto it = props.begin(); it != props.end(); ++it) {
- QualifiedId name = namePrefix;
- name << it.key();
- switch (it.value()->type()) {
- case Value::ItemValueType:
- evaluateProfileValues(name, std::static_pointer_cast<ItemValue>(it.value())->item(),
- profileItem, values);
- break;
- case Value::VariantValueType:
- values.insert(name.join(QLatin1Char('.')),
- std::static_pointer_cast<VariantValue>(it.value())->value());
- break;
- case Value::JSSourceValueType:
- item->setType(ItemType::ModulePrefix); // TODO: Introduce new item type
- if (item != profileItem)
- item->setScope(profileItem);
- values.insert(name.join(QLatin1Char('.')),
- m_evaluator->value(item, it.key()).toVariant());
- break;
- }
- }
-}
-
-void ModuleLoader::handleProfile(Item *profileItem)
-{
- QVariantMap values;
- evaluateProfileValues(QualifiedId(), profileItem, profileItem, values);
- const bool condition = values.take(StringConstants::conditionProperty()).toBool();
- if (!condition)
- return;
- const QString profileName = values.take(StringConstants::nameProperty()).toString();
- if (profileName.isEmpty())
- throw ErrorInfo(Tr::tr("Every Profile item must have a name"), profileItem->location());
- if (profileName == Profile::fallbackName()) {
- throw ErrorInfo(Tr::tr("Reserved name '%1' cannot be used for an actual profile.")
- .arg(profileName), profileItem->location());
- }
- if (m_localProfiles.contains(profileName)) {
- throw ErrorInfo(Tr::tr("Local profile '%1' redefined.").arg(profileName),
- profileItem->location());
- }
- m_localProfiles.insert(profileName, values);
-}
-
-void ModuleLoader::collectNameFromOverride(const QString &overrideString)
-{
- static const auto extract = [](const QString &prefix, const QString &overrideString) {
- if (!overrideString.startsWith(prefix))
- return QString();
- const int startPos = prefix.length();
- const int endPos = overrideString.lastIndexOf(StringConstants::dot());
- if (endPos == -1)
- return QString();
- return overrideString.mid(startPos, endPos - startPos);
- };
- const QString &projectName = extract(StringConstants::projectsOverridePrefix(), overrideString);
- if (!projectName.isEmpty()) {
- m_projectNamesUsedInOverrides.insert(projectName);
- return;
- }
- const QString &productName = extract(StringConstants::productsOverridePrefix(), overrideString);
- if (!productName.isEmpty()) {
- m_productNamesUsedInOverrides.insert(productName.left(
- productName.indexOf(StringConstants::dot())));
- return;
- }
-}
-
-void ModuleLoader::checkProjectNamesInOverrides(const ModuleLoader::TopLevelProjectContext &tlp)
-{
- for (const QString &projectNameInOverride : m_projectNamesUsedInOverrides) {
- if (m_disabledProjects.contains(projectNameInOverride))
- continue;
- bool found = false;
- for (const ProjectContext * const p : tlp.projects) {
- if (p->name == projectNameInOverride) {
- found = true;
- break;
- }
- }
- if (!found) {
- handlePropertyError(Tr::tr("Unknown project '%1' in property override.")
- .arg(projectNameInOverride), m_parameters, m_logger);
- }
- }
-}
-
-void ModuleLoader::checkProductNamesInOverrides()
-{
- for (const QString &productNameInOverride : m_productNamesUsedInOverrides) {
- if (m_erroneousProducts.contains(productNameInOverride))
- continue;
- bool found = false;
- for (const auto &kv : m_productsByName) {
- // In an override string such as "a.b.c:d, we cannot tell whether we have a product
- // "a" and a module "b.c" or a product "a.b" and a module "c", so we need to take
- // care not to emit false positives here.
- if (kv.first == productNameInOverride
- || kv.first.startsWith(productNameInOverride + StringConstants::dot())) {
- found = true;
- break;
- }
- }
- if (!found) {
- handlePropertyError(Tr::tr("Unknown product '%1' in property override.")
- .arg(productNameInOverride), m_parameters, m_logger);
- }
- }
-}
-
-void ModuleLoader::setSearchPathsForProduct(ModuleLoader::ProductContext *product)
-{
- product->searchPaths = readExtraSearchPaths(product->item);
- Settings settings(m_parameters.settingsDirectory());
- const QVariantMap profileContents = product->project->result->profileConfigs
- .value(product->profileName).toMap();
- const QStringList prefsSearchPaths = Preferences(&settings, profileContents).searchPaths();
- const QStringList &currentSearchPaths = m_reader->allSearchPaths();
- for (const QString &p : prefsSearchPaths) {
- if (!currentSearchPaths.contains(p) && FileInfo(p).exists())
- product->searchPaths << p;
- }
-}
-
-ModuleLoader::ShadowProductInfo ModuleLoader::getShadowProductInfo(
- const ModuleLoader::ProductContext &product) const
-{
- const bool isShadowProduct = product.name.startsWith(StringConstants::shadowProductPrefix());
- return std::make_pair(isShadowProduct, isShadowProduct
- ? product.name.mid(StringConstants::shadowProductPrefix().size())
- : QString());
-}
-
-void ModuleLoader::collectProductsByName(const TopLevelProjectContext &topLevelProject)
-{
- for (ProjectContext * const project : topLevelProject.projects) {
- for (ProductContext &product : project->products)
- m_productsByName.insert({ product.name, &product });
- }
-}
-
-void ModuleLoader::collectProductsByType(const ModuleLoader::TopLevelProjectContext &topLevelProject)
-{
- for (ProjectContext * const project : topLevelProject.projects) {
- for (ProductContext &product : project->products) {
- try {
- const FileTags productTags
- = m_evaluator->fileTagsValue(product.item, StringConstants::typeProperty());
- for (const FileTag &tag : productTags)
- m_productsByType.insert({ tag, &product});
- } catch (const ErrorInfo &) {
- qCDebug(lcModuleLoader) << "product" << product.name << "has complex type "
- " and won't get an entry in the type map";
- }
- }
- }
-}
-
-void ModuleLoader::propagateModulesFromParent(ProductContext *productContext, Item *groupItem,
- const ModuleDependencies &reverseDepencencies)
-{
- QBS_CHECK(groupItem->type() == ItemType::Group);
- QHash<QualifiedId, Item *> moduleInstancesForGroup;
-
- // Step 1: Instantiate the product's modules for the group.
- for (Item::Module m : groupItem->parent()->modules()) {
- Item *targetItem = moduleInstanceItem(groupItem, m.name);
- targetItem->setPrototype(m.item);
-
- Item * const moduleScope = Item::create(targetItem->pool(), ItemType::Scope);
- moduleScope->setFile(groupItem->file());
- moduleScope->setProperties(m.item->scope()->properties()); // "project", "product", ids
- moduleScope->setScope(groupItem);
- targetItem->setScope(moduleScope);
-
- targetItem->setFile(m.item->file());
-
- // "parent" should point to the group/artifact parent
- targetItem->setParent(groupItem->parent());
-
- targetItem->setOuterItem(m.item);
-
- m.item = targetItem;
- groupItem->addModule(m);
- moduleInstancesForGroup.insert(m.name, targetItem);
- }
-
- // Step 2: Make the inter-module references point to the instances created in step 1.
- for (const Item::Module &module : groupItem->modules()) {
- Item::Modules adaptedModules;
- const Item::Modules &oldModules = module.item->prototype()->modules();
- for (Item::Module depMod : oldModules) {
- depMod.item = moduleInstancesForGroup.value(depMod.name);
- adaptedModules << depMod;
- if (depMod.name.front() == module.name.front())
- continue;
- const ItemValuePtr &modulePrefix = groupItem->itemProperty(depMod.name.front());
- QBS_CHECK(modulePrefix);
- module.item->setProperty(depMod.name.front(), modulePrefix);
- }
- module.item->setModules(adaptedModules);
- }
-
- const QualifiedIdSet &propsSetInGroup = gatherModulePropertiesSetInGroup(groupItem);
- if (propsSetInGroup.empty())
- return;
- productContext->info.modulePropertiesSetInGroups
- .insert(std::make_pair(groupItem, propsSetInGroup));
-
- // Step 3: Adapt defining items in values. This is potentially necessary if module properties
- // get assigned on the group level.
- for (const Item::Module &module : groupItem->modules()) {
- const QualifiedIdSet &dependents = reverseDepencencies.value(module.name);
- Item::Modules dependentModules;
- dependentModules.reserve(int(dependents.size()));
- for (const QualifiedId &depName : dependents) {
- Item * const itemOfDependent = moduleInstancesForGroup.value(depName);
- QBS_CHECK(itemOfDependent);
- Item::Module depMod;
- depMod.name = depName;
- depMod.item = itemOfDependent;
- dependentModules << depMod;
- }
- adjustDefiningItemsInGroupModuleInstances(module, dependentModules);
- }
-}
-
-static Item *createReplacementForDefiningItem(const Item *definingItem, ItemType type)
-{
- Item *replacement = Item::create(definingItem->pool(), type);
- replacement->setLocation(definingItem->location());
- definingItem->copyProperty(StringConstants::nameProperty(), replacement);
- return replacement;
-}
-
-void ModuleLoader::adjustDefiningItemsInGroupModuleInstances(const Item::Module &module,
- const Item::Modules &dependentModules)
-{
- if (!module.item->isPresentModule())
- return;
-
- // There are three cases:
- // a) The defining item is the "main" module instance, i.e. the one instantiated in the
- // product directly (or a parent group).
- // b) The defining item refers to the module prototype (or the replacement of it
- // created in the module merger [for products] or in this function [for parent groups]).
- // c) The defining item is a different instance of the module, i.e. it was instantiated
- // in some other module.
-
- std::unordered_map<Item *, Item *> definingItemReplacements;
-
- Item *modulePrototype = rootPrototype(module.item->prototype());
- QBS_CHECK(modulePrototype->type() == ItemType::Module
- || modulePrototype->type() == ItemType::Export);
-
- const Item::PropertyDeclarationMap &propDecls = modulePrototype->propertyDeclarations();
- for (const auto &decl : propDecls) {
- const QString &propName = decl.name();
-
- // Module properties assigned in the group are not relevant here, as nothing
- // gets inherited in that case. In particular, setting a list property
- // overwrites the value from the product's (or parent group's) instance completely,
- // rather than appending to it (concatenation happens via outer.concat()).
- ValueConstPtr propValue = module.item->ownProperty(propName);
- if (propValue)
- continue;
-
- // Find the nearest prototype instance that has the value assigned.
- // The result is either an instance of a parent group (or the parent group's
- // parent group and so on) or the instance of the product or the module prototype.
- // In the latter case, we don't have to do anything.
- const Item *instanceWithProperty = module.item;
- int prototypeChainLen = 0;
- do {
- instanceWithProperty = instanceWithProperty->prototype();
- QBS_CHECK(instanceWithProperty);
- ++prototypeChainLen;
- propValue = instanceWithProperty->ownProperty(propName);
- } while (!propValue);
- QBS_CHECK(propValue);
-
- if (propValue->type() != Value::JSSourceValueType)
- continue;
-
- bool hasDefiningItem = false;
- for (ValueConstPtr v = propValue; v && !hasDefiningItem; v = v->next())
- hasDefiningItem = v->definingItem();
- if (!hasDefiningItem)
- continue;
-
- const ValuePtr clonedValue = propValue->clone();
- for (ValuePtr v = clonedValue; v; v = v->next()) {
- QBS_CHECK(v->definingItem());
-
- Item *& replacement = definingItemReplacements[v->definingItem()];
- static const QString caseA = QStringLiteral("__group_case_a");
- if (v->definingItem() == instanceWithProperty
- || v->definingItem()->variantProperty(caseA)) {
- // Case a)
- // For values whose defining item is the product's (or parent group's) instance,
- // we take its scope and replace references to module instances with those from the
- // group's instance. This handles cases like the following:
- // Product {
- // name: "theProduct"
- // aModule.listProp: [name, otherModule.stringProp]
- // Group { name: "theGroup"; otherModule.stringProp: name }
- // ...
- // }
- // In the above example, aModule.listProp is set to ["theProduct", "theGroup"]
- // (plus potential values from the prototype and other module instances,
- // which are different Value objects in the "next chain").
- if (!replacement) {
- replacement = createReplacementForDefiningItem(v->definingItem(),
- v->definingItem()->type());
- Item * const scope = Item::create(v->definingItem()->pool(), ItemType::Scope);
- scope->setProperties(module.item->scope()->properties());
- Item * const scopeScope
- = Item::create(v->definingItem()->pool(), ItemType::Scope);
- scopeScope->setProperties(v->definingItem()->scope()->scope()->properties());
- scope->setScope(scopeScope);
- replacement->setScope(scope);
- const Item::PropertyMap &groupScopeProperties
- = module.item->scope()->scope()->properties();
- for (auto propIt = groupScopeProperties.begin();
- propIt != groupScopeProperties.end(); ++propIt) {
- if (propIt.value()->type() == Value::ItemValueType)
- scopeScope->setProperty(propIt.key(), propIt.value());
- }
- }
- replacement->setPropertyDeclaration(propName, decl);
- replacement->setProperty(propName, v);
- replacement->setProperty(caseA, VariantValue::invalidValue());
- } else if (v->definingItem()->type() == ItemType::Module) {
- // Case b)
- // For values whose defining item is the module prototype, we change the scope to
- // the group's instance, analogous to what we do in
- // ModuleMerger::appendPrototypeValueToNextChain().
- QBS_CHECK(!decl.isScalar());
- QBS_CHECK(!v->next());
- Item *& replacement = definingItemReplacements[v->definingItem()];
- if (!replacement) {
- replacement = createReplacementForDefiningItem(v->definingItem(),
- ItemType::Module);
- replacement->setScope(module.item);
- }
- QBS_CHECK(!replacement->hasOwnProperty(caseA));
- qCDebug(lcModuleLoader).noquote().nospace()
- << "replacing defining item for prototype; module is "
- << module.name.toString() << module.item
- << ", property is " << propName
- << ", old defining item was " << v->definingItem()
- << " with scope" << v->definingItem()->scope()
- << ", new defining item is" << replacement
- << " with scope" << replacement->scope();
- if (v->type() == Value::JSSourceValueType) {
- qCDebug(lcModuleLoader) << "value source code is"
- << std::static_pointer_cast<JSSourceValue>(v)->sourceCode().toString();
- }
- replacement->setPropertyDeclaration(propName, decl);
- replacement->setProperty(propName, v);
- } else {
- // Look for instance scopes of other module instances in defining items and
- // replace the affected values.
- // This is case c) as introduced above. See ModuleMerger::replaceItemInScopes()
- // for a detailed explanation.
-
- QBS_CHECK(v->definingItem()->scope() && v->definingItem()->scope()->scope());
- bool found = false;
- for (const Item::Module &depMod : dependentModules) {
- const Item *depModPrototype = depMod.item->prototype();
- for (int i = 1; i < prototypeChainLen; ++i)
- depModPrototype = depModPrototype->prototype();
- if (v->definingItem()->scope()->scope() != depModPrototype)
- continue;
-
- found = true;
- Item *& replacement = definingItemReplacements[v->definingItem()];
- if (!replacement) {
- replacement = createReplacementForDefiningItem(v->definingItem(),
- v->definingItem()->type());
- replacement->setProperties(v->definingItem()->properties());
- for (const auto &decl : v->definingItem()->propertyDeclarations())
- replacement->setPropertyDeclaration(decl.name(), decl);
- replacement->setPrototype(v->definingItem()->prototype());
- replacement->setScope(Item::create(v->definingItem()->pool(),
- ItemType::Scope));
- replacement->scope()->setScope(depMod.item);
- }
- QBS_CHECK(!replacement->hasOwnProperty(caseA));
- qCDebug(lcModuleLoader) << "reset instance scope of module"
- << depMod.name.toString() << "in property"
- << propName << "of module" << module.name;
- }
- QBS_CHECK(found);
- }
- QBS_CHECK(replacement);
- v->setDefiningItem(replacement);
- }
- module.item->setProperty(propName, clonedValue);
- }
-}
-
-void ModuleLoader::resolveDependencies(DependsContext *dependsContext, Item *item,
- ProductContext *productContext)
-{
- QBS_CHECK(m_dependencyResolvingPass == 1 || m_dependencyResolvingPass == 2);
-
- if (!productContext || m_dependencyResolvingPass == 1) {
- const Item::Module baseModule = loadBaseModule(dependsContext->product, item);
- item->addModule(baseModule);
- }
-
- // Resolve all Depends items.
- ItemModuleList loadedModules;
- QList<Item *> dependsItemPerLoadedModule;
- ProductDependencies productDependencies;
- const auto handleDependsItem = [&](Item *child) {
- if (child->type() != ItemType::Depends)
- return;
-
- int lastModulesCount = loadedModules.size();
- try {
- resolveDependsItem(dependsContext, child->parent(), child, &loadedModules,
- &productDependencies);
- } catch (const ErrorInfo &e) {
- if (!productContext)
- throw;
- handleProductError(e, productContext);
- }
- for (int i = lastModulesCount; i < loadedModules.size(); ++i)
- dependsItemPerLoadedModule.push_back(child);
-
- };
- if (productContext && m_dependencyResolvingPass == 2) {
- for (const auto &deferData : productContext->deferredDependsItems) {
- dependsContext->exportingProductItem = deferData.first;
- for (Item * const dependsItem : deferData.second)
- handleDependsItem(dependsItem);
- }
- } else {
- for (Item * const child : item->children())
- handleDependsItem(child);
- }
- QBS_CHECK(loadedModules.size() == dependsItemPerLoadedModule.size());
-
- Item *lastDependsItem = nullptr;
- for (Item * const dependsItem : dependsItemPerLoadedModule) {
- if (dependsItem == lastDependsItem)
- continue;
- adjustParametersScopes(dependsItem, dependsItem);
- forwardParameterDeclarations(dependsItem, loadedModules);
- lastDependsItem = dependsItem;
- }
-
- for (int i = 0; i < loadedModules.size(); ++i) {
- Item::Module &module = loadedModules[i];
- mergeParameters(module.parameters, extractParameters(dependsItemPerLoadedModule.at(i)));
- item->addModule(module);
-
- const QString moduleName = module.name.toString();
- std::for_each(productDependencies.begin(), productDependencies.end(),
- [&module, &moduleName] (ModuleLoaderResult::ProductInfo::Dependency &dep) {
- if (dep.name == moduleName)
- dep.parameters = module.parameters;
- });
- }
-
- dependsContext->productDependencies->insert(
- dependsContext->productDependencies->end(),
- productDependencies.cbegin(), productDependencies.cend());
-}
-
-class RequiredChainManager
-{
-public:
- RequiredChainManager(std::vector<bool> &requiredChain, bool required)
- : m_requiredChain(requiredChain)
- {
- m_requiredChain.push_back(required);
- }
-
- ~RequiredChainManager() { m_requiredChain.pop_back(); }
-
-private:
- std::vector<bool> &m_requiredChain;
-};
-
-void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *parentItem,
- Item *dependsItem, ItemModuleList *moduleResults,
- ProductDependencies *productResults)
-{
- checkCancelation();
- if (!checkItemCondition(dependsItem)) {
- qCDebug(lcModuleLoader) << "Depends item disabled, ignoring.";
- return;
- }
- bool nameIsSet;
- const QString name = m_evaluator->stringValue(dependsItem, StringConstants::nameProperty(),
- QString(), &nameIsSet);
- bool submodulesPropertySet;
- const QStringList submodules = m_evaluator->stringListValue(
- dependsItem, StringConstants::submodulesProperty(), &submodulesPropertySet);
- if (submodules.empty() && submodulesPropertySet) {
- qCDebug(lcModuleLoader) << "Ignoring Depends item with empty submodules list.";
- return;
- }
- if (Q_UNLIKELY(submodules.size() > 1 && !dependsItem->id().isEmpty())) {
- QString msg = Tr::tr("A Depends item with more than one module cannot have an id.");
- throw ErrorInfo(msg, dependsItem->location());
- }
- const FallbackMode fallbackMode = m_parameters.fallbackProviderEnabled()
- && m_evaluator->boolValue(dependsItem, StringConstants::enableFallbackProperty())
- ? FallbackMode::Enabled : FallbackMode::Disabled;
-
- QList<QualifiedId> moduleNames;
- const QualifiedId nameParts = QualifiedId::fromString(name);
- if (submodules.empty()) {
- // Ignore explicit dependencies on the base module, which has already been loaded.
- if (name == StringConstants::qbsModule())
- return;
-
- moduleNames << nameParts;
- } else {
- for (const QString &submodule : submodules)
- moduleNames << nameParts + QualifiedId::fromString(submodule);
- }
-
- Item::Module result;
- bool productTypesIsSet;
- m_evaluator->stringValue(dependsItem, StringConstants::productTypesProperty(),
- QString(), &productTypesIsSet);
- if (m_dependencyResolvingPass == 1 && productTypesIsSet) {
- qCDebug(lcModuleLoader) << "queuing product" << dependsContext->product->name
- << "for a second dependencies resolving pass";
- m_productsWithDeferredDependsItems[dependsContext->product].insert(
- DeferredDependsContext(dependsContext->exportingProductItem, parentItem));
- return;
- }
-
- const bool isRequiredValue =
- m_evaluator->boolValue(dependsItem, StringConstants::requiredProperty());
- const bool isRequired = !productTypesIsSet
- && isRequiredValue
- && !contains(m_requiredChain, false);
- const Version minVersion = Version::fromString(
- m_evaluator->stringValue(dependsItem,
- StringConstants::versionAtLeastProperty()));
- const Version maxVersion = Version::fromString(
- m_evaluator->stringValue(dependsItem, StringConstants::versionBelowProperty()));
- const VersionRange versionRange(minVersion, maxVersion);
- QStringList multiplexConfigurationIds = m_evaluator->stringListValue(
- dependsItem,
- StringConstants::multiplexConfigurationIdsProperty());
- if (multiplexConfigurationIds.empty())
- multiplexConfigurationIds << QString();
-
- for (const QualifiedId &moduleName : qAsConst(moduleNames)) {
- // Don't load the same module twice. Duplicate Depends statements can easily
- // happen due to inheritance.
- const auto it = std::find_if(moduleResults->begin(), moduleResults->end(),
- [moduleName](const Item::Module &m) { return m.name == moduleName; });
- if (it != moduleResults->end()) {
- it->required = it->required || isRequired;
- it->requiredValue = it->requiredValue || isRequiredValue;
- it->fallbackEnabled = it->fallbackEnabled && fallbackMode == FallbackMode::Enabled;
- it->versionRange.narrowDown(versionRange);
- continue;
- }
-
- QVariantMap defaultParameters;
- Item *moduleItem = loadModule(dependsContext->product, dependsContext->exportingProductItem,
- parentItem, dependsItem->location(), dependsItem->id(),
- moduleName, multiplexConfigurationIds.first(), fallbackMode,
- isRequired, &result.isProduct, &defaultParameters);
- if (!moduleItem) {
- const QString productName = ResolvedProduct::fullDisplayName(
- dependsContext->product->name,
- dependsContext->product->multiplexConfigurationId);
- if (!multiplexConfigurationIds.first().isEmpty()) {
- const QString depName = ResolvedProduct::fullDisplayName(
- moduleName.toString(), multiplexConfigurationIds.first());
- throw ErrorInfo(Tr::tr("Dependency from product '%1' to product '%2' not "
- "fulfilled.").arg(productName, depName));
- }
- ErrorInfo e(Tr::tr("Dependency '%1' not found for product '%2'.")
- .arg(moduleName.toString(), productName), dependsItem->location());
- throw e;
- }
- if (result.isProduct && !m_dependsChain.empty() && !m_dependsChain.back().isProduct) {
- throw ErrorInfo(Tr::tr("Invalid dependency on product '%1': Modules cannot depend on "
- "products. You may want to turn your module into a product and "
- "add the dependency in that product's Export item.")
- .arg(moduleName.toString()), dependsItem->location());
- }
- qCDebug(lcModuleLoader) << "module loaded:" << moduleName.toString();
- result.name = moduleName;
- result.item = moduleItem;
- result.requiredValue = isRequiredValue;
- result.required = isRequired;
- result.fallbackEnabled = fallbackMode == FallbackMode::Enabled;
- result.parameters = defaultParameters;
- result.versionRange = versionRange;
- moduleResults->push_back(result);
- if (result.isProduct) {
- qCDebug(lcModuleLoader) << "product dependency loaded:" << moduleName.toString();
- bool profilesPropertyWasSet = false;
- QStringList profiles = m_evaluator->stringListValue(dependsItem,
- StringConstants::profilesProperty(),
- &profilesPropertyWasSet);
- if (profiles.empty()) {
- if (profilesPropertyWasSet)
- profiles.push_back(StringConstants::star());
- else
- profiles.push_back(QString());
- }
- for (const QString &profile : qAsConst(profiles)) {
- for (const QString &multiplexId : qAsConst(multiplexConfigurationIds)) {
- ModuleLoaderResult::ProductInfo::Dependency dependency;
- dependency.name = moduleName.toString();
- dependency.profile = profile;
- dependency.multiplexConfigurationId = multiplexId;
- dependency.isRequired = isRequired;
- productResults->push_back(dependency);
- }
- }
- }
- }
-}
-
-void ModuleLoader::forwardParameterDeclarations(const Item *dependsItem,
- const ItemModuleList &modules)
-{
- for (auto it = dependsItem->properties().begin(); it != dependsItem->properties().end(); ++it) {
- if (it.value()->type() != Value::ItemValueType)
- continue;
- forwardParameterDeclarations(it.key(),
- std::static_pointer_cast<ItemValue>(it.value())->item(),
- modules);
- }
-}
-
-void ModuleLoader::forwardParameterDeclarations(const QualifiedId &moduleName, Item *item,
- const ItemModuleList &modules)
-{
- auto it = std::find_if(modules.begin(), modules.end(), [&moduleName] (const Item::Module &m) {
- return m.name == moduleName;
- });
- if (it != modules.end()) {
- item->setPropertyDeclarations(m_parameterDeclarations.value(rootPrototype(it->item)));
- } else {
- for (auto it = item->properties().begin(); it != item->properties().end(); ++it) {
- if (it.value()->type() != Value::ItemValueType)
- continue;
- forwardParameterDeclarations(QualifiedId(moduleName) << it.key(),
- std::static_pointer_cast<ItemValue>(it.value())->item(),
- modules);
- }
- }
-}
-
-void ModuleLoader::resolveParameterDeclarations(const Item *module)
-{
- Item::PropertyDeclarationMap decls;
- const auto &moduleChildren = module->children();
- for (Item *param : moduleChildren) {
- if (param->type() != ItemType::Parameter)
- continue;
- const auto &paramDecls = param->propertyDeclarations();
- for (auto it = paramDecls.begin(); it != paramDecls.end(); ++it)
- decls.insert(it.key(), it.value());
- }
- m_parameterDeclarations.insert(module, decls);
-}
-
-static bool isItemValue(const ValuePtr &v)
-{
- return v->type() == Value::ItemValueType;
-}
-
-static Item::PropertyMap filterItemProperties(const Item::PropertyMap &properties)
-{
- Item::PropertyMap result;
- auto itEnd = properties.end();
- for (auto it = properties.begin(); it != itEnd; ++it) {
- if (isItemValue(it.value()))
- result.insert(it.key(), it.value());
- }
- return result;
-}
-
-static QVariantMap safeToVariant(const QScriptValue &v)
-{
- QVariantMap result;
- QScriptValueIterator it(v);
- while (it.hasNext()) {
- it.next();
- QScriptValue u = it.value();
- if (u.isError())
- throw ErrorInfo(u.toString());
- result[it.name()] = (u.isObject() && !u.isArray() && !u.isRegExp())
- ? safeToVariant(u) : it.value().toVariant();
- }
- return result;
-}
-
-QVariantMap ModuleLoader::extractParameters(Item *dependsItem) const
-{
- QVariantMap result;
- const Item::PropertyMap &itemProperties = filterItemProperties(
- rootPrototype(dependsItem)->properties());
- if (itemProperties.empty())
- return result;
-
- auto origProperties = dependsItem->properties();
- dependsItem->setProperties(itemProperties);
- QScriptValue sv = m_evaluator->scriptValue(dependsItem);
- try {
- result = safeToVariant(sv);
- } catch (const ErrorInfo &exception) {
- auto ei = exception;
- ei.prepend(Tr::tr("Error in dependency parameter."), dependsItem->location());
- throw ei;
- }
- dependsItem->setProperties(origProperties);
- return result;
-}
-
-[[noreturn]] static void throwModuleNamePrefixError(const QualifiedId &shortName,
- const QualifiedId &longName, const CodeLocation &codeLocation)
-{
- throw ErrorInfo(Tr::tr("The name of module '%1' is equal to the first component of the "
- "name of module '%2', which is not allowed")
- .arg(shortName.toString(), longName.toString()), codeLocation);
-}
-
-Item *ModuleLoader::moduleInstanceItem(Item *containerItem, const QualifiedId &moduleName)
-{
- QBS_CHECK(!moduleName.empty());
- Item *instance = containerItem;
- for (int i = 0; i < moduleName.size(); ++i) {
- const QString &moduleNameSegment = moduleName.at(i);
- const ValuePtr v = instance->ownProperty(moduleName.at(i));
- if (v && v->type() == Value::ItemValueType) {
- instance = std::static_pointer_cast<ItemValue>(v)->item();
- } else {
- const ItemType itemType = i < moduleName.size() - 1 ? ItemType::ModulePrefix
- : ItemType::ModuleInstance;
- auto newItem = Item::create(m_pool, itemType);
- instance->setProperty(moduleNameSegment, ItemValue::create(newItem));
- instance = newItem;
- }
- if (i < moduleName.size() - 1) {
- if (instance->type() == ItemType::ModuleInstance) {
- QualifiedId conflictingName = QStringList(moduleName.mid(0, i + 1));
- throwModuleNamePrefixError(conflictingName, moduleName, CodeLocation());
- }
- QBS_CHECK(instance->type() == ItemType::ModulePrefix);
- }
- }
- QBS_CHECK(instance != containerItem);
- return instance;
-}
-
-ModuleLoader::ProductModuleInfo *ModuleLoader::productModule(ProductContext *productContext,
- const QString &name, const QString &multiplexId, bool &productNameMatch)
-{
- auto &exportsData = productContext->project->topLevelProject->productModules;
- const auto firstIt = exportsData.find(name);
- productNameMatch = firstIt != exportsData.end();
- for (auto it = firstIt; it != exportsData.end() && it.key() == name; ++it) {
- if (it.value().multiplexId == multiplexId)
- return &it.value();
- }
- if (multiplexId.isEmpty() && firstIt != exportsData.end())
- return &firstIt.value();
- return nullptr;
-}
-
-ModuleLoader::ProductContext *ModuleLoader::product(ProjectContext *projectContext,
- const QString &name)
-{
- auto itEnd = projectContext->products.end();
- auto it = std::find_if(projectContext->products.begin(), itEnd,
- [&name] (const ProductContext &ctx) {
- return ctx.name == name;
- });
- return it == itEnd ? nullptr : &*it;
-}
-
-ModuleLoader::ProductContext *ModuleLoader::product(TopLevelProjectContext *tlpContext,
- const QString &name)
-{
- ProductContext *result = nullptr;
- for (auto prj : tlpContext->projects) {
- result = product(prj, name);
- if (result)
- break;
- }
- return result;
-}
-
-class ModuleLoader::DependsChainManager
-{
-public:
- DependsChainManager(std::vector<DependsChainEntry> &dependsChain, const QualifiedId &module,
- const CodeLocation &dependsLocation)
- : m_dependsChain(dependsChain)
- {
- const bool alreadyInChain = Internal::any_of(dependsChain,
- [&module](const DependsChainEntry &e) {
- return e.name == module;
- });
- if (alreadyInChain) {
- ErrorInfo error;
- error.append(Tr::tr("Cyclic dependencies detected:"));
- for (const DependsChainEntry &e : qAsConst(m_dependsChain))
- error.append(e.name.toString(), e.location);
- error.append(module.toString(), dependsLocation);
- throw error;
- }
- m_dependsChain.emplace_back(module, dependsLocation);
- }
-
- ~DependsChainManager() { m_dependsChain.pop_back(); }
-
-private:
- std::vector<DependsChainEntry> &m_dependsChain;
-};
-
-static bool isBaseModule(QStringView fullModuleName)
-{
- return fullModuleName == StringConstants::qbsModule();
-}
-
-class DelayedPropertyChanger
-{
-public:
- ~DelayedPropertyChanger()
- {
- applyNow();
- }
-
- void setLater(Item *item, const QString &name, const ValuePtr &value)
- {
- QBS_CHECK(m_item == nullptr);
- m_item = item;
- m_name = name;
- m_value = value;
- }
-
- void removeLater(Item *item, const QString &name)
- {
- QBS_CHECK(m_item == nullptr);
- m_item = item;
- m_name = name;
- }
-
- void applyNow()
- {
- if (!m_item || m_name.isEmpty())
- return;
- if (m_value)
- m_item->setProperty(m_name, m_value);
- else
- m_item->removeProperty(m_name);
- m_item = nullptr;
- m_name.clear();
- m_value.reset();
- }
-
-private:
- Item *m_item = nullptr;
- QString m_name;
- ValuePtr m_value;
-};
-
-Item *ModuleLoader::loadModule(ProductContext *productContext, Item *exportingProductItem,
- Item *item, const CodeLocation &dependsItemLocation,
- const QString &moduleId, const QualifiedId &moduleName,
- const QString &multiplexId, FallbackMode fallbackMode,
- bool isRequired, bool *isProductDependency,
- QVariantMap *defaultParameters)
-{
- qCDebug(lcModuleLoader) << "loadModule name:" << moduleName.toString() << "id:" << moduleId;
-
- RequiredChainManager requiredChainManager(m_requiredChain, isRequired);
- DependsChainManager dependsChainManager(m_dependsChain, moduleName, dependsItemLocation);
-
- Item *moduleInstance = moduleId.isEmpty()
- ? moduleInstanceItem(item, moduleName)
- : moduleInstanceItem(item, QStringList(moduleId));
- if (moduleInstance->scope())
- return moduleInstance; // already handled
-
- if (Q_UNLIKELY(moduleInstance->type() == ItemType::ModulePrefix)) {
- for (const Item::Module &m : item->modules()) {
- if (m.name.front() == moduleName.front())
- throwModuleNamePrefixError(moduleName, m.name, dependsItemLocation);
- }
- }
- QBS_CHECK(moduleInstance->type() == ItemType::ModuleInstance);
-
- // Prepare module instance for evaluating Module.condition.
- DelayedPropertyChanger delayedPropertyChanger;
- const QString &qbsModuleName = StringConstants::qbsModule();
- const auto fullName = moduleName.toString();
- if (!isBaseModule(fullName)) {
- ItemValuePtr qbsProp = productContext->item->itemProperty(qbsModuleName);
- if (qbsProp) {
- ValuePtr qbsModuleValue = moduleInstance->ownProperty(qbsModuleName);
- if (qbsModuleValue)
- delayedPropertyChanger.setLater(moduleInstance, qbsModuleName, qbsModuleValue);
- else
- delayedPropertyChanger.removeLater(moduleInstance, qbsModuleName);
- moduleInstance->setProperty(qbsModuleName, qbsProp);
- }
- }
-
- SearchPathsManager searchPathsManager(m_reader.get()); // paths can be added by providers
- Item *modulePrototype = nullptr;
- ProductModuleInfo * const pmi = productModule(productContext, fullName,
- multiplexId, *isProductDependency);
- if (pmi) {
- m_dependsChain.back().isProduct = true;
- modulePrototype = pmi->exportItem;
- if (defaultParameters)
- *defaultParameters = pmi->defaultParameters;
- } else if (!*isProductDependency) {
- modulePrototype = searchAndLoadModuleFile(productContext, dependsItemLocation,
- moduleName, fallbackMode, isRequired, moduleInstance);
- }
- delayedPropertyChanger.applyNow();
- if (!modulePrototype)
- return nullptr;
-
- searchPathsManager.reset(); // deps must be processed in a clean state
-
- instantiateModule(productContext, exportingProductItem, item, moduleInstance, modulePrototype,
- moduleName, pmi);
- return moduleInstance;
-}
-
-struct PrioritizedItem
-{
- PrioritizedItem(Item *item, int priority, int searchPathIndex)
- : item(item), priority(priority), searchPathIndex(searchPathIndex)
- {
- }
-
- Item *item = nullptr;
- int priority = 0;
- int searchPathIndex = 0;
-};
-
-static Item *chooseModuleCandidate(const std::vector<PrioritizedItem> &candidates,
- const QString &moduleName)
-{
- auto maxIt = std::max_element(candidates.begin(), candidates.end(),
- [] (const PrioritizedItem &a, const PrioritizedItem &b) {
- if (a.priority < b.priority)
- return true;
- if (a.priority > b.priority)
- return false;
- return a.searchPathIndex > b.searchPathIndex;
- });
-
- size_t nmax = std::count_if(candidates.begin(), candidates.end(),
- [maxIt] (const PrioritizedItem &i) {
- return i.priority == maxIt->priority && i.searchPathIndex == maxIt->searchPathIndex;
- });
-
- if (nmax > 1) {
- ErrorInfo e(Tr::tr("There is more than one equally prioritized candidate for module '%1'.")
- .arg(moduleName));
- for (size_t i = 0; i < candidates.size(); ++i) {
- const auto candidate = candidates.at(i);
- if (candidate.priority == maxIt->priority) {
- //: The %1 denotes the number of the candidate.
- e.append(Tr::tr("candidate %1").arg(i + 1), candidates.at(i).item->location());
- }
- }
- throw e;
- }
-
- return maxIt->item;
-}
-
-Item *ModuleLoader::searchAndLoadModuleFile(ProductContext *productContext,
- const CodeLocation &dependsItemLocation, const QualifiedId &moduleName,
- FallbackMode fallbackMode, bool isRequired, Item *moduleInstance)
-{
- auto existingPaths = findExistingModulePaths(m_reader->allSearchPaths(), moduleName);
-
- if (existingPaths.isEmpty()) { // no suitable names found, try to use providers
- AccumulatingTimer providersTimer(
- m_parameters.logElapsedTime() ? &m_elapsedTimeModuleProviders : nullptr);
- auto result = m_moduleProviderLoader->executeModuleProvider(
- *productContext,
- dependsItemLocation,
- moduleName,
- fallbackMode);
- if (result.providerAddedSearchPaths) {
- qCDebug(lcModuleLoader) << "Re-checking for module" << moduleName.toString()
- << "with newly added search paths from module provider";
- existingPaths = findExistingModulePaths(m_reader->allSearchPaths(), moduleName);
- }
- }
-
- const QString fullName = moduleName.toString();
- bool triedToLoadModule = false;
- std::vector<PrioritizedItem> candidates;
- candidates.reserve(size_t(existingPaths.size()));
- for (int i = 0; i < existingPaths.size(); ++i) {
- const QString &dirPath = existingPaths.at(i);
- QStringList &moduleFileNames = getModuleFileNames(dirPath);
- for (auto it = moduleFileNames.begin(); it != moduleFileNames.end(); ) {
- const QString &filePath = *it;
- const auto [module, triedToLoad] = loadModuleFile(
- productContext, fullName, filePath, moduleInstance);
- if (module)
- candidates.emplace_back(module, 0, i);
- if (!triedToLoad)
- it = moduleFileNames.erase(it);
- else
- ++it;
- triedToLoadModule = triedToLoadModule || triedToLoad;
- }
- }
-
- if (candidates.empty()) {
- if (!isRequired)
- return createNonPresentModule(fullName, QStringLiteral("not found"), nullptr);
- if (Q_UNLIKELY(triedToLoadModule)) {
- throw ErrorInfo(Tr::tr("Module %1 could not be loaded.").arg(fullName),
- dependsItemLocation);
- }
- return nullptr;
- }
-
- Item *moduleItem;
- if (candidates.size() == 1) {
- moduleItem = candidates.at(0).item;
- } else {
- for (auto &candidate : candidates) {
- candidate.priority = m_evaluator->intValue(candidate.item,
- StringConstants::priorityProperty(),
- candidate.priority);
- }
- moduleItem = chooseModuleCandidate(candidates, fullName);
- }
-
- const auto it = productContext->unknownProfilePropertyErrors.find(moduleItem);
- if (it != productContext->unknownProfilePropertyErrors.cend()) {
- const QString fullProductName = ResolvedProduct::fullDisplayName
- (productContext->name, productContext->multiplexConfigurationId);
- ErrorInfo error(Tr::tr("Loading module '%1' for product '%2' failed due to invalid values "
- "in profile '%3':").arg(fullName, fullProductName,
- productContext->profileName));
- for (const ErrorInfo &e : it->second)
- error.append(e.toString());
- handlePropertyError(error, m_parameters, m_logger);
- }
- return moduleItem;
-}
-
-QStringList &ModuleLoader::getModuleFileNames(const QString &dirPath)
-{
- QStringList &moduleFileNames = m_moduleDirListCache[dirPath];
- if (moduleFileNames.empty()) {
- QDirIterator dirIter(dirPath, StringConstants::qbsFileWildcards());
- while (dirIter.hasNext())
- moduleFileNames += dirIter.next();
- }
- return moduleFileNames;
-}
-
-static Item *findDeepestModuleInstance(Item *instance)
-{
- while (instance->prototype() && instance->prototype()->type() == ItemType::ModuleInstance)
- instance = instance->prototype();
- return instance;
-}
-
-std::pair<Item *, bool> ModuleLoader::loadModuleFile(
- ProductContext *productContext, const QString &fullModuleName,
- const QString &filePath, Item *moduleInstance)
-{
- checkCancelation();
-
- qCDebug(lcModuleLoader) << "loadModuleFile" << fullModuleName << "from" << filePath;
-
- const auto [module, triedToLoad] =
- getModulePrototype(productContext, fullModuleName, filePath);
- if (!module)
- return {nullptr, triedToLoad};
-
- const auto key = std::make_pair(module, productContext);
- const auto it = m_modulePrototypeEnabledInfo.find(key);
- if (it != m_modulePrototypeEnabledInfo.end()) {
- qCDebug(lcModuleLoader) << "prototype cache hit (level 2)";
- return {it.value() ? module : nullptr, triedToLoad};
- }
-
- // Set the name before evaluating any properties. EvaluatorScriptClass reads the module name.
- module->setProperty(StringConstants::nameProperty(), VariantValue::create(fullModuleName));
-
- Item *deepestModuleInstance = findDeepestModuleInstance(moduleInstance);
- Item *origDeepestModuleInstancePrototype = deepestModuleInstance->prototype();
- deepestModuleInstance->setPrototype(module);
- bool enabled = checkItemCondition(moduleInstance, module);
- deepestModuleInstance->setPrototype(origDeepestModuleInstancePrototype);
- if (!enabled) {
- qCDebug(lcModuleLoader) << "condition of module" << fullModuleName << "is false";
- m_modulePrototypeEnabledInfo.insert(key, false);
- return {nullptr, triedToLoad};
- }
-
- if (isBaseModule(fullModuleName))
- setupBaseModulePrototype(module);
- else
- resolveParameterDeclarations(module);
-
- m_modulePrototypeEnabledInfo.insert(key, true);
- return {module, triedToLoad};
-}
-
-// Returns the module prototype item and a boolean indicating if we tried to load it from the file
-std::pair<Item *, bool> ModuleLoader::getModulePrototype(ProductContext *productContext,
- const QString &fullModuleName, const QString &filePath)
-{
- auto &prototypeList = m_modulePrototypes[filePath];
- for (const auto &prototype : prototypeList) {
- if (prototype.second == productContext->profileName) {
- qCDebug(lcModuleLoader) << "prototype cache hit (level 1)";
- return {prototype.first, true};
- }
- }
- Item * const module = loadItemFromFile(filePath, CodeLocation());
- if (module->type() != ItemType::Module) {
- qCDebug(lcModuleLoader).nospace()
- << "Alleged module " << fullModuleName << " has type '"
- << module->typeName() << "', so it's not a module after all.";
- return {nullptr, false};
- }
- prototypeList.emplace_back(module, productContext->profileName);
-
- // Module properties that are defined in the profile are used as default values.
- // This is the reason we need to have different items per profile.
- const QVariantMap profileModuleProperties
- = productContext->moduleProperties.value(fullModuleName).toMap();
- for (auto it = profileModuleProperties.cbegin(); it != profileModuleProperties.cend(); ++it) {
- if (Q_UNLIKELY(!module->hasProperty(it.key()))) {
- productContext->unknownProfilePropertyErrors[module].emplace_back
- (Tr::tr("Unknown property: %1.%2").arg(fullModuleName, it.key()));
- continue;
- }
- const PropertyDeclaration decl = module->propertyDeclaration(it.key());
- VariantValuePtr v = VariantValue::create(
- PropertyDeclaration::convertToPropertyType(it.value(), decl.type(),
- QStringList(fullModuleName), it.key()));
- module->setProperty(it.key(), v);
- }
-
- return {module, true};
-}
-
-Item::Module ModuleLoader::loadBaseModule(ProductContext *productContext, Item *item)
-{
- const QualifiedId baseModuleName(StringConstants::qbsModule());
- Item::Module baseModuleDesc;
- baseModuleDesc.name = baseModuleName;
- baseModuleDesc.item = loadModule(productContext, nullptr, item, CodeLocation(), QString(),
- baseModuleName, QString(), FallbackMode::Disabled, true,
- &baseModuleDesc.isProduct, nullptr);
- if (productContext->item) {
- const Item * const qbsInstanceItem
- = moduleInstanceItem(productContext->item, baseModuleName);
- const Item::PropertyMap &props = qbsInstanceItem->properties();
- for (auto it = props.cbegin(); it != props.cend(); ++it) {
- if (it.value()->type() == Value::VariantValueType)
- baseModuleDesc.item->setProperty(it.key(), it.value());
- }
- }
- QBS_CHECK(!baseModuleDesc.isProduct);
- if (Q_UNLIKELY(!baseModuleDesc.item))
- throw ErrorInfo(Tr::tr("Cannot load base qbs module."));
- return baseModuleDesc;
-}
-
-void ModuleLoader::setupBaseModulePrototype(Item *prototype)
-{
- prototype->setProperty(QStringLiteral("hostPlatform"),
- VariantValue::create(HostOsInfo::hostOSIdentifier()));
- prototype->setProperty(QStringLiteral("hostArchitecture"),
- VariantValue::create(HostOsInfo::hostOSArchitecture()));
- prototype->setProperty(QStringLiteral("libexecPath"),
- VariantValue::create(m_parameters.libexecPath()));
-
- const Version qbsVersion = LanguageInfo::qbsVersion();
- prototype->setProperty(QStringLiteral("versionMajor"),
- VariantValue::create(qbsVersion.majorVersion()));
- prototype->setProperty(QStringLiteral("versionMinor"),
- VariantValue::create(qbsVersion.minorVersion()));
- prototype->setProperty(QStringLiteral("versionPatch"),
- VariantValue::create(qbsVersion.patchLevel()));
-}
-
-static void collectItemsWithId_impl(Item *item, QList<Item *> *result)
-{
- if (!item->id().isEmpty())
- result->push_back(item);
- for (Item * const child : item->children())
- collectItemsWithId_impl(child, result);
-}
-
-static QList<Item *> collectItemsWithId(Item *item)
-{
- QList<Item *> result;
- collectItemsWithId_impl(item, &result);
- return result;
-}
-
-static std::vector<std::pair<QualifiedId, ItemValuePtr>> instanceItemProperties(Item *item)
-{
- std::vector<std::pair<QualifiedId, ItemValuePtr>> result;
- QualifiedId name;
- const auto func = [&] (Item *item, const auto &f) -> void {
- for (auto it = item->properties().begin(), end = item->properties().end();
- it != end; ++it) {
- if (it.value()->type() != Value::ItemValueType)
- continue;
- ItemValuePtr itemValue = std::static_pointer_cast<ItemValue>(it.value());
- if (!itemValue->item())
- continue;
- name.push_back(it.key());
- if (itemValue->item()->type() == ItemType::ModulePrefix)
- f(itemValue->item(), f);
- else
- result.emplace_back(name, itemValue);
- name.removeLast();
- }
- };
- func(item, func);
- return result;
-}
-
-void ModuleLoader::instantiateModule(ProductContext *productContext, Item *exportingProduct,
- Item *instanceScope, Item *moduleInstance, Item *modulePrototype,
- const QualifiedId &moduleName, ProductModuleInfo *productModuleInfo)
-{
- Item *deepestModuleInstance = findDeepestModuleInstance(moduleInstance);
- deepestModuleInstance->setPrototype(modulePrototype);
- const QString fullName = moduleName.toString();
- const QString generalOverrideKey = QStringLiteral("modules.") + fullName;
- const QString perProductOverrideKey = StringConstants::productsOverridePrefix()
- + productContext->name + QLatin1Char('.') + fullName;
- for (Item *instance = moduleInstance; instance; instance = instance->prototype()) {
- overrideItemProperties(instance, generalOverrideKey, m_parameters.overriddenValuesTree());
- if (fullName == QStringLiteral("qbs"))
- overrideItemProperties(instance, fullName, m_parameters.overriddenValuesTree());
- overrideItemProperties(instance, perProductOverrideKey,
- m_parameters.overriddenValuesTree());
- if (instance == deepestModuleInstance)
- break;
- }
-
- moduleInstance->setFile(modulePrototype->file());
- moduleInstance->setLocation(modulePrototype->location());
- QBS_CHECK(moduleInstance->type() == ItemType::ModuleInstance);
-
- // create module scope
- Item *moduleScope = Item::create(m_pool, ItemType::Scope);
- QBS_CHECK(instanceScope->file());
- moduleScope->setFile(instanceScope->file());
- moduleScope->setScope(instanceScope);
- QBS_CHECK(productContext->project->scope);
- productContext->project->scope->copyProperty(StringConstants::projectVar(), moduleScope);
- if (productContext->scope)
- productContext->scope->copyProperty(StringConstants::productVar(), moduleScope);
- else
- QBS_CHECK(fullName == StringConstants::qbsModule()); // Dummy product.
-
- if (productModuleInfo) {
- exportingProduct = productModuleInfo->exportItem->parent();
- QBS_CHECK(exportingProduct);
- QBS_CHECK(exportingProduct->type() == ItemType::Product);
- }
-
- if (exportingProduct) {
- const auto exportingProductItemValue = ItemValue::create(exportingProduct);
- moduleScope->setProperty(QStringLiteral("exportingProduct"), exportingProductItemValue);
-
- const auto importingProductItemValue = ItemValue::create(productContext->item);
- moduleScope->setProperty(QStringLiteral("importingProduct"), importingProductItemValue);
-
- moduleScope->setProperty(StringConstants::projectVar(),
- ItemValue::create(exportingProduct->parent()));
-
- PropertyDeclaration pd(StringConstants::qbsSourceDirPropertyInternal(),
- PropertyDeclaration::String, QString(),
- PropertyDeclaration::PropertyNotAvailableInConfig);
- moduleInstance->setPropertyDeclaration(pd.name(), pd);
- ValuePtr v = exportingProduct
- ->property(StringConstants::sourceDirectoryProperty())->clone();
- moduleInstance->setProperty(pd.name(), v);
- }
- moduleInstance->setScope(moduleScope);
-
- QHash<Item *, Item *> prototypeInstanceMap;
- prototypeInstanceMap[modulePrototype] = moduleInstance;
-
- // create instances for every child of the prototype
- createChildInstances(moduleInstance, modulePrototype, &prototypeInstanceMap);
-
- // create ids from from the prototype in the instance
- if (modulePrototype->file()->idScope()) {
- const auto items = collectItemsWithId(modulePrototype);
- for (Item * const itemWithId : items) {
- Item *idProto = itemWithId;
- Item *idInstance = prototypeInstanceMap.value(idProto);
- QBS_ASSERT(idInstance, continue);
- ItemValuePtr idInstanceValue = ItemValue::create(idInstance);
- moduleScope->setProperty(itemWithId->id(), idInstanceValue);
- }
- }
-
- // For foo.bar in modulePrototype create an item foo in moduleInstance.
- for (const auto &[propertyName, itemValue] : instanceItemProperties(modulePrototype)) {
- if (itemValue->item()->properties().empty())
- continue;
- qCDebug(lcModuleLoader) << "The prototype of " << moduleName
- << " sets properties on " << propertyName.toString();
- Item *item = moduleInstanceItem(moduleInstance, propertyName);
- item->setPrototype(itemValue->item());
- if (itemValue->createdByPropertiesBlock()) {
- ItemValuePtr itemValue = moduleInstance->itemProperty(propertyName.front());
- for (int i = 1; i < propertyName.size(); ++i)
- itemValue = itemValue->item()->itemProperty(propertyName.at(i));
- itemValue->setCreatedByPropertiesBlock(true);
- }
- }
-
- // Resolve dependencies of this module instance.
- DependsContext dependsContext;
- dependsContext.product = productContext;
- dependsContext.exportingProductItem = exportingProduct;
- QBS_ASSERT(moduleInstance->modules().empty(), moduleInstance->removeModules());
- if (productModuleInfo) {
- dependsContext.productDependencies = &productContext->productModuleDependencies[fullName];
- resolveDependencies(&dependsContext, moduleInstance);
- } else if (!isBaseModule(fullName)) {
- dependsContext.productDependencies = &productContext->info.usedProducts;
- resolveDependencies(&dependsContext, moduleInstance);
- }
-
- // Check readonly properties.
- const auto end = moduleInstance->properties().cend();
- for (auto it = moduleInstance->properties().cbegin(); it != end; ++it) {
- const PropertyDeclaration &pd = moduleInstance->propertyDeclaration(it.key());
- if (!pd.flags().testFlag(PropertyDeclaration::ReadOnlyFlag))
- continue;
- throw ErrorInfo(Tr::tr("Cannot set read-only property '%1'.").arg(pd.name()),
- moduleInstance->property(pd.name())->location());
- }
-}
-
-void ModuleLoader::createChildInstances(Item *instance, Item *prototype,
- QHash<Item *, Item *> *prototypeInstanceMap) const
-{
- instance->childrenReserve(instance->children().size() + prototype->children().size());
-
- for (Item * const childPrototype : prototype->children()) {
- Item *childInstance = Item::create(m_pool, childPrototype->type());
- prototypeInstanceMap->insert(childPrototype, childInstance);
- childInstance->setPrototype(childPrototype);
- childInstance->setFile(childPrototype->file());
- childInstance->setId(childPrototype->id());
- childInstance->setLocation(childPrototype->location());
- childInstance->setScope(instance->scope());
- Item::addChild(instance, childInstance);
- createChildInstances(childInstance, childPrototype, prototypeInstanceMap);
- }
-}
-
-void ModuleLoader::checkCancelation() const
-{
- if (m_progressObserver && m_progressObserver->canceled()) {
- throw ErrorInfo(Tr::tr("Project resolving canceled for configuration %1.")
- .arg(TopLevelProject::deriveId(m_parameters.finalBuildConfigurationTree())));
- }
-}
-
-bool ModuleLoader::checkItemCondition(Item *item, Item *itemToDisable)
-{
- if (m_evaluator->boolValue(item, StringConstants::conditionProperty()))
- return true;
- m_disabledItems += itemToDisable ? itemToDisable : item;
- return false;
-}
-
-QStringList ModuleLoader::readExtraSearchPaths(Item *item, bool *wasSet)
-{
- QStringList result;
- const QStringList paths = m_evaluator->stringListValue(
- item, StringConstants::qbsSearchPathsProperty(), wasSet);
- const JSSourceValueConstPtr prop = item->sourceProperty(
- StringConstants::qbsSearchPathsProperty());
-
- // Value can come from within a project file or as an overridden value from the user
- // (e.g command line).
- const QString basePath = FileInfo::path(prop ? prop->file()->filePath()
- : m_parameters.projectFilePath());
- for (const QString &path : paths)
- result += FileInfo::resolvePath(basePath, path);
- return result;
-}
-
-void ModuleLoader::copyProperties(const Item *sourceProject, Item *targetProject)
-{
- if (!sourceProject)
- return;
- const QList<PropertyDeclaration> builtinProjectProperties = BuiltinDeclarations::instance()
- .declarationsForType(ItemType::Project).properties();
- Set<QString> builtinProjectPropertyNames;
- for (const PropertyDeclaration &p : builtinProjectProperties)
- builtinProjectPropertyNames << p.name();
-
- for (Item::PropertyDeclarationMap::ConstIterator it
- = sourceProject->propertyDeclarations().constBegin();
- it != sourceProject->propertyDeclarations().constEnd(); ++it) {
-
- // We must not inherit built-in properties such as "name",
- // but there are exceptions.
- if (it.key() == StringConstants::qbsSearchPathsProperty()
- || it.key() == StringConstants::profileProperty()
- || it.key() == StringConstants::buildDirectoryProperty()
- || it.key() == StringConstants::sourceDirectoryProperty()
- || it.key() == StringConstants::minimumQbsVersionProperty()) {
- const JSSourceValueConstPtr &v = targetProject->sourceProperty(it.key());
- QBS_ASSERT(v, continue);
- if (v->sourceCode() == StringConstants::undefinedValue())
- sourceProject->copyProperty(it.key(), targetProject);
- continue;
- }
-
- if (builtinProjectPropertyNames.contains(it.key()))
- continue;
-
- if (targetProject->hasOwnProperty(it.key()))
- continue; // Ignore stuff the target project already has.
-
- targetProject->setPropertyDeclaration(it.key(), it.value());
- sourceProject->copyProperty(it.key(), targetProject);
- }
-}
-
-Item *ModuleLoader::wrapInProjectIfNecessary(Item *item)
-{
- if (item->type() == ItemType::Project)
- return item;
- Item *prj = Item::create(item->pool(), ItemType::Project);
- Item::addChild(prj, item);
- prj->setFile(item->file());
- prj->setLocation(item->location());
- prj->setupForBuiltinType(m_logger);
- return prj;
-}
-
-QString ModuleLoader::findExistingModulePath(const QString &searchPath,
- const QualifiedId &moduleName)
-{
- // isFileCaseCorrect is a very expensive call on macOS, so we cache the value for the
- // modules and search paths we've already processed
- auto &moduleInfo = m_existingModulePathCache[{searchPath, moduleName}];
- if (moduleInfo)
- return *moduleInfo;
-
- QString dirPath = searchPath + QStringLiteral("/modules");
-
- for (const QString &moduleNamePart : moduleName) {
- dirPath = FileInfo::resolvePath(dirPath, moduleNamePart);
- if (!FileInfo::exists(dirPath) || !FileInfo::isFileCaseCorrect(dirPath)) {
- return *(moduleInfo = QString());
- }
- }
-
- return *(moduleInfo = dirPath);
-}
-
-QStringList ModuleLoader::findExistingModulePaths(
- const QStringList &searchPaths, const QualifiedId &moduleName)
-{
- QStringList result;
- result.reserve(searchPaths.size());
- for (const auto &path: searchPaths) {
- const QString dirPath = findExistingModulePath(path, moduleName);
- if (!dirPath.isEmpty())
- result.append(dirPath);
- }
- return result;
-}
-
-void ModuleLoader::setScopeForDescendants(Item *item, Item *scope)
-{
- for (Item * const child : item->children()) {
- child->setScope(scope);
- setScopeForDescendants(child, scope);
- }
-}
-
-void ModuleLoader::overrideItemProperties(Item *item, const QString &buildConfigKey,
- const QVariantMap &buildConfig)
-{
- const QVariant buildConfigValue = buildConfig.value(buildConfigKey);
- if (buildConfigValue.isNull())
- return;
- item->overrideProperties(buildConfigValue.toMap(), buildConfigKey, m_parameters, m_logger);
-}
-
-void ModuleLoader::collectAllModules(Item *item, std::vector<Item::Module> *modules)
-{
- for (const Item::Module &m : item->modules()) {
- if (moduleRepresentsDisabledProduct(m))
- m.item->removeModules();
- auto it = std::find_if(modules->begin(), modules->end(),
- [m] (const Item::Module &m2) { return m.name == m2.name; });
- if (it != modules->end()) {
- // If a module is required somewhere, it is required in the top-level item.
- if (m.required)
- it->required = true;
- it->versionRange.narrowDown(m.versionRange);
- it->fallbackEnabled = it->fallbackEnabled && m.fallbackEnabled;
- continue;
- }
- modules->push_back(m);
- collectAllModules(m.item, modules);
- }
-}
-
-std::vector<Item::Module> ModuleLoader::allModules(Item *item)
-{
- std::vector<Item::Module> lst;
- collectAllModules(item, &lst);
- return lst;
-}
-
-bool ModuleLoader::moduleRepresentsDisabledProduct(const Item::Module &module)
-{
- if (!module.isProduct)
- return false;
- const Item *exportItem = module.item->prototype();
- while (exportItem && exportItem->type() != ItemType::Export)
- exportItem = exportItem->prototype();
- QBS_CHECK(exportItem);
- Item * const productItem = exportItem->parent();
- QBS_CHECK(productItem->type() == ItemType::Product);
- return m_disabledItems.contains(productItem) || !checkItemCondition(productItem);
-}
-
-void ModuleLoader::addProductModuleDependencies(ProductContext *productContext, const QString &name)
-{
- auto deps = productContext->productModuleDependencies.at(name);
- QList<ModuleLoaderResult::ProductInfo::Dependency> depsToAdd;
- const bool productIsMultiplexed = !productContext->multiplexConfigurationId.isEmpty();
- for (auto &dep : deps) {
- const auto productRange = m_productsByName.equal_range(dep.name);
- std::vector<const ProductContext *> dependencies;
- bool hasNonMultiplexedDependency = false;
- for (auto it = productRange.first; it != productRange.second; ++it) {
- if (!it->second->multiplexConfigurationId.isEmpty()) {
- dependencies.push_back(it->second);
- if (productIsMultiplexed && dep.profile.isEmpty())
- break;
- } else {
- hasNonMultiplexedDependency = true;
- break;
- }
- }
-
- if (hasNonMultiplexedDependency) {
- depsToAdd.push_back(dep);
- continue;
- }
-
- for (std::size_t i = 0; i < dependencies.size(); ++i) {
- const bool profileMatch = dep.profile.isEmpty()
- || dep.profile == StringConstants::star()
- || dep.profile == dependencies.at(i)->profileName;
- if (i == 0) {
- if (productIsMultiplexed && dep.profile.isEmpty()) {
- const ValuePtr &multiplexConfigIdProp = productContext->item->property(
- StringConstants::multiplexConfigurationIdProperty());
- dep.multiplexConfigurationId = std::static_pointer_cast<VariantValue>(
- multiplexConfigIdProp)->value().toString();
- depsToAdd.push_back(dep);
- break;
- }
- if (profileMatch) {
- dep.multiplexConfigurationId = dependencies.at(i)->multiplexConfigurationId;
- depsToAdd.push_back(dep);
- }
- } else if (profileMatch) {
- ModuleLoaderResult::ProductInfo::Dependency newDependency = dep;
- newDependency.multiplexConfigurationId
- = dependencies.at(i)->multiplexConfigurationId;
- depsToAdd << newDependency;
- }
- }
- }
- productContext->info.usedProducts.insert(productContext->info.usedProducts.end(),
- depsToAdd.cbegin(), depsToAdd.cend());
-}
-
-static void collectProductModuleDependencies(Item *item, Set<QualifiedId> &allDeps)
-{
- for (const Item::Module &m : item->modules()) {
- if (m.isProduct && allDeps.insert(m.name).second)
- collectProductModuleDependencies(m.item, allDeps);
- }
-}
-
-void ModuleLoader::addProductModuleDependencies(ModuleLoader::ProductContext *ctx)
-{
- Set<QualifiedId> deps;
- collectProductModuleDependencies(ctx->item, deps);
- for (const QualifiedId &dep : deps)
- addProductModuleDependencies(ctx, dep.toString());
-}
-
-void ModuleLoader::addTransitiveDependencies(ProductContext *ctx)
-{
- AccumulatingTimer timer(m_parameters.logElapsedTime()
- ? &m_elapsedTimeTransitiveDependencies : nullptr);
- qCDebug(lcModuleLoader) << "addTransitiveDependencies";
-
- std::vector<Item::Module> transitiveDeps = allModules(ctx->item);
- std::sort(transitiveDeps.begin(), transitiveDeps.end());
- for (const Item::Module &m : ctx->item->modules()) {
- auto it = std::lower_bound(transitiveDeps.begin(), transitiveDeps.end(), m);
- QBS_CHECK(it != transitiveDeps.end() && it->name == m.name);
- transitiveDeps.erase(it);
- }
- for (const Item::Module &module : qAsConst(transitiveDeps)) {
- if (module.isProduct) {
- ctx->item->addModule(module);
- } else {
- const FallbackMode fallbackMode = module.fallbackEnabled
- ? FallbackMode::Enabled : FallbackMode::Disabled;
- Item::Module dep;
- dep.item = loadModule(ctx, nullptr, ctx->item, ctx->item->location(), QString(),
- module.name, QString(), fallbackMode,
- module.required, &dep.isProduct, &dep.parameters);
- if (!dep.item) {
- throw ErrorInfo(Tr::tr("Module '%1' not found when setting up transitive "
- "dependencies for product '%2'.").arg(module.name.toString(),
- ctx->name),
- ctx->item->location());
- }
- dep.name = module.name;
- dep.required = module.required;
- dep.versionRange = module.versionRange;
- dep.fallbackEnabled = fallbackMode == FallbackMode::Enabled;
- ctx->item->addModule(dep);
- }
- }
-}
-
-Item *ModuleLoader::createNonPresentModule(const QString &name, const QString &reason, Item *module)
-{
- qCDebug(lcModuleLoader) << "Non-required module '" << name << "' not loaded (" << reason << ")."
- << "Creating dummy module for presence check.";
- if (!module) {
- module = Item::create(m_pool, ItemType::ModuleInstance);
- module->setFile(FileContext::create());
- module->setProperty(StringConstants::nameProperty(), VariantValue::create(name));
- }
- module->setProperty(StringConstants::presentProperty(), VariantValue::falseValue());
- return module;
-}
-
-void ModuleLoader::handleProductError(const ErrorInfo &error,
- ModuleLoader::ProductContext *productContext)
-{
- const bool alreadyHadError = productContext->info.delayedError.hasError();
- if (!alreadyHadError) {
- productContext->info.delayedError.append(Tr::tr("Error while handling product '%1':")
- .arg(productContext->name),
- productContext->item->location());
- }
- if (error.isInternalError()) {
- if (alreadyHadError) {
- qCDebug(lcModuleLoader()) << "ignoring subsequent internal error" << error.toString()
- << "in product" << productContext->name;
- return;
- }
- for (const auto &kv : productContext->productModuleDependencies) {
- const auto rangeForName = m_productsByName.equal_range(kv.first);
- for (auto rangeIt = rangeForName.first; rangeIt != rangeForName.second; ++rangeIt) {
- const ProductContext * const dep = rangeIt->second;
- if (dep->info.delayedError.hasError()) {
- qCDebug(lcModuleLoader()) << "ignoring internal error" << error.toString()
- << "in product" << productContext->name
- << "assumed to be caused by erroneous dependency"
- << dep->name;
- return;
- }
- }
- }
- }
- const auto errorItems = error.items();
- for (const ErrorItem &ei : errorItems)
- productContext->info.delayedError.append(ei.description(), ei.codeLocation());
- productContext->project->result->productInfos[productContext->item] = productContext->info;
- m_disabledItems << productContext->item;
- m_erroneousProducts.insert(productContext->name);
-}
-
-static void gatherAssignedProperties(ItemValue *iv, const QualifiedId &prefix,
- QualifiedIdSet &properties)
-{
- const Item::PropertyMap &props = iv->item()->properties();
- for (auto it = props.cbegin(); it != props.cend(); ++it) {
- switch (it.value()->type()) {
- case Value::JSSourceValueType:
- properties << (QualifiedId(prefix) << it.key());
- break;
- case Value::ItemValueType:
- if (iv->item()->type() == ItemType::ModulePrefix) {
- gatherAssignedProperties(std::static_pointer_cast<ItemValue>(it.value()).get(),
- QualifiedId(prefix) << it.key(), properties);
- }
- break;
- default:
- break;
- }
- }
-}
-
-QualifiedIdSet ModuleLoader::gatherModulePropertiesSetInGroup(const Item *group)
-{
- QualifiedIdSet propsSetInGroup;
- const Item::PropertyMap &props = group->properties();
- for (auto it = props.cbegin(); it != props.cend(); ++it) {
- if (it.value()->type() == Value::ItemValueType) {
- gatherAssignedProperties(std::static_pointer_cast<ItemValue>(it.value()).get(),
- QualifiedId(it.key()), propsSetInGroup);
- }
- }
- return propsSetInGroup;
-}
-
-void ModuleLoader::markModuleTargetGroups(Item *group, const Item::Module &module)
-{
- QBS_CHECK(group->type() == ItemType::Group);
- if (m_evaluator->boolValue(group, StringConstants::filesAreTargetsProperty())) {
- group->setProperty(StringConstants::modulePropertyInternal(),
- VariantValue::create(module.name.toString()));
- }
- for (Item * const child : group->children())
- markModuleTargetGroups(child, module);
-}
-
-void ModuleLoader::copyGroupsFromModuleToProduct(const ProductContext &productContext,
- const Item::Module &module,
- const Item *modulePrototype)
-{
- for (Item * const child : modulePrototype->children()) {
- if (child->type() == ItemType::Group) {
- Item * const clonedGroup = child->clone();
- clonedGroup->setScope(productContext.scope);
- setScopeForDescendants(clonedGroup, productContext.scope);
- Item::addChild(productContext.item, clonedGroup);
- markModuleTargetGroups(clonedGroup, module);
- }
- }
-}
-
-void ModuleLoader::copyGroupsFromModulesToProduct(const ProductContext &productContext)
-{
- for (const Item::Module &module : productContext.item->modules()) {
- Item *prototype = module.item;
- bool modulePassedValidation;
- while ((modulePassedValidation = prototype->isPresentModule()) && prototype->prototype())
- prototype = prototype->prototype();
- if (modulePassedValidation)
- copyGroupsFromModuleToProduct(productContext, module, prototype);
- }
-}
-
-QString ModuleLoaderResult::ProductInfo::Dependency::uniqueName() const
-{
- return ResolvedProduct::uniqueName(name, multiplexConfigurationId);
-}
-
-QString ModuleLoader::ProductContext::uniqueName() const
-{
- return ResolvedProduct::uniqueName(name, multiplexConfigurationId);
-}
-
-} // namespace Internal
-} // namespace qbs