aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/corelib/language
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2023-04-28 15:08:11 +0200
committerChristian Kandeler <christian.kandeler@qt.io>2023-05-04 11:18:03 +0000
commit61e53de1f777c84bc0e5d90ad2f18525b726129d (patch)
tree76ba1ac776e895ba329471f1a26c3cd0d8e00b83 /src/lib/corelib/language
parent78afa2b55fab3ef11166e1021f199693a92eeb75 (diff)
Introduce loader/ subdirectory
Project loading functionality is implemented in various source files these days, so it makes sense to group them together. Change-Id: Iba42b0246c40610d2a03bf6cc7ed7d3bec9d5536 Reviewed-by: Ivan Komissarov <ABBAPOH@gmail.com>
Diffstat (limited to 'src/lib/corelib/language')
-rw-r--r--src/lib/corelib/language/astimportshandler.cpp305
-rw-r--r--src/lib/corelib/language/astimportshandler.h94
-rw-r--r--src/lib/corelib/language/astpropertiesitemhandler.cpp192
-rw-r--r--src/lib/corelib/language/astpropertiesitemhandler.h63
-rw-r--r--src/lib/corelib/language/groupshandler.cpp320
-rw-r--r--src/lib/corelib/language/groupshandler.h84
-rw-r--r--src/lib/corelib/language/itemreader.cpp238
-rw-r--r--src/lib/corelib/language/itemreader.h121
-rw-r--r--src/lib/corelib/language/itemreaderastvisitor.cpp391
-rw-r--r--src/lib/corelib/language/itemreaderastvisitor.h97
-rw-r--r--src/lib/corelib/language/itemreadervisitorstate.cpp195
-rw-r--r--src/lib/corelib/language/itemreadervisitorstate.h89
-rw-r--r--src/lib/corelib/language/language.cpp2
-rw-r--r--src/lib/corelib/language/loader.cpp215
-rw-r--r--src/lib/corelib/language/loader.h88
-rw-r--r--src/lib/corelib/language/localprofiles.cpp164
-rw-r--r--src/lib/corelib/language/localprofiles.h68
-rw-r--r--src/lib/corelib/language/moduleinstantiator.cpp337
-rw-r--r--src/lib/corelib/language/moduleinstantiator.h101
-rw-r--r--src/lib/corelib/language/moduleloader.cpp529
-rw-r--r--src/lib/corelib/language/moduleloader.h99
-rw-r--r--src/lib/corelib/language/modulepropertymerger.cpp304
-rw-r--r--src/lib/corelib/language/modulepropertymerger.h99
-rw-r--r--src/lib/corelib/language/moduleproviderloader.cpp374
-rw-r--r--src/lib/corelib/language/moduleproviderloader.h146
-rw-r--r--src/lib/corelib/language/probesresolver.cpp303
-rw-r--r--src/lib/corelib/language/probesresolver.h107
-rw-r--r--src/lib/corelib/language/productitemmultiplexer.cpp288
-rw-r--r--src/lib/corelib/language/productitemmultiplexer.h84
-rw-r--r--src/lib/corelib/language/projectresolver.cpp1801
-rw-r--r--src/lib/corelib/language/projectresolver.h202
-rw-r--r--src/lib/corelib/language/projecttreebuilder.cpp2213
-rw-r--r--src/lib/corelib/language/projecttreebuilder.h103
33 files changed, 1 insertions, 9815 deletions
diff --git a/src/lib/corelib/language/astimportshandler.cpp b/src/lib/corelib/language/astimportshandler.cpp
deleted file mode 100644
index d634af7e4..000000000
--- a/src/lib/corelib/language/astimportshandler.cpp
+++ /dev/null
@@ -1,305 +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 "astimportshandler.h"
-
-#include "asttools.h"
-#include "builtindeclarations.h"
-#include "filecontext.h"
-#include "itemreadervisitorstate.h"
-#include "jsextensions/jsextensions.h"
-
-#include <logging/logger.h>
-#include <logging/translator.h>
-#include <parser/qmljsast_p.h>
-#include <tools/error.h>
-#include <tools/fileinfo.h>
-#include <tools/qttools.h>
-#include <tools/stringconstants.h>
-#include <tools/version.h>
-
-#include <QtCore/qdiriterator.h>
-
-namespace qbs {
-namespace Internal {
-
-ASTImportsHandler::ASTImportsHandler(ItemReaderVisitorState &visitorState, Logger &logger,
- const FileContextPtr &file)
- : m_visitorState(visitorState)
- , m_logger(logger)
- , m_file(file)
- , m_directory(FileInfo::path(m_file->filePath()))
-{
-}
-
-void ASTImportsHandler::handleImports(const QbsQmlJS::AST::UiImportList *uiImportList)
-{
- const auto searchPaths = m_file->searchPaths();
- for (const QString &searchPath : searchPaths)
- collectPrototypes(searchPath + QStringLiteral("/imports"), QString());
-
- // files in the same directory are available as prototypes
- collectPrototypes(m_directory, QString());
-
- bool baseImported = false;
- for (const auto *it = uiImportList; it; it = it->next)
- handleImport(it->import, &baseImported);
- if (!baseImported) {
- QStringRef qbsref(&StringConstants::qbsModule());
- QbsQmlJS::AST::UiQualifiedId qbsURI(qbsref);
- qbsURI.finish();
- QbsQmlJS::AST::UiImport imp(&qbsURI);
- handleImport(&imp, &baseImported);
- }
-
- for (auto it = m_jsImports.constBegin(); it != m_jsImports.constEnd(); ++it)
- m_file->addJsImport(it.value());
-}
-
-void ASTImportsHandler::handleImport(const QbsQmlJS::AST::UiImport *import, bool *baseImported)
-{
- QStringList importUri;
- bool isBase = false;
- if (import->importUri) {
- importUri = toStringList(import->importUri);
- isBase = (importUri.size() == 1 && importUri.front() == StringConstants::qbsModule())
- || (importUri.size() == 2 && importUri.front() == StringConstants::qbsModule()
- && importUri.last() == StringConstants::baseVar());
- if (isBase) {
- *baseImported = true;
- checkImportVersion(import->versionToken);
- } else if (import->versionToken.length) {
- m_logger.printWarning(ErrorInfo(Tr::tr("Superfluous version specification."),
- toCodeLocation(m_file->filePath(), import->versionToken)));
- }
- }
-
- QString as;
- if (isBase) {
- if (Q_UNLIKELY(!import->importId.isNull())) {
- throw ErrorInfo(Tr::tr("Import of qbs.base must have no 'as <Name>'"),
- toCodeLocation(m_file->filePath(), import->importIdToken));
- }
- } else {
- if (importUri.size() == 2 && importUri.front() == StringConstants::qbsModule()) {
- const QString extensionName = importUri.last();
- if (JsExtensions::hasExtension(extensionName)) {
- if (Q_UNLIKELY(!import->importId.isNull())) {
- throw ErrorInfo(Tr::tr("Import of built-in extension '%1' "
- "must not have 'as' specifier.").arg(extensionName),
- toCodeLocation(m_file->filePath(), import->asToken));
- }
- if (Q_UNLIKELY(m_file->jsExtensions().contains(extensionName))) {
- m_logger.printWarning(ErrorInfo(Tr::tr("Built-in extension '%1' already "
- "imported.").arg(extensionName),
- toCodeLocation(m_file->filePath(),
- import->importToken)));
- } else {
- m_file->addJsExtension(extensionName);
- }
- return;
- }
- }
-
- if (import->importId.isNull()) {
- if (!import->fileName.isNull()) {
- throw ErrorInfo(Tr::tr("File imports require 'as <Name>'"),
- toCodeLocation(m_file->filePath(), import->importToken));
- }
- if (importUri.empty()) {
- throw ErrorInfo(Tr::tr("Invalid import URI."),
- toCodeLocation(m_file->filePath(), import->importToken));
- }
- as = importUri.last();
- } else {
- as = import->importId.toString();
- }
-
- if (Q_UNLIKELY(JsExtensions::hasExtension(as)))
- throw ErrorInfo(Tr::tr("Cannot reuse the name of built-in extension '%1'.").arg(as),
- toCodeLocation(m_file->filePath(), import->importIdToken));
- if (Q_UNLIKELY(!m_importAsNames.insert(as).second)) {
- throw ErrorInfo(Tr::tr("Cannot import into the same name more than once."),
- toCodeLocation(m_file->filePath(), import->importIdToken));
- }
- }
-
- if (!import->fileName.isNull()) {
- QString filePath = FileInfo::resolvePath(m_directory, import->fileName.toString());
-
- QFileInfo fi(filePath);
- if (Q_UNLIKELY(!fi.exists()))
- throw ErrorInfo(Tr::tr("Cannot find imported file %0.")
- .arg(QDir::toNativeSeparators(filePath)),
- CodeLocation(m_file->filePath(), import->fileNameToken.startLine,
- import->fileNameToken.startColumn));
- filePath = fi.canonicalFilePath();
- if (fi.isDir()) {
- collectPrototypesAndJsCollections(filePath, as,
- toCodeLocation(m_file->filePath(), import->fileNameToken));
- } else {
- if (filePath.endsWith(QStringLiteral(".js"), Qt::CaseInsensitive)) {
- JsImport &jsImport = m_jsImports[as];
- jsImport.scopeName = as;
- jsImport.filePaths.push_back(filePath);
- jsImport.location
- = toCodeLocation(m_file->filePath(), import->firstSourceLocation());
- } else if (filePath.endsWith(QStringLiteral(".qbs"), Qt::CaseInsensitive)) {
- m_typeNameToFile.insert(QStringList(as), filePath);
- } else {
- throw ErrorInfo(Tr::tr("Can only import .qbs and .js files"),
- CodeLocation(m_file->filePath(), import->fileNameToken.startLine,
- import->fileNameToken.startColumn));
- }
- }
- } else if (!importUri.empty()) {
- const QString importPath = isBase
- ? QStringLiteral("qbs/base") : importUri.join(QDir::separator());
- bool found = m_typeNameToFile.contains(importUri);
- if (!found) {
- const auto searchPaths = m_file->searchPaths();
- for (const QString &searchPath : searchPaths) {
- const QFileInfo fi(FileInfo::resolvePath(
- FileInfo::resolvePath(searchPath,
- StringConstants::importsDir()),
- importPath));
- if (fi.isDir()) {
- // ### versioning, qbsdir file, etc.
- const QString &resultPath = fi.absoluteFilePath();
- collectPrototypesAndJsCollections(resultPath, as,
- toCodeLocation(m_file->filePath(), import->fileNameToken));
- found = true;
- break;
- }
- }
- }
- if (Q_UNLIKELY(!found)) {
- throw ErrorInfo(Tr::tr("import %1 not found")
- .arg(importUri.join(QLatin1Char('.'))),
- toCodeLocation(m_file->filePath(), import->fileNameToken));
- }
- }
-}
-
-Version ASTImportsHandler::readImportVersion(const QString &str, const CodeLocation &location)
-{
- const Version v = Version::fromString(str);
- if (Q_UNLIKELY(!v.isValid()))
- throw ErrorInfo(Tr::tr("Cannot parse version number in import statement."), location);
- if (Q_UNLIKELY(v.patchLevel() != 0)) {
- throw ErrorInfo(Tr::tr("Version number in import statement cannot have more than "
- "two components."), location);
- }
- return v;
-}
-
-bool ASTImportsHandler::addPrototype(const QString &fileName, const QString &filePath,
- const QString &as, bool needsCheck)
-{
- if (needsCheck && fileName.size() <= 4)
- return false;
-
- const QString componentName = fileName.left(fileName.size() - 4);
- // ### validate componentName
-
- if (needsCheck && !componentName.at(0).isUpper())
- return false;
-
- QStringList prototypeName;
- if (!as.isEmpty())
- prototypeName.push_back(as);
- prototypeName.push_back(componentName);
- if (!m_typeNameToFile.contains(prototypeName))
- m_typeNameToFile.insert(prototypeName, filePath);
- return true;
-}
-
-void ASTImportsHandler::checkImportVersion(const QbsQmlJS::AST::SourceLocation &versionToken) const
-{
- if (!versionToken.length)
- return;
- const QString importVersionString
- = m_file->content().mid(versionToken.offset, versionToken.length);
- const Version importVersion = readImportVersion(importVersionString,
- toCodeLocation(m_file->filePath(), versionToken));
- if (Q_UNLIKELY(importVersion != BuiltinDeclarations::instance().languageVersion()))
- throw ErrorInfo(Tr::tr("Incompatible qbs language version %1. This is version %2.").arg(
- importVersionString,
- BuiltinDeclarations::instance().languageVersion().toString()),
- toCodeLocation(m_file->filePath(), versionToken));
-
-}
-
-void ASTImportsHandler::collectPrototypes(const QString &path, const QString &as)
-{
- QStringList fileNames; // Yes, file *names*.
- if (m_visitorState.findDirectoryEntries(path, &fileNames)) {
- for (const QString &fileName : qAsConst(fileNames))
- addPrototype(fileName, path + QLatin1Char('/') + fileName, as, false);
- return;
- }
-
- QDirIterator dirIter(path, StringConstants::qbsFileWildcards());
- while (dirIter.hasNext()) {
- const QString filePath = dirIter.next();
- const QString fileName = dirIter.fileName();
- if (addPrototype(fileName, filePath, as, true))
- fileNames << fileName;
- }
- m_visitorState.cacheDirectoryEntries(path, fileNames);
-
-}
-
-void ASTImportsHandler::collectPrototypesAndJsCollections(const QString &path, const QString &as,
- const CodeLocation &location)
-{
- collectPrototypes(path, as);
- QDirIterator dirIter(path, StringConstants::jsFileWildcards());
- while (dirIter.hasNext()) {
- dirIter.next();
- JsImport &jsImport = m_jsImports[as];
- if (jsImport.scopeName.isNull()) {
- jsImport.scopeName = as;
- jsImport.location = location;
- }
- jsImport.filePaths.push_back(dirIter.filePath());
- }
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/astimportshandler.h b/src/lib/corelib/language/astimportshandler.h
deleted file mode 100644
index e9c2b6c27..000000000
--- a/src/lib/corelib/language/astimportshandler.h
+++ /dev/null
@@ -1,94 +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$
-**
-****************************************************************************/
-#ifndef QBS_ASTIMPORTSHANDLER_H
-#define QBS_ASTIMPORTSHANDLER_H
-
-#include "forward_decls.h"
-
-#include <parser/qmljsastfwd_p.h>
-#include <tools/set.h>
-
-#include <QtCore/qhash.h>
-#include <QtCore/qstringlist.h>
-
-namespace qbs {
-class CodeLocation;
-class Version;
-
-namespace Internal {
-class ItemReaderVisitorState;
-class JsImport;
-class Logger;
-
-class ASTImportsHandler
-{
-public:
- ASTImportsHandler(ItemReaderVisitorState &visitorState, Logger &logger,
- const FileContextPtr &file);
-
- void handleImports(const QbsQmlJS::AST::UiImportList *uiImportList);
-
- QHash<QStringList, QString> typeNameFileMap() const { return m_typeNameToFile; }
-
-private:
- static Version readImportVersion(const QString &str, const CodeLocation &location);
-
- bool addPrototype(const QString &fileName, const QString &filePath, const QString &as,
- bool needsCheck);
- void checkImportVersion(const QbsQmlJS::AST::SourceLocation &versionToken) const;
- void collectPrototypes(const QString &path, const QString &as);
- void collectPrototypesAndJsCollections(const QString &path, const QString &as,
- const CodeLocation &location);
- void handleImport(const QbsQmlJS::AST::UiImport *import, bool *baseImported);
-
- ItemReaderVisitorState &m_visitorState;
- Logger &m_logger;
- const FileContextPtr &m_file;
- const QString m_directory;
- QHash<QStringList, QString> m_typeNameToFile;
- Set<QString> m_importAsNames;
-
- using JsImportsHash = QHash<QString, JsImport>;
- JsImportsHash m_jsImports;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // Include guard
diff --git a/src/lib/corelib/language/astpropertiesitemhandler.cpp b/src/lib/corelib/language/astpropertiesitemhandler.cpp
deleted file mode 100644
index cc4c02232..000000000
--- a/src/lib/corelib/language/astpropertiesitemhandler.cpp
+++ /dev/null
@@ -1,192 +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 "astpropertiesitemhandler.h"
-
-#include "item.h"
-#include "value.h"
-
-#include <logging/translator.h>
-#include <tools/error.h>
-#include <tools/qbsassert.h>
-#include <tools/stringconstants.h>
-
-namespace qbs {
-namespace Internal {
-
-ASTPropertiesItemHandler::ASTPropertiesItemHandler(Item *parentItem) : m_parentItem(parentItem)
-{
-}
-
-void ASTPropertiesItemHandler::handlePropertiesItems()
-{
- // TODO: Simply forbid Properties items to have child items and get rid of this check.
- if (m_parentItem->type() != ItemType::Properties)
- setupAlternatives();
-}
-
-void ASTPropertiesItemHandler::setupAlternatives()
-{
- auto it = m_parentItem->m_children.begin();
- while (it != m_parentItem->m_children.end()) {
- Item * const child = *it;
- bool remove = false;
- if (child->type() == ItemType::Properties) {
- handlePropertiesBlock(child);
- remove = m_parentItem->type() != ItemType::Export;
- }
- if (remove)
- it = m_parentItem->m_children.erase(it);
- else
- ++it;
- }
-}
-
-class PropertiesBlockConverter
-{
-public:
- PropertiesBlockConverter(const JSSourceValue::AltProperty &condition,
- const JSSourceValue::AltProperty &overrideListProperties,
- Item *propertiesBlockContainer, const Item *propertiesBlock)
- : m_propertiesBlockContainer(propertiesBlockContainer)
- , m_propertiesBlock(propertiesBlock)
- {
- m_alternative.condition = condition;
- m_alternative.overrideListProperties = overrideListProperties;
- }
-
- void apply()
- {
- doApply(m_propertiesBlockContainer, m_propertiesBlock);
- }
-
-private:
- JSSourceValue::Alternative m_alternative;
- Item * const m_propertiesBlockContainer;
- const Item * const m_propertiesBlock;
-
- void doApply(Item *outer, const Item *inner)
- {
- for (auto it = inner->properties().constBegin();
- it != inner->properties().constEnd(); ++it) {
- if (inner == m_propertiesBlock
- && (it.key() == StringConstants::conditionProperty()
- || it.key() == StringConstants::overrideListPropertiesProperty())) {
- continue;
- }
- if (it.value()->type() == Value::ItemValueType) {
- Item * const innerVal = std::static_pointer_cast<ItemValue>(it.value())->item();
- ItemValuePtr outerVal = outer->itemProperty(it.key());
- if (!outerVal) {
- outerVal = ItemValue::create(Item::create(outer->pool(), innerVal->type()),
- true);
- outer->setProperty(it.key(), outerVal);
- }
- doApply(outerVal->item(), innerVal);
- } else if (it.value()->type() == Value::JSSourceValueType) {
- const ValuePtr outerVal = outer->property(it.key());
- if (Q_UNLIKELY(outerVal && outerVal->type() != Value::JSSourceValueType)) {
- throw ErrorInfo(Tr::tr("Incompatible value type in unconditional value at %1.")
- .arg(outerVal->location().toString()));
- }
- doApply(it.key(), outer, std::static_pointer_cast<JSSourceValue>(outerVal),
- std::static_pointer_cast<JSSourceValue>(it.value()));
- } else {
- QBS_CHECK(!"Unexpected value type in conditional value.");
- }
- }
- }
-
- void doApply(const QString &propertyName, Item *item, JSSourceValuePtr value,
- const JSSourceValuePtr &conditionalValue)
- {
- if (!value) {
- value = JSSourceValue::create(true);
- value->setFile(conditionalValue->file());
- item->setProperty(propertyName, value);
- value->setSourceCode(StringConstants::baseVar());
- value->setSourceUsesBase();
- }
- m_alternative.value = conditionalValue;
- value->addAlternative(m_alternative);
- }
-};
-
-static JSSourceValue::AltProperty getPropertyData(const Item *propertiesItem, const QString &name)
-{
- const ValuePtr value = propertiesItem->property(name);
- if (!value) {
- if (name == StringConstants::conditionProperty()) {
- throw ErrorInfo(Tr::tr("Properties.condition must be provided."),
- propertiesItem->location());
- }
- return JSSourceValue::AltProperty(StringConstants::falseValue(),
- propertiesItem->location());
- }
- if (Q_UNLIKELY(value->type() != Value::JSSourceValueType)) {
- throw ErrorInfo(Tr::tr("Properties.%1 must be a value binding.").arg(name),
- propertiesItem->location());
- }
- if (name == StringConstants::overrideListPropertiesProperty()) {
- const Item *parent = propertiesItem->parent();
- while (parent) {
- if (parent->type() == ItemType::Product)
- break;
- parent = parent->parent();
- }
- if (!parent) {
- throw ErrorInfo(Tr::tr("Properties.overrideListProperties can only be set "
- "in a Product item."));
- }
-
- }
- const JSSourceValuePtr srcval = std::static_pointer_cast<JSSourceValue>(value);
- return JSSourceValue::AltProperty(srcval->sourceCodeForEvaluation(), srcval->location());
-}
-
-void ASTPropertiesItemHandler::handlePropertiesBlock(const Item *propertiesItem)
-{
- const auto condition = getPropertyData(propertiesItem, StringConstants::conditionProperty());
- const auto overrideListProperties = getPropertyData(propertiesItem,
- StringConstants::overrideListPropertiesProperty());
- PropertiesBlockConverter(condition, overrideListProperties, m_parentItem,
- propertiesItem).apply();
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/astpropertiesitemhandler.h b/src/lib/corelib/language/astpropertiesitemhandler.h
deleted file mode 100644
index 413512ee5..000000000
--- a/src/lib/corelib/language/astpropertiesitemhandler.h
+++ /dev/null
@@ -1,63 +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$
-**
-****************************************************************************/
-#ifndef QBS_ASTPROPERTIESITEMHANDLER_H
-#define QBS_ASTPROPERTIESITEMHANDLER_H
-
-namespace qbs {
-namespace Internal {
-class Item;
-
-class ASTPropertiesItemHandler
-{
-public:
- ASTPropertiesItemHandler(Item *parentItem);
-
- void handlePropertiesItems();
-
-private:
- void setupAlternatives();
- void handlePropertiesBlock(const Item *propertiesItem);
-
- Item * const m_parentItem;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // Include guard.
diff --git a/src/lib/corelib/language/groupshandler.cpp b/src/lib/corelib/language/groupshandler.cpp
deleted file mode 100644
index b9acfc553..000000000
--- a/src/lib/corelib/language/groupshandler.cpp
+++ /dev/null
@@ -1,320 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "groupshandler.h"
-
-#include "evaluator.h"
-#include "item.h"
-#include "moduleinstantiator.h"
-#include "value.h"
-
-#include <logging/translator.h>
-#include <tools/profiling.h>
-#include <tools/setupprojectparameters.h>
-#include <tools/stringconstants.h>
-
-namespace qbs::Internal {
-class GroupsHandler::Private
-{
-public:
- Private(const SetupProjectParameters &parameters, ModuleInstantiator &instantiator,
- Evaluator &evaluator, Logger &logger)
- : parameters(parameters), moduleInstantiator(instantiator), evaluator(evaluator),
- logger(logger) {}
-
- void gatherAssignedProperties(ItemValue *iv, const QualifiedId &prefix,
- QualifiedIdSet &properties);
- void markModuleTargetGroups(Item *group, const Item::Module &module);
- void moveGroupsFromModuleToProduct(Item *product, Item *productScope,
- const Item::Module &module);
- void moveGroupsFromModulesToProduct(Item *product, Item *productScope);
- void propagateModulesFromParent(Item *group);
- void handleGroup(Item *product, Item *group);
- void adjustScopesInGroupModuleInstances(Item *groupItem, const Item::Module &module);
- QualifiedIdSet gatherModulePropertiesSetInGroup(const Item *group);
-
- const SetupProjectParameters &parameters;
- ModuleInstantiator &moduleInstantiator;
- Evaluator &evaluator;
- Logger &logger;
- std::unordered_map<const Item *, QualifiedIdSet> modulePropsSetInGroups;
- Set<Item *> disabledGroups;
- qint64 elapsedTime = 0;
-};
-
-GroupsHandler::GroupsHandler(const SetupProjectParameters &parameters,
- ModuleInstantiator &instantiator, Evaluator &evaluator, Logger &logger)
- : d(new Private(parameters, instantiator, evaluator, logger)) {}
-GroupsHandler::~GroupsHandler() { delete d; }
-
-void GroupsHandler::setupGroups(Item *product, Item *productScope)
-{
- AccumulatingTimer timer(d->parameters.logElapsedTime() ? &d->elapsedTime : nullptr);
-
- d->modulePropsSetInGroups.clear();
- d->disabledGroups.clear();
- d->moveGroupsFromModulesToProduct(product, productScope);
- for (Item * const child : product->children()) {
- if (child->type() == ItemType::Group)
- d->handleGroup(product, child);
- }
-}
-
-std::unordered_map<const Item *, QualifiedIdSet> GroupsHandler::modulePropertiesSetInGroups() const
-{
- return d->modulePropsSetInGroups;
-}
-
-Set<Item *> GroupsHandler::disabledGroups() const
-{
- return d->disabledGroups;
-}
-
-void GroupsHandler::printProfilingInfo(int indent)
-{
- if (!d->parameters.logElapsedTime())
- return;
- d->logger.qbsLog(LoggerInfo, true) << QByteArray(indent, ' ')
- << Tr::tr("Setting up Groups took %1.")
- .arg(elapsedTimeString(d->elapsedTime));
-}
-
-void GroupsHandler::Private::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;
- }
- }
-}
-
-void GroupsHandler::Private::markModuleTargetGroups(Item *group, const Item::Module &module)
-{
- QBS_CHECK(group->type() == ItemType::Group);
- if (evaluator.boolValue(group, StringConstants::filesAreTargetsProperty())) {
- group->setProperty(StringConstants::modulePropertyInternal(),
- VariantValue::create(module.name.toString()));
- }
- for (Item * const child : group->children())
- markModuleTargetGroups(child, module);
-}
-
-void GroupsHandler::Private::moveGroupsFromModuleToProduct(Item *product, Item *productScope,
- const Item::Module &module)
-{
- if (!module.item->isPresentModule())
- return;
- for (auto it = module.item->children().begin(); it != module.item->children().end();) {
- Item * const child = *it;
- if (child->type() != ItemType::Group) {
- ++it;
- continue;
- }
- child->setScope(productScope);
- setScopeForDescendants(child, productScope);
- Item::addChild(product, child);
- markModuleTargetGroups(child, module);
- it = module.item->children().erase(it);
- }
-}
-
-void GroupsHandler::Private::moveGroupsFromModulesToProduct(Item *product, Item *productScope)
-{
- for (const Item::Module &module : product->modules())
- moveGroupsFromModuleToProduct(product, productScope, module);
-}
-
-// TODO: I don't completely understand this function, and I suspect we do both too much
-// and too little here. In particular, I'm not sure why we should even have to do anything
-// with groups that don't attach properties.
-// Set aside a day or two at some point to fully grasp all the details and rewrite accordingly.
-void GroupsHandler::Private::propagateModulesFromParent(Item *group)
-{
- QBS_CHECK(group->type() == ItemType::Group);
- QHash<QualifiedId, Item *> moduleInstancesForGroup;
-
- // Step 1: "Instantiate" the product's modules for the group.
- for (Item::Module m : group->parent()->modules()) {
- Item * const targetItem = moduleInstantiator.retrieveModuleInstanceItem(group, m.name);
- QBS_CHECK(targetItem->type() == ItemType::ModuleInstancePlaceholder);
- targetItem->setPrototype(m.item);
-
- Item * const moduleScope = Item::create(targetItem->pool(), ItemType::Scope);
- moduleScope->setFile(group->file());
- moduleScope->setProperties(m.item->scope()->properties()); // "project", "product", ids
- moduleScope->setScope(group);
- targetItem->setScope(moduleScope);
-
- targetItem->setFile(m.item->file());
-
- // "parent" should point to the group/artifact parent
- targetItem->setParent(group->parent());
-
- targetItem->setOuterItem(m.item);
-
- m.item = targetItem;
- group->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 : group->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 = group->itemProperty(depMod.name.front());
- QBS_CHECK(modulePrefix);
- module.item->setProperty(depMod.name.front(), modulePrefix);
- }
- module.item->setModules(adaptedModules);
- }
-
- const QualifiedIdSet &propsSetInGroup = gatherModulePropertiesSetInGroup(group);
- if (propsSetInGroup.empty())
- return;
- modulePropsSetInGroups.insert(std::make_pair(group, propsSetInGroup));
-
- // Step 3: Adapt scopes in values. This is potentially necessary if module properties
- // get assigned on the group level.
- for (const Item::Module &module : group->modules())
- adjustScopesInGroupModuleInstances(group, module);
-}
-
-void GroupsHandler::Private::handleGroup(Item *product, Item *group)
-{
- propagateModulesFromParent(group);
- if (!evaluator.boolValue(group, StringConstants::conditionProperty()))
- disabledGroups << group;
- for (Item * const child : group->children()) {
- if (child->type() == ItemType::Group)
- handleGroup(product, child);
- }
-}
-
-void GroupsHandler::Private::adjustScopesInGroupModuleInstances(Item *groupItem,
- const Item::Module &module)
-{
- if (!module.item->isPresentModule())
- return;
-
- Item *modulePrototype = module.item->rootPrototype();
- 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();
-
- // Nothing gets inherited for module properties assigned directly in the group.
- // 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()).
- ValuePtr propValue = module.item->ownProperty(propName);
- if (propValue) {
- propValue->setScope(module.item->scope(), {});
- 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;
- do {
- instanceWithProperty = instanceWithProperty->prototype();
- QBS_CHECK(instanceWithProperty);
- propValue = instanceWithProperty->ownProperty(propName);
- } while (!propValue);
- QBS_CHECK(propValue);
-
- if (propValue->type() != Value::JSSourceValueType)
- continue;
-
- if (propValue->scope())
- module.item->setProperty(propName, propValue->clone());
- }
-
- for (const ValuePtr &prop : module.item->properties()) {
- if (prop->type() != Value::JSSourceValueType) {
- QBS_CHECK(!prop->next());
- continue;
- }
- for (ValuePtr v = prop; v; v = v->next()) {
- if (!v->scope())
- continue;
- for (const Item::Module &module : groupItem->modules()) {
- if (v->scope() == module.item->prototype()) {
- v->setScope(module.item, {});
- break;
- }
- }
- }
- }
-}
-
-QualifiedIdSet GroupsHandler::Private::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;
-}
-
-} // namespace qbs::Internal
diff --git a/src/lib/corelib/language/groupshandler.h b/src/lib/corelib/language/groupshandler.h
deleted file mode 100644
index d3948cbef..000000000
--- a/src/lib/corelib/language/groupshandler.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#pragma once
-
-#include "qualifiedid.h"
-
-#include <tools/set.h>
-
-#include <unordered_map>
-#include <utility>
-
-namespace qbs {
-class SetupProjectParameters;
-namespace Internal {
-class Evaluator;
-class Item;
-class Logger;
-class ModuleInstantiator;
-
-// Sets up Group items for the actual resolving stage. Responsibilities:
-// - Moving Group items located in modules over to the product.
-// - Identifying groups declaring target artifacts and marking them accordingly.
-// - Setting up group-level module instances to ensure proper resolving of per-group module
-// properties.
-// - As a side effect of the above point, collecting all properties set on the Group level
-// to help the ProjectResolver decide which properties need to be re-evaluated at all,
-// which is an important optimization (see commit 9cd8653eef).
-class GroupsHandler
-{
-public:
- GroupsHandler(const SetupProjectParameters &parameters, ModuleInstantiator &instantiator,
- Evaluator &evaluator, Logger &logger);
- ~GroupsHandler();
-
- void setupGroups(Item *product, Item *productScope);
- std::unordered_map<const Item *, QualifiedIdSet> modulePropertiesSetInGroups() const;
- Set<Item *> disabledGroups() const;
-
- void printProfilingInfo(int indent);
-
-private:
- class Private;
- Private * const d;
-};
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/itemreader.cpp b/src/lib/corelib/language/itemreader.cpp
deleted file mode 100644
index e71eba43d..000000000
--- a/src/lib/corelib/language/itemreader.cpp
+++ /dev/null
@@ -1,238 +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 "itemreader.h"
-
-#include "deprecationinfo.h"
-#include "evaluator.h"
-#include "item.h"
-#include "itemreadervisitorstate.h"
-#include "value.h"
-
-#include <tools/profiling.h>
-#include <tools/stringconstants.h>
-#include <tools/stlutils.h>
-
-#include <QtCore/qfileinfo.h>
-
-#include <algorithm>
-
-namespace qbs {
-namespace Internal {
-
-static void makePathsCanonical(QStringList &paths)
-{
- Internal::removeIf(paths, [](QString &p) {
- p = QFileInfo(p).canonicalFilePath();
- return p.isEmpty();
- });
-}
-
-ItemReader::ItemReader(Logger &logger)
- : m_visitorState(std::make_unique<ItemReaderVisitorState>(logger))
-{
-}
-
-ItemReader::~ItemReader() = default;
-
-void ItemReader::setSearchPaths(const QStringList &searchPaths)
-{
- m_searchPaths = searchPaths;
- makePathsCanonical(m_searchPaths);
- m_allSearchPaths.clear();
-}
-
-void ItemReader::pushExtraSearchPaths(const QStringList &extraSearchPaths)
-{
- m_extraSearchPaths.push_back(extraSearchPaths);
- makePathsCanonical(m_extraSearchPaths.back());
- m_allSearchPaths.clear();
-}
-
-void ItemReader::popExtraSearchPaths()
-{
- m_extraSearchPaths.pop_back();
- m_allSearchPaths.clear();
-}
-
-const std::vector<QStringList> &ItemReader::extraSearchPathsStack() const
-{
- return m_extraSearchPaths;
-}
-
-void ItemReader::setExtraSearchPathsStack(const std::vector<QStringList> &s)
-{
- m_extraSearchPaths = s;
- m_allSearchPaths.clear();
-}
-
-void ItemReader::clearExtraSearchPathsStack()
-{
- m_extraSearchPaths.clear();
- m_allSearchPaths.clear();
-}
-
-const QStringList &ItemReader::allSearchPaths() const
-{
- if (m_allSearchPaths.empty()) {
- std::for_each(m_extraSearchPaths.crbegin(), m_extraSearchPaths.crend(),
- [this] (const QStringList &paths) {
- m_allSearchPaths += paths;
- });
- m_allSearchPaths += m_searchPaths;
- m_allSearchPaths.removeDuplicates();
- }
- return m_allSearchPaths;
-}
-
-Item *ItemReader::readFile(const QString &filePath)
-{
- AccumulatingTimer readFileTimer(m_elapsedTime != -1 ? &m_elapsedTime : nullptr);
- return m_visitorState->readFile(filePath, allSearchPaths(), m_pool);
-}
-
-Item *ItemReader::readFile(const QString &filePath, const CodeLocation &referencingLocation)
-{
- try {
- return readFile(filePath);
- } catch (const ErrorInfo &e) {
- if (e.hasLocation())
- throw;
- throw ErrorInfo(e.toString(), referencingLocation);
- }
-}
-
-Set<QString> ItemReader::filesRead() const
-{
- return m_visitorState->filesRead();
-}
-
-void ItemReader::setEnableTiming(bool on)
-{
- m_elapsedTime = on ? 0 : -1;
-}
-
-void ItemReader::setDeprecationWarningMode(DeprecationWarningMode mode)
-{
- m_visitorState->setDeprecationWarningMode(mode);
-}
-
-void ItemReader::handlePropertyOptions(Item *optionsItem, Evaluator &evaluator)
-{
- const QString name = evaluator.stringValue(optionsItem, StringConstants::nameProperty());
- if (name.isEmpty()) {
- throw ErrorInfo(Tr::tr("PropertyOptions item needs a name property"),
- optionsItem->location());
- }
- const QString description = evaluator.stringValue(
- optionsItem, StringConstants::descriptionProperty());
- const auto removalVersion = Version::fromString(
- evaluator.stringValue(optionsItem, StringConstants::removalVersionProperty()));
- const auto allowedValues = 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_visitorState->logger().printWarning(e);
- }
- optionsItem->parent()->setPropertyDeclaration(name, decl);
-}
-
-void ItemReader::handleAllPropertyOptionsItems(Item *item, Evaluator &evaluator)
-{
- QList<Item *> childItems = item->children();
- auto childIt = childItems.begin();
- while (childIt != childItems.end()) {
- Item * const child = *childIt;
- if (child->type() == ItemType::PropertyOptions) {
- handlePropertyOptions(child, evaluator);
- childIt = childItems.erase(childIt);
- } else {
- handleAllPropertyOptionsItems(child, evaluator);
- ++childIt;
- }
- }
- item->setChildren(childItems);
-}
-
-Item *ItemReader::setupItemFromFile(
- const QString &filePath, const CodeLocation &referencingLocation, Evaluator &evaluator)
-{
- Item *item = readFile(filePath, referencingLocation);
- handleAllPropertyOptionsItems(item, evaluator);
- return item;
-}
-
-SearchPathsManager::SearchPathsManager(ItemReader &itemReader, const QStringList &extraSearchPaths)
- : m_itemReader(itemReader),
- m_oldSize(itemReader.extraSearchPathsStack().size())
-{
- if (!extraSearchPaths.isEmpty())
- m_itemReader.pushExtraSearchPaths(extraSearchPaths);
-}
-
-SearchPathsManager::~SearchPathsManager()
-{
- while (m_itemReader.extraSearchPathsStack().size() > m_oldSize)
- m_itemReader.popExtraSearchPaths();
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/itemreader.h b/src/lib/corelib/language/itemreader.h
deleted file mode 100644
index 751c9b1b6..000000000
--- a/src/lib/corelib/language/itemreader.h
+++ /dev/null
@@ -1,121 +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$
-**
-****************************************************************************/
-
-#ifndef QBS_ITEMREADER_H
-#define QBS_ITEMREADER_H
-
-#include <logging/logger.h>
-#include <tools/deprecationwarningmode.h>
-#include <tools/set.h>
-
-#include <QtCore/qstringlist.h>
-
-#include <memory>
-
-namespace qbs {
-namespace Internal {
-class Evaluator;
-class Item;
-class ItemPool;
-class ItemReaderVisitorState;
-
-/*
- * Reads a qbs file and creates a tree of Item objects.
- *
- * In this stage the following steps are performed:
- * - The QML/JS parser creates the AST.
- * - The AST is converted to a tree of Item objects.
- *
- * This class is also responsible for the QMLish inheritance semantics.
- */
-class ItemReader
-{
-public:
- ItemReader(Logger &logger);
- ~ItemReader();
-
- void setPool(ItemPool *pool) { m_pool = pool; }
- void setSearchPaths(const QStringList &searchPaths);
- void pushExtraSearchPaths(const QStringList &extraSearchPaths);
- void popExtraSearchPaths();
- const std::vector<QStringList> &extraSearchPathsStack() const;
- void setExtraSearchPathsStack(const std::vector<QStringList> &s);
- void clearExtraSearchPathsStack();
- const QStringList &allSearchPaths() const;
-
- // Parses a file, creates an item for it, generates PropertyDeclarations from
- // PropertyOptions items and removes said items from the item tree.
- Item *setupItemFromFile(const QString &filePath, const CodeLocation &referencingLocation,
- Evaluator &evaluator);
-
- Set<QString> filesRead() const;
-
- void setEnableTiming(bool on);
- qint64 elapsedTime() const { return m_elapsedTime; }
-
- void setDeprecationWarningMode(DeprecationWarningMode mode);
-
-private:
- Item *readFile(const QString &filePath);
- Item *readFile(const QString &filePath, const CodeLocation &referencingLocation);
- void handlePropertyOptions(Item *optionsItem, Evaluator &evaluator);
- void handleAllPropertyOptionsItems(Item *item, Evaluator &evaluator);
-
- ItemPool *m_pool = nullptr;
- QStringList m_searchPaths;
- std::vector<QStringList> m_extraSearchPaths;
- mutable QStringList m_allSearchPaths;
- const std::unique_ptr<ItemReaderVisitorState> m_visitorState;
- qint64 m_elapsedTime = -1;
-};
-
-class SearchPathsManager {
-public:
- SearchPathsManager(ItemReader &itemReader, const QStringList &extraSearchPaths = {});
- ~SearchPathsManager();
-
-private:
- ItemReader &m_itemReader;
- size_t m_oldSize{0};
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // QBS_ITEMREADER_H
diff --git a/src/lib/corelib/language/itemreaderastvisitor.cpp b/src/lib/corelib/language/itemreaderastvisitor.cpp
deleted file mode 100644
index f43104836..000000000
--- a/src/lib/corelib/language/itemreaderastvisitor.cpp
+++ /dev/null
@@ -1,391 +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 "itemreaderastvisitor.h"
-
-#include "astimportshandler.h"
-#include "astpropertiesitemhandler.h"
-#include "asttools.h"
-#include "builtindeclarations.h"
-#include "filecontext.h"
-#include "identifiersearch.h"
-#include "item.h"
-#include "itemreadervisitorstate.h"
-#include "value.h"
-
-#include <api/languageinfo.h>
-#include <jsextensions/jsextensions.h>
-#include <parser/qmljsast_p.h>
-#include <tools/codelocation.h>
-#include <tools/error.h>
-#include <tools/qbsassert.h>
-#include <tools/qttools.h>
-#include <tools/stringconstants.h>
-#include <logging/translator.h>
-
-#include <algorithm>
-
-using namespace QbsQmlJS;
-
-namespace qbs {
-namespace Internal {
-
-ItemReaderASTVisitor::ItemReaderASTVisitor(ItemReaderVisitorState &visitorState,
- FileContextPtr file, ItemPool *itemPool, Logger &logger)
- : m_visitorState(visitorState)
- , m_file(std::move(file))
- , m_itemPool(itemPool)
- , m_logger(logger)
-{
-}
-
-bool ItemReaderASTVisitor::visit(AST::UiProgram *uiProgram)
-{
- ASTImportsHandler importsHandler(m_visitorState, m_logger, m_file);
- importsHandler.handleImports(uiProgram->imports);
- m_typeNameToFile = importsHandler.typeNameFileMap();
- return true;
-}
-
-static ItemValuePtr findItemProperty(const Item *container, const Item *item)
-{
- ItemValuePtr itemValue;
- const auto &srcprops = container->properties();
- auto it = std::find_if(srcprops.begin(), srcprops.end(), [item] (const ValuePtr &v) {
- return v->type() == Value::ItemValueType
- && std::static_pointer_cast<ItemValue>(v)->item() == item;
- });
- if (it != srcprops.end())
- itemValue = std::static_pointer_cast<ItemValue>(it.value());
- return itemValue;
-}
-
-bool ItemReaderASTVisitor::visit(AST::UiObjectDefinition *ast)
-{
- const QString typeName = ast->qualifiedTypeNameId->name.toString();
- const CodeLocation itemLocation = toCodeLocation(ast->qualifiedTypeNameId->identifierToken);
- const Item *baseItem = nullptr;
- Item *mostDerivingItem = nullptr;
-
- Item *item = Item::create(m_itemPool, ItemType::Unknown);
- item->setFile(m_file);
- item->setLocation(itemLocation);
-
- // Inheritance resolving, part 1: Find out our actual type name (needed for setting
- // up children and alternatives).
- const QStringList fullTypeName = toStringList(ast->qualifiedTypeNameId);
- const QString baseTypeFileName = m_typeNameToFile.value(fullTypeName);
- ItemType itemType;
- if (!baseTypeFileName.isEmpty()) {
- const bool isMostDerivingItem = (m_visitorState.mostDerivingItem() == nullptr);
- if (isMostDerivingItem)
- m_visitorState.setMostDerivingItem(item);
- mostDerivingItem = m_visitorState.mostDerivingItem();
- baseItem = m_visitorState.readFile(baseTypeFileName, m_file->searchPaths(), m_itemPool);
- if (isMostDerivingItem)
- m_visitorState.setMostDerivingItem(nullptr);
- QBS_CHECK(baseItem->type() <= ItemType::LastActualItem);
- itemType = baseItem->type();
- } else {
- if (fullTypeName.size() > 1) {
- throw ErrorInfo(Tr::tr("Invalid item '%1'. Did you mean to set a module property?")
- .arg(fullTypeName.join(QLatin1Char('.'))), itemLocation);
- }
- itemType = BuiltinDeclarations::instance().typeForName(typeName, itemLocation);
- checkDeprecationStatus(itemType, typeName, itemLocation);
- if (itemType == ItemType::Properties && m_item && m_item->type() == ItemType::SubProject)
- itemType = ItemType::PropertiesInSubProject;
- }
-
- item->m_type = itemType;
-
- if (m_item)
- Item::addChild(m_item, item); // Add this item to the children of the parent item.
- else
- m_item = item; // This is the root item.
-
- if (ast->initializer) {
- Item *mdi = m_visitorState.mostDerivingItem();
- m_visitorState.setMostDerivingItem(nullptr);
- qSwap(m_item, item);
- const ItemType oldInstanceItemType = m_instanceItemType;
- if (itemType == ItemType::Parameters || itemType == ItemType::Depends)
- m_instanceItemType = ItemType::ModuleParameters;
- ast->initializer->accept(this);
- m_instanceItemType = oldInstanceItemType;
- qSwap(m_item, item);
- m_visitorState.setMostDerivingItem(mdi);
- }
-
- ASTPropertiesItemHandler(item).handlePropertiesItems();
-
- // Inheritance resolving, part 2 (depends on alternatives having been set up).
- if (baseItem) {
- inheritItem(item, baseItem);
- if (baseItem->file()->idScope()) {
- // Make ids from the derived file visible in the base file.
- // ### Do we want to turn off this feature? It's QMLish but kind of strange.
- item->file()->ensureIdScope(m_itemPool);
- baseItem->file()->idScope()->setPrototype(item->file()->idScope());
-
- // Replace the base item with the most deriving item.
- ItemValuePtr baseItemIdValue = findItemProperty(baseItem->file()->idScope(), baseItem);
- if (baseItemIdValue)
- baseItemIdValue->setItem(mostDerivingItem);
- }
- } else {
- // Only the item at the top of the inheritance chain is a built-in item.
- // We cannot do this in "part 1", because then the visitor would complain about duplicate
- // bindings.
- item->setupForBuiltinType(m_visitorState.deprecationWarningMode(), m_logger);
- }
-
- return false;
-}
-
-void ItemReaderASTVisitor::checkDuplicateBinding(Item *item, const QStringList &bindingName,
- const AST::SourceLocation &sourceLocation)
-{
- if (Q_UNLIKELY(item->hasOwnProperty(bindingName.last()))) {
- QString msg = Tr::tr("Duplicate binding for '%1'");
- throw ErrorInfo(msg.arg(bindingName.join(QLatin1Char('.'))),
- toCodeLocation(sourceLocation));
- }
-}
-
-bool ItemReaderASTVisitor::visit(AST::UiPublicMember *ast)
-{
- PropertyDeclaration p;
- if (Q_UNLIKELY(ast->name.isEmpty()))
- throw ErrorInfo(Tr::tr("public member without name"));
- if (Q_UNLIKELY(ast->memberType.isEmpty()))
- throw ErrorInfo(Tr::tr("public member without type"));
- if (Q_UNLIKELY(ast->type == AST::UiPublicMember::Signal))
- throw ErrorInfo(Tr::tr("public member with signal type not supported"));
- p.setName(ast->name.toString());
- p.setType(PropertyDeclaration::propertyTypeFromString(ast->memberType.toString()));
- if (p.type() == PropertyDeclaration::UnknownType) {
- throw ErrorInfo(Tr::tr("Unknown type '%1' in property declaration.")
- .arg(ast->memberType.toString()), toCodeLocation(ast->typeToken));
- }
- if (Q_UNLIKELY(!ast->typeModifier.isEmpty())) {
- throw ErrorInfo(Tr::tr("public member with type modifier '%1' not supported").arg(
- ast->typeModifier.toString()));
- }
- if (ast->isReadonlyMember)
- p.setFlags(PropertyDeclaration::ReadOnlyFlag);
-
- m_item->m_propertyDeclarations.insert(p.name(), p);
-
- const JSSourceValuePtr value = JSSourceValue::create();
- value->setFile(m_file);
- if (ast->statement) {
- handleBindingRhs(ast->statement, value);
- const QStringList bindingName(p.name());
- checkDuplicateBinding(m_item, bindingName, ast->colonToken);
- }
-
- m_item->setProperty(p.name(), value);
- return false;
-}
-
-bool ItemReaderASTVisitor::visit(AST::UiScriptBinding *ast)
-{
- QBS_CHECK(ast->qualifiedId);
- QBS_CHECK(!ast->qualifiedId->name.isEmpty());
-
- const QStringList bindingName = toStringList(ast->qualifiedId);
-
- if (bindingName.length() == 1 && bindingName.front() == QStringLiteral("id")) {
- const auto * const expStmt = AST::cast<AST::ExpressionStatement *>(ast->statement);
- if (Q_UNLIKELY(!expStmt))
- throw ErrorInfo(Tr::tr("id: must be followed by identifier"));
- const auto * const idExp = AST::cast<AST::IdentifierExpression *>(expStmt->expression);
- if (Q_UNLIKELY(!idExp || idExp->name.isEmpty()))
- throw ErrorInfo(Tr::tr("id: must be followed by identifier"));
- m_item->m_id = idExp->name.toString();
- m_file->ensureIdScope(m_itemPool);
- ItemValueConstPtr existingId = m_file->idScope()->itemProperty(m_item->id());
- if (existingId) {
- ErrorInfo e(Tr::tr("The id '%1' is not unique.").arg(m_item->id()));
- e.append(Tr::tr("First occurrence is here."), existingId->item()->location());
- e.append(Tr::tr("Next occurrence is here."), m_item->location());
- throw e;
- }
- m_file->idScope()->setProperty(m_item->id(), ItemValue::create(m_item));
- return false;
- }
-
- const JSSourceValuePtr value = JSSourceValue::create();
- handleBindingRhs(ast->statement, value);
-
- Item * const targetItem = targetItemForBinding(bindingName, value);
- checkDuplicateBinding(targetItem, bindingName, ast->qualifiedId->identifierToken);
- targetItem->setProperty(bindingName.last(), value);
- return false;
-}
-
-bool ItemReaderASTVisitor::handleBindingRhs(AST::Statement *statement,
- const JSSourceValuePtr &value)
-{
- QBS_CHECK(statement);
- QBS_CHECK(value);
-
- if (AST::cast<AST::Block *>(statement))
- value->setHasFunctionForm();
-
- value->setFile(m_file);
- value->setSourceCode(textViewOf(m_file->content(), statement));
- value->setLocation(statement->firstSourceLocation().startLine,
- statement->firstSourceLocation().startColumn);
-
- bool usesBase, usesOuter, usesOriginal;
- IdentifierSearch idsearch;
- idsearch.add(StringConstants::baseVar(), &usesBase);
- idsearch.add(StringConstants::outerVar(), &usesOuter);
- idsearch.add(StringConstants::originalVar(), &usesOriginal);
- idsearch.start(statement);
- if (usesBase)
- value->setSourceUsesBase();
- if (usesOuter)
- value->setSourceUsesOuter();
- if (usesOriginal)
- value->setSourceUsesOriginal();
- return false;
-}
-
-CodeLocation ItemReaderASTVisitor::toCodeLocation(const AST::SourceLocation &location) const
-{
- return CodeLocation(m_file->filePath(), location.startLine, location.startColumn);
-}
-
-Item *ItemReaderASTVisitor::targetItemForBinding(const QStringList &bindingName,
- const JSSourceValueConstPtr &value)
-{
- Item *targetItem = m_item;
- const int c = bindingName.size() - 1;
- for (int i = 0; i < c; ++i) {
- ValuePtr v = targetItem->ownProperty(bindingName.at(i));
- if (!v) {
- const ItemType itemType = i < c - 1 ? ItemType::ModulePrefix : m_instanceItemType;
- Item *newItem = Item::create(m_itemPool, itemType);
- newItem->setLocation(value->location());
- v = ItemValue::create(newItem);
- targetItem->setProperty(bindingName.at(i), v);
- }
- if (Q_UNLIKELY(v->type() != Value::ItemValueType)) {
- QString msg = Tr::tr("Binding to non-item property.");
- throw ErrorInfo(msg, value->location());
- }
- targetItem = std::static_pointer_cast<ItemValue>(v)->item();
- }
- return targetItem;
-}
-
-void ItemReaderASTVisitor::inheritItem(Item *dst, const Item *src)
-{
- int insertPos = 0;
- for (Item *child : qAsConst(src->m_children)) {
- dst->m_children.insert(insertPos++, child);
- child->m_parent = dst;
- }
-
- for (const PropertyDeclaration &pd : src->propertyDeclarations()) {
- if (pd.flags().testFlag(PropertyDeclaration::ReadOnlyFlag)
- && dst->hasOwnProperty(pd.name())) {
- throw ErrorInfo(Tr::tr("Cannot set read-only property '%1'.").arg(pd.name()),
- dst->property(pd.name())->location());
- }
- dst->setPropertyDeclaration(pd.name(), pd);
- }
-
- for (auto it = src->properties().constBegin(); it != src->properties().constEnd(); ++it) {
- ValuePtr &v = dst->m_properties[it.key()];
- if (!v) {
- v = it.value();
- continue;
- }
- if (v->type() == Value::ItemValueType && it.value()->type() != Value::ItemValueType)
- throw ErrorInfo(Tr::tr("Binding to non-item property."), v->location());
- if (v->type() != it.value()->type())
- continue;
- switch (v->type()) {
- case Value::JSSourceValueType: {
- JSSourceValuePtr sv = std::static_pointer_cast<JSSourceValue>(v);
- QBS_CHECK(!sv->baseValue());
- const JSSourceValuePtr baseValue = std::static_pointer_cast<JSSourceValue>(it.value());
- sv->setBaseValue(baseValue);
- for (const JSSourceValue::Alternative &alt : sv->m_alternatives)
- alt.value->setBaseValue(baseValue);
- break;
- }
- case Value::ItemValueType:
- inheritItem(std::static_pointer_cast<ItemValue>(v)->item(),
- std::static_pointer_cast<const ItemValue>(it.value())->item());
- break;
- default:
- QBS_CHECK(!"unexpected value type");
- }
- }
-}
-
-void ItemReaderASTVisitor::checkDeprecationStatus(ItemType itemType, const QString &itemName,
- const CodeLocation &itemLocation)
-{
- const ItemDeclaration itemDecl = BuiltinDeclarations::instance().declarationsForType(itemType);
- const ErrorInfo error = itemDecl.checkForDeprecation(m_visitorState.deprecationWarningMode(),
- itemName, itemLocation, m_logger);
- if (error.hasError())
- throw error;
-}
-
-void ItemReaderASTVisitor::doCheckItemTypes(const Item *item)
-{
- const ItemDeclaration decl = BuiltinDeclarations::instance().declarationsForType(item->type());
- for (const Item * const child : item->children()) {
- if (!decl.isChildTypeAllowed(child->type())) {
- throw ErrorInfo(Tr::tr("Items of type '%1' cannot contain items of type '%2'.")
- .arg(item->typeName(), child->typeName()), child->location());
- }
- doCheckItemTypes(child);
- }
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/itemreaderastvisitor.h b/src/lib/corelib/language/itemreaderastvisitor.h
deleted file mode 100644
index e8522efd6..000000000
--- a/src/lib/corelib/language/itemreaderastvisitor.h
+++ /dev/null
@@ -1,97 +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$
-**
-****************************************************************************/
-
-#ifndef QBS_ITEMREADERASTVISITOR_H
-#define QBS_ITEMREADERASTVISITOR_H
-
-#include "forward_decls.h"
-#include "itemtype.h"
-
-#include <logging/logger.h>
-#include <parser/qmljsastvisitor_p.h>
-
-#include <QtCore/qhash.h>
-#include <QtCore/qstringlist.h>
-
-namespace qbs {
-class CodeLocation;
-
-namespace Internal {
-class Item;
-class ItemPool;
-class ItemReaderVisitorState;
-
-class ItemReaderASTVisitor : public QbsQmlJS::AST::Visitor
-{
-public:
- ItemReaderASTVisitor(ItemReaderVisitorState &visitorState, FileContextPtr file,
- ItemPool *itemPool, Logger &logger);
- void checkItemTypes() { doCheckItemTypes(rootItem()); }
-
- Item *rootItem() const { return m_item; }
-
-private:
- bool visit(QbsQmlJS::AST::UiProgram *uiProgram) override;
- bool visit(QbsQmlJS::AST::UiObjectDefinition *ast) override;
- bool visit(QbsQmlJS::AST::UiPublicMember *ast) override;
- bool visit(QbsQmlJS::AST::UiScriptBinding *ast) override;
-
- bool handleBindingRhs(QbsQmlJS::AST::Statement *statement, const JSSourceValuePtr &value);
- CodeLocation toCodeLocation(const QbsQmlJS::AST::SourceLocation &location) const;
- void checkDuplicateBinding(Item *item, const QStringList &bindingName,
- const QbsQmlJS::AST::SourceLocation &sourceLocation);
- Item *targetItemForBinding(const QStringList &binding, const JSSourceValueConstPtr &value);
- static void inheritItem(Item *dst, const Item *src);
- void checkDeprecationStatus(ItemType itemType, const QString &itemName,
- const CodeLocation &itemLocation);
- void doCheckItemTypes(const Item *item);
-
- ItemReaderVisitorState &m_visitorState;
- const FileContextPtr m_file;
- ItemPool * const m_itemPool;
- Logger &m_logger;
- QHash<QStringList, QString> m_typeNameToFile;
- Item *m_item = nullptr;
- ItemType m_instanceItemType = ItemType::ModuleInstancePlaceholder;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // QBS_ITEMREADERASTVISITOR_H
diff --git a/src/lib/corelib/language/itemreadervisitorstate.cpp b/src/lib/corelib/language/itemreadervisitorstate.cpp
deleted file mode 100644
index a51b7eab4..000000000
--- a/src/lib/corelib/language/itemreadervisitorstate.cpp
+++ /dev/null
@@ -1,195 +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 "itemreadervisitorstate.h"
-
-#include "asttools.h"
-#include "filecontext.h"
-#include "itemreaderastvisitor.h"
-
-#include <logging/translator.h>
-#include <parser/qmljsengine_p.h>
-#include <parser/qmljslexer_p.h>
-#include <parser/qmljsparser_p.h>
-#include <tools/error.h>
-
-#include <QtCore/qshareddata.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qfileinfo.h>
-#include <QtCore/qshareddata.h>
-#include <QtCore/qtextstream.h>
-
-namespace qbs {
-namespace Internal {
-
-class ASTCacheValueData : public QSharedData
-{
- Q_DISABLE_COPY(ASTCacheValueData)
-public:
- ASTCacheValueData()
- : ast(nullptr)
- , processing(false)
- {
- }
-
- QString code;
- QbsQmlJS::Engine engine;
- QbsQmlJS::AST::UiProgram *ast;
- bool processing;
-};
-
-class ASTCacheValue
-{
-public:
- ASTCacheValue()
- : d(new ASTCacheValueData)
- {
- }
-
- ASTCacheValue(const ASTCacheValue &other) = default;
-
- void setProcessingFlag(bool b) { d->processing = b; }
- bool isProcessing() const { return d->processing; }
-
- void setCode(const QString &code) { d->code = code; }
- QString code() const { return d->code; }
-
- QbsQmlJS::Engine *engine() const { return &d->engine; }
-
- void setAst(QbsQmlJS::AST::UiProgram *ast) { d->ast = ast; }
- QbsQmlJS::AST::UiProgram *ast() const { return d->ast; }
- bool isValid() const { return d->ast; }
-
-private:
- QExplicitlySharedDataPointer<ASTCacheValueData> d;
-};
-
-class ItemReaderVisitorState::ASTCache : public std::unordered_map<QString, ASTCacheValue> {};
-
-
-ItemReaderVisitorState::ItemReaderVisitorState(Logger &logger)
- : m_logger(logger)
- , m_astCache(std::make_unique<ASTCache>())
-{
-
-}
-
-ItemReaderVisitorState::~ItemReaderVisitorState() = default;
-
-Item *ItemReaderVisitorState::readFile(const QString &filePath, const QStringList &searchPaths,
- ItemPool *itemPool)
-{
- ASTCacheValue &cacheValue = (*m_astCache)[filePath];
- if (cacheValue.isValid()) {
- if (Q_UNLIKELY(cacheValue.isProcessing()))
- throw ErrorInfo(Tr::tr("Loop detected when importing '%1'.").arg(filePath));
- } else {
- QFile file(filePath);
- if (Q_UNLIKELY(!file.open(QFile::ReadOnly)))
- throw ErrorInfo(Tr::tr("Cannot open '%1'.").arg(filePath));
-
- m_filesRead.insert(filePath);
- QTextStream stream(&file);
- setupDefaultCodec(stream);
- const QString &code = stream.readAll();
- QbsQmlJS::Lexer lexer(cacheValue.engine());
- lexer.setCode(code, 1);
- QbsQmlJS::Parser parser(cacheValue.engine());
-
- file.close();
- if (!parser.parse()) {
- const QList<QbsQmlJS::DiagnosticMessage> &parserMessages = parser.diagnosticMessages();
- if (Q_UNLIKELY(!parserMessages.empty())) {
- ErrorInfo err;
- for (const QbsQmlJS::DiagnosticMessage &msg : parserMessages)
- err.append(msg.message, toCodeLocation(filePath, msg.loc));
- throw err;
- }
- }
-
- cacheValue.setCode(code);
- cacheValue.setAst(parser.ast());
- }
-
- const FileContextPtr file = FileContext::create();
- file->setFilePath(QFileInfo(filePath).absoluteFilePath());
- file->setContent(cacheValue.code());
- file->setSearchPaths(searchPaths);
-
- ItemReaderASTVisitor astVisitor(*this, file, itemPool, m_logger);
- {
- class ProcessingFlagManager {
- public:
- ProcessingFlagManager(ASTCacheValue &v) : m_cacheValue(v) { v.setProcessingFlag(true); }
- ~ProcessingFlagManager() { m_cacheValue.setProcessingFlag(false); }
- private:
- ASTCacheValue &m_cacheValue;
- } processingFlagManager(cacheValue);
- cacheValue.ast()->accept(&astVisitor);
- }
- astVisitor.checkItemTypes();
- return astVisitor.rootItem();
-}
-
-void ItemReaderVisitorState::cacheDirectoryEntries(const QString &dirPath, const QStringList &entries)
-{
- m_directoryEntries.insert(dirPath, entries);
-}
-
-bool ItemReaderVisitorState::findDirectoryEntries(const QString &dirPath, QStringList *entries) const
-{
- const auto it = m_directoryEntries.constFind(dirPath);
- if (it == m_directoryEntries.constEnd())
- return false;
- *entries = it.value();
- return true;
-}
-
-Item *ItemReaderVisitorState::mostDerivingItem() const
-{
- return m_mostDerivingItem;
-}
-
-void ItemReaderVisitorState::setMostDerivingItem(Item *item)
-{
- m_mostDerivingItem = item;
-}
-
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/itemreadervisitorstate.h b/src/lib/corelib/language/itemreadervisitorstate.h
deleted file mode 100644
index dc22cfb42..000000000
--- a/src/lib/corelib/language/itemreadervisitorstate.h
+++ /dev/null
@@ -1,89 +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$
-**
-****************************************************************************/
-#ifndef QBS_ITEMREADERVISITORSTATE_H
-#define QBS_ITEMREADERVISITORSTATE_H
-
-#include <tools/deprecationwarningmode.h>
-#include <tools/set.h>
-
-#include <QtCore/qstringlist.h>
-
-#include <memory>
-
-namespace qbs {
-namespace Internal {
-class Item;
-class ItemPool;
-class Logger;
-
-class ItemReaderVisitorState
-{
-public:
- ItemReaderVisitorState(Logger &logger);
- ~ItemReaderVisitorState();
-
- Logger &logger() { return m_logger; }
- Set<QString> filesRead() const { return m_filesRead; }
-
- Item *readFile(const QString &filePath, const QStringList &searchPaths, ItemPool *itemPool);
-
- void cacheDirectoryEntries(const QString &dirPath, const QStringList &entries);
- bool findDirectoryEntries(const QString &dirPath, QStringList *entries) const;
-
- Item *mostDerivingItem() const;
- void setMostDerivingItem(Item *item);
-
- void setDeprecationWarningMode(DeprecationWarningMode mode) { m_deprecationWarningMode = mode; }
- DeprecationWarningMode deprecationWarningMode() const { return m_deprecationWarningMode; }
-
-private:
- DeprecationWarningMode m_deprecationWarningMode = defaultDeprecationWarningMode();
- Logger &m_logger;
- Set<QString> m_filesRead;
- QHash<QString, QStringList> m_directoryEntries;
- Item *m_mostDerivingItem = nullptr;
-
- class ASTCache;
- const std::unique_ptr<ASTCache> m_astCache;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // Include guard.
diff --git a/src/lib/corelib/language/language.cpp b/src/lib/corelib/language/language.cpp
index fe92caba9..58ca987e9 100644
--- a/src/lib/corelib/language/language.cpp
+++ b/src/lib/corelib/language/language.cpp
@@ -41,7 +41,6 @@
#include "artifactproperties.h"
#include "builtindeclarations.h"
-#include "productitemmultiplexer.h"
#include "propertymapinternal.h"
#include "scriptengine.h"
@@ -52,6 +51,7 @@
#include <buildgraph/rulegraph.h> // TODO: Move to language?
#include <buildgraph/transformer.h>
#include <jsextensions/jsextensions.h>
+#include <loader/productitemmultiplexer.h>
#include <logging/categories.h>
#include <logging/translator.h>
#include <tools/buildgraphlocker.h>
diff --git a/src/lib/corelib/language/loader.cpp b/src/lib/corelib/language/loader.cpp
deleted file mode 100644
index 00d944e5f..000000000
--- a/src/lib/corelib/language/loader.cpp
+++ /dev/null
@@ -1,215 +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 "loader.h"
-
-#include "evaluator.h"
-#include "itempool.h"
-#include "language.h"
-#include "projectresolver.h"
-#include "projecttreebuilder.h"
-#include "scriptengine.h"
-
-#include <logging/translator.h>
-#include <tools/fileinfo.h>
-#include <tools/profile.h>
-#include <tools/progressobserver.h>
-#include <tools/qbsassert.h>
-#include <tools/settings.h>
-#include <tools/setupprojectparameters.h>
-#include <tools/stringconstants.h>
-
-#include <QtCore/qdir.h>
-#include <QtCore/qobject.h>
-
-namespace qbs {
-namespace Internal {
-
-Loader::Loader(ScriptEngine *engine, Logger logger)
- : m_logger(std::move(logger))
- , m_progressObserver(nullptr)
- , m_engine(engine)
-{
- m_logger.storeWarnings();
-}
-
-void Loader::setProgressObserver(ProgressObserver *observer)
-{
- m_progressObserver = observer;
-}
-
-void Loader::setSearchPaths(const QStringList &_searchPaths)
-{
- QStringList searchPaths;
- for (const QString &searchPath : _searchPaths) {
- if (!FileInfo::exists(searchPath)) {
- m_logger.qbsWarning() << Tr::tr("Search path '%1' does not exist.")
- .arg(QDir::toNativeSeparators(searchPath));
- } else {
- searchPaths += searchPath;
- }
- }
-
- m_searchPaths = searchPaths;
-}
-
-void Loader::setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes)
-{
- m_oldProjectProbes = oldProbes;
-}
-
-void Loader::setOldProductProbes(const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes)
-{
- m_oldProductProbes = oldProbes;
-}
-
-void Loader::setStoredProfiles(const QVariantMap &profiles)
-{
- m_storedProfiles = profiles;
-}
-
-void Loader::setStoredModuleProviderInfo(const StoredModuleProviderInfo &providerInfo)
-{
- m_storedModuleProviderInfo = providerInfo;
-}
-
-TopLevelProjectPtr Loader::loadProject(const SetupProjectParameters &_parameters)
-{
- SetupProjectParameters parameters = _parameters;
-
- if (parameters.topLevelProfile().isEmpty()) {
- Settings settings(parameters.settingsDirectory());
- QString profileName = settings.defaultProfile();
- if (profileName.isEmpty()) {
- m_logger.qbsDebug() << Tr::tr("No profile specified and no default profile exists. "
- "Using default property values.");
- profileName = Profile::fallbackName();
- }
- parameters.setTopLevelProfile(profileName);
- parameters.expandBuildConfiguration();
- }
-
- setupProjectFilePath(parameters);
- QBS_CHECK(QFileInfo(parameters.projectFilePath()).isAbsolute());
- m_logger.qbsDebug() << "Using project file '"
- << QDir::toNativeSeparators(parameters.projectFilePath()) << "'.";
-
- m_engine->setEnvironment(parameters.adjustedEnvironment());
- m_engine->checkAndClearException({});
- m_engine->clearImportsCache();
- m_engine->clearRequestedProperties();
- m_engine->enableProfiling(parameters.logElapsedTime());
- m_logger.clearWarnings();
- EvalContextSwitcher evalContextSwitcher(m_engine, EvalContext::PropertyEvaluation);
-
- // At this point, we cannot set a sensible total effort, because we know nothing about
- // the project yet. That's why we use a placeholder here, so the user at least
- // sees that an operation is starting. The real total effort will be set later when
- // we have enough information.
- if (m_progressObserver) {
- m_progressObserver->initialize(Tr::tr("Resolving project for configuration %1")
- .arg(TopLevelProject::deriveId(parameters.finalBuildConfigurationTree())), 1);
- m_progressObserver->setScriptEngine(m_engine);
- }
-
- const FileTime resolveTime = FileTime::currentTime();
- Evaluator evaluator(m_engine);
- ItemPool pool;
- ProjectTreeBuilder projectTreeBuilder(parameters, pool, evaluator, m_logger);
- projectTreeBuilder.setProgressObserver(m_progressObserver);
- projectTreeBuilder.setSearchPaths(m_searchPaths);
- projectTreeBuilder.setOldProjectProbes(m_oldProjectProbes);
- projectTreeBuilder.setOldProductProbes(m_oldProductProbes);
- projectTreeBuilder.setLastResolveTime(m_lastResolveTime);
- projectTreeBuilder.setStoredProfiles(m_storedProfiles);
- projectTreeBuilder.setStoredModuleProviderInfo(m_storedModuleProviderInfo);
- const ProjectTreeBuilder::Result loadResult = projectTreeBuilder.load();
- ProjectResolver resolver(&evaluator, loadResult, std::move(parameters), m_logger);
- resolver.setProgressObserver(m_progressObserver);
- TopLevelProjectPtr project = resolver.resolve();
- project->lastStartResolveTime = resolveTime;
- project->lastEndResolveTime = FileTime::currentTime();
-
- // E.g. if the top-level project is disabled.
- if (m_progressObserver)
- m_progressObserver->setFinished();
-
- return project;
-}
-
-void Loader::setupProjectFilePath(SetupProjectParameters &parameters)
-{
- QString projectFilePath = parameters.projectFilePath();
- if (projectFilePath.isEmpty())
- projectFilePath = QDir::currentPath();
- const QFileInfo projectFileInfo(projectFilePath);
- if (!projectFileInfo.exists())
- throw ErrorInfo(Tr::tr("Project file '%1' cannot be found.").arg(projectFilePath));
- if (projectFileInfo.isRelative())
- projectFilePath = projectFileInfo.absoluteFilePath();
- if (projectFileInfo.isFile()) {
- parameters.setProjectFilePath(projectFilePath);
- return;
- }
- if (!projectFileInfo.isDir())
- throw ErrorInfo(Tr::tr("Project file '%1' has invalid type.").arg(projectFilePath));
-
- const QStringList &actualFileNames
- = QDir(projectFilePath).entryList(StringConstants::qbsFileWildcards(), QDir::Files);
- if (actualFileNames.empty()) {
- QString error;
- if (parameters.projectFilePath().isEmpty())
- error = Tr::tr("No project file given and none found in current directory.\n");
- else
- error = Tr::tr("No project file found in directory '%1'.").arg(projectFilePath);
- throw ErrorInfo(error);
- }
- if (actualFileNames.size() > 1) {
- throw ErrorInfo(Tr::tr("More than one project file found in directory '%1'.")
- .arg(projectFilePath));
- }
- projectFilePath.append(QLatin1Char('/')).append(actualFileNames.front());
-
- projectFilePath = QDir::current().filePath(projectFilePath);
- projectFilePath = QDir::cleanPath(projectFilePath);
- parameters.setProjectFilePath(projectFilePath);
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/loader.h b/src/lib/corelib/language/loader.h
deleted file mode 100644
index 2c8b08446..000000000
--- a/src/lib/corelib/language/loader.h
+++ /dev/null
@@ -1,88 +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$
-**
-****************************************************************************/
-#ifndef QBS_LOADER_H
-#define QBS_LOADER_H
-
-#include "forward_decls.h"
-#include "moduleproviderinfo.h"
-#include <logging/logger.h>
-#include <tools/filetime.h>
-
-#include <QtCore/qstringlist.h>
-
-namespace qbs {
-class Settings;
-class SetupProjectParameters;
-namespace Internal {
-class Logger;
-class ProgressObserver;
-class ScriptEngine;
-
-class QBS_AUTOTEST_EXPORT Loader
-{
-public:
- Loader(ScriptEngine *engine, Logger logger);
-
- void setProgressObserver(ProgressObserver *observer);
- void setSearchPaths(const QStringList &searchPaths);
- void setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes);
- void setOldProductProbes(const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes);
- void setLastResolveTime(const FileTime &time) { m_lastResolveTime = time; }
- void setStoredProfiles(const QVariantMap &profiles);
- void setStoredModuleProviderInfo(const StoredModuleProviderInfo &providerInfo);
- TopLevelProjectPtr loadProject(const SetupProjectParameters &parameters);
-
- static void setupProjectFilePath(SetupProjectParameters &parameters);
-
-private:
- Logger m_logger;
- ProgressObserver *m_progressObserver;
- ScriptEngine * const m_engine;
- QStringList m_searchPaths;
- std::vector<ProbeConstPtr> m_oldProjectProbes;
- QHash<QString, std::vector<ProbeConstPtr>> m_oldProductProbes;
- StoredModuleProviderInfo m_storedModuleProviderInfo;
- QVariantMap m_storedProfiles;
- FileTime m_lastResolveTime;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // QBS_LOADER_H
diff --git a/src/lib/corelib/language/localprofiles.cpp b/src/lib/corelib/language/localprofiles.cpp
deleted file mode 100644
index 1571ee15e..000000000
--- a/src/lib/corelib/language/localprofiles.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "localprofiles.h"
-
-#include "evaluator.h"
-#include "item.h"
-#include "qualifiedid.h"
-#include "scriptengine.h"
-#include "value.h"
-
-#include <logging/translator.h>
-#include <tools/profile.h>
-#include <tools/scripttools.h>
-#include <tools/stringconstants.h>
-
-namespace qbs::Internal {
-class LocalProfiles::Private
-{
-public:
- Private(const SetupProjectParameters &parameters, Evaluator &evaluator, Logger &logger)
- : parameters(parameters), evaluator(evaluator), logger(logger) {}
-
- void handleProfile(Item *profileItem);
- void evaluateProfileValues(const QualifiedId &namePrefix, Item *item, Item *profileItem,
- QVariantMap &values);
- void collectProfiles(Item *productOrProject, Item *projectScope);
-
- const SetupProjectParameters &parameters;
- Evaluator &evaluator;
- Logger &logger;
- QVariantMap profiles;
-};
-
-LocalProfiles::LocalProfiles(const SetupProjectParameters &parameters, Evaluator &evaluator,
- Logger &logger)
- : d(new Private(parameters, evaluator, logger)) {}
-LocalProfiles::~LocalProfiles() { delete d; }
-
-void LocalProfiles::collectProfilesFromItems(Item *productOrProject, Item *projectScope)
-{
- d->collectProfiles(productOrProject, projectScope);
-}
-
-const QVariantMap &LocalProfiles::profiles() const
-{
- return d->profiles;
-}
-
-void LocalProfiles::Private::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 (profiles.contains(profileName)) {
- throw ErrorInfo(Tr::tr("Local profile '%1' redefined.").arg(profileName),
- profileItem->location());
- }
- profiles.insert(profileName, values);
-}
-
-void LocalProfiles::Private::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:
- if (item != profileItem)
- item->setScope(profileItem);
- const ScopedJsValue sv(evaluator.engine()->context(),
- evaluator.value(item, it.key()));
- values.insert(name.join(QLatin1Char('.')),
- getJsVariant(evaluator.engine()->context(), sv));
- break;
- }
- }
-}
-
-void LocalProfiles::Private::collectProfiles(Item *productOrProject, Item *projectScope)
-{
- Item * scope = productOrProject->type() == ItemType::Project ? projectScope : nullptr;
- for (auto it = productOrProject->children().begin();
- it != productOrProject->children().end();) {
- Item * const childItem = *it;
- if (childItem->type() == ItemType::Profile) {
- if (!scope) {
- const ItemValuePtr itemValue = ItemValue::create(productOrProject);
- scope = Item::create(productOrProject->pool(), ItemType::Scope);
- scope->setProperty(StringConstants::productVar(), itemValue);
- scope->setFile(productOrProject->file());
- scope->setScope(projectScope);
- }
- childItem->setScope(scope);
- try {
- handleProfile(childItem);
- } catch (const ErrorInfo &e) {
- handlePropertyError(e, parameters, logger);
- }
- it = productOrProject->children().erase(it); // TODO: delete item and scope
- } else {
- if (childItem->type() == ItemType::Product)
- collectProfiles(childItem, projectScope);
- ++it;
- }
- }
-}
-
-} // namespace qbs::Internal
diff --git a/src/lib/corelib/language/localprofiles.h b/src/lib/corelib/language/localprofiles.h
deleted file mode 100644
index 3e6b77f4d..000000000
--- a/src/lib/corelib/language/localprofiles.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#pragma once
-
-#include <QVariantMap>
-
-namespace qbs {
-class SetupProjectParameters;
-namespace Internal {
-class Evaluator;
-class Item;
-class Logger;
-
-// This class evaluates all Profile items encountered in the project tree and holds the results.
-class LocalProfiles
-{
-public:
- LocalProfiles(const SetupProjectParameters &parameters, Evaluator &evaluator, Logger &logger);
- ~LocalProfiles();
-
- void collectProfilesFromItems(Item *productOrProject, Item *projectScope);
- const QVariantMap &profiles() const;
-
-private:
- class Private;
- Private * const d;
-};
-
-} // namespace Internal
-} // namespace qbs
-
diff --git a/src/lib/corelib/language/moduleinstantiator.cpp b/src/lib/corelib/language/moduleinstantiator.cpp
deleted file mode 100644
index 91776213a..000000000
--- a/src/lib/corelib/language/moduleinstantiator.cpp
+++ /dev/null
@@ -1,337 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "moduleinstantiator.h"
-
-#include "item.h"
-#include "itempool.h"
-#include "modulepropertymerger.h"
-#include "qualifiedid.h"
-#include "value.h"
-
-#include <logging/logger.h>
-#include <logging/translator.h>
-#include <tools/profiling.h>
-#include <tools/setupprojectparameters.h>
-#include <tools/stringconstants.h>
-
-#include <utility>
-
-namespace qbs::Internal {
-class ModuleInstantiator::Private
-{
-public:
- Private(const SetupProjectParameters &parameters, ItemPool &itemPool,
- ModulePropertyMerger &propertyMerger, Logger &logger)
- : parameters(parameters), itemPool(itemPool), propertyMerger(propertyMerger),
- logger(logger) {}
-
- void overrideProperties(const Context &context);
- void setupScope(const Context &context);
- void exchangePlaceholderItem(Item *product, Item *loadingItem, const QString &loadingName,
- Item *moduleItemForItemValues, const QualifiedId &moduleName, const QString &id,
- bool isProductDependency, bool alreadyLoaded);
- std::pair<const Item *, Item *>
- getOrSetModuleInstanceItem(Item *container, Item *moduleItem, const QualifiedId &moduleName,
- const QString &id, bool replace);
-
- const SetupProjectParameters &parameters;
- ItemPool &itemPool;
- ModulePropertyMerger &propertyMerger;
- Logger &logger;
- qint64 elapsedTime = 0;
-};
-
-ModuleInstantiator::ModuleInstantiator(
- const SetupProjectParameters &parameters, ItemPool &itemPool,
- ModulePropertyMerger &propertyMerger, Logger &logger)
- : d(new Private(parameters, itemPool, propertyMerger, logger)) {}
-ModuleInstantiator::~ModuleInstantiator() { delete d; }
-
-void ModuleInstantiator::instantiate(const Context &context)
-{
- AccumulatingTimer timer(d->parameters.logElapsedTime() ? &d->elapsedTime : nullptr);
-
- // This part needs to be done only once per module and product, and only if the module
- // was successfully loaded.
- if (context.module && !context.alreadyLoaded) {
- context.module->setType(ItemType::ModuleInstance);
- d->overrideProperties(context);
- d->setupScope(context);
- }
-
- // This strange-looking code deals with the fact that our syntax cannot properly handle
- // dependencies on several multiplexed variants of the same product.
- // See getOrSetModuleInstanceItem() below for details.
- Item * const moduleItemForItemValues
- = context.moduleWithSameName ? context.moduleWithSameName
- : context.module;
-
- // Now attach the module instance as an item value to the loading item, potentially
- // evicting a previously attached placeholder item and merging its values into the instance.
- // Note that we potentially do this twice, once for the actual loading item and once
- // for the product item, if the two are different. The reason is this:
- // For convenience, in the product item we allow attaching to properties from indirectly
- // loaded modules. For instance:
- // Product {
- // Depends { name: "Qt.core" }
- // cpp.cxxLanguageVersion: "c++20" // Works even though there is no Depends item for cpp
- // }
- // It's debatable whether that's a good feature, but it has been working (accidentally?)
- // for a long time, and removing it now would break a lot of existing projects.
- d->exchangePlaceholderItem(
- context.product, context.loadingItem, context.loadingName, moduleItemForItemValues,
- context.moduleName, context.id, context.exportingProduct, context.alreadyLoaded);
-
- if (!context.alreadyLoaded && context.product && context.product != context.loadingItem) {
- d->exchangePlaceholderItem(
- context.product, context.product, context.productName, moduleItemForItemValues,
- context.moduleName, context.id, context.exportingProduct, context.alreadyLoaded);
- }
-}
-
-void ModuleInstantiator::Private::exchangePlaceholderItem(
- Item *product, Item *loadingItem, const QString &loadingName, Item *moduleItemForItemValues,
- const QualifiedId &moduleName, const QString &id, bool isProductModule, bool alreadyLoaded)
-{
- // If we have a module item, set an item value pointing to it as a property on the loading item.
- // Evict a possibly existing placeholder item, and return it to us, so we can merge its values
- // into the instance.
- const auto &[oldItem, newItem] = getOrSetModuleInstanceItem(
- loadingItem, moduleItemForItemValues, moduleName, id, true);
-
- // The new item always exists, even if we don't have a module item. In that case, the
- // function created a placeholder item for us, which we then have to turn into a
- // non-present module.
- QBS_CHECK(newItem);
- if (!moduleItemForItemValues) {
- createNonPresentModule(itemPool, moduleName.toString(), QLatin1String("not found"),
- newItem);
- return;
- }
-
- // If the old and the new items are the same, it means the existing item value already
- // pointed to a module instance (rather than a placeholder).
- // This can happen in two cases:
- // a) Multiple identical Depends items on the same level (easily possible with inheritance).
- // b) Dependencies to multiplexed variants of the same product
- // (see getOrSetModuleInstanceItem() below for details).
- if (oldItem == newItem) {
- QBS_CHECK(oldItem->type() == ItemType::ModuleInstance);
- QBS_CHECK(alreadyLoaded || isProductModule);
- return;
- }
-
- // In all other cases, our request to set the module instance item must have been honored.
- QBS_CHECK(newItem == moduleItemForItemValues);
-
- // If there was no placeholder item, we don't have to merge any values and are done.
- if (!oldItem)
- return;
-
- // If an item was replaced, then it must have been a placeholder.
- QBS_CHECK(oldItem->type() == ItemType::ModuleInstancePlaceholder);
-
- // Prevent setting read-only properties.
- for (auto it = oldItem->properties().cbegin(); it != oldItem->properties().cend(); ++it) {
- const PropertyDeclaration &pd = moduleItemForItemValues->propertyDeclaration(it.key());
- if (pd.flags().testFlag(PropertyDeclaration::ReadOnlyFlag)) {
- throw ErrorInfo(Tr::tr("Cannot set read-only property '%1'.").arg(pd.name()),
- it.value()->location());
- }
- }
-
- // Now merge the locally attached values into the actual module instance.
- propertyMerger.mergeFromLocalInstance(product, loadingItem, loadingName,
- oldItem, moduleItemForItemValues);
-
- // TODO: We'd like to delete the placeholder item here, because it's not
- // being referenced anymore and there's a lot of them. However, this
- // is not supported by ItemPool. Investigate the use of std::pmr.
-}
-
-Item *ModuleInstantiator::retrieveModuleInstanceItem(Item *containerItem, const QualifiedId &name)
-{
- return d->getOrSetModuleInstanceItem(containerItem, nullptr, name, {}, false).second;
-}
-
-Item *ModuleInstantiator::retrieveQbsItem(Item *containerItem)
-{
- return retrieveModuleInstanceItem(containerItem, StringConstants::qbsModule());
-}
-
-// This important function deals with retrieving and setting (pseudo-)module instances from/on
-// items.
-// Use case 1: Suppose we resolve the dependency cpp in module Qt.core, which also contains
-// property bindings for cpp such as "cpp.defines: [...]".
-// The "cpp" part of this binding is represented by an ItemValue whose
-// item is of type ModuleInstancePlaceholder, originally set up by ItemReaderASTVisitor.
-// This function will be called with the actual cpp module item and will
-// replace the placeholder item in the item value. It will also return
-// the placeholder item for subsequent merging of its properties with the
-// ones of the actual module (in ModulePropertyMerger::mergeFromLocalInstance()).
-// If there were no cpp property bindings defined in Qt.core, then we'd still
-// have to replace the placeholder item, because references to "cpp" on the
-// right-hand-side of other properties must refer to the module item.
-// This is the common use of this function as employed by resolveProdsucDependencies().
-// Note that if a product has dependencies on more than one variant of a multiplexed
-// product, these dependencies are competing for the item value property name,
-// i.e. this case is not properly supported by the syntax. You must therefore not
-// export properties from multiplexed products that will be different between the
-// variants. In this function, the condition manifests itself by a module instance
-// being encountered instead of a module instance placeholder, in which case
-// nothing is done at all.
-// Use case 2: We inject a fake qbs module into a project item, so qbs properties set in profiles
-// can be accessed in the project level. Doing this is discouraged, and the
-// functionality is kept mostly for backwards compatibility. The moduleItem
-// parameter is null in this case, and the item will be created by the function itself.
-// Use case 3: A temporary qbs module is attached to a product during low-level setup, namely
-// in product multiplexing and setting qbs.profile.
-// Use case 4: Module propagation to the the Group level.
-// In all cases, the first returned item is the existing one, and the second returned item
-// is the new one. Depending on the use case, they might be null and might also be the same item.
-std::pair<const Item *, Item *> ModuleInstantiator::Private::getOrSetModuleInstanceItem(
- Item *container, Item *moduleItem, const QualifiedId &moduleName, const QString &id,
- bool replace)
-{
- Item *instance = container;
- const QualifiedId itemValueName
- = !id.isEmpty() ? QualifiedId::fromString(id) : moduleName;
- for (int i = 0; i < itemValueName.size(); ++i) {
- const QString &moduleNameSegment = itemValueName.at(i);
- const ValuePtr v = instance->ownProperty(itemValueName.at(i));
- if (v && v->type() == Value::ItemValueType) {
- ItemValue * const itemValue = std::static_pointer_cast<ItemValue>(v).get();
- instance = itemValue->item();
- if (i == itemValueName.size() - 1) {
- if (replace && instance != moduleItem
- && instance->type() == ItemType::ModuleInstancePlaceholder) {
- if (!moduleItem)
- moduleItem = Item::create(&itemPool, ItemType::ModuleInstancePlaceholder);
- itemValue->setItem(moduleItem);
- }
- return {instance, itemValue->item()};
- }
- } else {
- Item *newItem = i < itemValueName.size() - 1
- ? Item::create(&itemPool, ItemType::ModulePrefix) : moduleItem
- ? moduleItem : Item::create(&itemPool, ItemType::ModuleInstancePlaceholder);
- instance->setProperty(moduleNameSegment, ItemValue::create(newItem));
- instance = newItem;
- }
- }
- return {nullptr, instance};
-}
-
-void ModuleInstantiator::printProfilingInfo(int indent)
-{
- if (!d->parameters.logElapsedTime())
- return;
- d->logger.qbsLog(LoggerInfo, true) << QByteArray(indent, ' ')
- << Tr::tr("Instantiating modules took %1.")
- .arg(elapsedTimeString(d->elapsedTime));
-}
-
-void ModuleInstantiator::Private::overrideProperties(const ModuleInstantiator::Context &context)
-{
- // Users can override module properties on the command line with the
- // modules.<module-name>.<property-name>:<value> syntax.
- // For simplicity and backwards compatibility, qbs properties can also be given without
- // the "modules." prefix, i.e. just qbs.<property-name>:<value>.
- // In addition, users can override module properties just for certain products
- // using the products.<product-name>.<module-name>.<property-name>:<value> syntax.
- // Such product-specific overrides have higher precedence.
- const QString fullName = context.moduleName.toString();
- const QString generalOverrideKey = QStringLiteral("modules.") + fullName;
- const QString perProductOverrideKey = StringConstants::productsOverridePrefix()
- + context.productName + QLatin1Char('.') + fullName;
- context.module->overrideProperties(parameters.overriddenValuesTree(), generalOverrideKey,
- parameters, logger);
- if (fullName == StringConstants::qbsModule()) {
- context.module->overrideProperties(parameters.overriddenValuesTree(), fullName, parameters,
- logger);
- }
- context.module->overrideProperties(parameters.overriddenValuesTree(), perProductOverrideKey,
- parameters, logger);
-}
-
-void ModuleInstantiator::Private::setupScope(const ModuleInstantiator::Context &context)
-{
- Item * const scope = Item::create(&itemPool, ItemType::Scope);
- QBS_CHECK(context.module->file());
- scope->setFile(context.module->file());
- QBS_CHECK(context.projectScope);
- context.projectScope->copyProperty(StringConstants::projectVar(), scope);
- if (context.productScope)
- context.productScope->copyProperty(StringConstants::productVar(), scope);
- else
- QBS_CHECK(context.moduleName.toString() == StringConstants::qbsModule()); // Dummy product.
-
- if (!context.module->id().isEmpty())
- scope->setProperty(context.module->id(), ItemValue::create(context.module));
- for (Item * const child : context.module->children()) {
- child->setScope(scope);
- if (!child->id().isEmpty())
- scope->setProperty(child->id(), ItemValue::create(child));
- }
- context.module->setScope(scope);
-
- if (context.exportingProduct) {
- QBS_CHECK(context.exportingProduct->type() == ItemType::Product);
-
- const auto exportingProductItemValue = ItemValue::create(context.exportingProduct);
- scope->setProperty(QStringLiteral("exportingProduct"), exportingProductItemValue);
-
- const auto importingProductItemValue = ItemValue::create(context.product);
- scope->setProperty(QStringLiteral("importingProduct"), importingProductItemValue);
-
- // FIXME: This looks wrong. Introduce exportingProject variable?
- scope->setProperty(StringConstants::projectVar(),
- ItemValue::create(context.exportingProduct->parent()));
-
- PropertyDeclaration pd(StringConstants::qbsSourceDirPropertyInternal(),
- PropertyDeclaration::String, QString(),
- PropertyDeclaration::PropertyNotAvailableInConfig);
- context.module->setPropertyDeclaration(pd.name(), pd);
- context.module->setProperty(pd.name(), context.exportingProduct->property(
- StringConstants::sourceDirectoryProperty()));
- }
-}
-
-} // namespace qbs::Internal
diff --git a/src/lib/corelib/language/moduleinstantiator.h b/src/lib/corelib/language/moduleinstantiator.h
deleted file mode 100644
index f235b83fa..000000000
--- a/src/lib/corelib/language/moduleinstantiator.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#pragma once
-
-#include <QtGlobal>
-
-QT_BEGIN_NAMESPACE
-class QString;
-QT_END_NAMESPACE
-
-namespace qbs {
-class SetupProjectParameters;
-namespace Internal {
-class Item;
-class ItemPool;
-class Logger;
-class ModulePropertyMerger;
-class QualifiedId;
-
-// This class is responsible for setting up a proper module instance from a bunch of items:
-// - Set the item type to ItemType::ModuleInstance (from Module or Export).
-// - Apply possible command-line overrides for module properties.
-// - Replace a possible module instance placeholder in the loading item with the actual instance
-// and merge their values employing the ModulePropertyMerger.
-// - Setting up the module instance scope.
-// In addition, it also provides helper functions for retrieving/setting module instance items
-// for special purposes.
-class ModuleInstantiator
-{
-public:
- ModuleInstantiator(const SetupProjectParameters &parameters, ItemPool &itemPool,
- ModulePropertyMerger &propertyMerger, Logger &logger);
- ~ModuleInstantiator();
-
- struct Context {
- Item * const product;
- const QString &productName;
- Item * const loadingItem;
- const QString &loadingName;
- Item * const module;
- Item * const moduleWithSameName;
- Item * const exportingProduct;
- Item * const productScope;
- Item * const projectScope;
- const QualifiedId &moduleName;
- const QString &id;
- const bool alreadyLoaded;
- };
- void instantiate(const Context &context);
-
- // Note that these will also create the respective item value if it does not exist yet.
- Item *retrieveModuleInstanceItem(Item *containerItem, const QualifiedId &name);
- Item *retrieveQbsItem(Item *containerItem);
-
- void printProfilingInfo(int indent);
-
-private:
- class Private;
- Private * const d;
-};
-
-} // namespace Internal
-} // namespace qbs
-
diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp
deleted file mode 100644
index 1772ee808..000000000
--- a/src/lib/corelib/language/moduleloader.cpp
+++ /dev/null
@@ -1,529 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "moduleloader.h"
-
-#include "evaluator.h"
-#include "itemreader.h"
-#include "moduleproviderloader.h"
-#include "productitemmultiplexer.h"
-#include "value.h"
-
-#include <api/languageinfo.h>
-#include <logging/categories.h>
-#include <logging/translator.h>
-#include <tools/error.h>
-#include <tools/fileinfo.h>
-#include <tools/hostosinfo.h>
-#include <tools/profiling.h>
-#include <tools/setupprojectparameters.h>
-#include <tools/stringconstants.h>
-
-#include <QDirIterator>
-#include <QHash>
-
-#include <unordered_map>
-#include <utility>
-
-namespace qbs::Internal {
-
-class ModuleLoader::Private
-{
-public:
- Private(const SetupProjectParameters &setupParameters, ModuleProviderLoader &providerLoader,
- ItemReader &itemReader, Evaluator &evaluator, Logger &logger)
- : setupParameters(setupParameters), providerLoader(providerLoader),
- itemReader(itemReader), evaluator(evaluator), logger(logger) {}
-
- std::pair<Item *, bool> loadModuleFile(const ProductContext &product,
- const QString &moduleName, const QString &filePath);
- std::pair<Item *, bool> getModulePrototype(const ModuleLoader::ProductContext &product,
- const QString &moduleName, const QString &filePath);
- bool evaluateModuleCondition(const ModuleLoader::ProductContext &product, Item *module,
- const QString &fullModuleName);
- void forwardParameterDeclarations(const QualifiedId &moduleName, Item *item,
- const Item::Modules &modules);
-
- const SetupProjectParameters &setupParameters;
- ModuleProviderLoader &providerLoader;
- ItemReader &itemReader;
- Evaluator &evaluator;
- Logger &logger;
-
- // The keys are file paths, the values are module prototype items accompanied by a profile.
- std::unordered_map<QString, std::vector<std::pair<Item *, QString>>> modulePrototypes;
-
- // The keys are module prototypes and products, the values specify whether the module's
- // condition is true for that product.
- std::unordered_map<std::pair<Item *, const Item *>, bool> modulePrototypeEnabledInfo;
-
- std::unordered_map<const Item *, std::vector<ErrorInfo>> unknownProfilePropertyErrors;
- std::unordered_map<const Item *, Item::PropertyDeclarationMap> parameterDeclarations;
- std::unordered_map<const Item *, std::optional<QVariantMap>> providerConfigsPerProduct;
- QHash<std::pair<QString, QualifiedId>, std::optional<QString>> existingModulePathCache;
- std::map<QString, QStringList> moduleDirListCache;
-
- qint64 elapsedTimeModuleProviders = 0;
-};
-
-ModuleLoader::ModuleLoader(
- const SetupProjectParameters &setupParameters, ModuleProviderLoader &providerLoader,
- ItemReader &itemReader, Evaluator &evaluator, Logger &logger)
- : d(new Private(setupParameters, providerLoader, itemReader, evaluator, logger)) { }
-
-ModuleLoader::~ModuleLoader() { delete d; }
-
-struct PrioritizedItem
-{
- PrioritizedItem(Item *item, int priority, int searchPathIndex)
- : item(item), priority(priority), searchPathIndex(searchPathIndex) { }
-
- Item * const item;
- int priority = 0;
- const int searchPathIndex;
-};
-
-static Item *chooseModuleCandidate(const std::vector<PrioritizedItem> &candidates,
- const QString &moduleName)
-{
- // TODO: This should also consider the version requirement.
-
- 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;
-}
-
-ModuleLoader::Result ModuleLoader::searchAndLoadModuleFile(
- const ProductContext &productContext, const CodeLocation &dependsItemLocation,
- const QualifiedId &moduleName, FallbackMode fallbackMode, bool isRequired)
-{
- const auto findExistingModulePath = [&](const QString &searchPath) {
- // 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 = d->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);
- };
- const auto findExistingModulePaths = [&] {
- const QStringList &searchPaths = d->itemReader.allSearchPaths();
- QStringList result;
- result.reserve(searchPaths.size());
- for (const auto &path: searchPaths) {
- const QString dirPath = findExistingModulePath(path);
- if (!dirPath.isEmpty())
- result.append(dirPath);
- }
- return result;
- };
-
- SearchPathsManager searchPathsManager(d->itemReader);
-
- Result loadResult;
- auto existingPaths = findExistingModulePaths();
- if (existingPaths.isEmpty()) { // no suitable names found, try to use providers
- AccumulatingTimer providersTimer(
- d->setupParameters.logElapsedTime() ? &d->elapsedTimeModuleProviders : nullptr);
- std::optional<QVariantMap> &providerConfig
- = d->providerConfigsPerProduct[productContext.productItem];
- auto result = d->providerLoader.executeModuleProviders(
- {productContext.productItem, productContext.projectItem, productContext.name,
- productContext.uniqueName, productContext.moduleProperties, providerConfig},
- dependsItemLocation,
- moduleName,
- fallbackMode);
- loadResult.providerProbes << result.probes;
- if (!providerConfig)
- providerConfig = result.providerConfig;
- if (result.searchPaths) {
- qCDebug(lcModuleLoader) << "Re-checking for module" << moduleName.toString()
- << "with newly added search paths from module provider";
- d->itemReader.pushExtraSearchPaths(*result.searchPaths);
- existingPaths = findExistingModulePaths();
- }
- }
-
- const auto getModuleFileNames = [&](const QString &dirPath) -> QStringList & {
- QStringList &moduleFileNames = d->moduleDirListCache[dirPath];
- if (moduleFileNames.empty()) {
- QDirIterator dirIter(dirPath, StringConstants::qbsFileWildcards());
- while (dirIter.hasNext())
- moduleFileNames += dirIter.next();
- }
- return moduleFileNames;
- };
-
- 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] = d->loadModuleFile(productContext, fullName,
- filePath);
- if (module)
- candidates.emplace_back(module, 0, i);
- if (!triedToLoad)
- it = moduleFileNames.erase(it);
- else
- ++it;
- triedToLoadModule = triedToLoadModule || triedToLoad;
- }
- }
-
- if (candidates.empty()) {
- if (!isRequired) {
- loadResult.moduleItem = createNonPresentModule(
- *productContext.projectItem->pool(), fullName, QStringLiteral("not found"),
- nullptr);
- return loadResult;
- }
- if (Q_UNLIKELY(triedToLoadModule)) {
- throw ErrorInfo(Tr::tr("Module %1 could not be loaded.").arg(fullName),
- dependsItemLocation);
- }
- return loadResult;
- }
-
- if (candidates.size() == 1) {
- loadResult.moduleItem = candidates.at(0).item;
- } else {
- for (auto &candidate : candidates) {
- candidate.priority = d->evaluator.intValue(candidate.item,
- StringConstants::priorityProperty(),
- candidate.priority);
- }
- loadResult.moduleItem = chooseModuleCandidate(candidates, fullName);
- }
-
- const QString fullProductName = ProductItemMultiplexer::fullProductDisplayName(
- productContext.name, productContext.multiplexId);
- const auto it = d->unknownProfilePropertyErrors.find(loadResult.moduleItem);
- if (it != d->unknownProfilePropertyErrors.cend()) {
- ErrorInfo error(Tr::tr("Loading module '%1' for product '%2' failed due to invalid values "
- "in profile '%3':")
- .arg(moduleName.toString(), fullProductName, productContext.profile));
- for (const ErrorInfo &e : it->second)
- error.append(e.toString());
- handlePropertyError(error, d->setupParameters, d->logger);
- }
-
- return loadResult;
-}
-
-std::pair<Item *, bool> ModuleLoader::Private::loadModuleFile(
- const ProductContext &product, const QString &moduleName, const QString &filePath)
-{
- qCDebug(lcModuleLoader) << "loadModuleFile" << moduleName << "from" << filePath;
-
- const auto [module, triedToLoad] = getModulePrototype(product, moduleName, filePath);
- if (!module)
- return {nullptr, triedToLoad};
-
- const auto key = std::make_pair(module, product.productItem);
- const auto it = modulePrototypeEnabledInfo.find(key);
- if (it != modulePrototypeEnabledInfo.end()) {
- qCDebug(lcModuleLoader) << "prototype cache hit (level 2)";
- return {it->second ? module : nullptr, triedToLoad};
- }
-
- if (!evaluateModuleCondition(product, module, moduleName)) {
- qCDebug(lcModuleLoader) << "condition of module" << moduleName << "is false";
- modulePrototypeEnabledInfo.insert({key, false});
- return {nullptr, triedToLoad};
- }
-
- if (moduleName == StringConstants::qbsModule()) {
- module->setProperty(QStringLiteral("hostPlatform"),
- VariantValue::create(HostOsInfo::hostOSIdentifier()));
- module->setProperty(QStringLiteral("hostArchitecture"),
- VariantValue::create(HostOsInfo::hostOSArchitecture()));
- module->setProperty(QStringLiteral("libexecPath"),
- VariantValue::create(setupParameters.libexecPath()));
-
- const Version qbsVersion = LanguageInfo::qbsVersion();
- module->setProperty(QStringLiteral("versionMajor"),
- VariantValue::create(qbsVersion.majorVersion()));
- module->setProperty(QStringLiteral("versionMinor"),
- VariantValue::create(qbsVersion.minorVersion()));
- module->setProperty(QStringLiteral("versionPatch"),
- VariantValue::create(qbsVersion.patchLevel()));
- } else {
- 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());
- }
- parameterDeclarations.insert({module, decls});
- }
-
- modulePrototypeEnabledInfo.insert({key, true});
- return {module, triedToLoad};
-}
-
-std::pair<Item *, bool> ModuleLoader::Private::getModulePrototype(
- const ProductContext &product, const QString &moduleName, const QString &filePath)
-{
- auto &prototypeList = modulePrototypes[filePath];
- for (const auto &prototype : prototypeList) {
- if (prototype.second == product.profile) {
- qCDebug(lcModuleLoader) << "prototype cache hit (level 1)";
- return {prototype.first, true};
- }
- }
-
- Item * const module = itemReader.setupItemFromFile(filePath, CodeLocation(), evaluator);
- if (module->type() != ItemType::Module) {
- qCDebug(lcModuleLoader).nospace()
- << "Alleged module " << moduleName << " has type '"
- << module->typeName() << "', so it's not a module after all.";
- return {nullptr, false};
- }
- prototypeList.emplace_back(module, product.profile);
-
- // 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
- = product.profileModuleProperties.value(moduleName).toMap();
- for (auto it = profileModuleProperties.cbegin(); it != profileModuleProperties.cend(); ++it) {
- if (Q_UNLIKELY(!module->hasProperty(it.key()))) {
- unknownProfilePropertyErrors[module].emplace_back(Tr::tr("Unknown property: %1.%2")
- .arg(moduleName, it.key()));
- continue;
- }
- const PropertyDeclaration decl = module->propertyDeclaration(it.key());
- VariantValuePtr v = VariantValue::create(
- PropertyDeclaration::convertToPropertyType(it.value(), decl.type(),
- QStringList(moduleName), it.key()));
- v->markAsSetByProfile();
- module->setProperty(it.key(), v);
- }
-
- return {module, true};
-}
-
-bool ModuleLoader::Private::evaluateModuleCondition(const ProductContext &product,
- Item *module, const QString &fullModuleName)
-{
- // Evaluator reqires module name to be set.
- module->setProperty(StringConstants::nameProperty(), VariantValue::create(fullModuleName));
-
- // Temporarily make the product's qbs module instance available, so the condition
- // can use qbs.targetOS etc.
- class TempQbsModuleProvider {
- public:
- TempQbsModuleProvider(const ProductContext &product,
- Item *module, const QString &moduleName)
- : m_module(module), m_needsQbsItem(moduleName != StringConstants::qbsModule())
- {
- if (m_needsQbsItem) {
- m_prevQbsItemValue = module->property(StringConstants::qbsModule());
- module->setProperty(StringConstants::qbsModule(),
- product.productItem->property(StringConstants::qbsModule()));
- }
- }
- ~TempQbsModuleProvider()
- {
- if (!m_needsQbsItem)
- return;
- if (m_prevQbsItemValue)
- m_module->setProperty(StringConstants::qbsModule(), m_prevQbsItemValue);
- else
- m_module->removeProperty(StringConstants::qbsModule());
- }
- private:
- Item * const m_module;
- ValuePtr m_prevQbsItemValue;
- const bool m_needsQbsItem;
- };
-
- const TempQbsModuleProvider tempQbs(product, module, fullModuleName);
- return evaluator.boolValue(module, StringConstants::conditionProperty());
-}
-
-class DependencyParameterDeclarationCheck
-{
-public:
- DependencyParameterDeclarationCheck(
- const QString &productName, const Item *productItem,
- const std::unordered_map<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());
- }
-
- const auto decls = m_parameterDeclarations.find(m->item->rootPrototype());
- if (decls == m_parameterDeclarations.end() || !decls->second.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 * const m_productItem;
- const std::unordered_map<const Item *, Item::PropertyDeclarationMap> &m_parameterDeclarations;
-};
-
-void ModuleLoader::checkDependencyParameterDeclarations(const Item *productItem,
- const QString &productName) const
-{
- DependencyParameterDeclarationCheck dpdc(productName, productItem, d->parameterDeclarations);
- for (const Item::Module &dep : productItem->modules()) {
- if (!dep.parameters.empty())
- dpdc(dep.parameters);
- }
-}
-
-void ModuleLoader::forwardParameterDeclarations(const Item *dependsItem,
- const Item::Modules &modules)
-{
- for (auto it = dependsItem->properties().begin(); it != dependsItem->properties().end(); ++it) {
- if (it.value()->type() != Value::ItemValueType)
- continue;
- d->forwardParameterDeclarations(it.key(),
- std::static_pointer_cast<ItemValue>(it.value())->item(),
- modules);
- }
-}
-
-void ModuleLoader::Private::forwardParameterDeclarations(const QualifiedId &moduleName, Item *item,
- const Item::Modules &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(parameterDeclarations[it->item->rootPrototype()]);
- } 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::printProfilingInfo(int indent)
-{
- if (!d->setupParameters.logElapsedTime())
- return;
- d->logger.qbsLog(LoggerInfo, true)
- << QByteArray(indent, ' ')
- << Tr::tr("Running module providers took %1.")
- .arg(elapsedTimeString(d->elapsedTimeModuleProviders));
-}
-
-} // namespace qbs::Internal
diff --git a/src/lib/corelib/language/moduleloader.h b/src/lib/corelib/language/moduleloader.h
deleted file mode 100644
index 37ed34c08..000000000
--- a/src/lib/corelib/language/moduleloader.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#pragma once
-
-#include "forward_decls.h"
-#include "item.h"
-
-#include <QString>
-#include <QVariantMap>
-
-#include <vector>
-
-namespace qbs {
-class CodeLocation;
-class SetupProjectParameters;
-namespace Internal {
-class Evaluator;
-enum class FallbackMode;
-class ItemReader;
-class Logger;
-class ModuleProviderLoader;
-
-class ModuleLoader
-{
-public:
- ModuleLoader(const SetupProjectParameters &setupParameters,
- ModuleProviderLoader &providerLoader, ItemReader &itemReader,
- Evaluator &evaluator, Logger &logger);
- ~ModuleLoader();
-
- struct ProductContext {
- Item * const productItem;
- const Item * const projectItem;
- const QString &name;
- const QString &uniqueName;
- const QString &profile;
- const QString &multiplexId;
- const QVariantMap &moduleProperties;
- const QVariantMap &profileModuleProperties;
- };
- struct Result {
- Item *moduleItem = nullptr;
- std::vector<ProbeConstPtr> providerProbes;
- };
- Result searchAndLoadModuleFile(const ProductContext &productContext,
- const CodeLocation &dependsItemLocation,
- const QualifiedId &moduleName,
- FallbackMode fallbackMode, bool isRequired);
-
- void checkDependencyParameterDeclarations(const Item *productItem,
- const QString &productName) const;
- void forwardParameterDeclarations(const Item *dependsItem, const Item::Modules &modules);
- void printProfilingInfo(int indent);
-
-private:
- class Private;
- Private * const d;
-};
-
-} // namespace Internal
-} // namespace qbs
-
diff --git a/src/lib/corelib/language/modulepropertymerger.cpp b/src/lib/corelib/language/modulepropertymerger.cpp
deleted file mode 100644
index d45329d12..000000000
--- a/src/lib/corelib/language/modulepropertymerger.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "modulepropertymerger.h"
-
-#include "evaluator.h"
-#include "item.h"
-#include "value.h"
-
-#include <logging/translator.h>
-#include <tools/profiling.h>
-#include <tools/set.h>
-#include <tools/setupprojectparameters.h>
-
-namespace qbs::Internal {
-class ModulePropertyMerger::Private
-{
-public:
- Private(const SetupProjectParameters &parameters, Evaluator &evaluator, Logger &logger)
- : parameters(parameters), evaluator(evaluator), logger(logger) {}
-
- int compareValuePriorities(const Item *productItem, const ValueConstPtr &v1,
- const ValueConstPtr &v2);
- ValuePtr mergeListValues(const Item *productItem, const ValuePtr &currentHead, const ValuePtr &newElem);
- void mergePropertyFromLocalInstance(const Item *productItem, Item *loadingItem,
- const QString &loadingName, Item *globalInstance,
- const QString &name, const ValuePtr &value);
- bool doFinalMerge(const Item *productItem, Item *moduleItem);
- bool doFinalMerge(const Item *productItem, const PropertyDeclaration &propertyDecl,
- ValuePtr &propertyValue);
-
- const SetupProjectParameters &parameters;
- Evaluator &evaluator;
- Logger &logger;
- qint64 elapsedTime = 0;
-};
-
-void ModulePropertyMerger::mergeFromLocalInstance(
- const Item *productItem, Item *loadingItem, const QString &loadingName,
- const Item *localInstance, Item *globalInstance)
-{
- AccumulatingTimer t(d->parameters.logElapsedTime() ? &d->elapsedTime : nullptr);
-
- for (auto it = localInstance->properties().constBegin();
- it != localInstance->properties().constEnd(); ++it) {
- d->mergePropertyFromLocalInstance(productItem, loadingItem, loadingName,
- globalInstance, it.key(), it.value());
- }
-}
-
-void ModulePropertyMerger::doFinalMerge(const Item *productItem)
-{
- AccumulatingTimer t(d->parameters.logElapsedTime() ? &d->elapsedTime : nullptr);
-
- Set<const Item *> itemsToInvalidate;
- for (const Item::Module &module : productItem->modules()) {
- if (d->doFinalMerge(productItem, module.item))
- itemsToInvalidate << module.item;
- }
- const auto collectDependentItems = [&itemsToInvalidate](const Item *item,
- const auto &collect) -> bool {
- const bool alreadyInSet = itemsToInvalidate.contains(item);
- bool addItem = false;
- for (const Item::Module &m : item->modules()) {
- if (collect(m.item, collect))
- addItem = true;
- }
- if (addItem && !alreadyInSet)
- itemsToInvalidate << item;
- return addItem || alreadyInSet;
- };
- collectDependentItems(productItem, collectDependentItems);
- for (const Item * const item : itemsToInvalidate)
- d->evaluator.clearCache(item);
-}
-
-void ModulePropertyMerger::printProfilingInfo(int indent)
-{
- if (!d->parameters.logElapsedTime())
- return;
- d->logger.qbsLog(LoggerInfo, true) << QByteArray(indent, ' ')
- << Tr::tr("Merging module property values took %1.")
- .arg(elapsedTimeString(d->elapsedTime));
-}
-
-ModulePropertyMerger::ModulePropertyMerger(
- const SetupProjectParameters &parameters, Evaluator &evaluator, Logger &logger)
- : d(new Private(parameters, evaluator, logger)) { }
-ModulePropertyMerger::~ModulePropertyMerger() { delete d; }
-
-int ModulePropertyMerger::Private::compareValuePriorities(
- const Item *productItem, const ValueConstPtr &v1, const ValueConstPtr &v2)
-{
- QBS_CHECK(v1);
- QBS_CHECK(v2);
- QBS_CHECK(v1->scope() != v2->scope());
- QBS_CHECK(v1->type() == Value::JSSourceValueType || v2->type() == Value::JSSourceValueType);
-
- const int prio1 = v1->priority(productItem);
- const int prio2 = v2->priority(productItem);
- if (prio1 != prio2)
- return prio1 - prio2;
- const int prioDiff = v1->scopeName().compare(v2->scopeName()); // Sic! See 8ff1dd0044
- QBS_CHECK(prioDiff != 0);
- return prioDiff;
-}
-
-ValuePtr ModulePropertyMerger::Private::mergeListValues(
- const Item *productItem, const ValuePtr &currentHead, const ValuePtr &newElem)
-{
- QBS_CHECK(newElem);
- QBS_CHECK(!newElem->next());
-
- if (!currentHead)
- return !newElem->expired(productItem) ? newElem : newElem->next();
-
- QBS_CHECK(!currentHead->expired(productItem));
-
- if (newElem->expired(productItem))
- return currentHead;
-
- if (compareValuePriorities(productItem, currentHead, newElem) < 0) {
- newElem->setNext(currentHead);
- return newElem;
- }
- currentHead->setNext(mergeListValues(productItem, currentHead->next(), newElem));
- return currentHead;
-}
-
-void ModulePropertyMerger::Private::mergePropertyFromLocalInstance(
- const Item *productItem, Item *loadingItem, const QString &loadingName, Item *globalInstance,
- const QString &name, const ValuePtr &value)
-{
- const PropertyDeclaration decl = globalInstance->propertyDeclaration(name);
- if (!decl.isValid()) {
- if (value->type() == Value::ItemValueType || value->createdByPropertiesBlock())
- return;
- throw ErrorInfo(Tr::tr("Property '%1' is not declared.")
- .arg(name), value->location());
- }
- if (const ErrorInfo error = decl.checkForDeprecation(
- parameters.deprecationWarningMode(), value->location(), logger);
- error.hasError()) {
- handlePropertyError(error, parameters, logger);
- return;
- }
- if (value->setInternally()) { // E.g. qbs.architecture after multiplexing.
- globalInstance->setProperty(decl.name(), value);
- return;
- }
- QBS_CHECK(value->type() != Value::ItemValueType);
- const ValuePtr globalVal = globalInstance->ownProperty(decl.name());
- value->setScope(loadingItem, loadingName);
- QBS_CHECK(globalVal);
-
- // Values set internally cannot be overridden by JS values.
- // The same goes for values set on the command line.
- // Note that in both cases, there is no merging for list properties: The override is absolute.
- if (globalVal->setInternally() || globalVal->setByCommandLine())
- return;
-
- QBS_CHECK(value->type() == Value::JSSourceValueType);
-
- if (decl.isScalar()) {
- QBS_CHECK(!globalVal->expired(productItem));
- QBS_CHECK(!value->expired(productItem));
- if (compareValuePriorities(productItem, globalVal, value) < 0) {
- value->setCandidates(globalVal->candidates());
- globalVal->setCandidates({});
- value->addCandidate(globalVal);
- globalInstance->setProperty(decl.name(), value);
- } else {
- globalVal->addCandidate(value);
- }
- } else {
- if (const ValuePtr &newChainStart = mergeListValues(productItem, globalVal, value);
- newChainStart != globalVal) {
- globalInstance->setProperty(decl.name(), newChainStart);
- }
- }
-}
-
-bool ModulePropertyMerger::Private::doFinalMerge(const Item *productItem, Item *moduleItem)
-{
- if (!moduleItem->isPresentModule())
- return false;
- bool mustInvalidateCache = false;
- for (auto it = moduleItem->properties().begin(); it != moduleItem->properties().end(); ++it) {
- if (doFinalMerge(productItem, moduleItem->propertyDeclaration(it.key()), it.value()))
- mustInvalidateCache = true;
- }
- return mustInvalidateCache;
-}
-
-bool ModulePropertyMerger::Private::doFinalMerge(
- const Item *productItem, const PropertyDeclaration &propertyDecl, ValuePtr &propertyValue)
-{
- if (propertyValue->type() == Value::VariantValueType) {
- QBS_CHECK(!propertyValue->next());
- return false;
- }
- if (!propertyDecl.isValid())
- return false; // Caught later by dedicated checker.
- propertyValue->resetPriority();
- if (propertyDecl.isScalar()) {
- if (propertyValue->candidates().empty())
- return false;
- std::pair<int, std::vector<ValuePtr>> candidatesWithHighestPrio;
- candidatesWithHighestPrio.first = propertyValue->priority(productItem);
- candidatesWithHighestPrio.second.push_back(propertyValue);
- for (const ValuePtr &v : propertyValue->candidates()) {
- const int prio = v->priority(productItem);
- if (prio < candidatesWithHighestPrio.first)
- continue;
- if (prio > candidatesWithHighestPrio.first) {
- candidatesWithHighestPrio.first = prio;
- candidatesWithHighestPrio.second = {v};
- continue;
- }
- candidatesWithHighestPrio.second.push_back(v);
- }
- ValuePtr chosenValue = candidatesWithHighestPrio.second.front();
- if (int(candidatesWithHighestPrio.second.size()) > 1) {
- ErrorInfo error(Tr::tr("Conflicting scalar values for property '%1'.")
- .arg(propertyDecl.name()));
- error.append({}, chosenValue->location());
- QBS_CHECK(chosenValue->type() == Value::JSSourceValueType);
- QStringView sourcCode = static_cast<JSSourceValue *>(
- chosenValue.get())->sourceCode();
- for (int i = 1; i < int(candidatesWithHighestPrio.second.size()); ++i) {
- const ValuePtr &v = candidatesWithHighestPrio.second.at(i);
- QBS_CHECK(v->type() == Value::JSSourceValueType);
-
- // Note that this is a bit silly: The source code could still evaluate to
- // different values in the end.
- if (static_cast<JSSourceValue *>(v.get())->sourceCode() != sourcCode)
- error.append({}, v->location());
- }
- if (error.items().size() > 2)
- logger.printWarning(error);
- }
-
- if (propertyValue == chosenValue)
- return false;
- propertyValue = chosenValue;
- return true;
- }
- if (!propertyValue->next())
- return false;
- std::vector<ValuePtr> singleValuesBefore;
- for (ValuePtr current = propertyValue; current;) {
- singleValuesBefore.push_back(current);
- const ValuePtr next = current->next();
- if (next)
- current->setNext({});
- current = next;
- }
- ValuePtr newValue;
- for (const ValuePtr &v : singleValuesBefore)
- newValue = mergeListValues(productItem, newValue, v);
- std::vector<ValuePtr> singleValuesAfter;
- for (ValuePtr current = propertyValue; current; current = current->next())
- singleValuesAfter.push_back(current);
- propertyValue = newValue;
- return singleValuesBefore != singleValuesAfter;
-}
-
-} // namespace qbs::Internal
diff --git a/src/lib/corelib/language/modulepropertymerger.h b/src/lib/corelib/language/modulepropertymerger.h
deleted file mode 100644
index fc388cfbf..000000000
--- a/src/lib/corelib/language/modulepropertymerger.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#pragma once
-
-#include <QtGlobal>
-
-namespace qbs {
-class SetupProjectParameters;
-namespace Internal {
-class Evaluator;
-class Item;
-class Logger;
-
-// This class comprises functions for collecting values attached to module properties
-// in different contexts.
-// For example, in the Qt.core module you will find a property binding such as this:
-// cpp.defines: "QT_CORE_LIB"
-// while in the Qt.widgets module, it will look like this:
-// cpp.defines: "QT_WIDGETS_LIB"
-// A product with a dependency on both these modules will end up with a value of
-// ["QT_WIDGETS_LIB", "QT_CORE_LIB"], plus potentially other defines set elsewhere.
-// Each of these values is assigned a priority that roughly corresponds to the "level" at which
-// the module containing the property binding resides in the dependency hierarchy.
-// For list properties, the priorities determine the order of the respecive values in the
-// final array, for scalar values they determine which one survives. Different scalar values
-// with the same priority trigger a warning message.
-// Since the right-hand side of a binding can refer to properties of the surrounding context,
-// each such value gets its own scope.
-class ModulePropertyMerger
-{
-public:
- ModulePropertyMerger(const SetupProjectParameters &parameters, Evaluator &evaluator,
- Logger &logger);
- ~ModulePropertyMerger();
-
- // This function is called when a module is loaded via a Depends item.
- // loadingItem is the product or module containing the Depends item.
- // loadingName is the name of that module. It is used as a tie-breaker for list property values
- // with equal priority.
- // localInstance is the module instance placeholder in the ItemValue of a property binding,
- // i.e. the "cpp" in "cpp.defines".
- // globalInstance is the actual module into which the properties from localInstance get merged.
- void mergeFromLocalInstance(const Item *productItem, Item *loadingItem,
- const QString &loadingName, const Item *localInstance,
- Item *globalInstance);
-
- // This function is called after all dependencies have been resolved. It uses its global
- // knowledge of module priorities to potentially adjust the order of list values or
- // favor different scalar values. It can also remove previously merged-in values again;
- // this can happen if a module fails to load after it already merged some values, or
- // if it fails validation in the end.
- void doFinalMerge(const Item *productItem);
-
- void printProfilingInfo(int indent);
-
-private:
- class Private;
- Private * const d;
-};
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/moduleproviderloader.cpp b/src/lib/corelib/language/moduleproviderloader.cpp
deleted file mode 100644
index 22605bf9f..000000000
--- a/src/lib/corelib/language/moduleproviderloader.cpp
+++ /dev/null
@@ -1,374 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "moduleproviderloader.h"
-
-#include "builtindeclarations.h"
-#include "evaluator.h"
-#include "item.h"
-#include "itemreader.h"
-#include "probesresolver.h"
-
-#include <language/scriptengine.h>
-#include <language/value.h>
-
-#include <logging/categories.h>
-#include <logging/translator.h>
-
-#include <tools/fileinfo.h>
-#include <tools/jsliterals.h>
-#include <tools/scripttools.h>
-#include <tools/setupprojectparameters.h>
-#include <tools/stlutils.h>
-#include <tools/stringconstants.h>
-
-#include <QtCore/qtemporaryfile.h>
-
-namespace qbs {
-namespace Internal {
-
-ModuleProviderLoader::ModuleProviderLoader(
- const SetupProjectParameters &parameters, ItemReader &reader, Evaluator &evaluator,
- ProbesResolver &probesResolver, Logger &logger)
- : m_parameters(parameters)
- , m_reader(reader)
- , m_evaluator(evaluator)
- , m_probesResolver(probesResolver)
- , m_logger(logger)
-{
-}
-
-ModuleProviderLoader::ModuleProviderResult ModuleProviderLoader::executeModuleProviders(
- const ProductContext &productContext,
- const CodeLocation &dependsItemLocation,
- const QualifiedId &moduleName,
- FallbackMode fallbackMode)
-{
- ModuleProviderLoader::ModuleProviderResult result;
- std::vector<Provider> providersToRun;
- qCDebug(lcModuleLoader) << "Module" << moduleName.toString()
- << "not found, checking for module providers";
- const auto providerNames = getModuleProviders(productContext.productItem);
- if (providerNames) {
- providersToRun = transformed<std::vector<Provider>>(*providerNames, [](const auto &name) {
- return Provider{name, ModuleProviderLookup::Named}; });
- } else {
- for (QualifiedId providerName = moduleName; !providerName.empty();
- providerName.pop_back()) {
- providersToRun.push_back({providerName, ModuleProviderLookup::Scoped});
- }
- }
- result = executeModuleProvidersHelper(productContext, dependsItemLocation, providersToRun);
-
- if (fallbackMode == FallbackMode::Enabled
- && !result.providerFound
- && !providerNames) {
- qCDebug(lcModuleLoader) << "Specific module provider not found for"
- << moduleName.toString() << ", setting up fallback.";
- result = executeModuleProvidersHelper(
- productContext,
- dependsItemLocation,
- {{moduleName, ModuleProviderLookup::Fallback}});
- }
-
- return result;
-}
-
-ModuleProviderLoader::ModuleProviderResult ModuleProviderLoader::executeModuleProvidersHelper(
- const ProductContext &product,
- const CodeLocation &dependsItemLocation,
- const std::vector<Provider> &providers)
-{
- if (providers.empty())
- return {};
- QStringList allSearchPaths;
- ModuleProviderResult result;
- result.providerConfig = product.providerConfig ? *product.providerConfig
- : getModuleProviderConfig(product);
- const auto qbsModule = evaluateQbsModule(product);
- for (const auto &[name, lookupType] : providers) {
- const QVariantMap config = result.providerConfig.value(name.toString()).toMap();
- ModuleProviderInfo &info = m_storedModuleProviderInfo.providers[
- {name.toString(), config, qbsModule, int(lookupType)}];
- const bool fromCache = !info.name.isEmpty();
- if (!fromCache) {
- info.name = name;
- info.config = config;
- info.providerFile = findModuleProviderFile(name, lookupType);
- if (!info.providerFile.isEmpty()) {
- qCDebug(lcModuleLoader) << "Running provider" << name << "at" << info.providerFile;
- const auto evalResult = evaluateModuleProvider(
- product, dependsItemLocation, name, info.providerFile, config, qbsModule);
- info.searchPaths = evalResult.first;
- result.probes << evalResult.second;
- info.transientOutput = m_parameters.dryRun();
- }
- }
- if (info.providerFile.isEmpty()) {
- if (lookupType == ModuleProviderLookup::Named)
- throw ErrorInfo(Tr::tr("Unknown provider '%1'").arg(name.toString()));
- continue;
- }
- if (fromCache)
- qCDebug(lcModuleLoader) << "Re-using provider" << name.toString() << "from cache";
-
- result.providerFound = true;
- if (info.searchPaths.empty()) {
- qCDebug(lcModuleLoader)
- << "Module provider did run, but did not set up any modules.";
- continue;
- }
- qCDebug(lcModuleLoader) << "Module provider added" << info.searchPaths.size()
- << "new search path(s)";
-
- allSearchPaths << info.searchPaths;
- }
- if (allSearchPaths.isEmpty())
- return result;
-
- result.searchPaths = std::move(allSearchPaths);
-
- return result;
-}
-
-QVariantMap ModuleProviderLoader::getModuleProviderConfig(const ProductContext &product)
-{
- QVariantMap providerConfig;
- const ItemValueConstPtr configItemValue =
- product.productItem->itemProperty(StringConstants::moduleProviders());
- if (configItemValue) {
- const std::function<void(const Item *, QualifiedId)> collectMap
- = [this, &providerConfig, &collectMap](const Item *item, const QualifiedId &name) {
- const Item::PropertyMap &props = item->properties();
- for (auto it = props.begin(); it != props.end(); ++it) {
- QVariant value;
- switch (it.value()->type()) {
- case Value::ItemValueType: {
- const auto childItem = static_cast<ItemValue *>(it.value().get())->item();
- childItem->setScope(item->scope());
- collectMap(childItem, QualifiedId(name) << it.key());
- continue;
- }
- case Value::JSSourceValueType: {
- const ScopedJsValue sv(m_evaluator.engine()->context(),
- m_evaluator.value(item, it.key()));
- value = getJsVariant(m_evaluator.engine()->context(), sv);
- break;
- }
- case Value::VariantValueType:
- value = static_cast<VariantValue *>(it.value().get())->value();
- break;
- }
- QVariantMap m = providerConfig.value(name.toString()).toMap();
- m.insert(it.key(), value);
- providerConfig.insert(name.toString(), m);
- }
- };
- configItemValue->item()->setScope(product.productItem);
- collectMap(configItemValue->item(), QualifiedId());
- }
- for (auto it = product.moduleProperties.begin(); it != product.moduleProperties.end(); ++it) {
- if (!it.key().startsWith(QStringLiteral("moduleProviders.")))
- continue;
- const QString provider = it.key().mid(QStringLiteral("moduleProviders.").size());
- const QVariantMap providerConfigFromBuildConfig = it.value().toMap();
- if (providerConfigFromBuildConfig.empty())
- continue;
- QVariantMap currentMapForProvider = providerConfig.value(provider).toMap();
- for (auto propIt = providerConfigFromBuildConfig.begin();
- propIt != providerConfigFromBuildConfig.end(); ++propIt) {
- currentMapForProvider.insert(propIt.key(), propIt.value());
- }
- providerConfig.insert(provider, currentMapForProvider);
- }
- return providerConfig;
-}
-
-std::optional<std::vector<QualifiedId>> ModuleProviderLoader::getModuleProviders(Item *item)
-{
- while (item) {
- const auto providers =
- m_evaluator.optionalStringListValue(item, StringConstants::qbsModuleProviders());
- if (providers) {
- return transformed<std::vector<QualifiedId>>(*providers, [](const auto &provider) {
- return QualifiedId::fromString(provider); });
- }
- item = item->parent();
- }
- return std::nullopt;
-}
-
-QString ModuleProviderLoader::findModuleProviderFile(
- const QualifiedId &name, ModuleProviderLookup lookupType)
-{
- for (const QString &path : m_reader.allSearchPaths()) {
- QString fullPath = FileInfo::resolvePath(path, QStringLiteral("module-providers"));
- switch (lookupType) {
- case ModuleProviderLookup::Named: {
- const auto result =
- FileInfo::resolvePath(fullPath, name.toString() + QStringLiteral(".qbs"));
- if (FileInfo::exists(result)) {
- fullPath = result;
- break;
- }
- [[fallthrough]];
- }
- case ModuleProviderLookup::Scoped:
- for (const QString &component : name)
- fullPath = FileInfo::resolvePath(fullPath, component);
- fullPath = FileInfo::resolvePath(fullPath, QStringLiteral("provider.qbs"));
- break;
- case ModuleProviderLookup::Fallback:
- fullPath = FileInfo::resolvePath(fullPath, QStringLiteral("__fallback/provider.qbs"));
- break;
- }
- if (!FileInfo::exists(fullPath)) {
- qCDebug(lcModuleLoader) << "No module provider found at" << fullPath;
- continue;
- }
- return fullPath;
- }
- return {};
-}
-
-QVariantMap ModuleProviderLoader::evaluateQbsModule(const ProductContext &product) const
-{
- const QString properties[] = {
- QStringLiteral("sysroot"),
- QStringLiteral("toolchain"),
- };
- const auto qbsItemValue = std::static_pointer_cast<ItemValue>(
- product.productItem->property(StringConstants::qbsModule()));
- QVariantMap result;
- for (const auto &property : properties) {
- const ScopedJsValue val(m_evaluator.engine()->context(),
- m_evaluator.value(qbsItemValue->item(), property));
- auto value = getJsVariant(m_evaluator.engine()->context(), val);
- if (!value.isValid())
- continue;
-
- // The xcode module sets qbs.sysroot; the resulting value is bogus before the probes
- // have run.
- if (property == QLatin1String("sysroot") && !FileInfo::isAbsolute(value.toString()))
- continue;
-
- result[property] = std::move(value);
- }
- return result;
-}
-
-Item *ModuleProviderLoader::createProviderScope(const ProductContext &product, const QVariantMap &qbsModule)
-{
- const auto qbsItemValue = std::static_pointer_cast<ItemValue>(
- product.productItem->property(StringConstants::qbsModule()));
-
- Item *fakeQbsModule = Item::create(product.productItem->pool(), ItemType::Scope);
-
- for (auto it = qbsModule.begin(), end = qbsModule.end(); it != end; ++it) {
- fakeQbsModule->setProperty(it.key(), VariantValue::create(it.value()));
- }
-
- Item *scope = Item::create(product.productItem->pool(), ItemType::Scope);
- scope->setFile(qbsItemValue->item()->file());
- scope->setProperty(StringConstants::qbsModule(), ItemValue::create(fakeQbsModule));
- return scope;
-}
-
-std::pair<QStringList, std::vector<ProbeConstPtr> > ModuleProviderLoader::evaluateModuleProvider(const ProductContext &product,
- const CodeLocation &dependsItemLocation,
- const QualifiedId &name,
- const QString &providerFile,
- const QVariantMap &moduleConfig,
- const QVariantMap &qbsModule)
-{
- QTemporaryFile dummyItemFile;
- if (!dummyItemFile.open()) {
- throw ErrorInfo(Tr::tr("Failed to create temporary file for running module provider "
- "for dependency '%1': %2").arg(name.toString(),
- dummyItemFile.errorString()));
- }
- m_tempQbsFiles << dummyItemFile.fileName();
- qCDebug(lcModuleLoader) << "Instantiating module provider at" << providerFile;
- const QString projectBuildDir = product.projectItem->variantProperty(
- StringConstants::buildDirectoryProperty())->value().toString();
- const QString searchPathBaseDir = ModuleProviderInfo::outputDirPath(projectBuildDir, name);
-
- // include qbs module into hash
- auto jsConfig = moduleConfig;
- jsConfig[StringConstants::qbsModule()] = qbsModule;
-
- QTextStream stream(&dummyItemFile);
- using Qt::endl;
- setupDefaultCodec(stream);
- stream << "import qbs.FileInfo" << endl;
- stream << "import qbs.Utilities" << endl;
- stream << "import '" << providerFile << "' as Provider" << endl;
- stream << "Provider {" << endl;
- stream << " name: " << toJSLiteral(name.toString()) << endl;
- stream << " property var config: (" << toJSLiteral(jsConfig) << ')' << endl;
- stream << " outputBaseDir: FileInfo.joinPaths(baseDirPrefix, "
- " Utilities.getHash(JSON.stringify(config)))" << endl;
- stream << " property string baseDirPrefix: " << toJSLiteral(searchPathBaseDir) << endl;
- stream << " property stringList searchPaths: (relativeSearchPaths || [])"
- " .map(function(p) { return FileInfo.joinPaths(outputBaseDir, p); })"
- << endl;
- stream << "}" << endl;
- stream.flush();
- Item * const providerItem = m_reader.setupItemFromFile(
- dummyItemFile.fileName(), dependsItemLocation, m_evaluator);
- if (providerItem->type() != ItemType::ModuleProvider) {
- throw ErrorInfo(Tr::tr("File '%1' declares an item of type '%2', "
- "but '%3' was expected.")
- .arg(providerFile, providerItem->typeName(),
- BuiltinDeclarations::instance().nameForType(ItemType::ModuleProvider)));
- }
-
- providerItem->setScope(createProviderScope(product, qbsModule));
- providerItem->overrideProperties(moduleConfig, name, m_parameters, m_logger);
- std::vector<ProbeConstPtr> probes = m_probesResolver.resolveProbes(
- {product.name, product.uniqueName}, providerItem);
-
- EvalContextSwitcher contextSwitcher(m_evaluator.engine(), EvalContext::ModuleProvider);
- return std::make_pair(m_evaluator.stringListValue(providerItem, QStringLiteral("searchPaths")),
- std::move(probes));
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/moduleproviderloader.h b/src/lib/corelib/language/moduleproviderloader.h
deleted file mode 100644
index b6240cb03..000000000
--- a/src/lib/corelib/language/moduleproviderloader.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef MODULEPROVIDERLOADER_H
-#define MODULEPROVIDERLOADER_H
-
-#include "forward_decls.h"
-#include "moduleproviderinfo.h"
-
-#include <QtCore/qmap.h>
-#include <QtCore/qvariant.h>
-
-#include <optional>
-#include <vector>
-
-namespace qbs {
-class SetupProjectParameters;
-namespace Internal {
-class Evaluator;
-class Item;
-class ItemReader;
-class Logger;
-class ProbesResolver;
-
-enum class FallbackMode { Enabled, Disabled };
-
-class ModuleProviderLoader
-{
-public:
- explicit ModuleProviderLoader(const SetupProjectParameters &parameters, ItemReader &itemReader,
- Evaluator &evaluator, ProbesResolver &probesResolver,
- Logger &logger);
-
- enum class ModuleProviderLookup { Scoped, Named, Fallback };
-
- struct Provider
- {
- QualifiedId name;
- ModuleProviderLookup lookup;
- };
-
- struct ModuleProviderResult
- {
- std::vector<ProbeConstPtr> probes;
- QVariantMap providerConfig;
- bool providerFound = false;
- std::optional<QStringList> searchPaths;
- };
-
- const StoredModuleProviderInfo &storedModuleProviderInfo() const
- {
- return m_storedModuleProviderInfo;
- }
-
- void setStoredModuleProviderInfo(StoredModuleProviderInfo moduleProviderInfo)
- {
- m_storedModuleProviderInfo = std::move(moduleProviderInfo);
- }
-
- const Set<QString> &tempQbsFiles() const { return m_tempQbsFiles; }
-
- struct ProductContext {
- Item * const productItem;
- const Item * const projectItem;
- const QString &name;
- const QString &uniqueName;
- const QVariantMap &moduleProperties;
- const std::optional<QVariantMap> providerConfig;
- };
- ModuleProviderResult executeModuleProviders(
- const ProductContext &productContext,
- const CodeLocation &dependsItemLocation,
- const QualifiedId &moduleName,
- FallbackMode fallbackMode);
-
-private:
- ModuleProviderResult executeModuleProvidersHelper(
- const ProductContext &product,
- const CodeLocation &dependsItemLocation,
- const std::vector<Provider> &providers);
- QVariantMap getModuleProviderConfig(const ProductContext &product);
-
- std::optional<std::vector<QualifiedId>> getModuleProviders(Item *item);
-
- QString findModuleProviderFile(const QualifiedId &name, ModuleProviderLookup lookupType);
- QVariantMap evaluateQbsModule(const ProductContext &product) const;
- Item *createProviderScope(const ProductContext &product, const QVariantMap &qbsModule);
- std::pair<QStringList, std::vector<ProbeConstPtr>> evaluateModuleProvider(
- const ProductContext &product,
- const CodeLocation &location,
- const QualifiedId &name,
- const QString &providerFile,
- const QVariantMap &moduleConfig,
- const QVariantMap &qbsModule);
-
-private:
- const SetupProjectParameters &m_parameters;
- ItemReader &m_reader;
- Evaluator &m_evaluator;
- ProbesResolver &m_probesResolver;
- Logger &m_logger;
- StoredModuleProviderInfo m_storedModuleProviderInfo;
- Set<QString> m_tempQbsFiles;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // MODULEPROVIDERLOADER_H
diff --git a/src/lib/corelib/language/probesresolver.cpp b/src/lib/corelib/language/probesresolver.cpp
deleted file mode 100644
index 1462c706b..000000000
--- a/src/lib/corelib/language/probesresolver.cpp
+++ /dev/null
@@ -1,303 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Copyright (C) 2022 Raphaël Cotty <raphael.cotty@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "probesresolver.h"
-
-#include "evaluator.h"
-#include "filecontext.h"
-#include "item.h"
-#include "itemreader.h"
-#include "language.h"
-#include "scriptengine.h"
-#include "value.h"
-
-#include <api/languageinfo.h>
-#include <language/language.h>
-#include <logging/categories.h>
-#include <logging/logger.h>
-#include <logging/translator.h>
-#include <tools/profiling.h>
-#include <tools/scripttools.h>
-#include <tools/setupprojectparameters.h>
-#include <tools/stringconstants.h>
-
-#include <quickjs.h>
-
-namespace qbs {
-namespace Internal {
-
-static QString probeGlobalId(Item *probe)
-{
- QString id;
-
- for (Item *obj = probe; obj; obj = obj->prototype()) {
- if (!obj->id().isEmpty()) {
- id = obj->id();
- break;
- }
- }
-
- if (id.isEmpty())
- return {};
-
- QBS_CHECK(probe->file());
- return id + QLatin1Char('_') + probe->file()->filePath();
-}
-
-ProbesResolver::ProbesResolver(const SetupProjectParameters &parameters, Evaluator &evaluator,
- Logger &logger)
- : m_parameters(parameters), m_evaluator(evaluator), m_logger(logger)
-{
-}
-
-void ProbesResolver::setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes)
-{
- m_oldProjectProbes.clear();
- for (const ProbeConstPtr& probe : oldProbes)
- m_oldProjectProbes[probe->globalId()] << probe;
-}
-
-void ProbesResolver::setOldProductProbes(
- const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes)
-{
- m_oldProductProbes = oldProbes;
-}
-
-std::vector<ProbeConstPtr> ProbesResolver::resolveProbes(const ProductContext &productContext, Item *item)
-{
- AccumulatingTimer probesTimer(m_parameters.logElapsedTime() ? &m_elapsedTimeProbes : nullptr);
- EvalContextSwitcher evalContextSwitcher(m_evaluator.engine(), EvalContext::ProbeExecution);
- std::vector<ProbeConstPtr> probes;
- for (Item * const child : item->children())
- if (child->type() == ItemType::Probe)
- probes.push_back(resolveProbe(productContext, item, child));
- return probes;
-}
-
-ProbeConstPtr ProbesResolver::resolveProbe(const ProductContext &productContext, Item *parent,
- Item *probe)
-{
- qCDebug(lcModuleLoader) << "Resolving Probe at " << probe->location().toString();
- ++m_probesEncountered;
- const QString &probeId = probeGlobalId(probe);
- if (Q_UNLIKELY(probeId.isEmpty()))
- throw ErrorInfo(Tr::tr("Probe.id must be set."), probe->location());
- const JSSourceValueConstPtr configureScript
- = probe->sourceProperty(StringConstants::configureProperty());
- QBS_CHECK(configureScript);
- if (Q_UNLIKELY(configureScript->sourceCode() == StringConstants::undefinedValue()))
- throw ErrorInfo(Tr::tr("Probe.configure must be set."), probe->location());
- using ProbeProperty = std::pair<QString, ScopedJsValue>;
- std::vector<ProbeProperty> probeBindings;
- ScriptEngine * const engine = m_evaluator.engine();
- JSContext * const ctx = engine->context();
- QVariantMap initialProperties;
- for (Item *obj = probe; obj; obj = obj->prototype()) {
- const Item::PropertyMap &props = obj->properties();
- for (auto it = props.cbegin(); it != props.cend(); ++it) {
- const QString &name = it.key();
- if (name == StringConstants::configureProperty())
- continue;
- const JSValue value = m_evaluator.value(probe, name);
- probeBindings.emplace_back(name, ScopedJsValue(ctx, value));
- if (name != StringConstants::conditionProperty())
- initialProperties.insert(name, getJsVariant(ctx, value));
- }
- }
- const bool condition = m_evaluator.boolValue(probe, StringConstants::conditionProperty());
- const QString &sourceCode = configureScript->sourceCode().toString();
- ProbeConstPtr resolvedProbe;
- if (parent->type() == ItemType::Project
- || productContext.name.startsWith(StringConstants::shadowProductPrefix())) {
- resolvedProbe = findOldProjectProbe(probeId, condition, initialProperties, sourceCode);
- } else {
- resolvedProbe = findOldProductProbe(productContext.uniqueName, condition,
- initialProperties, sourceCode);
- }
- if (!resolvedProbe) {
- resolvedProbe = findCurrentProbe(probe->location(), condition, initialProperties);
- if (resolvedProbe) {
- qCDebug(lcModuleLoader) << "probe results cached from current run";
- ++m_probesCachedCurrent;
- }
- } else {
- qCDebug(lcModuleLoader) << "probe results cached from earlier run";
- ++m_probesCachedOld;
- }
- ScopedJsValue configureScope(ctx, JS_UNDEFINED);
- std::vector<QString> importedFilesUsedInConfigure;
- if (!condition) {
- qCDebug(lcModuleLoader) << "Probe disabled; skipping";
- } else if (!resolvedProbe) {
- ++m_probesRun;
- qCDebug(lcModuleLoader) << "configure script needs to run";
- const Evaluator::FileContextScopes fileCtxScopes
- = m_evaluator.fileContextScopes(configureScript->file());
- configureScope.setValue(engine->newObject());
- for (const ProbeProperty &b : probeBindings)
- setJsProperty(ctx, configureScope, b.first, JS_DupValue(ctx, b.second));
- engine->clearRequestedProperties();
- ScopedJsValue sv(ctx, engine->evaluate(JsValueOwner::Caller,
- configureScript->sourceCodeForEvaluation(), {}, 1,
- {fileCtxScopes.fileScope, fileCtxScopes.importScope, configureScope}));
- if (JsException ex = engine->checkAndClearException(configureScript->location()))
- throw ex.toErrorInfo();
- importedFilesUsedInConfigure = engine->importedFilesUsedInScript();
- } else {
- importedFilesUsedInConfigure = resolvedProbe->importedFilesUsed();
- }
- QVariantMap properties;
- for (const ProbeProperty &b : probeBindings) {
- QVariant newValue;
- if (resolvedProbe) {
- newValue = resolvedProbe->properties().value(b.first);
- } else {
- if (condition) {
- JSValue v = getJsProperty(ctx, configureScope, b.first);
- const JSValue saved = v;
- ScopedJsValue valueMgr(ctx, saved);
- const PropertyDeclaration decl = probe->propertyDeclaration(b.first);
- m_evaluator.convertToPropertyType(decl, probe->location(), v);
-
- // If the value was converted from scalar to array as per our convenience
- // functionality, then the original value is now the only element of a
- // newly allocated array and thus gets deleted via that array.
- // The array itself is owned by the script engine, so we must stay out of
- // memory management here.
- if (v != saved)
- valueMgr.setValue(JS_UNDEFINED);
-
- if (JsException ex = engine->checkAndClearException({}))
- throw ex.toErrorInfo();
- newValue = getJsVariant(ctx, v);
- } else {
- newValue = initialProperties.value(b.first);
- }
- }
- if (newValue != getJsVariant(ctx, b.second))
- probe->setProperty(b.first, VariantValue::create(newValue));
- if (!resolvedProbe)
- properties.insert(b.first, newValue);
- }
- if (!resolvedProbe) {
- resolvedProbe = Probe::create(probeId, probe->location(), condition,
- sourceCode, properties, initialProperties,
- importedFilesUsedInConfigure);
- m_currentProbes[probe->location()] << resolvedProbe;
- }
- return resolvedProbe;
-}
-
-ProbeConstPtr ProbesResolver::findOldProjectProbe(
- const QString &globalId,
- bool condition,
- const QVariantMap &initialProperties,
- const QString &sourceCode) const
-{
- if (m_parameters.forceProbeExecution())
- return {};
-
- for (const ProbeConstPtr &oldProbe : m_oldProjectProbes.value(globalId)) {
- if (probeMatches(oldProbe, condition, initialProperties, sourceCode, CompareScript::Yes))
- return oldProbe;
- }
-
- return {};
-}
-
-ProbeConstPtr ProbesResolver::findOldProductProbe(
- const QString &productName,
- bool condition,
- const QVariantMap &initialProperties,
- const QString &sourceCode) const
-{
- if (m_parameters.forceProbeExecution())
- return {};
-
- for (const ProbeConstPtr &oldProbe : m_oldProductProbes.value(productName)) {
- if (probeMatches(oldProbe, condition, initialProperties, sourceCode, CompareScript::Yes))
- return oldProbe;
- }
-
- return {};
-}
-
-ProbeConstPtr ProbesResolver::findCurrentProbe(
- const CodeLocation &location,
- bool condition,
- const QVariantMap &initialProperties) const
-{
- const std::vector<ProbeConstPtr> &cachedProbes = m_currentProbes.value(location);
- for (const ProbeConstPtr &probe : cachedProbes) {
- if (probeMatches(probe, condition, initialProperties, QString(), CompareScript::No))
- return probe;
- }
- return {};
-}
-
-bool ProbesResolver::probeMatches(const ProbeConstPtr &probe, bool condition,
- const QVariantMap &initialProperties, const QString &configureScript,
- CompareScript compareScript) const
-{
- return probe->condition() == condition
- && probe->initialProperties() == initialProperties
- && (compareScript == CompareScript::No
- || (probe->configureScript() == configureScript
- && !probe->needsReconfigure(m_lastResolveTime)));
-}
-
-void ProbesResolver::printProfilingInfo(int indent)
-{
- if (!m_parameters.logElapsedTime())
- return;
- const QByteArray prefix(indent, ' ');
- m_logger.qbsLog(LoggerInfo, true)
- << prefix
- << Tr::tr("Running Probes took %1.").arg(elapsedTimeString(m_elapsedTimeProbes));
- m_logger.qbsLog(LoggerInfo, true)
- << prefix
- << Tr::tr("%1 probes encountered, %2 configure scripts executed, "
- "%3 re-used from current run, %4 re-used from earlier run.")
- .arg(m_probesEncountered).arg(m_probesRun).arg(m_probesCachedCurrent)
- .arg(m_probesCachedOld);
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/probesresolver.h b/src/lib/corelib/language/probesresolver.h
deleted file mode 100644
index 4aae0b887..000000000
--- a/src/lib/corelib/language/probesresolver.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Copyright (C) 2022 Raphaël Cotty <raphael.cotty@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef PROBESRESOLVER_H
-#define PROBESRESOLVER_H
-
-#include "forward_decls.h"
-
-#include <tools/filetime.h>
-
-#include <QString>
-
-#include <vector>
-
-namespace qbs {
-class SetupProjectParameters;
-namespace Internal {
-class Item;
-class Evaluator;
-class Logger;
-
-class ProbesResolver
-{
-public:
- explicit ProbesResolver(const SetupProjectParameters &parameters, Evaluator &evaluator,
- Logger &logger);
- void setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes);
- void setOldProductProbes(const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes);
- void printProfilingInfo(int indent);
-
- struct ProductContext {
- const QString &name;
- const QString &uniqueName;
- };
- std::vector<ProbeConstPtr> resolveProbes(const ProductContext &productContext, Item *item);
-
-private:
- ProbeConstPtr findOldProjectProbe(const QString &globalId, bool condition,
- const QVariantMap &initialProperties,
- const QString &sourceCode) const;
- ProbeConstPtr findOldProductProbe(const QString &productName, bool condition,
- const QVariantMap &initialProperties,
- const QString &sourceCode) const;
- ProbeConstPtr findCurrentProbe(const CodeLocation &location, bool condition,
- const QVariantMap &initialProperties) const;
- enum class CompareScript { No, Yes };
- bool probeMatches(const ProbeConstPtr &probe, bool condition,
- const QVariantMap &initialProperties, const QString &configureScript,
- CompareScript compareScript) const;
- ProbeConstPtr resolveProbe(const ProductContext &productContext, Item *parent, Item *probe);
-
- qint64 m_elapsedTimeProbes = 0;
- quint64 m_probesEncountered = 0;
- quint64 m_probesRun = 0;
- quint64 m_probesCachedCurrent = 0;
- quint64 m_probesCachedOld = 0;
-
- const SetupProjectParameters &m_parameters;
- Evaluator &m_evaluator;
- Logger &m_logger;
- QHash<QString, std::vector<ProbeConstPtr>> m_oldProjectProbes;
- QHash<QString, std::vector<ProbeConstPtr>> m_oldProductProbes;
- FileTime m_lastResolveTime;
- QHash<CodeLocation, std::vector<ProbeConstPtr>> m_currentProbes;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // PROBESRESOLVER_H
diff --git a/src/lib/corelib/language/productitemmultiplexer.cpp b/src/lib/corelib/language/productitemmultiplexer.cpp
deleted file mode 100644
index c737be7f1..000000000
--- a/src/lib/corelib/language/productitemmultiplexer.cpp
+++ /dev/null
@@ -1,288 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "productitemmultiplexer.h"
-
-#include "evaluator.h"
-#include "item.h"
-#include "scriptengine.h"
-#include "value.h"
-
-#include <logging/translator.h>
-#include <tools/scripttools.h>
-#include <tools/setupprojectparameters.h>
-#include <tools/stringconstants.h>
-
-#include <QJsonDocument>
-#include <QThreadStorage>
-
-#include <vector>
-
-
-namespace qbs::Internal {
-namespace {
-using MultiplexConfigurationByIdTable = QThreadStorage<QHash<QString, QVariantMap>>;
-using MultiplexRow = std::vector<VariantValuePtr>;
-using MultiplexTable = std::vector<MultiplexRow>;
-class MultiplexInfo
-{
-public:
- std::vector<QString> properties;
- MultiplexTable table;
- bool aggregate = false;
- VariantValuePtr multiplexedType;
-
- QString toIdString(size_t row) const;
-};
-} // namespace
-
-Q_GLOBAL_STATIC(MultiplexConfigurationByIdTable, multiplexConfigurationsById);
-
-class ProductItemMultiplexer::Private
-{
-public:
- Private(const SetupProjectParameters &parameters, Evaluator &evaluator, Logger &logger,
- QbsItemRetriever qbsItemRetriever)
- : parameters(parameters), evaluator(evaluator), logger(logger),
- qbsItemRetriever(std::move(qbsItemRetriever)) {}
-
- MultiplexInfo extractMultiplexInfo(Item *productItem, Item *qbsModuleItem);
- MultiplexTable combine(const MultiplexTable &table, const MultiplexRow &values);
-
- const SetupProjectParameters &parameters;
- Evaluator &evaluator;
- Logger &logger;
- const QbsItemRetriever qbsItemRetriever;
-};
-
-ProductItemMultiplexer::ProductItemMultiplexer(
- const SetupProjectParameters &parameters, Evaluator &evaluator, Logger &logger,
- const QbsItemRetriever &qbsItemRetriever)
- : d(new Private(parameters, evaluator, logger, qbsItemRetriever)) {}
-
-ProductItemMultiplexer::~ProductItemMultiplexer() { delete d; }
-
-QList<Item *> ProductItemMultiplexer::multiplex(
- const QString &productName,
- Item *productItem,
- Item *tempQbsModuleItem,
- const std::function<void ()> &dropTempQbsModule)
-{
- const auto multiplexInfo = d->extractMultiplexInfo(productItem, tempQbsModuleItem);
- dropTempQbsModule();
- 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 * const qbsItem = d->qbsItemRetriever(item);
- 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::multiplexConfigurationIdsProperty(), v);
- dependsItem->setProperty(StringConstants::profilesProperty(),
- VariantValue::create(QStringList()));
- dependsItem->setFile(aggregator->file());
- dependsItem->setupForBuiltinType(d->parameters.deprecationWarningMode(), d->logger);
- Item::addChild(aggregator, dependsItem);
- }
- }
-
- return additionalProductItems;
-}
-
-MultiplexInfo ProductItemMultiplexer::Private::extractMultiplexInfo(Item *productItem,
- Item *qbsModuleItem)
-{
- static const QString mpmKey = QStringLiteral("multiplexMap");
-
- JSContext * const ctx = evaluator.engine()->context();
- const ScopedJsValue multiplexMap(ctx, evaluator.value(qbsModuleItem, mpmKey));
- const QStringList multiplexByQbsProperties = evaluator.stringListValue(
- productItem, StringConstants::multiplexByQbsPropertiesProperty());
-
- MultiplexInfo multiplexInfo;
- multiplexInfo.aggregate = evaluator.boolValue(
- productItem, StringConstants::aggregateProperty());
-
- const QString multiplexedType = evaluator.stringValue(
- productItem, StringConstants::multiplexedTypeProperty());
- if (!multiplexedType.isEmpty())
- multiplexInfo.multiplexedType = VariantValue::create(multiplexedType);
-
- Set<QString> uniqueMultiplexByQbsProperties;
- for (const QString &key : multiplexByQbsProperties) {
- const QString mappedKey = getJsStringProperty(ctx, multiplexMap, key);
- 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 ScopedJsValue arr(ctx, evaluator.value(qbsModuleItem, key));
- if (JS_IsUndefined(arr))
- continue;
- if (!JS_IsArray(ctx, arr))
- throw ErrorInfo(Tr::tr("Property '%1' must be an array.").arg(key));
-
- const quint32 arrlen = getJsIntProperty(ctx, arr, StringConstants::lengthProperty());
- if (arrlen == 0)
- continue;
-
- MultiplexRow mprow;
- mprow.resize(arrlen);
- QVariantList entriesForKey;
- for (quint32 i = 0; i < arrlen; ++i) {
- const ScopedJsValue sv(ctx, JS_GetPropertyUint32(ctx, arr, i));
- const QVariant value = getJsVariant(ctx, sv);
- 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;
-
-}
-
-MultiplexTable ProductItemMultiplexer::Private::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;
-}
-
-QVariantMap ProductItemMultiplexer::multiplexIdToVariantMap(const QString &multiplexId)
-{
- if (multiplexId.isEmpty())
- return QVariantMap();
-
- // We assume that MultiplexInfo::toIdString() has been called for this
- // particular multiplex configuration.
- QVariantMap result = multiplexConfigurationsById->localData().value(multiplexId);
- QBS_CHECK(!result.isEmpty());
- return result;
-}
-
-QString ProductItemMultiplexer::fullProductDisplayName(const QString &name,
- const QString &multiplexId)
-{
- static const auto multiplexIdToString =[](const QString &id) {
- return QString::fromUtf8(QByteArray::fromBase64(id.toUtf8()));
- };
- QString result = name;
- if (!multiplexId.isEmpty())
- result.append(QLatin1Char(' ')).append(multiplexIdToString(multiplexId));
- return result;
-}
-
-QString 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;
-}
-
-} // namespace qbs::Internal
diff --git a/src/lib/corelib/language/productitemmultiplexer.h b/src/lib/corelib/language/productitemmultiplexer.h
deleted file mode 100644
index d99267336..000000000
--- a/src/lib/corelib/language/productitemmultiplexer.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#pragma once
-
-#include <QList>
-#include <QVariantMap>
-
-#include <functional>
-
-namespace qbs {
-class SetupProjectParameters;
-namespace Internal {
-class Evaluator;
-class Item;
-class Logger;
-
-// This class deals with product multiplexing over the various defined axes.
-// For instance, a product with qbs.architectures: ["x86", "arm"] will get multiplexed into
-// two products with qbs.architecture: "x86" and qbs.architecture: "arm", respectively.
-class ProductItemMultiplexer
-{
-public:
- using QbsItemRetriever = std::function<Item *(Item *)>;
- ProductItemMultiplexer(const SetupProjectParameters &parameters, Evaluator &evaluator,
- Logger &logger, const QbsItemRetriever &qbsItemRetriever);
- ~ProductItemMultiplexer();
-
- // Checks whether the product item is to be multiplexed and returns the list of additional
- // product items. In the normal, non-multiplex case, this list is empty.
- QList<Item *> multiplex(
- const QString &productName,
- Item *productItem,
- Item *tempQbsModuleItem,
- const std::function<void()> &dropTempQbsModule
- );
-
- QVariantMap multiplexIdToVariantMap(const QString &multiplexId);
-
- static QString fullProductDisplayName(const QString &name, const QString &multiplexId);
-
-private:
- class Private;
- Private * const d;
-};
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp
deleted file mode 100644
index 403fd35bc..000000000
--- a/src/lib/corelib/language/projectresolver.cpp
+++ /dev/null
@@ -1,1801 +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 "projectresolver.h"
-
-#include "artifactproperties.h"
-#include "builtindeclarations.h"
-#include "evaluator.h"
-#include "filecontext.h"
-#include "item.h"
-#include "language.h"
-#include "propertymapinternal.h"
-#include "resolvedfilecontext.h"
-#include "scriptengine.h"
-#include "value.h"
-
-#include <jsextensions/jsextensions.h>
-#include <jsextensions/moduleproperties.h>
-#include <logging/categories.h>
-#include <logging/translator.h>
-#include <tools/error.h>
-#include <tools/fileinfo.h>
-#include <tools/joblimits.h>
-#include <tools/jsliterals.h>
-#include <tools/profiling.h>
-#include <tools/progressobserver.h>
-#include <tools/scripttools.h>
-#include <tools/qbsassert.h>
-#include <tools/qttools.h>
-#include <tools/setupprojectparameters.h>
-#include <tools/stlutils.h>
-#include <tools/stringconstants.h>
-
-#include <quickjs.h>
-
-#include <QtCore/qdir.h>
-#include <QtCore/qregularexpression.h>
-
-#include <algorithm>
-#include <memory>
-#include <queue>
-
-namespace qbs {
-namespace Internal {
-
-extern bool debugProperties;
-
-static const FileTag unknownFileTag()
-{
- static const FileTag tag("unknown-file-tag");
- return tag;
-}
-
-struct ProjectResolver::ProjectContext
-{
- ProjectContext *parentContext = nullptr;
- ResolvedProjectPtr project;
- std::vector<FileTaggerConstPtr> fileTaggers;
- std::vector<RulePtr> rules;
- JobLimits jobLimits;
- ResolvedModulePtr dummyModule;
-};
-
-struct ProjectResolver::ProductContext
-{
- ResolvedProductPtr product;
- QString buildDirectory;
- Item *item = nullptr;
- using ArtifactPropertiesInfo = std::pair<ArtifactPropertiesPtr, std::vector<CodeLocation>>;
- QHash<QStringList, ArtifactPropertiesInfo> artifactPropertiesPerFilter;
- ProjectResolver::FileLocations sourceArtifactLocations;
- GroupConstPtr currentGroup;
-};
-
-struct ProjectResolver::ModuleContext
-{
- ResolvedModulePtr module;
- JobLimits jobLimits;
-};
-
-class CancelException { };
-
-
-ProjectResolver::ProjectResolver(Evaluator *evaluator, ProjectTreeBuilder::Result loadResult,
- SetupProjectParameters setupParameters, Logger &logger)
- : m_evaluator(evaluator)
- , m_logger(logger)
- , m_engine(m_evaluator->engine())
- , m_progressObserver(nullptr)
- , m_setupParams(std::move(setupParameters))
- , m_loadResult(std::move(loadResult))
-{
- QBS_CHECK(FileInfo::isAbsolute(m_setupParams.buildRoot()));
-}
-
-ProjectResolver::~ProjectResolver() = default;
-
-void ProjectResolver::setProgressObserver(ProgressObserver *observer)
-{
- m_progressObserver = observer;
-}
-
-static void checkForDuplicateProductNames(const TopLevelProjectConstPtr &project)
-{
- const std::vector<ResolvedProductPtr> allProducts = project->allProducts();
- for (size_t i = 0; i < allProducts.size(); ++i) {
- const ResolvedProductConstPtr product1 = allProducts.at(i);
- const QString productName = product1->uniqueName();
- for (size_t j = i + 1; j < allProducts.size(); ++j) {
- const ResolvedProductConstPtr product2 = allProducts.at(j);
- if (product2->uniqueName() == productName) {
- ErrorInfo error;
- error.append(Tr::tr("Duplicate product name '%1'.").arg(product1->name));
- error.append(Tr::tr("First product defined here."), product1->location);
- error.append(Tr::tr("Second product defined here."), product2->location);
- throw error;
- }
- }
- }
-}
-
-TopLevelProjectPtr ProjectResolver::resolve()
-{
- TimedActivityLogger projectResolverTimer(m_logger, Tr::tr("ProjectResolver"),
- m_setupParams.logElapsedTime());
- qCDebug(lcProjectResolver) << "resolving" << m_loadResult.root->file()->filePath();
-
- m_productContext = nullptr;
- m_moduleContext = nullptr;
- m_elapsedTimeModPropEval = m_elapsedTimeAllPropEval = m_elapsedTimeGroups = 0;
- TopLevelProjectPtr tlp;
- try {
- tlp = resolveTopLevelProject();
- printProfilingInfo();
- } catch (const CancelException &) {
- throw ErrorInfo(Tr::tr("Project resolving canceled for configuration '%1'.")
- .arg(TopLevelProject::deriveId(m_setupParams.finalBuildConfigurationTree())));
- }
- return tlp;
-}
-
-void ProjectResolver::checkCancelation() const
-{
- if (m_progressObserver && m_progressObserver->canceled())
- throw CancelException();
-}
-
-QString ProjectResolver::verbatimValue(const ValueConstPtr &value, bool *propertyWasSet) const
-{
- QString result;
- if (value && value->type() == Value::JSSourceValueType) {
- const JSSourceValueConstPtr sourceValue = std::static_pointer_cast<const JSSourceValue>(
- value);
- result = sourceCodeForEvaluation(sourceValue);
- if (propertyWasSet)
- *propertyWasSet = !sourceValue->isBuiltinDefaultValue();
- } else {
- if (propertyWasSet)
- *propertyWasSet = false;
- }
- return result;
-}
-
-QString ProjectResolver::verbatimValue(Item *item, const QString &name, bool *propertyWasSet) const
-{
- return verbatimValue(item->property(name), propertyWasSet);
-}
-
-void ProjectResolver::ignoreItem(Item *item, ProjectContext *projectContext)
-{
- Q_UNUSED(item);
- Q_UNUSED(projectContext);
-}
-
-static void makeSubProjectNamesUniqe(const ResolvedProjectPtr &parentProject)
-{
- Set<QString> subProjectNames;
- Set<ResolvedProjectPtr> projectsInNeedOfNameChange;
- for (const ResolvedProjectPtr &p : qAsConst(parentProject->subProjects)) {
- if (!subProjectNames.insert(p->name).second)
- projectsInNeedOfNameChange << p;
- makeSubProjectNamesUniqe(p);
- }
- while (!projectsInNeedOfNameChange.empty()) {
- auto it = projectsInNeedOfNameChange.begin();
- while (it != projectsInNeedOfNameChange.end()) {
- const ResolvedProjectPtr p = *it;
- p->name += QLatin1Char('_');
- if (subProjectNames.insert(p->name).second) {
- it = projectsInNeedOfNameChange.erase(it);
- } else {
- ++it;
- }
- }
- }
-}
-
-TopLevelProjectPtr ProjectResolver::resolveTopLevelProject()
-{
- if (m_progressObserver)
- m_progressObserver->setMaximum(int(m_loadResult.productInfos.size()));
- TopLevelProjectPtr project = TopLevelProject::create();
- project->buildDirectory = TopLevelProject::deriveBuildDirectory(m_setupParams.buildRoot(),
- TopLevelProject::deriveId(m_setupParams.finalBuildConfigurationTree()));
- project->buildSystemFiles = m_loadResult.qbsFiles;
- project->profileConfigs = m_loadResult.profileConfigs;
- project->probes = m_loadResult.projectProbes;
- project->moduleProviderInfo = m_loadResult.storedModuleProviderInfo;
- ProjectContext projectContext;
- projectContext.project = project;
-
- resolveProject(m_loadResult.root, &projectContext);
- ErrorInfo accumulatedErrors;
- for (const ErrorInfo &e : m_queuedErrors)
- appendError(accumulatedErrors, e);
- if (accumulatedErrors.hasError())
- throw accumulatedErrors;
-
- project->setBuildConfiguration(m_setupParams.finalBuildConfigurationTree());
- project->overriddenValues = m_setupParams.overriddenValues();
- project->canonicalFilePathResults = m_engine->canonicalFilePathResults();
- project->fileExistsResults = m_engine->fileExistsResults();
- project->directoryEntriesResults = m_engine->directoryEntriesResults();
- project->fileLastModifiedResults = m_engine->fileLastModifiedResults();
- project->environment = m_engine->environment();
- project->buildSystemFiles.unite(m_engine->imports());
- makeSubProjectNamesUniqe(project);
- resolveProductDependencies();
- collectExportedProductDependencies();
- checkForDuplicateProductNames(project);
-
- for (const ResolvedProductPtr &product : project->allProducts()) {
- if (!product->enabled)
- continue;
-
- applyFileTaggers(product);
- matchArtifactProperties(product, product->allEnabledFiles());
-
- // Let a positive value of qbs.install imply the file tag "installable".
- for (const SourceArtifactPtr &artifact : product->allFiles()) {
- if (artifact->properties->qbsPropertyValue(StringConstants::installProperty()).toBool())
- artifact->fileTags += "installable";
- }
- }
- project->warningsEncountered = m_logger.warnings();
- return project;
-}
-
-void ProjectResolver::resolveProject(Item *item, ProjectContext *projectContext)
-{
- checkCancelation();
-
- if (projectContext->parentContext)
- projectContext->project->enabled = projectContext->parentContext->project->enabled;
- projectContext->project->location = item->location();
- try {
- resolveProjectFully(item, projectContext);
- } catch (const ErrorInfo &error) {
- if (!projectContext->project->enabled) {
- qCDebug(lcProjectResolver) << "error resolving project"
- << projectContext->project->location << error.toString();
- return;
- }
- if (m_setupParams.productErrorMode() == ErrorHandlingMode::Strict)
- throw;
- m_logger.printWarning(error);
- }
-}
-
-void ProjectResolver::resolveProjectFully(Item *item, ProjectResolver::ProjectContext *projectContext)
-{
- projectContext->project->enabled = projectContext->project->enabled
- && m_evaluator->boolValue(item, StringConstants::conditionProperty());
- projectContext->project->name = m_evaluator->stringValue(item, StringConstants::nameProperty());
- if (projectContext->project->name.isEmpty())
- projectContext->project->name = FileInfo::baseName(item->location().filePath()); // FIXME: Must also be changed in item?
- QVariantMap projectProperties;
- if (!projectContext->project->enabled) {
- projectProperties.insert(StringConstants::profileProperty(),
- m_evaluator->stringValue(item,
- StringConstants::profileProperty()));
- projectContext->project->setProjectProperties(projectProperties);
- return;
- }
-
- projectContext->dummyModule = ResolvedModule::create();
-
- for (Item::PropertyDeclarationMap::const_iterator it
- = item->propertyDeclarations().constBegin();
- it != item->propertyDeclarations().constEnd(); ++it) {
- if (it.value().flags().testFlag(PropertyDeclaration::PropertyNotAvailableInConfig))
- continue;
- const ValueConstPtr v = item->property(it.key());
- QBS_ASSERT(v && v->type() != Value::ItemValueType, continue);
- const ScopedJsValue sv(m_engine->context(), m_evaluator->value(item, it.key()));
- projectProperties.insert(it.key(), getJsVariant(m_engine->context(), sv));
- }
- projectContext->project->setProjectProperties(projectProperties);
-
- static const ItemFuncMap mapping = {
- { ItemType::Project, &ProjectResolver::resolveProject },
- { ItemType::SubProject, &ProjectResolver::resolveSubProject },
- { ItemType::Product, &ProjectResolver::resolveProduct },
- { ItemType::Probe, &ProjectResolver::ignoreItem },
- { ItemType::FileTagger, &ProjectResolver::resolveFileTagger },
- { ItemType::JobLimit, &ProjectResolver::resolveJobLimit },
- { ItemType::Rule, &ProjectResolver::resolveRule },
- { ItemType::PropertyOptions, &ProjectResolver::ignoreItem }
- };
-
- for (Item * const child : item->children()) {
- try {
- callItemFunction(mapping, child, projectContext);
- } catch (const ErrorInfo &e) {
- m_queuedErrors.push_back(e);
- }
- }
-
- for (const ResolvedProductPtr &product : projectContext->project->products)
- postProcess(product, projectContext);
-}
-
-void ProjectResolver::resolveSubProject(Item *item, ProjectResolver::ProjectContext *projectContext)
-{
- ProjectContext subProjectContext = createProjectContext(projectContext);
-
- Item * const projectItem = item->child(ItemType::Project);
- if (projectItem) {
- resolveProject(projectItem, &subProjectContext);
- return;
- }
-
- // No project item was found, which means the project was disabled.
- subProjectContext.project->enabled = false;
- Item * const propertiesItem = item->child(ItemType::PropertiesInSubProject);
- if (propertiesItem) {
- subProjectContext.project->name
- = m_evaluator->stringValue(propertiesItem, StringConstants::nameProperty());
- }
-}
-
-class ProjectResolver::ProductContextSwitcher
-{
-public:
- ProductContextSwitcher(ProjectResolver *resolver, ProductContext *newContext,
- ProgressObserver *progressObserver)
- : m_resolver(resolver), m_progressObserver(progressObserver)
- {
- QBS_CHECK(!m_resolver->m_productContext);
- m_resolver->m_productContext = newContext;
- }
-
- ~ProductContextSwitcher()
- {
- if (m_progressObserver)
- m_progressObserver->incrementProgressValue();
- m_resolver->m_productContext = nullptr;
- }
-
-private:
- ProjectResolver * const m_resolver;
- ProgressObserver * const m_progressObserver;
-};
-
-void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext)
-{
- checkCancelation();
- m_evaluator->clearPropertyDependencies();
- ProductContext productContext;
- productContext.item = item;
- ResolvedProductPtr product = ResolvedProduct::create();
- product->enabled = projectContext->project->enabled;
- product->moduleProperties = PropertyMapInternal::create();
- product->project = projectContext->project;
- productContext.product = product;
- product->location = item->location();
- ProductContextSwitcher contextSwitcher(this, &productContext, m_progressObserver);
- const auto errorFromDelayedError = [&] {
- ProjectTreeBuilder::Result::ProductInfo &pi = m_loadResult.productInfos[item];
- if (pi.delayedError.hasError()) {
- ErrorInfo errorInfo;
-
- // First item is "main error", gets prepended again in the catch clause.
- const QList<ErrorItem> &items = pi.delayedError.items();
- for (int i = 1; i < items.size(); ++i)
- errorInfo.append(items.at(i));
-
- pi.delayedError.clear();
- return errorInfo;
- }
- return ErrorInfo();
- };
-
- // Even if we previously encountered an error, try to continue for as long as possible
- // to provide IDEs with useful data (e.g. the list of files).
- // If we encounter a follow-up error, suppress it and report the original one instead.
- try {
- resolveProductFully(item, projectContext);
- if (const ErrorInfo error = errorFromDelayedError(); error.hasError())
- throw error;
- } catch (ErrorInfo e) {
- if (const ErrorInfo error = errorFromDelayedError(); error.hasError())
- e = error;
- QString mainErrorString = !product->name.isEmpty()
- ? Tr::tr("Error while handling product '%1':").arg(product->name)
- : Tr::tr("Error while handling product:");
- ErrorInfo fullError(mainErrorString, item->location());
- appendError(fullError, e);
- if (!product->enabled) {
- qCDebug(lcProjectResolver) << fullError.toString();
- return;
- }
- if (m_setupParams.productErrorMode() == ErrorHandlingMode::Strict)
- throw fullError;
- m_logger.printWarning(fullError);
- m_logger.printWarning(ErrorInfo(Tr::tr("Product '%1' had errors and was disabled.")
- .arg(product->name), item->location()));
- product->enabled = false;
- }
-}
-
-void ProjectResolver::resolveProductFully(Item *item, ProjectContext *projectContext)
-{
- const ResolvedProductPtr product = m_productContext->product;
- m_productItemMap.insert(product, item);
- projectContext->project->products.push_back(product);
- product->name = m_evaluator->stringValue(item, StringConstants::nameProperty());
-
- // product->buildDirectory() isn't valid yet, because the productProperties map is not ready.
- m_productContext->buildDirectory
- = m_evaluator->stringValue(item, StringConstants::buildDirectoryProperty());
- product->multiplexConfigurationId
- = m_evaluator->stringValue(item, StringConstants::multiplexConfigurationIdProperty());
- qCDebug(lcProjectResolver) << "resolveProduct" << product->uniqueName();
- m_productsByItem.insert(item, product);
- product->enabled = product->enabled
- && m_evaluator->boolValue(item, StringConstants::conditionProperty());
- ProjectTreeBuilder::Result::ProductInfo &pi = m_loadResult.productInfos[item];
- gatherProductTypes(product.get(), item);
- product->targetName = m_evaluator->stringValue(item, StringConstants::targetNameProperty());
- product->sourceDirectory = m_evaluator->stringValue(
- item, StringConstants::sourceDirectoryProperty());
- product->destinationDirectory = m_evaluator->stringValue(
- item, StringConstants::destinationDirProperty());
-
- if (product->destinationDirectory.isEmpty()) {
- product->destinationDirectory = m_productContext->buildDirectory;
- } else {
- product->destinationDirectory = FileInfo::resolvePath(
- product->topLevelProject()->buildDirectory,
- product->destinationDirectory);
- }
- product->probes = pi.probes;
- createProductConfig(product.get());
- product->productProperties.insert(StringConstants::destinationDirProperty(),
- product->destinationDirectory);
- ModuleProperties::init(m_evaluator->engine(), m_evaluator->scriptValue(item), product.get());
-
- QList<Item *> subItems = item->children();
- const ValuePtr filesProperty = item->property(StringConstants::filesProperty());
- if (filesProperty) {
- Item *fakeGroup = Item::create(item->pool(), ItemType::Group);
- fakeGroup->setFile(item->file());
- fakeGroup->setLocation(item->location());
- fakeGroup->setScope(item);
- fakeGroup->setProperty(StringConstants::nameProperty(), VariantValue::create(product->name));
- fakeGroup->setProperty(StringConstants::filesProperty(), filesProperty);
- fakeGroup->setProperty(StringConstants::excludeFilesProperty(),
- item->property(StringConstants::excludeFilesProperty()));
- fakeGroup->setProperty(StringConstants::overrideTagsProperty(),
- VariantValue::falseValue());
- fakeGroup->setupForBuiltinType(m_setupParams.deprecationWarningMode(), m_logger);
- subItems.prepend(fakeGroup);
- }
-
- static const ItemFuncMap mapping = {
- { ItemType::Depends, &ProjectResolver::ignoreItem },
- { ItemType::Rule, &ProjectResolver::resolveRule },
- { ItemType::FileTagger, &ProjectResolver::resolveFileTagger },
- { ItemType::JobLimit, &ProjectResolver::resolveJobLimit },
- { ItemType::Group, &ProjectResolver::resolveGroup },
- { ItemType::Product, &ProjectResolver::resolveShadowProduct },
- { ItemType::Export, &ProjectResolver::resolveExport },
- { ItemType::Probe, &ProjectResolver::ignoreItem },
- { ItemType::PropertyOptions, &ProjectResolver::ignoreItem }
- };
-
- for (Item * const child : qAsConst(subItems))
- callItemFunction(mapping, child, projectContext);
-
- for (const ProjectContext *p = projectContext; p; p = p->parentContext) {
- JobLimits tempLimits = p->jobLimits;
- product->jobLimits = tempLimits.update(product->jobLimits);
- }
-
- resolveModules(item, projectContext);
-
- for (const FileTag &t : qAsConst(product->fileTags))
- m_productsByType[t].push_back(product);
-}
-
-void ProjectResolver::resolveModules(const Item *item, ProjectContext *projectContext)
-{
- JobLimits jobLimits;
- for (const Item::Module &m : item->modules()) {
- resolveModule(m.name, m.item, m.productInfo.has_value(), m.parameters, jobLimits,
- projectContext);
- }
- for (int i = 0; i < jobLimits.count(); ++i) {
- const JobLimit &moduleJobLimit = jobLimits.jobLimitAt(i);
- if (m_productContext->product->jobLimits.getLimit(moduleJobLimit.pool()) == -1)
- m_productContext->product->jobLimits.setJobLimit(moduleJobLimit);
- }
-}
-
-void ProjectResolver::resolveModule(const QualifiedId &moduleName, Item *item, bool isProduct,
- const QVariantMap &parameters, JobLimits &jobLimits,
- ProjectContext *projectContext)
-{
- checkCancelation();
- if (!item->isPresentModule())
- return;
-
- ModuleContext * const oldModuleContext = m_moduleContext;
- ModuleContext moduleContext;
- moduleContext.module = ResolvedModule::create();
- m_moduleContext = &moduleContext;
-
- const ResolvedModulePtr &module = moduleContext.module;
- module->name = moduleName.toString();
- module->isProduct = isProduct;
- module->product = m_productContext->product.get();
- module->setupBuildEnvironmentScript.initialize(
- scriptFunctionValue(item, StringConstants::setupBuildEnvironmentProperty()));
- module->setupRunEnvironmentScript.initialize(
- scriptFunctionValue(item, StringConstants::setupRunEnvironmentProperty()));
-
- for (const Item::Module &m : item->modules()) {
- if (m.item->isPresentModule())
- module->moduleDependencies += m.name.toString();
- }
-
- m_productContext->product->modules.push_back(module);
- if (!parameters.empty())
- m_productContext->product->moduleParameters[module] = parameters;
-
- static const ItemFuncMap mapping {
- { ItemType::Group, &ProjectResolver::ignoreItem },
- { ItemType::Rule, &ProjectResolver::resolveRule },
- { ItemType::FileTagger, &ProjectResolver::resolveFileTagger },
- { ItemType::JobLimit, &ProjectResolver::resolveJobLimit },
- { ItemType::Scanner, &ProjectResolver::resolveScanner },
- { ItemType::PropertyOptions, &ProjectResolver::ignoreItem },
- { ItemType::Depends, &ProjectResolver::ignoreItem },
- { ItemType::Parameter, &ProjectResolver::ignoreItem },
- { ItemType::Properties, &ProjectResolver::ignoreItem },
- { ItemType::Probe, &ProjectResolver::ignoreItem }
- };
- for (Item *child : item->children())
- callItemFunction(mapping, child, projectContext);
- for (int i = 0; i < moduleContext.jobLimits.count(); ++i) {
- const JobLimit &newJobLimit = moduleContext.jobLimits.jobLimitAt(i);
- const int oldLimit = jobLimits.getLimit(newJobLimit.pool());
- if (oldLimit == -1 || oldLimit > newJobLimit.limit())
- jobLimits.setJobLimit(newJobLimit);
- }
-
- m_moduleContext = oldModuleContext;
-}
-
-void ProjectResolver::gatherProductTypes(ResolvedProduct *product, Item *item)
-{
- const VariantValuePtr type = item->variantProperty(StringConstants::typeProperty());
- if (type)
- product->fileTags = FileTags::fromStringList(type->value().toStringList());
-}
-
-SourceArtifactPtr ProjectResolver::createSourceArtifact(const ResolvedProductPtr &rproduct,
- const QString &fileName, const GroupPtr &group, bool wildcard,
- const CodeLocation &filesLocation, FileLocations *fileLocations,
- ErrorInfo *errorInfo)
-{
- const QString &baseDir = FileInfo::path(group->location.filePath());
- const QString absFilePath = QDir::cleanPath(FileInfo::resolvePath(baseDir, fileName));
- if (!wildcard && !FileInfo(absFilePath).exists()) {
- if (errorInfo)
- errorInfo->append(Tr::tr("File '%1' does not exist.").arg(absFilePath), filesLocation);
- rproduct->missingSourceFiles << absFilePath;
- return {};
- }
- if (group->enabled && fileLocations) {
- CodeLocation &loc = (*fileLocations)[std::make_pair(group->targetOfModule, absFilePath)];
- if (loc.isValid()) {
- if (errorInfo) {
- errorInfo->append(Tr::tr("Duplicate source file '%1'.").arg(absFilePath));
- errorInfo->append(Tr::tr("First occurrence is here."), loc);
- errorInfo->append(Tr::tr("Next occurrence is here."), filesLocation);
- }
- return {};
- }
- loc = filesLocation;
- }
- SourceArtifactPtr artifact = SourceArtifactInternal::create();
- artifact->absoluteFilePath = absFilePath;
- artifact->fileTags = group->fileTags;
- artifact->overrideFileTags = group->overrideTags;
- artifact->properties = group->properties;
- artifact->targetOfModule = group->targetOfModule;
- (wildcard ? group->wildcards->files : group->files).push_back(artifact);
- return artifact;
-}
-
-static QualifiedIdSet propertiesToEvaluate(std::deque<QualifiedId> initialProps,
- const PropertyDependencies &deps)
-{
- std::deque<QualifiedId> remainingProps = std::move(initialProps);
- QualifiedIdSet allProperties;
- while (!remainingProps.empty()) {
- const QualifiedId prop = remainingProps.front();
- remainingProps.pop_front();
- const auto insertResult = allProperties.insert(prop);
- if (!insertResult.second)
- continue;
- transform(deps.value(prop), remainingProps, [](const QualifiedId &id) { return id; });
- }
- return allProperties;
-}
-
-QVariantMap ProjectResolver::resolveAdditionalModuleProperties(const Item *group,
- const QVariantMap &currentValues)
-{
- // Step 1: Retrieve the properties directly set in the group
- const ModulePropertiesPerGroup &mp = mapValue(
- m_loadResult.productInfos, m_productContext->item).modulePropertiesSetInGroups;
- const auto it = mp.find(group);
- if (it == mp.end())
- return {};
- const QualifiedIdSet &propsSetInGroup = it->second;
-
- // Step 2: Gather all properties that depend on these properties.
- const QualifiedIdSet &propsToEval = propertiesToEvaluate(
- rangeTo<std::deque<QualifiedId>>(propsSetInGroup),
- m_evaluator->propertyDependencies());
-
- // Step 3: Evaluate all these properties and replace their values in the map
- QVariantMap modulesMap = currentValues;
- QHash<QString, QStringList> propsPerModule;
- for (auto fullPropName : propsToEval) {
- const QString moduleName
- = QualifiedId(fullPropName.mid(0, fullPropName.size() - 1)).toString();
- propsPerModule[moduleName] << fullPropName.last();
- }
- EvalCacheEnabler cachingEnabler(m_evaluator, m_productContext->product->sourceDirectory);
- for (const Item::Module &module : group->modules()) {
- const QString &fullModName = module.name.toString();
- const QStringList propsForModule = propsPerModule.take(fullModName);
- if (propsForModule.empty())
- continue;
- QVariantMap reusableValues = modulesMap.value(fullModName).toMap();
- for (const QString &prop : qAsConst(propsForModule))
- reusableValues.remove(prop);
- modulesMap.insert(fullModName,
- evaluateProperties(module.item, module.item, reusableValues, true, true));
- }
- return modulesMap;
-}
-
-void ProjectResolver::resolveGroup(Item *item, ProjectContext *projectContext)
-{
- checkCancelation();
- const bool parentEnabled = m_productContext->currentGroup
- ? m_productContext->currentGroup->enabled
- : m_productContext->product->enabled;
- const bool isEnabled = parentEnabled
- && m_evaluator->boolValue(item, StringConstants::conditionProperty());
- try {
- resolveGroupFully(item, projectContext, isEnabled);
- } catch (const ErrorInfo &error) {
- if (!isEnabled) {
- qCDebug(lcProjectResolver) << "error resolving group at" << item->location()
- << error.toString();
- return;
- }
- if (m_setupParams.productErrorMode() == ErrorHandlingMode::Strict)
- throw;
- m_logger.printWarning(error);
- }
-}
-
-void ProjectResolver::resolveGroupFully(Item *item, ProjectResolver::ProjectContext *projectContext,
- bool isEnabled)
-{
- AccumulatingTimer groupTimer(m_setupParams.logElapsedTime()
- ? &m_elapsedTimeGroups : nullptr);
-
- const auto getGroupPropertyMap = [this, item](const ArtifactProperties *existingProps) {
- PropertyMapPtr moduleProperties;
- bool newPropertyMapRequired = false;
- if (existingProps)
- moduleProperties = existingProps->propertyMap();
- if (!moduleProperties) {
- newPropertyMapRequired = true;
- moduleProperties = m_productContext->currentGroup
- ? m_productContext->currentGroup->properties
- : m_productContext->product->moduleProperties;
- }
- const QVariantMap newModuleProperties
- = resolveAdditionalModuleProperties(item, moduleProperties->value());
- if (!newModuleProperties.empty()) {
- if (newPropertyMapRequired)
- moduleProperties = PropertyMapInternal::create();
- moduleProperties->setValue(newModuleProperties);
- }
- return moduleProperties;
- };
-
- QStringList files = m_evaluator->stringListValue(item, StringConstants::filesProperty());
- bool fileTagsSet;
- const FileTags fileTags = m_evaluator->fileTagsValue(item, StringConstants::fileTagsProperty(),
- &fileTagsSet);
- const QStringList fileTagsFilter
- = m_evaluator->stringListValue(item, StringConstants::fileTagsFilterProperty());
- if (!fileTagsFilter.empty()) {
- if (Q_UNLIKELY(!files.empty()))
- throw ErrorInfo(Tr::tr("Group.files and Group.fileTagsFilters are exclusive."),
- item->location());
-
- if (!isEnabled)
- return;
-
- ProductContext::ArtifactPropertiesInfo &apinfo
- = m_productContext->artifactPropertiesPerFilter[fileTagsFilter];
- if (apinfo.first) {
- const auto it = std::find_if(apinfo.second.cbegin(), apinfo.second.cend(),
- [item](const CodeLocation &loc) {
- return item->location().filePath() == loc.filePath();
- });
- if (it != apinfo.second.cend()) {
- ErrorInfo error(Tr::tr("Conflicting fileTagsFilter in Group items."));
- error.append(Tr::tr("First item"), *it);
- error.append(Tr::tr("Second item"), item->location());
- throw error;
- }
- } else {
- apinfo.first = ArtifactProperties::create();
- apinfo.first->setFileTagsFilter(FileTags::fromStringList(fileTagsFilter));
- m_productContext->product->artifactProperties.push_back(apinfo.first);
- }
- apinfo.second.push_back(item->location());
- apinfo.first->setPropertyMapInternal(getGroupPropertyMap(apinfo.first.get()));
- apinfo.first->addExtraFileTags(fileTags);
- return;
- }
- QStringList patterns;
- for (int i = files.size(); --i >= 0;) {
- if (FileInfo::isPattern(files[i]))
- patterns.push_back(files.takeAt(i));
- }
- GroupPtr group = ResolvedGroup::create();
- bool prefixWasSet = false;
- group->prefix = m_evaluator->stringValue(item, StringConstants::prefixProperty(), QString(),
- &prefixWasSet);
- if (!prefixWasSet && m_productContext->currentGroup)
- group->prefix = m_productContext->currentGroup->prefix;
- if (!group->prefix.isEmpty()) {
- for (auto it = files.rbegin(), end = files.rend(); it != end; ++it)
- it->prepend(group->prefix);
- }
- group->location = item->location();
- group->enabled = isEnabled;
- group->properties = getGroupPropertyMap(nullptr);
- group->fileTags = fileTags;
- group->overrideTags = m_evaluator->boolValue(item, StringConstants::overrideTagsProperty());
- if (group->overrideTags && fileTagsSet) {
- if (group->fileTags.empty() )
- group->fileTags.insert(unknownFileTag());
- } else if (m_productContext->currentGroup) {
- group->fileTags.unite(m_productContext->currentGroup->fileTags);
- }
-
- const CodeLocation filesLocation = item->property(StringConstants::filesProperty())->location();
- const VariantValueConstPtr moduleProp = item->variantProperty(
- StringConstants::modulePropertyInternal());
- if (moduleProp)
- group->targetOfModule = moduleProp->value().toString();
- ErrorInfo fileError;
- if (!patterns.empty()) {
- group->wildcards = std::make_unique<SourceWildCards>();
- SourceWildCards *wildcards = group->wildcards.get();
- wildcards->group = group.get();
- wildcards->excludePatterns = m_evaluator->stringListValue(
- item, StringConstants::excludeFilesProperty());
- wildcards->patterns = patterns;
- const Set<QString> files = wildcards->expandPatterns(group,
- FileInfo::path(item->file()->filePath()),
- projectContext->project->topLevelProject()->buildDirectory);
- for (const QString &fileName : files)
- createSourceArtifact(m_productContext->product, fileName, group, true, filesLocation,
- &m_productContext->sourceArtifactLocations, &fileError);
- }
-
- for (const QString &fileName : qAsConst(files)) {
- createSourceArtifact(m_productContext->product, fileName, group, false, filesLocation,
- &m_productContext->sourceArtifactLocations, &fileError);
- }
- if (fileError.hasError()) {
- if (group->enabled) {
- if (m_setupParams.productErrorMode() == ErrorHandlingMode::Strict)
- throw ErrorInfo(fileError);
- m_logger.printWarning(fileError);
- } else {
- qCDebug(lcProjectResolver) << "error for disabled group:" << fileError.toString();
- }
- }
- group->name = m_evaluator->stringValue(item, StringConstants::nameProperty());
- if (group->name.isEmpty())
- group->name = Tr::tr("Group %1").arg(m_productContext->product->groups.size());
- m_productContext->product->groups.push_back(group);
-
- class GroupContextSwitcher {
- public:
- GroupContextSwitcher(ProductContext &context, const GroupConstPtr &newGroup)
- : m_context(context), m_oldGroup(context.currentGroup) {
- m_context.currentGroup = newGroup;
- }
- ~GroupContextSwitcher() { m_context.currentGroup = m_oldGroup; }
- private:
- ProductContext &m_context;
- const GroupConstPtr m_oldGroup;
- };
- GroupContextSwitcher groupSwitcher(*m_productContext, group);
- for (Item * const childItem : item->children())
- resolveGroup(childItem, projectContext);
-}
-
-void ProjectResolver::adaptExportedPropertyValues(const Item *shadowProductItem)
-{
- ExportedModule &m = m_productContext->product->exportedModule;
- const QVariantList prefixList = m.propertyValues.take(
- StringConstants::prefixMappingProperty()).toList();
- const QString shadowProductName = m_evaluator->stringValue(
- shadowProductItem, StringConstants::nameProperty());
- const QString shadowProductBuildDir = m_evaluator->stringValue(
- shadowProductItem, StringConstants::buildDirectoryProperty());
- QVariantMap prefixMap;
- for (const QVariant &v : prefixList) {
- const QVariantMap o = v.toMap();
- prefixMap.insert(o.value(QStringLiteral("prefix")).toString(),
- o.value(QStringLiteral("replacement")).toString());
- }
- const auto valueRefersToImportingProduct
- = [shadowProductName, shadowProductBuildDir](const QString &value) {
- return value.toLower().contains(shadowProductName.toLower())
- || value.contains(shadowProductBuildDir);
- };
- static const auto stringMapper = [](const QVariantMap &mappings, const QString &value)
- -> QString {
- for (auto it = mappings.cbegin(); it != mappings.cend(); ++it) {
- if (value.startsWith(it.key()))
- return it.value().toString() + value.mid(it.key().size());
- }
- return value;
- };
- const auto stringListMapper = [&valueRefersToImportingProduct](
- const QVariantMap &mappings, const QStringList &value) -> QStringList {
- QStringList result;
- result.reserve(value.size());
- for (const QString &s : value) {
- if (!valueRefersToImportingProduct(s))
- result.push_back(stringMapper(mappings, s));
- }
- return result;
- };
- const std::function<QVariant(const QVariantMap &, const QVariant &)> mapper
- = [&stringListMapper, &mapper](
- const QVariantMap &mappings, const QVariant &value) -> QVariant {
- switch (static_cast<QMetaType::Type>(value.userType())) {
- case QMetaType::QString:
- return stringMapper(mappings, value.toString());
- case QMetaType::QStringList:
- return stringListMapper(mappings, value.toStringList());
- case QMetaType::QVariantMap: {
- QVariantMap m = value.toMap();
- for (auto it = m.begin(); it != m.end(); ++it)
- it.value() = mapper(mappings, it.value());
- return m;
- }
- default:
- return value;
- }
- };
- for (auto it = m.propertyValues.begin(); it != m.propertyValues.end(); ++it)
- it.value() = mapper(prefixMap, it.value());
- for (auto it = m.modulePropertyValues.begin(); it != m.modulePropertyValues.end(); ++it)
- it.value() = mapper(prefixMap, it.value());
- for (ExportedModuleDependency &dep : m.moduleDependencies) {
- for (auto it = dep.moduleProperties.begin(); it != dep.moduleProperties.end(); ++it)
- it.value() = mapper(prefixMap, it.value());
- }
-}
-
-void ProjectResolver::collectExportedProductDependencies()
-{
- ResolvedProductPtr dummyProduct = ResolvedProduct::create();
- dummyProduct->enabled = false;
- for (const auto &exportingProductInfo : qAsConst(m_productExportInfo)) {
- const ResolvedProductPtr exportingProduct = exportingProductInfo.first;
- if (!exportingProduct->enabled)
- continue;
- Item * const importingProductItem = exportingProductInfo.second;
-
- std::vector<std::pair<ResolvedProductPtr, QVariantMap>> directDeps;
- for (const Item::Module &m : importingProductItem->modules()) {
- if (m.name.toString() != exportingProduct->name)
- continue;
- for (const Item::Module &dep : m.item->modules()) {
- if (dep.productInfo) {
- directDeps.emplace_back(m_productsByItem.value(dep.productInfo->item),
- m.parameters);
- }
- }
- }
- for (const auto &dep : directDeps) {
- if (!contains(exportingProduct->exportedModule.productDependencies,
- dep.first->uniqueName())) {
- exportingProduct->exportedModule.productDependencies.push_back(
- dep.first->uniqueName());
- }
- if (!dep.second.isEmpty()) {
- exportingProduct->exportedModule.dependencyParameters.insert(dep.first,
- dep.second);
- }
- }
- auto &productDeps = exportingProduct->exportedModule.productDependencies;
- std::sort(productDeps.begin(), productDeps.end());
- }
-}
-
-void ProjectResolver::resolveShadowProduct(Item *item, ProjectResolver::ProjectContext *)
-{
- if (!m_productContext->product->enabled)
- return;
- for (const auto &m : item->modules()) {
- if (m.name.toString() != m_productContext->product->name)
- continue;
- collectPropertiesForExportItem(m.item);
- for (const auto &dep : m.item->modules())
- collectPropertiesForModuleInExportItem(dep);
- break;
- }
- try {
- adaptExportedPropertyValues(item);
- } catch (const ErrorInfo &) {}
- m_productExportInfo.emplace_back(m_productContext->product, item);
-}
-
-void ProjectResolver::setupExportedProperties(const Item *item, const QString &namePrefix,
- std::vector<ExportedProperty> &properties)
-{
- const auto &props = item->properties();
- for (auto it = props.cbegin(); it != props.cend(); ++it) {
- const QString qualifiedName = namePrefix.isEmpty()
- ? it.key() : namePrefix + QLatin1Char('.') + it.key();
- if ((item->type() == ItemType::Export || item->type() == ItemType::Properties)
- && qualifiedName == StringConstants::prefixMappingProperty()) {
- continue;
- }
- const ValuePtr &v = it.value();
- if (v->type() == Value::ItemValueType) {
- setupExportedProperties(std::static_pointer_cast<ItemValue>(v)->item(),
- qualifiedName, properties);
- continue;
- }
- ExportedProperty exportedProperty;
- exportedProperty.fullName = qualifiedName;
- exportedProperty.type = item->propertyDeclaration(it.key()).type();
- if (v->type() == Value::VariantValueType) {
- exportedProperty.sourceCode = toJSLiteral(
- std::static_pointer_cast<VariantValue>(v)->value());
- } else {
- QBS_CHECK(v->type() == Value::JSSourceValueType);
- const JSSourceValue * const sv = static_cast<JSSourceValue *>(v.get());
- exportedProperty.sourceCode = sv->sourceCode().toString();
- }
- const ItemDeclaration itemDecl
- = BuiltinDeclarations::instance().declarationsForType(item->type());
- PropertyDeclaration propertyDecl;
- const auto itemProperties = itemDecl.properties();
- for (const PropertyDeclaration &decl : itemProperties) {
- if (decl.name() == it.key()) {
- propertyDecl = decl;
- exportedProperty.isBuiltin = true;
- break;
- }
- }
-
- // Do not add built-in properties that were left at their default value.
- if (!exportedProperty.isBuiltin || m_evaluator->isNonDefaultValue(item, it.key()))
- properties.push_back(exportedProperty);
- }
-
- // Order the list of properties, so the output won't look so random.
- static const auto less = [](const ExportedProperty &p1, const ExportedProperty &p2) -> bool {
- const int p1ComponentCount = p1.fullName.count(QLatin1Char('.'));
- const int p2ComponentCount = p2.fullName.count(QLatin1Char('.'));
- if (p1.isBuiltin && !p2.isBuiltin)
- return true;
- if (!p1.isBuiltin && p2.isBuiltin)
- return false;
- if (p1ComponentCount < p2ComponentCount)
- return true;
- if (p1ComponentCount > p2ComponentCount)
- return false;
- return p1.fullName < p2.fullName;
- };
- std::sort(properties.begin(), properties.end(), less);
-}
-
-static bool usesImport(const ExportedProperty &prop, const QRegularExpression &regex)
-{
- return prop.sourceCode.indexOf(regex) != -1;
-}
-
-static bool usesImport(const ExportedItem &item, const QRegularExpression &regex)
-{
- return any_of(item.properties,
- [regex](const ExportedProperty &p) { return usesImport(p, regex); })
- || any_of(item.children,
- [regex](const ExportedItemPtr &child) { return usesImport(*child, regex); });
-}
-
-static bool usesImport(const ExportedModule &module, const QString &name)
-{
- // Imports are used in three ways:
- // (1) var f = new TextFile(...);
- // (2) var path = FileInfo.joinPaths(...)
- // (3) var obj = DataCollection;
- const QString pattern = QStringLiteral("\\b%1\\b");
-
- const QRegularExpression regex(pattern.arg(name)); // std::regex is much slower
- return any_of(module.m_properties,
- [regex](const ExportedProperty &p) { return usesImport(p, regex); })
- || any_of(module.children,
- [regex](const ExportedItemPtr &child) { return usesImport(*child, regex); });
-}
-
-static QString getLineAtLocation(const CodeLocation &loc, const QString &content)
-{
- int pos = 0;
- int currentLine = 1;
- while (currentLine < loc.line()) {
- while (content.at(pos++) != QLatin1Char('\n'))
- ;
- ++currentLine;
- }
- const int eolPos = content.indexOf(QLatin1Char('\n'), pos);
- return content.mid(pos, eolPos - pos);
-}
-
-void ProjectResolver::resolveExport(Item *exportItem, ProjectContext *)
-{
- ExportedModule &exportedModule = m_productContext->product->exportedModule;
- setupExportedProperties(exportItem, QString(), exportedModule.m_properties);
- static const auto cmpFunc = [](const ExportedProperty &p1, const ExportedProperty &p2) {
- return p1.fullName < p2.fullName;
- };
- std::sort(exportedModule.m_properties.begin(), exportedModule.m_properties.end(), cmpFunc);
-
- transform(exportItem->children(), exportedModule.children,
- [&exportedModule, this](const auto &child) {
- return resolveExportChild(child, exportedModule); });
-
- for (const JsImport &jsImport : exportItem->file()->jsImports()) {
- if (usesImport(exportedModule, jsImport.scopeName)) {
- exportedModule.importStatements << getLineAtLocation(jsImport.location,
- exportItem->file()->content());
- }
- }
- const auto builtInImports = JsExtensions::extensionNames();
- for (const QString &builtinImport: builtInImports) {
- if (usesImport(exportedModule, builtinImport))
- exportedModule.importStatements << QStringLiteral("import qbs.") + builtinImport;
- }
- exportedModule.importStatements.sort();
-}
-
-// TODO: This probably wouldn't be necessary if we had item serialization.
-std::unique_ptr<ExportedItem> ProjectResolver::resolveExportChild(const Item *item,
- const ExportedModule &module)
-{
- std::unique_ptr<ExportedItem> exportedItem(new ExportedItem);
-
- // This is the type of the built-in base item. It may turn out that we need to support
- // derived items under Export. In that case, we probably need a new Item member holding
- // the original type name.
- exportedItem->name = item->typeName();
-
- transform(item->children(), exportedItem->children, [&module, this](const auto &child) {
- return resolveExportChild(child, module); });
-
- setupExportedProperties(item, QString(), exportedItem->properties);
- return exportedItem;
-}
-
-QString ProjectResolver::sourceCodeAsFunction(const JSSourceValueConstPtr &value,
- const PropertyDeclaration &decl) const
-{
- QString &scriptFunction = m_scriptFunctions[std::make_pair(value->sourceCode(),
- decl.functionArgumentNames())];
- if (!scriptFunction.isNull())
- return scriptFunction;
- const QString args = decl.functionArgumentNames().join(QLatin1Char(','));
- if (value->hasFunctionForm()) {
- // Insert the argument list.
- scriptFunction = value->sourceCodeForEvaluation();
- scriptFunction.insert(10, args);
- // Remove the function application "()" that has been
- // added in ItemReaderASTVisitor::visitStatement.
- scriptFunction.chop(2);
- } else {
- scriptFunction = QLatin1String("(function(") + args + QLatin1String("){return ")
- + value->sourceCode().toString() + QLatin1String(";})");
- }
- return scriptFunction;
-}
-
-QString ProjectResolver::sourceCodeForEvaluation(const JSSourceValueConstPtr &value) const
-{
- QString &code = m_sourceCode[value->sourceCode()];
- if (!code.isNull())
- return code;
- code = value->sourceCodeForEvaluation();
- return code;
-}
-
-ScriptFunctionPtr ProjectResolver::scriptFunctionValue(Item *item, const QString &name) const
-{
- JSSourceValuePtr value = item->sourceProperty(name);
- ScriptFunctionPtr &script = m_scriptFunctionMap[value ? value->location() : CodeLocation()];
- if (!script.get()) {
- script = ScriptFunction::create();
- const PropertyDeclaration decl = item->propertyDeclaration(name);
- script->sourceCode = sourceCodeAsFunction(value, decl);
- script->location = value->location();
- script->fileContext = resolvedFileContext(value->file());
- }
- return script;
-}
-
-ResolvedFileContextPtr ProjectResolver::resolvedFileContext(const FileContextConstPtr &ctx) const
-{
- ResolvedFileContextPtr &result = m_fileContextMap[ctx];
- if (!result)
- result = ResolvedFileContext::create(*ctx);
- return result;
-}
-
-void ProjectResolver::resolveRule(Item *item, ProjectContext *projectContext)
-{
- checkCancelation();
-
- if (!m_evaluator->boolValue(item, StringConstants::conditionProperty()))
- return;
-
- RulePtr rule = Rule::create();
-
- // read artifacts
- bool hasArtifactChildren = false;
- for (Item * const child : item->children()) {
- if (Q_UNLIKELY(child->type() != ItemType::Artifact)) {
- throw ErrorInfo(Tr::tr("'Rule' can only have children of type 'Artifact'."),
- child->location());
- }
- hasArtifactChildren = true;
- resolveRuleArtifact(rule, child);
- }
-
- rule->name = m_evaluator->stringValue(item, StringConstants::nameProperty());
- rule->prepareScript.initialize(
- scriptFunctionValue(item, StringConstants::prepareProperty()));
- rule->outputArtifactsScript.initialize(
- scriptFunctionValue(item, StringConstants::outputArtifactsProperty()));
- rule->outputFileTags = m_evaluator->fileTagsValue(
- item, StringConstants::outputFileTagsProperty());
- if (rule->outputArtifactsScript.isValid()) {
- if (hasArtifactChildren)
- throw ErrorInfo(Tr::tr("The Rule.outputArtifacts script is not allowed in rules "
- "that contain Artifact items."),
- item->location());
- }
- if (!hasArtifactChildren && rule->outputFileTags.empty()) {
- throw ErrorInfo(Tr::tr("A rule needs to have Artifact items or a non-empty "
- "outputFileTags property."), item->location());
- }
- rule->multiplex = m_evaluator->boolValue(item, StringConstants::multiplexProperty());
- rule->alwaysRun = m_evaluator->boolValue(item, StringConstants::alwaysRunProperty());
- rule->inputs = m_evaluator->fileTagsValue(item, StringConstants::inputsProperty());
- rule->inputsFromDependencies
- = m_evaluator->fileTagsValue(item, StringConstants::inputsFromDependenciesProperty());
- bool requiresInputsSet = false;
- rule->requiresInputs = m_evaluator->boolValue(item, StringConstants::requiresInputsProperty(),
- &requiresInputsSet);
- if (!requiresInputsSet)
- rule->requiresInputs = rule->declaresInputs();
- rule->auxiliaryInputs
- = m_evaluator->fileTagsValue(item, StringConstants::auxiliaryInputsProperty());
- rule->excludedInputs
- = m_evaluator->fileTagsValue(item, StringConstants::excludedInputsProperty());
- if (rule->excludedInputs.empty()) {
- rule->excludedInputs = m_evaluator->fileTagsValue(
- item, StringConstants::excludedAuxiliaryInputsProperty());
- }
- rule->explicitlyDependsOn
- = m_evaluator->fileTagsValue(item, StringConstants::explicitlyDependsOnProperty());
- rule->explicitlyDependsOnFromDependencies = m_evaluator->fileTagsValue(
- item, StringConstants::explicitlyDependsOnFromDependenciesProperty());
- rule->module = m_moduleContext ? m_moduleContext->module : projectContext->dummyModule;
- if (!rule->multiplex && !rule->declaresInputs()) {
- throw ErrorInfo(Tr::tr("Rule has no inputs, but is not a multiplex rule."),
- item->location());
- }
- if (!rule->multiplex && !rule->requiresInputs) {
- throw ErrorInfo(Tr::tr("Rule.requiresInputs is false for non-multiplex rule."),
- item->location());
- }
- if (!rule->declaresInputs() && rule->requiresInputs) {
- throw ErrorInfo(Tr::tr("Rule.requiresInputs is true, but the rule "
- "does not declare any input tags."), item->location());
- }
- if (m_productContext) {
- rule->product = m_productContext->product.get();
- m_productContext->product->rules.push_back(rule);
- } else {
- projectContext->rules.push_back(rule);
- }
-}
-
-void ProjectResolver::resolveRuleArtifact(const RulePtr &rule, Item *item)
-{
- RuleArtifactPtr artifact = RuleArtifact::create();
- rule->artifacts.push_back(artifact);
- artifact->location = item->location();
-
- if (const auto sourceProperty = item->sourceProperty(StringConstants::filePathProperty()))
- artifact->filePathLocation = sourceProperty->location();
-
- artifact->filePath = verbatimValue(item, StringConstants::filePathProperty());
- artifact->fileTags = m_evaluator->fileTagsValue(item, StringConstants::fileTagsProperty());
- artifact->alwaysUpdated = m_evaluator->boolValue(item,
- StringConstants::alwaysUpdatedProperty());
-
- QualifiedIdSet seenBindings;
- for (Item *obj = item; obj; obj = obj->prototype()) {
- for (QMap<QString, ValuePtr>::const_iterator it = obj->properties().constBegin();
- it != obj->properties().constEnd(); ++it)
- {
- if (it.value()->type() != Value::ItemValueType)
- continue;
- resolveRuleArtifactBinding(artifact,
- std::static_pointer_cast<ItemValue>(it.value())->item(),
- QStringList(it.key()), &seenBindings);
- }
- }
-}
-
-void ProjectResolver::resolveRuleArtifactBinding(const RuleArtifactPtr &ruleArtifact,
- Item *item,
- const QStringList &namePrefix,
- QualifiedIdSet *seenBindings)
-{
- for (QMap<QString, ValuePtr>::const_iterator it = item->properties().constBegin();
- it != item->properties().constEnd(); ++it)
- {
- const QStringList name = QStringList(namePrefix) << it.key();
- if (it.value()->type() == Value::ItemValueType) {
- resolveRuleArtifactBinding(ruleArtifact,
- std::static_pointer_cast<ItemValue>(it.value())->item(), name,
- seenBindings);
- } else if (it.value()->type() == Value::JSSourceValueType) {
- const auto insertResult = seenBindings->insert(name);
- if (!insertResult.second)
- continue;
- JSSourceValuePtr sourceValue = std::static_pointer_cast<JSSourceValue>(it.value());
- RuleArtifact::Binding rab;
- rab.name = name;
- rab.code = sourceCodeForEvaluation(sourceValue);
- rab.location = sourceValue->location();
- ruleArtifact->bindings.push_back(rab);
- } else {
- QBS_ASSERT(!"unexpected value type", continue);
- }
- }
-}
-
-void ProjectResolver::resolveFileTagger(Item *item, ProjectContext *projectContext)
-{
- checkCancelation();
- if (!m_evaluator->boolValue(item, StringConstants::conditionProperty()))
- return;
- std::vector<FileTaggerConstPtr> &fileTaggers = m_productContext
- ? m_productContext->product->fileTaggers
- : projectContext->fileTaggers;
- const QStringList patterns = m_evaluator->stringListValue(item,
- StringConstants::patternsProperty());
- if (patterns.empty())
- throw ErrorInfo(Tr::tr("FileTagger.patterns must be a non-empty list."), item->location());
-
- const FileTags fileTags = m_evaluator->fileTagsValue(item, StringConstants::fileTagsProperty());
- if (fileTags.empty())
- throw ErrorInfo(Tr::tr("FileTagger.fileTags must not be empty."), item->location());
-
- for (const QString &pattern : patterns) {
- if (pattern.isEmpty())
- throw ErrorInfo(Tr::tr("A FileTagger pattern must not be empty."), item->location());
- }
-
- const int priority = m_evaluator->intValue(item, StringConstants::priorityProperty());
- fileTaggers.push_back(FileTagger::create(patterns, fileTags, priority));
-}
-
-void ProjectResolver::resolveJobLimit(Item *item, ProjectResolver::ProjectContext *projectContext)
-{
- if (!m_evaluator->boolValue(item, StringConstants::conditionProperty()))
- return;
- const QString jobPool = m_evaluator->stringValue(item, StringConstants::jobPoolProperty());
- if (jobPool.isEmpty())
- throw ErrorInfo(Tr::tr("A JobLimit item needs to have a non-empty '%1' property.")
- .arg(StringConstants::jobPoolProperty()), item->location());
- bool jobCountWasSet;
- const int jobCount = m_evaluator->intValue(item, StringConstants::jobCountProperty(), -1,
- &jobCountWasSet);
- if (!jobCountWasSet) {
- throw ErrorInfo(Tr::tr("A JobLimit item needs to have a '%1' property.")
- .arg(StringConstants::jobCountProperty()), item->location());
- }
- if (jobCount < 0) {
- throw ErrorInfo(Tr::tr("A JobLimit item must have a non-negative '%1' property.")
- .arg(StringConstants::jobCountProperty()), item->location());
- }
- JobLimits &jobLimits = m_moduleContext
- ? m_moduleContext->jobLimits
- : m_productContext ? m_productContext->product->jobLimits
- : projectContext->jobLimits;
- JobLimit jobLimit(jobPool, jobCount);
- const int oldLimit = jobLimits.getLimit(jobPool);
- if (oldLimit == -1 || oldLimit > jobCount)
- jobLimits.setJobLimit(jobLimit);
-}
-
-void ProjectResolver::resolveScanner(Item *item, ProjectResolver::ProjectContext *projectContext)
-{
- checkCancelation();
- if (!m_evaluator->boolValue(item, StringConstants::conditionProperty())) {
- qCDebug(lcProjectResolver) << "scanner condition is false";
- return;
- }
-
- ResolvedScannerPtr scanner = ResolvedScanner::create();
- scanner->module = m_moduleContext ? m_moduleContext->module : projectContext->dummyModule;
- scanner->inputs = m_evaluator->fileTagsValue(item, StringConstants::inputsProperty());
- scanner->recursive = m_evaluator->boolValue(item, StringConstants::recursiveProperty());
- scanner->searchPathsScript.initialize(
- scriptFunctionValue(item, StringConstants::searchPathsProperty()));
- scanner->scanScript.initialize(
- scriptFunctionValue(item, StringConstants::scanProperty()));
- m_productContext->product->scanners.push_back(scanner);
-}
-
-void ProjectResolver::matchArtifactProperties(const ResolvedProductPtr &product,
- const std::vector<SourceArtifactPtr> &artifacts)
-{
- for (const SourceArtifactPtr &artifact : artifacts) {
- for (const auto &artifactProperties : product->artifactProperties) {
- if (!artifact->isTargetOfModule()
- && artifact->fileTags.intersects(artifactProperties->fileTagsFilter())) {
- artifact->properties = artifactProperties->propertyMap();
- }
- }
- }
-}
-
-void ProjectResolver::printProfilingInfo()
-{
- if (!m_setupParams.logElapsedTime())
- return;
- m_logger.qbsLog(LoggerInfo, true) << "\t" << Tr::tr("All property evaluation took %1.")
- .arg(elapsedTimeString(m_elapsedTimeAllPropEval));
- m_logger.qbsLog(LoggerInfo, true) << "\t" << Tr::tr("Module property evaluation took %1.")
- .arg(elapsedTimeString(m_elapsedTimeModPropEval));
- m_logger.qbsLog(LoggerInfo, true) << "\t"
- << Tr::tr("Resolving groups (without module property "
- "evaluation) took %1.")
- .arg(elapsedTimeString(m_elapsedTimeGroups));
-}
-
-class TempScopeSetter
-{
-public:
- TempScopeSetter(const ValuePtr &value, Item *newScope) : m_value(value), m_oldScope(value->scope())
- {
- value->setScope(newScope, {});
- }
- ~TempScopeSetter() { if (m_value) m_value->setScope(m_oldScope, {}); }
-
- TempScopeSetter(const TempScopeSetter &) = delete;
- TempScopeSetter &operator=(const TempScopeSetter &) = delete;
- TempScopeSetter &operator=(TempScopeSetter &&) = delete;
-
- TempScopeSetter(TempScopeSetter &&other) noexcept
- : m_value(std::move(other.m_value)), m_oldScope(other.m_oldScope)
- {
- other.m_value.reset();
- other.m_oldScope = nullptr;
- }
-
-private:
- ValuePtr m_value;
- Item *m_oldScope;
-};
-
-void ProjectResolver::collectPropertiesForExportItem(Item *productModuleInstance)
-{
- if (!productModuleInstance->isPresentModule())
- return;
- Item * const exportItem = productModuleInstance->prototype();
- QBS_CHECK(exportItem);
- QBS_CHECK(exportItem->type() == ItemType::Export);
- const ItemDeclaration::Properties exportDecls = BuiltinDeclarations::instance()
- .declarationsForType(ItemType::Export).properties();
- ExportedModule &exportedModule = m_productContext->product->exportedModule;
- const auto &props = exportItem->properties();
- for (auto it = props.begin(); it != props.end(); ++it) {
- const auto match
- = [it](const PropertyDeclaration &decl) { return decl.name() == it.key(); };
- if (it.key() != StringConstants::prefixMappingProperty() &&
- std::find_if(exportDecls.begin(), exportDecls.end(), match) != exportDecls.end()) {
- continue;
- }
- if (it.value()->type() == Value::ItemValueType) {
- collectPropertiesForExportItem(it.key(), it.value(), productModuleInstance,
- exportedModule.modulePropertyValues);
- } else {
- TempScopeSetter tss(it.value(), productModuleInstance);
- evaluateProperty(exportItem, it.key(), it.value(), exportedModule.propertyValues,
- false);
- }
- }
-}
-
-// Collects module properties assigned to in other (higher-level) modules.
-void ProjectResolver::collectPropertiesForModuleInExportItem(const Item::Module &module)
-{
- if (!module.item->isPresentModule())
- return;
- ExportedModule &exportedModule = m_productContext->product->exportedModule;
- if (module.productInfo || module.name.first() == StringConstants::qbsModule())
- return;
- const auto checkName = [module](const ExportedModuleDependency &d) {
- return module.name.toString() == d.name;
- };
- if (any_of(exportedModule.moduleDependencies, checkName))
- return;
-
- Item *modulePrototype = module.item->prototype();
- while (modulePrototype && modulePrototype->type() != ItemType::Module)
- modulePrototype = modulePrototype->prototype();
- if (!modulePrototype) // Can happen for broken products in relaxed mode.
- return;
- const Item::PropertyMap &props = modulePrototype->properties();
- ExportedModuleDependency dep;
- dep.name = module.name.toString();
- for (auto it = props.begin(); it != props.end(); ++it) {
- if (it.value()->type() == Value::ItemValueType)
- collectPropertiesForExportItem(it.key(), it.value(), module.item, dep.moduleProperties);
- }
- exportedModule.moduleDependencies.push_back(dep);
-
- for (const auto &dep : module.item->modules())
- collectPropertiesForModuleInExportItem(dep);
-}
-
-void ProjectResolver::resolveProductDependencies()
-{
- for (auto it = m_productsByItem.cbegin(); it != m_productsByItem.cend(); ++it) {
- const ResolvedProductPtr &product = it.value();
- for (const Item::Module &module : it.key()->modules()) {
- if (!module.productInfo)
- continue;
- const ResolvedProductPtr &dep = m_productsByItem.value(module.productInfo->item);
- QBS_CHECK(dep);
- QBS_CHECK(dep != product);
- it.value()->dependencies << dep;
- it.value()->dependencyParameters.insert(dep, module.parameters); // TODO: Streamline this with normal module dependencies?
- }
-
- // TODO: We might want to keep the topological sorting and get rid of "module module dependencies".
- std::sort(product->dependencies.begin(),product->dependencies.end(),
- [](const ResolvedProductPtr &p1, const ResolvedProductPtr &p2) {
- return p1->fullDisplayName() < p2->fullDisplayName();
- });
- }
-}
-
-void ProjectResolver::postProcess(const ResolvedProductPtr &product,
- ProjectContext *projectContext) const
-{
- product->fileTaggers << projectContext->fileTaggers;
- std::sort(std::begin(product->fileTaggers), std::end(product->fileTaggers),
- [] (const FileTaggerConstPtr &a, const FileTaggerConstPtr &b) {
- return a->priority() > b->priority();
- });
- for (const RulePtr &rule : projectContext->rules) {
- RulePtr clonedRule = rule->clone();
- clonedRule->product = product.get();
- product->rules.push_back(clonedRule);
- }
-}
-
-void ProjectResolver::applyFileTaggers(const ResolvedProductPtr &product) const
-{
- for (const SourceArtifactPtr &artifact : product->allEnabledFiles())
- applyFileTaggers(artifact, product);
-}
-
-void ProjectResolver::applyFileTaggers(const SourceArtifactPtr &artifact,
- const ResolvedProductConstPtr &product)
-{
- if (!artifact->overrideFileTags || artifact->fileTags.empty()) {
- const QString fileName = FileInfo::fileName(artifact->absoluteFilePath);
- const FileTags fileTags = product->fileTagsForFileName(fileName);
- artifact->fileTags.unite(fileTags);
- if (artifact->fileTags.empty())
- artifact->fileTags.insert(unknownFileTag());
- qCDebug(lcProjectResolver) << "adding file tags" << artifact->fileTags
- << "to" << fileName;
- }
-}
-
-QVariantMap ProjectResolver::evaluateModuleValues(Item *item, bool lookupPrototype)
-{
- AccumulatingTimer modPropEvalTimer(m_setupParams.logElapsedTime()
- ? &m_elapsedTimeModPropEval : nullptr);
- QVariantMap moduleValues;
- for (const Item::Module &module : item->modules()) {
- if (!module.item->isPresentModule())
- continue;
- const QString fullName = module.name.toString();
- moduleValues[fullName] = evaluateProperties(module.item, lookupPrototype, true);
- }
-
- return moduleValues;
-}
-
-QVariantMap ProjectResolver::evaluateProperties(Item *item, bool lookupPrototype, bool checkErrors)
-{
- const QVariantMap tmplt;
- return evaluateProperties(item, item, tmplt, lookupPrototype, checkErrors);
-}
-
-QVariantMap ProjectResolver::evaluateProperties(const Item *item, const Item *propertiesContainer,
- const QVariantMap &tmplt, bool lookupPrototype, bool checkErrors)
-{
- AccumulatingTimer propEvalTimer(m_setupParams.logElapsedTime()
- ? &m_elapsedTimeAllPropEval : nullptr);
- QVariantMap result = tmplt;
- for (QMap<QString, ValuePtr>::const_iterator it = propertiesContainer->properties().begin();
- it != propertiesContainer->properties().end(); ++it) {
- checkCancelation();
- evaluateProperty(item, it.key(), it.value(), result, checkErrors);
- }
- return lookupPrototype && propertiesContainer->prototype()
- ? evaluateProperties(item, propertiesContainer->prototype(), result, true, checkErrors)
- : result;
-}
-
-void ProjectResolver::evaluateProperty(const Item *item, const QString &propName,
- const ValuePtr &propValue, QVariantMap &result, bool checkErrors)
-{
- JSContext * const ctx = m_engine->context();
- switch (propValue->type()) {
- case Value::ItemValueType:
- {
- // Ignore items. Those point to module instances
- // and are handled in evaluateModuleValues().
- break;
- }
- case Value::JSSourceValueType:
- {
- if (result.contains(propName))
- break;
- const PropertyDeclaration pd = item->propertyDeclaration(propName);
- if (pd.flags().testFlag(PropertyDeclaration::PropertyNotAvailableInConfig)) {
- break;
- }
- const ScopedJsValue scriptValue(ctx, m_evaluator->property(item, propName));
- if (JsException ex = m_evaluator->engine()->checkAndClearException(propValue->location())) {
- if (checkErrors)
- throw ex.toErrorInfo();
- }
-
- // NOTE: Loses type information if scriptValue.isUndefined == true,
- // as such QScriptValues become invalid QVariants.
- QVariant v;
- if (JS_IsFunction(ctx, scriptValue)) {
- v = getJsString(ctx, scriptValue);
- } else {
- v = getJsVariant(ctx, scriptValue);
- QVariantMap m = v.toMap();
- if (m.contains(StringConstants::importScopeNamePropertyInternal())) {
- QVariantMap tmp = m;
- const ScopedJsValue proto(ctx, JS_GetPrototype(ctx, scriptValue));
- m = getJsVariant(ctx, proto).toMap();
- for (auto it = tmp.begin(); it != tmp.end(); ++it)
- m.insert(it.key(), it.value());
- v = m;
- }
- }
-
- if (pd.type() == PropertyDeclaration::Path && v.isValid()) {
- v = v.toString();
- } else if (pd.type() == PropertyDeclaration::PathList
- || pd.type() == PropertyDeclaration::StringList) {
- v = v.toStringList();
- } else if (pd.type() == PropertyDeclaration::VariantList) {
- v = v.toList();
- }
- checkAllowedValues(v, propValue->location(), pd, propName);
- result[propName] = v;
- break;
- }
- case Value::VariantValueType:
- {
- if (result.contains(propName))
- break;
- VariantValuePtr vvp = std::static_pointer_cast<VariantValue>(propValue);
- QVariant v = vvp->value();
-
- const PropertyDeclaration pd = item->propertyDeclaration(propName);
- if (v.isNull() && !pd.isScalar()) // QTBUG-51237
- v = QStringList();
-
- checkAllowedValues(v, propValue->location(), pd, propName);
- result[propName] = v;
- break;
- }
- }
-}
-
-void ProjectResolver::checkAllowedValues(
- const QVariant &value, const CodeLocation &loc, const PropertyDeclaration &decl,
- const QString &key) const
-{
- const auto type = decl.type();
- if (type != PropertyDeclaration::String && type != PropertyDeclaration::StringList)
- return;
-
- if (value.isNull())
- return;
-
- const auto &allowedValues = decl.allowedValues();
- if (allowedValues.isEmpty())
- return;
-
- const auto checkValue = [this, &loc, &allowedValues, &key](const QString &value)
- {
- if (!allowedValues.contains(value)) {
- const auto message = Tr::tr("Value '%1' is not allowed for property '%2'.")
- .arg(value, key);
- ErrorInfo error(message, loc);
- handlePropertyError(error, m_setupParams, m_logger);
- }
- };
-
- if (type == PropertyDeclaration::StringList) {
- const auto strings = value.toStringList();
- for (const auto &string: strings) {
- checkValue(string);
- }
- } else if (type == PropertyDeclaration::String) {
- checkValue(value.toString());
- }
-}
-
-void ProjectResolver::collectPropertiesForExportItem(const QualifiedId &moduleName,
- const ValuePtr &value, Item *moduleInstance, QVariantMap &moduleProps)
-{
- QBS_CHECK(value->type() == Value::ItemValueType);
- Item * const itemValueItem = std::static_pointer_cast<ItemValue>(value)->item();
- if (itemValueItem->propertyDeclarations().isEmpty()) {
- for (const Item::Module &module : moduleInstance->modules()) {
- if (module.name == moduleName) {
- itemValueItem->setPropertyDeclarations(module.item->propertyDeclarations());
- break;
- }
- }
- }
- if (itemValueItem->type() == ItemType::ModuleInstancePlaceholder) {
- struct EvalPreparer {
- EvalPreparer(Item *valueItem, const QualifiedId &moduleName)
- : valueItem(valueItem),
- hadName(!!valueItem->variantProperty(StringConstants::nameProperty()))
- {
- if (!hadName) {
- // Evaluator expects a name here.
- valueItem->setProperty(StringConstants::nameProperty(),
- VariantValue::create(moduleName.toString()));
- }
- }
- ~EvalPreparer()
- {
- if (!hadName)
- valueItem->setProperty(StringConstants::nameProperty(), VariantValuePtr());
- }
- Item * const valueItem;
- const bool hadName;
- };
- EvalPreparer ep(itemValueItem, moduleName);
- std::vector<TempScopeSetter> tss;
- for (const ValuePtr &v : itemValueItem->properties())
- tss.emplace_back(v, moduleInstance);
- moduleProps.insert(moduleName.toString(), evaluateProperties(itemValueItem, false, false));
- return;
- }
- QBS_CHECK(itemValueItem->type() == ItemType::ModulePrefix);
- const Item::PropertyMap &props = itemValueItem->properties();
- for (auto it = props.begin(); it != props.end(); ++it) {
- QualifiedId fullModuleName = moduleName;
- fullModuleName << it.key();
- collectPropertiesForExportItem(fullModuleName, it.value(), moduleInstance, moduleProps);
- }
-}
-
-void ProjectResolver::createProductConfig(ResolvedProduct *product)
-{
- EvalCacheEnabler cachingEnabler(m_evaluator, m_productContext->product->sourceDirectory);
- product->moduleProperties->setValue(evaluateModuleValues(m_productContext->item));
- product->productProperties = evaluateProperties(m_productContext->item, m_productContext->item,
- QVariantMap(), true, true);
-}
-
-void ProjectResolver::callItemFunction(const ItemFuncMap &mappings, Item *item,
- ProjectContext *projectContext)
-{
- const ItemFuncPtr f = mappings.value(item->type());
- QBS_CHECK(f);
- if (item->type() == ItemType::Project) {
- ProjectContext subProjectContext = createProjectContext(projectContext);
- (this->*f)(item, &subProjectContext);
- } else {
- (this->*f)(item, projectContext);
- }
-}
-
-ProjectResolver::ProjectContext ProjectResolver::createProjectContext(ProjectContext *parentProjectContext) const
-{
- ProjectContext subProjectContext;
- subProjectContext.parentContext = parentProjectContext;
- subProjectContext.project = ResolvedProject::create();
- parentProjectContext->project->subProjects.push_back(subProjectContext.project);
- subProjectContext.project->parentProject = parentProjectContext->project;
- return subProjectContext;
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/projectresolver.h b/src/lib/corelib/language/projectresolver.h
deleted file mode 100644
index a8b06b6e6..000000000
--- a/src/lib/corelib/language/projectresolver.h
+++ /dev/null
@@ -1,202 +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$
-**
-****************************************************************************/
-
-#ifndef PROJECTRESOLVER_H
-#define PROJECTRESOLVER_H
-
-#include "filetags.h"
-#include "item.h"
-#include "itemtype.h"
-#include "projecttreebuilder.h"
-#include "qualifiedid.h"
-
-#include <logging/logger.h>
-#include <tools/set.h>
-#include <tools/setupprojectparameters.h>
-
-#include <QtCore/qhash.h>
-#include <QtCore/qmap.h>
-#include <QtCore/qstringlist.h>
-
-#include <utility>
-#include <vector>
-
-namespace qbs {
-class JobLimits;
-namespace Internal {
-
-class Evaluator;
-class Item;
-class ProgressObserver;
-class ScriptEngine;
-
-class ProjectResolver
-{
-public:
- ProjectResolver(Evaluator *evaluator, ProjectTreeBuilder::Result loadResult,
- SetupProjectParameters setupParameters, Logger &logger);
- ~ProjectResolver();
-
- void setProgressObserver(ProgressObserver *observer);
- TopLevelProjectPtr resolve();
-
- static void applyFileTaggers(const SourceArtifactPtr &artifact,
- const ResolvedProductConstPtr &product);
-
- using FileLocations = QHash<std::pair<QString, QString>, CodeLocation>;
- static SourceArtifactPtr createSourceArtifact(const ResolvedProductPtr &rproduct,
- const QString &fileName, const GroupPtr &group, bool wildcard,
- const CodeLocation &filesLocation = CodeLocation(),
- FileLocations *fileLocations = nullptr, ErrorInfo *errorInfo = nullptr);
-
-private:
- struct ProjectContext;
- struct ProductContext;
- struct ModuleContext;
- class ProductContextSwitcher;
-
- void checkCancelation() const;
- QString verbatimValue(const ValueConstPtr &value, bool *propertyWasSet = nullptr) const;
- QString verbatimValue(Item *item, const QString &name, bool *propertyWasSet = nullptr) const;
- ScriptFunctionPtr scriptFunctionValue(Item *item, const QString &name) const;
- ResolvedFileContextPtr resolvedFileContext(const FileContextConstPtr &ctx) const;
- void ignoreItem(Item *item, ProjectContext *projectContext);
- TopLevelProjectPtr resolveTopLevelProject();
- void resolveProject(Item *item, ProjectContext *projectContext);
- void resolveProjectFully(Item *item, ProjectContext *projectContext);
- void resolveSubProject(Item *item, ProjectContext *projectContext);
- void resolveProduct(Item *item, ProjectContext *projectContext);
- void resolveProductFully(Item *item, ProjectContext *projectContext);
- void resolveModules(const Item *item, ProjectContext *projectContext);
- void resolveModule(const QualifiedId &moduleName, Item *item, bool isProduct,
- const QVariantMap &parameters, JobLimits &jobLimits,
- ProjectContext *projectContext);
- void gatherProductTypes(ResolvedProduct *product, Item *item);
- QVariantMap resolveAdditionalModuleProperties(const Item *group,
- const QVariantMap &currentValues);
- void resolveGroup(Item *item, ProjectContext *projectContext);
- void resolveGroupFully(Item *item, ProjectContext *projectContext, bool isEnabled);
- void resolveShadowProduct(Item *item, ProjectContext *);
- void resolveExport(Item *exportItem, ProjectContext *);
- std::unique_ptr<ExportedItem> resolveExportChild(const Item *item,
- const ExportedModule &module);
- void resolveRule(Item *item, ProjectContext *projectContext);
- void resolveRuleArtifact(const RulePtr &rule, Item *item);
- void resolveRuleArtifactBinding(const RuleArtifactPtr &ruleArtifact, Item *item,
- const QStringList &namePrefix,
- QualifiedIdSet *seenBindings);
- void resolveFileTagger(Item *item, ProjectContext *projectContext);
- void resolveJobLimit(Item *item, ProjectContext *projectContext);
- void resolveScanner(Item *item, ProjectContext *projectContext);
- void resolveProductDependencies();
- void postProcess(const ResolvedProductPtr &product, ProjectContext *projectContext) const;
- void applyFileTaggers(const ResolvedProductPtr &product) const;
- QVariantMap evaluateModuleValues(Item *item, bool lookupPrototype = true);
- QVariantMap evaluateProperties(Item *item, bool lookupPrototype, bool checkErrors);
- QVariantMap evaluateProperties(const Item *item, const Item *propertiesContainer,
- const QVariantMap &tmplt, bool lookupPrototype,
- bool checkErrors);
- void evaluateProperty(const Item *item, const QString &propName, const ValuePtr &propValue,
- QVariantMap &result, bool checkErrors);
- void checkAllowedValues(
- const QVariant &v, const CodeLocation &loc, const PropertyDeclaration &decl,
- const QString &key) const;
- void createProductConfig(ResolvedProduct *product);
- ProjectContext createProjectContext(ProjectContext *parentProjectContext) const;
- void adaptExportedPropertyValues(const Item *shadowProductItem);
- void collectExportedProductDependencies();
-
- struct ProductDependencyInfo
- {
- ProductDependencyInfo(ResolvedProductPtr product,
- QVariantMap parameters = QVariantMap())
- : product(std::move(product)), parameters(std::move(parameters))
- {
- }
-
- ResolvedProductPtr product;
- QVariantMap parameters;
- };
-
- QString sourceCodeAsFunction(const JSSourceValueConstPtr &value,
- const PropertyDeclaration &decl) const;
- QString sourceCodeForEvaluation(const JSSourceValueConstPtr &value) const;
- static void matchArtifactProperties(const ResolvedProductPtr &product,
- const std::vector<SourceArtifactPtr> &artifacts);
- void printProfilingInfo();
-
- void collectPropertiesForExportItem(Item *productModuleInstance);
- void collectPropertiesForModuleInExportItem(const Item::Module &module);
-
- void collectPropertiesForExportItem(const QualifiedId &moduleName,
- const ValuePtr &value, Item *moduleInstance, QVariantMap &moduleProps);
- void setupExportedProperties(const Item *item, const QString &namePrefix,
- std::vector<ExportedProperty> &properties);
-
- Evaluator *m_evaluator = nullptr;
- Logger &m_logger;
- ScriptEngine *m_engine = nullptr;
- ProgressObserver *m_progressObserver = nullptr;
- ProductContext *m_productContext = nullptr;
- ModuleContext *m_moduleContext = nullptr;
- QHash<Item *, ResolvedProductPtr> m_productsByItem;
- QHash<FileTag, QList<ResolvedProductPtr> > m_productsByType;
- QHash<ResolvedProductPtr, Item *> m_productItemMap;
- mutable QHash<FileContextConstPtr, ResolvedFileContextPtr> m_fileContextMap;
- mutable QHash<CodeLocation, ScriptFunctionPtr> m_scriptFunctionMap;
- mutable QHash<std::pair<QStringView, QStringList>, QString> m_scriptFunctions;
- mutable QHash<QStringView, QString> m_sourceCode;
- const SetupProjectParameters m_setupParams;
- ProjectTreeBuilder::Result m_loadResult;
- Set<CodeLocation> m_groupLocationWarnings;
- std::vector<std::pair<ResolvedProductPtr, Item *>> m_productExportInfo;
- std::vector<ErrorInfo> m_queuedErrors;
- qint64 m_elapsedTimeModPropEval = 0;
- qint64 m_elapsedTimeAllPropEval = 0;
- qint64 m_elapsedTimeGroups = 0;
-
- typedef void (ProjectResolver::*ItemFuncPtr)(Item *item, ProjectContext *projectContext);
- using ItemFuncMap = QMap<ItemType, ItemFuncPtr>;
- void callItemFunction(const ItemFuncMap &mappings, Item *item, ProjectContext *projectContext);
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // PROJECTRESOLVER_H
diff --git a/src/lib/corelib/language/projecttreebuilder.cpp b/src/lib/corelib/language/projecttreebuilder.cpp
deleted file mode 100644
index 4667e8a1b..000000000
--- a/src/lib/corelib/language/projecttreebuilder.cpp
+++ /dev/null
@@ -1,2213 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "projecttreebuilder.h"
-
-#include "builtindeclarations.h"
-#include "evaluator.h"
-#include "filecontext.h"
-#include "filetags.h"
-#include "groupshandler.h"
-#include "itemreader.h"
-#include "language.h"
-#include "localprofiles.h"
-#include "moduleinstantiator.h"
-#include "moduleloader.h"
-#include "modulepropertymerger.h"
-#include "probesresolver.h"
-#include "productitemmultiplexer.h"
-#include "scriptengine.h"
-#include "value.h"
-
-#include <logging/categories.h>
-#include <logging/translator.h>
-#include <tools/fileinfo.h>
-#include <tools/filetime.h>
-#include <tools/preferences.h>
-#include <tools/progressobserver.h>
-#include <tools/profile.h>
-#include <tools/profiling.h>
-#include <tools/scripttools.h>
-#include <tools/settings.h>
-#include <tools/setupprojectparameters.h>
-#include <tools/stringconstants.h>
-#include <tools/version.h>
-
-#include <QDir>
-#include <QDirIterator>
-#include <QFileInfo>
-
-#include <list>
-#include <memory>
-#include <optional>
-#include <queue>
-#include <stack>
-#include <utility>
-#include <vector>
-
-namespace qbs::Internal {
-
-namespace {
-
-class ContextBase
-{
-public:
- Item *item = nullptr;
- Item *scope = nullptr;
- QString name;
-};
-
-class ProjectContext;
-
-class ProductContext : public ContextBase
-{
-public:
- ProjectContext *project = nullptr;
- Item *mergedExportItem = nullptr;
- ProjectTreeBuilder::Result::ProductInfo info;
- QString profileName;
- QString multiplexConfigurationId;
- QVariantMap profileModuleProperties; // Tree-ified module properties from profile.
- QVariantMap moduleProperties; // Tree-ified module properties from profile + overridden values.
- QVariantMap defaultParameters; // In Export item.
- QStringList searchPaths;
-
- struct ResolvedDependsItem {
- Item *item = nullptr;
- QualifiedId name;
- QStringList subModules;
- FileTags productTypes;
- QStringList multiplexIds;
- std::optional<QStringList> profiles;
- VersionRange versionRange;
- QVariantMap parameters;
- bool limitToSubProject = false;
- FallbackMode fallbackMode = FallbackMode::Enabled;
- bool requiredLocally = true;
- bool requiredGlobally = true;
- };
- struct ResolvedAndMultiplexedDependsItem {
- ResolvedAndMultiplexedDependsItem(ProductContext *product,
- const ResolvedDependsItem &dependency)
- : product(product), item(dependency.item), name(product->name),
- versionRange(dependency.versionRange), parameters(dependency.parameters),
- fallbackMode(FallbackMode::Disabled), checkProduct(false) {}
- ResolvedAndMultiplexedDependsItem(const ResolvedDependsItem &dependency,
- QualifiedId name, QString profile, QString multiplexId)
- : item(dependency.item), name(std::move(name)), profile(std::move(profile)),
- multiplexId(std::move(multiplexId)),
- versionRange(dependency.versionRange), parameters(dependency.parameters),
- limitToSubProject(dependency.limitToSubProject), fallbackMode(dependency.fallbackMode),
- requiredLocally(dependency.requiredLocally),
- requiredGlobally(dependency.requiredGlobally) {}
- ResolvedAndMultiplexedDependsItem() = default;
- static ResolvedAndMultiplexedDependsItem makeBaseDependency() {
- ResolvedAndMultiplexedDependsItem item;
- item.fallbackMode = FallbackMode::Disabled;
- item.name = StringConstants::qbsModule();
- return item;
- }
-
- QString id() const;
- CodeLocation location() const;
- QString displayName() const;
-
- ProductContext *product = nullptr;
- Item *item = nullptr;
- QualifiedId name;
- QString profile;
- QString multiplexId;
- VersionRange versionRange;
- QVariantMap parameters;
- bool limitToSubProject = false;
- FallbackMode fallbackMode = FallbackMode::Enabled;
- bool requiredLocally = true;
- bool requiredGlobally = true;
- bool checkProduct = true;
- };
- struct DependenciesResolvingState {
- Item *loadingItem = nullptr;
- ResolvedAndMultiplexedDependsItem loadingItemOrigin;
- std::queue<Item *> pendingDependsItems;
- std::optional<ResolvedDependsItem> currentDependsItem;
- std::queue<ResolvedAndMultiplexedDependsItem> pendingResolvedDependencies;
- bool requiredByLoadingItem = true;
- };
- std::list<DependenciesResolvingState> resolveDependenciesState;
-
- QString uniqueName() const;
-};
-
-class TopLevelProjectContext
-{
-public:
- TopLevelProjectContext() = default;
- TopLevelProjectContext(const TopLevelProjectContext &) = delete;
- TopLevelProjectContext &operator=(const TopLevelProjectContext &) = delete;
- ~TopLevelProjectContext() { qDeleteAll(projects); }
-
- std::vector<ProjectContext *> projects;
- std::list<std::pair<ProductContext *, int>> productsToHandle;
- std::vector<ProbeConstPtr> probes;
- QString buildDirectory;
-};
-
-class ProjectContext : public ContextBase
-{
-public:
- TopLevelProjectContext *topLevelProject = nullptr;
- ProjectTreeBuilder::Result *result = nullptr;
- std::vector<ProductContext> products;
- std::vector<QStringList> searchPathsStack;
-};
-
-
-using ShadowProductInfo = std::pair<bool, QString>;
-enum class Deferral { Allowed, NotAllowed };
-enum class HandleDependency { Use, Ignore, Defer };
-
-class TimingData {
-public:
- qint64 prepareProducts = 0;
- qint64 productDependencies = 0;
- qint64 handleProducts = 0;
- qint64 propertyChecking = 0;
-};
-
-} // namespace
-
-class ProjectTreeBuilder::Private
-{
-public:
- Private(const SetupProjectParameters &parameters, ItemPool &itemPool, Evaluator &evaluator,
- Logger &logger)
- : parameters(parameters), itemPool(itemPool), evaluator(evaluator), logger(logger) {}
-
- Item *loadTopLevelProjectItem();
- void checkOverriddenValues();
- void collectNameFromOverride(const QString &overrideString);
- Item *loadItemFromFile(const QString &filePath, const CodeLocation &referencingLocation);
- Item *wrapInProjectIfNecessary(Item *item);
- void handleTopLevelProject(Result &loadResult, const Set<QString> &referencedFilePaths);
- void handleProject(Result *loadResult, TopLevelProjectContext *topLevelProjectContext,
- Item *projectItem, const Set<QString> &referencedFilePaths);
- void prepareProduct(ProjectContext &projectContext, Item *productItem);
- void handleNextProduct(TopLevelProjectContext &tlp);
- void handleProduct(ProductContext &productContext, Deferral deferral);
- bool resolveDependencies(ProductContext &product, Deferral deferral);
- void setSearchPathsForProduct(ProductContext &product);
- void handleSubProject(ProjectContext &projectContext, Item *projectItem,
- const Set<QString> &referencedFilePaths);
- void initProductProperties(const ProductContext &product);
- void printProfilingInfo();
- void checkProjectNamesInOverrides(const TopLevelProjectContext &tlp);
- void checkProductNamesInOverrides();
- void collectProductsByName(const TopLevelProjectContext &topLevelProject);
- void adjustDependsItemForMultiplexing(const ProductContext &product, Item *dependsItem);
- ShadowProductInfo getShadowProductInfo(const ProductContext &product) const;
- void handleProductError(const ErrorInfo &error, ProductContext &productContext);
- void handleModuleSetupError(ProductContext &product, const Item::Module &module,
- const ErrorInfo &error);
- bool checkItemCondition(Item *item);
- QStringList readExtraSearchPaths(Item *item, bool *wasSet = nullptr);
- QList<Item *> multiplexProductItem(ProductContext &dummyContext, Item *productItem);
- void checkCancelation() const;
- QList<Item *> loadReferencedFile(const QString &relativePath,
- const CodeLocation &referencingLocation,
- const Set<QString> &referencedFilePaths,
- ProductContext &dummyContext);
- void copyProperties(const Item *sourceProject, Item *targetProject);
- bool mergeExportItems(ProductContext &productContext);
- bool checkExportItemCondition(Item *exportItem, const ProductContext &product);
- void resolveProbes(ProductContext &product, Item *item);
-
- Item *loadBaseModule(ProductContext &product, Item *item);
-
- struct LoadModuleResult {
- Item *moduleItem = nullptr;
- ProductContext *product = nullptr;
- HandleDependency handleDependency = HandleDependency::Use;
- };
- LoadModuleResult loadModule(ProductContext &product, Item *loadingItem,
- const ProductContext::ResolvedAndMultiplexedDependsItem &dependency,
- Deferral deferral);
-
- std::optional<ProductContext::ResolvedDependsItem>
- resolveDependsItem(const ProductContext &product, Item *dependsItem);
- std::queue<ProductContext::ResolvedAndMultiplexedDependsItem>
- multiplexDependency(const ProductContext &product,
- const ProductContext::ResolvedDependsItem &dependency);
- static bool haveSameSubProject(const ProductContext &p1, const ProductContext &p2);
- QVariantMap extractParameters(Item *dependsItem) const;
-
- const SetupProjectParameters &parameters;
- ItemPool &itemPool;
- Evaluator &evaluator;
- Logger &logger;
- ProgressObserver *progressObserver = nullptr;
- TimingData timingData;
- ItemReader reader{logger};
- ProbesResolver probesResolver{parameters, evaluator, logger};
- ModuleProviderLoader moduleProviderLoader{parameters, reader, evaluator, probesResolver, logger};
- ModuleLoader moduleLoader{parameters, moduleProviderLoader, reader, evaluator, logger};
- ModulePropertyMerger propertyMerger{parameters, evaluator, logger};
- ModuleInstantiator moduleInstantiator{parameters, itemPool, propertyMerger, logger};
- ProductItemMultiplexer multiplexer{parameters, evaluator, logger, [this](Item *productItem) {
- return moduleInstantiator.retrieveQbsItem(productItem);
- }};
- GroupsHandler groupsHandler{parameters, moduleInstantiator, evaluator, logger};
- LocalProfiles localProfiles{parameters, evaluator, logger};
- FileTime lastResolveTime;
- QVariantMap storedProfiles;
-
- Set<Item *> disabledItems;
- std::unique_ptr<Settings> settings;
- Set<QString> projectNamesUsedInOverrides;
- Set<QString> productNamesUsedInOverrides;
- Set<QString> disabledProjects;
- Set<QString> erroneousProducts;
- std::multimap<QString, ProductContext *> productsByName;
-
- // For fast look-up when resolving Depends.productTypes.
- // The contract is that it contains fully handled, error-free, enabled products.
- std::multimap<FileTag, ProductContext *> productsByType;
-
- Version qbsVersion;
- Item *tempScopeItem = nullptr;
-
-private:
- class TempBaseModuleAttacher {
- public:
- TempBaseModuleAttacher(Private *d, ProductContext &product);
- ~TempBaseModuleAttacher() { drop(); }
- void drop();
- Item *tempBaseModuleItem() const { return m_tempBaseModule; }
-
- private:
- Item * const m_productItem;
- ValuePtr m_origQbsValue;
- Item *m_tempBaseModule = nullptr;
- };
-};
-
-ProjectTreeBuilder::ProjectTreeBuilder(const SetupProjectParameters &parameters, ItemPool &itemPool,
- Evaluator &evaluator, Logger &logger)
- : d(new Private(parameters, itemPool, evaluator, logger))
-{
- d->reader.setDeprecationWarningMode(parameters.deprecationWarningMode());
- d->reader.setEnableTiming(parameters.logElapsedTime());
- d->settings = std::make_unique<Settings>(parameters.settingsDirectory());
-}
-
-ProjectTreeBuilder::~ProjectTreeBuilder() { delete d; }
-
-void ProjectTreeBuilder::setProgressObserver(ProgressObserver *progressObserver)
-{
- d->progressObserver = progressObserver;
-}
-
-void ProjectTreeBuilder::setSearchPaths(const QStringList &searchPaths)
-{
- d->reader.setSearchPaths(searchPaths);
- qCDebug(lcModuleLoader) << "initial search paths:" << searchPaths;
-}
-
-void ProjectTreeBuilder::setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes)
-{
- d->probesResolver.setOldProjectProbes(oldProbes);
-}
-
-void ProjectTreeBuilder::setOldProductProbes(
- const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes)
-{
- d->probesResolver.setOldProductProbes(oldProbes);
-}
-
-void ProjectTreeBuilder::setLastResolveTime(const FileTime &time) { d->lastResolveTime = time; }
-
-void ProjectTreeBuilder::setStoredProfiles(const QVariantMap &profiles)
-{
- d->storedProfiles = profiles;
-}
-
-void ProjectTreeBuilder::setStoredModuleProviderInfo(
- const StoredModuleProviderInfo &moduleProviderInfo)
-{
- d->moduleProviderLoader.setStoredModuleProviderInfo(moduleProviderInfo);
-}
-
-ProjectTreeBuilder::Result ProjectTreeBuilder::load()
-{
- TimedActivityLogger mainTimer(d->logger, Tr::tr("ProjectTreeBuilder"),
- d->parameters.logElapsedTime());
- qCDebug(lcModuleLoader) << "load" << d->parameters.projectFilePath();
-
- d->checkOverriddenValues();
- d->reader.setPool(&d->itemPool);
-
- Result result;
- result.profileConfigs = d->storedProfiles;
- result.root = d->loadTopLevelProjectItem();
- d->handleTopLevelProject(result, {QDir::cleanPath(d->parameters.projectFilePath())});
-
- result.qbsFiles = d->reader.filesRead() - d->moduleProviderLoader.tempQbsFiles();
- for (auto it = d->localProfiles.profiles().begin(); it != d->localProfiles.profiles().end();
- ++it) {
- result.profileConfigs.remove(it.key());
- }
-
- d->printProfilingInfo();
-
- return result;
-}
-
-Item *ProjectTreeBuilder::Private::loadTopLevelProjectItem()
-{
- const QStringList topLevelSearchPaths
- = parameters.finalBuildConfigurationTree()
- .value(StringConstants::projectPrefix()).toMap()
- .value(StringConstants::qbsSearchPathsProperty()).toStringList();
- SearchPathsManager searchPathsManager(reader, topLevelSearchPaths);
- Item * const root = loadItemFromFile(parameters.projectFilePath(), CodeLocation());
- if (!root)
- return {};
-
- switch (root->type()) {
- case ItemType::Product:
- return wrapInProjectIfNecessary(root);
- case ItemType::Project:
- return root;
- 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());
- }
-}
-
-void ProjectTreeBuilder::Private::checkOverriddenValues()
-{
- static const auto matchesPrefix = [](const QString &key) {
- static const QStringList prefixes({StringConstants::projectPrefix(),
- QStringLiteral("projects"),
- QStringLiteral("products"), QStringLiteral("modules"),
- StringConstants::moduleProviders(),
- StringConstants::qbsModule()});
- for (const auto &prefix : prefixes) {
- if (key.startsWith(prefix + QLatin1Char('.')))
- return true;
- }
- return false;
- };
- const QVariantMap &overriddenValues = parameters.overriddenValues();
- for (auto it = overriddenValues.begin(); it != overriddenValues.end(); ++it) {
- if (matchesPrefix(it.key())) {
- collectNameFromOverride(it.key());
- continue;
- }
-
- ErrorInfo e(Tr::tr("Property override key '%1' not understood.").arg(it.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, parameters, logger);
- }
-}
-
-void ProjectTreeBuilder::Private::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()) {
- projectNamesUsedInOverrides.insert(projectName);
- return;
- }
- const QString &productName = extract(StringConstants::productsOverridePrefix(), overrideString);
- if (!productName.isEmpty()) {
- productNamesUsedInOverrides.insert(productName.left(
- productName.indexOf(StringConstants::dot())));
- return;
- }
-}
-
-Item *ProjectTreeBuilder::Private::loadItemFromFile(const QString &filePath,
- const CodeLocation &referencingLocation)
-{
- return reader.setupItemFromFile(filePath, referencingLocation, evaluator);
-}
-
-Item *ProjectTreeBuilder::Private::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(parameters.deprecationWarningMode(), logger);
- return prj;
-}
-
-void ProjectTreeBuilder::Private::handleTopLevelProject(Result &loadResult,
- const Set<QString> &referencedFilePaths)
-{
- TopLevelProjectContext tlp;
- tlp.buildDirectory = TopLevelProject::deriveBuildDirectory(
- parameters.buildRoot(),
- TopLevelProject::deriveId(parameters.finalBuildConfigurationTree()));
- Item * const projectItem = loadResult.root;
- projectItem->setProperty(StringConstants::sourceDirectoryProperty(),
- VariantValue::create(QFileInfo(projectItem->file()->filePath())
- .absolutePath()));
- projectItem->setProperty(StringConstants::buildDirectoryProperty(),
- VariantValue::create(tlp.buildDirectory));
- projectItem->setProperty(StringConstants::profileProperty(),
- VariantValue::create(parameters.topLevelProfile()));
- handleProject(&loadResult, &tlp, projectItem, referencedFilePaths);
- checkProjectNamesInOverrides(tlp);
- collectProductsByName(tlp);
- checkProductNamesInOverrides();
-
- for (ProjectContext * const projectContext : qAsConst(tlp.projects)) {
- for (ProductContext &productContext : projectContext->products)
- tlp.productsToHandle.emplace_back(&productContext, -1);
- }
- while (!tlp.productsToHandle.empty())
- handleNextProduct(tlp);
-
- loadResult.projectProbes = tlp.probes;
- loadResult.storedModuleProviderInfo = moduleProviderLoader.storedModuleProviderInfo();
-
- reader.clearExtraSearchPathsStack();
- AccumulatingTimer timer(parameters.logElapsedTime()
- ? &timingData.propertyChecking : nullptr);
- checkPropertyDeclarations(projectItem, disabledItems, parameters, logger);
-}
-
-void ProjectTreeBuilder::Private::handleProject(
- Result *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(&itemPool, ItemType::Scope);
- projectContext.scope->setFile(projectItem->file());
- projectContext.scope->setProperty(StringConstants::projectVar(), itemValue);
- ProductContext dummyProductContext;
- dummyProductContext.project = &projectContext;
- dummyProductContext.moduleProperties = parameters.finalBuildConfigurationTree();
- dummyProductContext.profileModuleProperties = dummyProductContext.moduleProperties;
- dummyProductContext.profileName = parameters.topLevelProfile();
- loadBaseModule(dummyProductContext, projectItem);
-
- projectItem->overrideProperties(parameters.overriddenValuesTree(),
- StringConstants::projectPrefix(), parameters, logger);
- projectContext.name = evaluator.stringValue(projectItem, StringConstants::nameProperty());
- if (projectContext.name.isEmpty()) {
- projectContext.name = FileInfo::baseName(projectItem->location().filePath());
- projectItem->setProperty(StringConstants::nameProperty(),
- VariantValue::create(projectContext.name));
- }
- projectItem->overrideProperties(parameters.overriddenValuesTree(),
- StringConstants::projectsOverridePrefix() + projectContext.name,
- parameters, logger);
- if (!checkItemCondition(projectItem)) {
- disabledProjects.insert(projectContext.name);
- return;
- }
- topLevelProjectContext->projects.push_back(p.release());
- SearchPathsManager searchPathsManager(reader, readExtraSearchPaths(projectItem)
- << projectItem->file()->dirPath());
- projectContext.searchPathsStack = reader.extraSearchPathsStack();
- projectContext.item = projectItem;
-
- const QString minVersionStr
- = 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 (!qbsVersion.isValid())
- qbsVersion = Version::fromString(QLatin1String(QBS_VERSION));
- if (qbsVersion < minVersion) {
- throw ErrorInfo(Tr::tr("The project requires at least qbs version %1, but "
- "this is qbs version %2.").arg(minVersion.toString(),
- qbsVersion.toString()));
- }
-
- for (Item * const child : projectItem->children())
- child->setScope(projectContext.scope);
-
- resolveProbes(dummyProductContext, projectItem);
- projectContext.topLevelProject->probes << dummyProductContext.info.probes;
-
- localProfiles.collectProfilesFromItems(projectItem, projectContext.scope);
-
- 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 = 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 (parameters.productErrorMode() == ErrorHandlingMode::Strict)
- throw;
- 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;
- }
- }
-
-}
-
-void ProjectTreeBuilder::Private::prepareProduct(ProjectContext &projectContext, Item *productItem)
-{
- AccumulatingTimer timer(parameters.logElapsedTime()
- ? &timingData.prepareProducts : nullptr);
- checkCancelation();
- qCDebug(lcModuleLoader) << "prepareProduct" << productItem->file()->filePath();
-
- ProductContext productContext;
- productContext.item = productItem;
- productContext.project = &projectContext;
-
- // Retrieve name, profile and multiplex id.
- productContext.name = evaluator.stringValue(productItem, StringConstants::nameProperty());
- QBS_CHECK(!productContext.name.isEmpty());
- const ItemValueConstPtr qbsItemValue = productItem->itemProperty(StringConstants::qbsModule());
- if (qbsItemValue && qbsItemValue->item()->hasProperty(StringConstants::profileProperty())) {
- TempBaseModuleAttacher tbma(this, productContext);
- productContext.profileName = evaluator.stringValue(
- tbma.tempBaseModuleItem(), StringConstants::profileProperty(), QString());
- } else {
- productContext.profileName = parameters.topLevelProfile();
- }
- productContext.multiplexConfigurationId = evaluator.stringValue(
- productItem, StringConstants::multiplexConfigurationIdProperty());
- QBS_CHECK(!productContext.profileName.isEmpty());
-
- // Set up full module property map based on the profile.
- const auto it = projectContext.result->profileConfigs.constFind(productContext.profileName);
- QVariantMap flatConfig;
- if (it == projectContext.result->profileConfigs.constEnd()) {
- const Profile profile(productContext.profileName, settings.get(), localProfiles.profiles());
- if (!profile.exists()) {
- ErrorInfo error(Tr::tr("Profile '%1' does not exist.").arg(profile.name()),
- productItem->location());
- handleProductError(error, productContext);
- return;
- }
- flatConfig = SetupProjectParameters::expandedBuildConfiguration(
- profile, parameters.configurationName());
- projectContext.result->profileConfigs.insert(productContext.profileName, flatConfig);
- } else {
- flatConfig = it.value().toMap();
- }
- productContext.profileModuleProperties = SetupProjectParameters::finalBuildConfigurationTree(
- flatConfig, {});
- productContext.moduleProperties = SetupProjectParameters::finalBuildConfigurationTree(
- flatConfig, parameters.overriddenValues());
- initProductProperties(productContext);
-
- // Set up product scope. This is mainly for using the "product" and "project"
- // variables in some contexts.
- ItemValuePtr itemValue = ItemValue::create(productItem);
- productContext.scope = Item::create(&itemPool, 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(parameters.deprecationWarningMode(), 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(parameters.deprecationWarningMode(), logger);
- Item::addChild(importer, dependsItem);
- Item::addChild(productItem, importer);
- prepareProduct(projectContext, importer);
-}
-
-void ProjectTreeBuilder::Private::handleNextProduct(TopLevelProjectContext &tlp)
-{
- auto [product, queueSizeOnInsert] = tlp.productsToHandle.front();
- tlp.productsToHandle.pop_front();
-
- // If the queue of in-progress products has shrunk since the last time we tried handling
- // this product, there has been forward progress and we can allow a deferral.
- const Deferral deferral = queueSizeOnInsert == -1
- || queueSizeOnInsert > int(tlp.productsToHandle.size())
- ? Deferral::Allowed : Deferral::NotAllowed;
-
- reader.setExtraSearchPathsStack(product->project->searchPathsStack);
- try {
- handleProduct(*product, deferral);
- if (product->name.startsWith(StringConstants::shadowProductPrefix()))
- tlp.probes << product->info.probes;
- } catch (const ErrorInfo &err) {
- handleProductError(err, *product);
- }
-
- // The search paths stack can change during dependency resolution (due to module providers);
- // check that we've rolled back all the changes
- QBS_CHECK(reader.extraSearchPathsStack() == product->project->searchPathsStack);
-
- // If we encountered a dependency to an in-progress product or to a bulk dependency,
- // we defer handling this product if it hasn't failed yet and there is still forward progress.
- if (!product->info.delayedError.hasError() && !product->resolveDependenciesState.empty())
- tlp.productsToHandle.emplace_back(product, int(tlp.productsToHandle.size()));
-}
-
-void ProjectTreeBuilder::Private::handleProduct(ProductContext &product, Deferral deferral)
-{
- checkCancelation();
-
- AccumulatingTimer timer(parameters.logElapsedTime() ? &timingData.handleProducts : nullptr);
- if (product.info.delayedError.hasError())
- return;
-
- if (!resolveDependencies(product, deferral))
- return;
-
- // Run probes for modules and product.
- for (const Item::Module &module : product.item->modules()) {
- if (!module.item->isPresentModule())
- continue;
- if (module.productInfo && disabledItems.contains(module.productInfo->item)) {
- createNonPresentModule(itemPool, module.name.toString(),
- QLatin1String("module's exporting product is disabled"),
- module.item);
- continue;
- }
- try {
- resolveProbes(product, module.item);
- if (module.versionRange.minimum.isValid()
- || module.versionRange.maximum.isValid()) {
- if (module.versionRange.maximum.isValid()
- && module.versionRange.minimum >= module.versionRange.maximum) {
- throw ErrorInfo(Tr::tr("Impossible version constraint [%1,%2) set for module "
- "'%3'").arg(module.versionRange.minimum.toString(),
- module.versionRange.maximum.toString(),
- module.name.toString()));
- }
- const Version moduleVersion = Version::fromString(
- evaluator.stringValue(module.item,
- StringConstants::versionProperty()));
- if (moduleVersion < module.versionRange.minimum) {
- throw ErrorInfo(Tr::tr("Module '%1' has version %2, but it needs to be "
- "at least %3.").arg(module.name.toString(),
- moduleVersion.toString(),
- module.versionRange.minimum.toString()));
- }
- if (module.versionRange.maximum.isValid()
- && moduleVersion >= module.versionRange.maximum) {
- throw ErrorInfo(Tr::tr("Module '%1' has version %2, but it needs to be "
- "lower than %3.").arg(module.name.toString(),
- moduleVersion.toString(),
- module.versionRange.maximum.toString()));
- }
- }
- } catch (const ErrorInfo &error) {
- handleModuleSetupError(product, module, error);
- if (product.info.delayedError.hasError())
- return;
- }
- }
- resolveProbes(product, product.item);
-
- // After the probes have run, we can switch on the evaluator cache.
- FileTags fileTags = evaluator.fileTagsValue(product.item, StringConstants::typeProperty());
- EvalCacheEnabler cacheEnabler(&evaluator, evaluator.stringValue(
- product.item,
- StringConstants::sourceDirectoryProperty()));
-
- // Run module validation scripts.
- for (const Item::Module &module : product.item->modules()) {
- if (!module.item->isPresentModule())
- continue;
- try {
- evaluator.boolValue(module.item, StringConstants::validateProperty());
- for (const auto &dep : module.item->modules()) {
- if (dep.required && !dep.item->isPresentModule()) {
- throw ErrorInfo(Tr::tr("Module '%1' depends on module '%2', which was not "
- "loaded successfully")
- .arg(module.name.toString(), dep.name.toString()));
- }
- }
- fileTags += evaluator.fileTagsValue(
- module.item, StringConstants::additionalProductTypesProperty());
- } catch (const ErrorInfo &error) {
- handleModuleSetupError(product, module, error);
- if (product.info.delayedError.hasError())
- return;
- }
- }
-
- // Disable modules that have been pulled in only by now-disabled modules.
- // Note that this has to happen in the reverse order compared to the other loops,
- // with the leaves checked last.
- for (auto it = product.item->modules().rbegin(); it != product.item->modules().rend(); ++it) {
- const Item::Module &module = *it;
- if (!module.item->isPresentModule())
- continue;
- bool hasPresentLoadingItem = false;
- for (const Item * const loadingItem : module.loadingItems) {
- if (loadingItem == product.item) {
- hasPresentLoadingItem = true;
- break;
- }
- if (!loadingItem->isPresentModule())
- continue;
- if (loadingItem->prototype() && loadingItem->prototype()->type() == ItemType::Export) {
- QBS_CHECK(loadingItem->prototype()->parent()->type() == ItemType::Product);
- if (disabledItems.contains(loadingItem->prototype()->parent()))
- continue;
- }
- hasPresentLoadingItem = true;
- break;
- }
- if (!hasPresentLoadingItem) {
- createNonPresentModule(itemPool, module.name.toString(),
- QLatin1String("imported only by disabled module(s)"),
- module.item);
- continue;
- }
- }
-
- // Now do the canonical module property values merge. Note that this will remove
- // previously attached values from modules that failed validation.
- // Evaluator cache entries that could potentially change due to this will be purged.
- propertyMerger.doFinalMerge(product.item);
-
- const bool enabled = checkItemCondition(product.item);
- moduleLoader.checkDependencyParameterDeclarations(product.item, product.name);
-
- groupsHandler.setupGroups(product.item, product.scope);
- product.info.modulePropertiesSetInGroups = groupsHandler.modulePropertiesSetInGroups();
- disabledItems.unite(groupsHandler.disabledGroups());
-
- // Collect the full list of fileTags, including the values contributed by modules.
- if (!product.info.delayedError.hasError() && enabled) {
- for (const FileTag &tag : fileTags)
- productsByType.insert({tag, &product});
- product.item->setProperty(StringConstants::typeProperty(),
- VariantValue::create(sorted(fileTags.toStringList())));
- }
- product.project->result->productInfos[product.item] = product.info;
-}
-
-bool ProjectTreeBuilder::Private::resolveDependencies(ProductContext &product, Deferral deferral)
-{
- AccumulatingTimer timer(parameters.logElapsedTime()
- ? &timingData.productDependencies : nullptr);
-
- // Initialize the state with the direct Depends items of the product item.
- // This branch is executed once per product, while the function might be entered
- // multiple times due to deferrals.
- if (product.resolveDependenciesState.empty()) {
- setSearchPathsForProduct(product);
- std::queue<Item *> topLevelDependsItems;
- for (Item * const child : product.item->children()) {
- if (child->type() == ItemType::Depends)
- topLevelDependsItems.push(child);
- }
- product.resolveDependenciesState.push_front({product.item, {}, topLevelDependsItems, });
- product.resolveDependenciesState.front().pendingResolvedDependencies.push(
- ProductContext::ResolvedAndMultiplexedDependsItem::makeBaseDependency());
- }
-
- SearchPathsManager searchPathsMgr(reader, product.searchPaths);
-
- while (!product.resolveDependenciesState.empty()) {
-fixme:
- auto &state = product.resolveDependenciesState.front();
- while (!state.pendingResolvedDependencies.empty()) {
- QBS_CHECK(!state.currentDependsItem);
- const auto dependency = state.pendingResolvedDependencies.front();
- try {
- const LoadModuleResult res = loadModule(product, state.loadingItem, dependency,
- deferral);
- switch (res.handleDependency) {
- case HandleDependency::Defer:
- QBS_CHECK(deferral == Deferral::Allowed);
- if (res.product)
- state.pendingResolvedDependencies.front().product = res.product;
- return false;
- case HandleDependency::Ignore:
- state.pendingResolvedDependencies.pop();
- continue;
- case HandleDependency::Use:
- if (dependency.name.toString() == StringConstants::qbsModule()) {
- state.pendingResolvedDependencies.pop();
- continue;
- }
- break;
- }
-
- QBS_CHECK(res.moduleItem);
- std::queue<Item *> moduleDependsItems;
- for (Item * const child : res.moduleItem->children()) {
- if (child->type() == ItemType::Depends)
- moduleDependsItems.push(child);
- }
-
- state.pendingResolvedDependencies.pop();
- product.resolveDependenciesState.push_front(
- {res.moduleItem, dependency, moduleDependsItems, {}, {},
- dependency.requiredGlobally || state.requiredByLoadingItem});
- product.resolveDependenciesState.front().pendingResolvedDependencies.push(
- ProductContext::ResolvedAndMultiplexedDependsItem::makeBaseDependency());
- break;
- } catch (const ErrorInfo &e) {
- if (dependency.name.toString() == StringConstants::qbsModule())
- throw e;
-
- if (!dependency.requiredLocally) {
- state.pendingResolvedDependencies.pop();
- continue;
- }
-
- // See QBS-1338 for why we do not abort handling the product.
- state.pendingResolvedDependencies.pop();
- Item::Modules &modules = product.item->modules();
- while (product.resolveDependenciesState.size() > 1) {
- const auto loadingItemModule = std::find_if(
- modules.begin(), modules.end(), [&](const Item::Module &m) {
- return m.item == product.resolveDependenciesState.front().loadingItem;
- });
- for (auto it = loadingItemModule; it != modules.end(); ++it) {
- createNonPresentModule(itemPool, it->name.toString(),
- QLatin1String("error in Depends chain"), it->item);
- }
- modules.erase(loadingItemModule, modules.end());
- product.resolveDependenciesState.pop_front();
- }
- handleProductError(e, product);
- goto fixme;
- }
- }
- if (&state != &product.resolveDependenciesState.front())
- continue;
-
- if (state.currentDependsItem) {
- QBS_CHECK(state.pendingResolvedDependencies.empty());
-
- // We postpone handling Depends.productTypes for as long as possible, because
- // the full type of a product becomes available only after its modules have been loaded.
- if (!state.currentDependsItem->productTypes.empty() && deferral == Deferral::Allowed)
- return false;
-
- state.pendingResolvedDependencies = multiplexDependency(product,
- *state.currentDependsItem);
- state.currentDependsItem.reset();
- continue;
- }
-
- while (!state.pendingDependsItems.empty()) {
- QBS_CHECK(!state.currentDependsItem);
- QBS_CHECK(state.pendingResolvedDependencies.empty());
- Item * const dependsItem = state.pendingDependsItems.front();
- state.pendingDependsItems.pop();
- adjustDependsItemForMultiplexing(product, dependsItem);
- state.currentDependsItem = resolveDependsItem(product, dependsItem);
- if (!state.currentDependsItem)
- continue;
- state.currentDependsItem->requiredGlobally = state.currentDependsItem->requiredLocally
- && state.loadingItemOrigin.requiredGlobally;
- goto fixme;
- }
-
- QBS_CHECK(!state.currentDependsItem);
- QBS_CHECK(state.pendingResolvedDependencies.empty());
- QBS_CHECK(state.pendingDependsItems.empty());
-
- // This ensures a sorted module list in the product (dependers after dependencies).
- if (product.resolveDependenciesState.size() > 1) {
- QBS_CHECK(state.loadingItem->type() == ItemType::ModuleInstance);
- Item::Modules &modules = product.item->modules();
- const auto loadingItemModule = std::find_if(modules.begin(), modules.end(),
- [&](const Item::Module &m) {
- return m.item == state.loadingItem;
- });
- QBS_CHECK(loadingItemModule != modules.end());
- const Item::Module tempModule = *loadingItemModule;
- modules.erase(loadingItemModule);
- modules.push_back(tempModule);
- }
- product.resolveDependenciesState.pop_front();
- }
- return true;
-}
-
-void ProjectTreeBuilder::Private::setSearchPathsForProduct(ProductContext &product)
-{
- QBS_CHECK(product.searchPaths.isEmpty());
-
- product.searchPaths = readExtraSearchPaths(product.item);
- Settings settings(parameters.settingsDirectory());
- const QStringList prefsSearchPaths = Preferences(&settings, product.profileModuleProperties)
- .searchPaths();
- const QStringList &currentSearchPaths = reader.allSearchPaths();
- for (const QString &p : prefsSearchPaths) {
- if (!currentSearchPaths.contains(p) && FileInfo(p).exists())
- product.searchPaths << p;
- }
-}
-
-void ProjectTreeBuilder::Private::handleSubProject(
- 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 = nullptr;
- QString subProjectFilePath;
- try {
- const QString projectFileDirPath = FileInfo::path(projectItem->file()->filePath());
- const QString relativeFilePath
- = 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 (parameters.productErrorMode() == ErrorHandlingMode::Strict)
- throw;
- logger.printWarning(error);
- return;
- }
-
- loadedItem = wrapInProjectIfNecessary(loadedItem);
- const bool inheritProperties = evaluator.boolValue(
- projectItem, StringConstants::inheritPropertiesProperty());
-
- if (inheritProperties)
- copyProperties(projectItem->parent(), loadedItem);
- if (propertiesItem) {
- const Item::PropertyMap &overriddenProperties = propertiesItem->properties();
- for (auto it = overriddenProperties.begin(); it != overriddenProperties.end(); ++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);
-}
-
-void ProjectTreeBuilder::Private::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 ProjectTreeBuilder::Private::printProfilingInfo()
-{
- if (!parameters.logElapsedTime())
- return;
- logger.qbsLog(LoggerInfo, true) << " "
- << Tr::tr("Project file loading and parsing took %1.")
- .arg(elapsedTimeString(reader.elapsedTime()));
- logger.qbsLog(LoggerInfo, true) << " "
- << Tr::tr("Preparing products took %1.")
- .arg(elapsedTimeString(timingData.prepareProducts));
- logger.qbsLog(LoggerInfo, true) << " "
- << Tr::tr("Handling products took %1.")
- .arg(elapsedTimeString(timingData.handleProducts));
- logger.qbsLog(LoggerInfo, true) << " "
- << Tr::tr("Setting up product dependencies took %1.")
- .arg(elapsedTimeString(timingData.productDependencies));
- moduleLoader.printProfilingInfo(6);
- moduleInstantiator.printProfilingInfo(6);
- propertyMerger.printProfilingInfo(6);
- groupsHandler.printProfilingInfo(4);
- probesResolver.printProfilingInfo(4);
- logger.qbsLog(LoggerInfo, true) << " "
- << Tr::tr("Property checking took %1.")
- .arg(elapsedTimeString(timingData.propertyChecking));
-}
-
-void ProjectTreeBuilder::Private::checkProjectNamesInOverrides(const TopLevelProjectContext &tlp)
-{
- for (const QString &projectNameInOverride : projectNamesUsedInOverrides) {
- if (disabledProjects.contains(projectNameInOverride))
- continue;
- if (!any_of(tlp.projects, [&projectNameInOverride](const ProjectContext *p) {
- return p->name == projectNameInOverride; })) {
- handlePropertyError(Tr::tr("Unknown project '%1' in property override.")
- .arg(projectNameInOverride), parameters, logger);
- }
- }
-}
-
-void ProjectTreeBuilder::Private::checkProductNamesInOverrides()
-{
- for (const QString &productNameInOverride : productNamesUsedInOverrides) {
- if (erroneousProducts.contains(productNameInOverride))
- continue;
- if (!any_of(productsByName, [&productNameInOverride](
- const std::pair<QString, ProductContext *> &elem) {
- // 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.
- return elem.first == productNameInOverride
- || elem.first.startsWith(productNameInOverride + StringConstants::dot());
- })) {
- handlePropertyError(Tr::tr("Unknown product '%1' in property override.")
- .arg(productNameInOverride), parameters, logger);
- }
- }
-}
-
-void ProjectTreeBuilder::Private::collectProductsByName(
- const TopLevelProjectContext &topLevelProject)
-{
- for (ProjectContext * const project : topLevelProject.projects) {
- for (ProductContext &product : project->products)
- productsByName.insert({product.name, &product});
- }
-}
-
-void ProjectTreeBuilder::Private::adjustDependsItemForMultiplexing(const ProductContext &product,
- Item *dependsItem)
-{
- const QString name = 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;
- }
- const auto productRange = productsByName.equal_range(name);
- if (productRange.first == productRange.second)
- return; // Dependency is a module. Nothing to adjust.
-
- bool profilesPropertyIsSet;
- const QStringList profiles = evaluator.stringListValue(
- dependsItem, StringConstants::profilesProperty(), &profilesPropertyIsSet);
-
- 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();
-
- static const auto 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;
- };
-
- // 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
- = multiplexer.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
- = multiplexer.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 = ProductItemMultiplexer::fullProductDisplayName(
- 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 = ProductItemMultiplexer::fullProductDisplayName(
- product.name, product.multiplexConfigurationId);
- QStringList candidateNames;
- for (const auto &id : qAsConst(multiplexIds))
- candidateNames << ProductItemMultiplexer::fullProductDisplayName(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));
-}
-
-ShadowProductInfo ProjectTreeBuilder::Private::getShadowProductInfo(
- const 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 ProjectTreeBuilder::Private::handleProductError(const ErrorInfo &error,
- 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;
- }
- }
- 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;
- disabledItems << productContext.item;
- erroneousProducts.insert(productContext.name);
-}
-
-void ProjectTreeBuilder::Private::handleModuleSetupError(
- ProductContext &product, const Item::Module &module, const ErrorInfo &error)
-{
- if (module.required) {
- handleProductError(error, product);
- } else {
- qCDebug(lcModuleLoader()) << "non-required module" << module.name.toString()
- << "found, but not usable in product" << product.name
- << error.toString();
- createNonPresentModule(itemPool, module.name.toString(),
- QStringLiteral("failed validation"), module.item);
- }
-}
-
-bool ProjectTreeBuilder::Private::checkItemCondition(Item *item)
-{
- if (evaluator.boolValue(item, StringConstants::conditionProperty()))
- return true;
- disabledItems += item;
- return false;
-}
-
-QStringList ProjectTreeBuilder::Private::readExtraSearchPaths(Item *item, bool *wasSet)
-{
- QStringList result;
- const QStringList paths = 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()
- : parameters.projectFilePath());
- for (const QString &path : paths)
- result += FileInfo::resolvePath(basePath, path);
- return result;
-}
-
-QList<Item *> ProjectTreeBuilder::Private::multiplexProductItem(ProductContext &dummyContext,
- Item *productItem)
-{
- // Overriding the product item properties must be done here already, because multiplexing
- // properties might depend on product properties.
- const QString &nameKey = StringConstants::nameProperty();
- QString productName = evaluator.stringValue(productItem, nameKey);
- if (productName.isEmpty()) {
- productName = FileInfo::completeBaseName(productItem->file()->filePath());
- productItem->setProperty(nameKey, VariantValue::create(productName));
- }
- productItem->overrideProperties(parameters.overriddenValuesTree(),
- StringConstants::productsOverridePrefix() + productName,
- parameters, logger);
- dummyContext.item = productItem;
- TempBaseModuleAttacher tbma(this, dummyContext);
- return multiplexer.multiplex(productName, productItem, tbma.tempBaseModuleItem(),
- [&] { tbma.drop(); });
-}
-
-void ProjectTreeBuilder::Private::checkCancelation() const
-{
- if (progressObserver && progressObserver->canceled()) {
- throw ErrorInfo(Tr::tr("Project resolving canceled for configuration %1.")
- .arg(TopLevelProject::deriveId(
- parameters.finalBuildConfigurationTree())));
- }
-}
-
-QList<Item *> ProjectTreeBuilder::Private::loadReferencedFile(
- const QString &relativePath, const CodeLocation &referencingLocation,
- const Set<QString> &referencedFilePaths, 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) {
- localProfiles.collectProfilesFromItems(subItem, dummyContext.project->scope);
- loadedItems << multiplexProductItem(dummyContext, subItem);
- }
- return loadedItems;
-}
-
-void ProjectTreeBuilder::Private::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 (auto it = sourceProject->propertyDeclarations().begin();
- it != sourceProject->propertyDeclarations().end(); ++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);
- }
-}
-
-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)
- adjustParametersScopes(std::static_pointer_cast<ItemValue>(value)->item(), scope);
- }
-}
-
-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 (auto it = valueItem->properties().begin(); it != valueItem->properties().end(); ++it)
- mergeProperty(subItem, it.key(), it.value());
- return;
- }
-
- // 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 ProjectTreeBuilder::Private::mergeExportItems(ProductContext &productContext)
-{
- std::vector<Item *> exportItems;
- QList<Item *> children = productContext.item->children();
-
- const 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;
- QVariantMap defaultParameters;
- 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(defaultParameters,
- getJsVariant(evaluator.engine()->context(),
- evaluator.scriptValue(child)).toMap());
- } else {
- 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, parameters, 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(parameters.deprecationWarningMode(), logger);
-
- productContext.mergedExportItem = merged;
- productContext.defaultParameters = defaultParameters;
- return !exportItems.empty();
-}
-
-// TODO: This seems dubious. Can we merge the conditions instead?
-bool ProjectTreeBuilder::Private::checkExportItemCondition(Item *exportItem,
- const ProductContext &product)
-{
- 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, product, &tempScopeItem);
- return checkItemCondition(exportItem);
-}
-
-void ProjectTreeBuilder::Private::resolveProbes(ProductContext &product, Item *item)
-{
- product.info.probes << probesResolver.resolveProbes({product.name, product.uniqueName()}, item);
-}
-
-static void checkForModuleNamePrefixCollision(
- const Item *product, const ProductContext::ResolvedAndMultiplexedDependsItem &dependency)
-{
- if (!product)
- return;
-
- for (const Item::Module &m : product->modules()) {
- if (m.name.length() == dependency.name.length()
- || m.name.front() != dependency.name.front()) {
- continue;
- }
- QualifiedId shortName;
- QualifiedId longName;
- if (m.name < dependency.name) {
- shortName = m.name;
- longName = dependency.name;
- } else {
- shortName = dependency.name;
- longName = m.name;
- }
- 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()), dependency.location());
- }
-}
-
-// Note: This function is never called for regular loading of the base module into a product,
-// but only for the special cases of loading the dummy base module into a project
-// and temporarily providing a base module for product multiplexing.
-Item *ProjectTreeBuilder::Private::loadBaseModule(ProductContext &product, Item *item)
-{
- const QualifiedId baseModuleName(StringConstants::qbsModule());
- const auto baseDependency
- = ProductContext::ResolvedAndMultiplexedDependsItem::makeBaseDependency();
- Item * const moduleItem = loadModule(product, item, baseDependency, Deferral::NotAllowed)
- .moduleItem;
- if (Q_UNLIKELY(!moduleItem))
- throw ErrorInfo(Tr::tr("Cannot load base qbs module."));
- return moduleItem;
-}
-
-ProjectTreeBuilder::Private::LoadModuleResult
-ProjectTreeBuilder::Private::loadModule(ProductContext &product, Item *loadingItem,
- const ProductContext::ResolvedAndMultiplexedDependsItem &dependency,
- Deferral deferral)
-{
- qCDebug(lcModuleLoader) << "loadModule name:" << dependency.name.toString()
- << "id:" << dependency.id();
-
- QBS_CHECK(loadingItem);
-
- const auto findExistingModule = [&dependency](Item *item) -> std::pair<Item::Module *, Item *> {
- if (!item) // Happens if and only if called via loadBaseModule().
- return {};
- Item *moduleWithSameName = nullptr;
- for (Item::Module &m : item->modules()) {
- if (m.name != dependency.name)
- continue;
- if (!m.productInfo) {
- QBS_CHECK(!dependency.product);
- return {&m, m.item};
- }
- if ((dependency.profile.isEmpty() || (m.productInfo->profile == dependency.profile))
- && m.productInfo->multiplexId == dependency.multiplexId) {
- return {&m, m.item};
- }
- moduleWithSameName = m.item;
- }
- return {nullptr, moduleWithSameName};
- };
- ProductContext *productDep = nullptr;
- Item *moduleItem = nullptr;
- static const auto displayName = [](const ProductContext &p) {
- return ProductItemMultiplexer::fullProductDisplayName(p.name, p.multiplexConfigurationId);
- };
- const auto &[existingModule, moduleWithSameName] = findExistingModule(product.item);
- if (existingModule) {
- // Merge version range and required property. These will be checked again
- // after probes resolving.
- if (existingModule->name.toString() != StringConstants::qbsModule()) {
- moduleLoader.forwardParameterDeclarations(dependency.item, product.item->modules());
-
- // TODO: Use priorities like for property values. See QBS-1300.
- mergeParameters(existingModule->parameters, dependency.parameters);
-
- existingModule->versionRange.narrowDown(dependency.versionRange);
- existingModule->required |= dependency.requiredGlobally;
- if (int(product.resolveDependenciesState.size())
- > existingModule->maxDependsChainLength) {
- existingModule->maxDependsChainLength = product.resolveDependenciesState.size();
- }
- }
- QBS_CHECK(existingModule->item);
- moduleItem = existingModule->item;
- if (!contains(existingModule->loadingItems, loadingItem))
- existingModule->loadingItems.push_back(loadingItem);
- } else if (dependency.product) {
- // We have already done the look-up.
- productDep = dependency.product;
- } else {
- // First check if there's a matching product.
- const auto candidates = productsByName.equal_range(dependency.name.toString());
- for (auto it = candidates.first; it != candidates.second; ++it) {
- const auto candidate = it->second;
- if (candidate->multiplexConfigurationId != dependency.multiplexId)
- continue;
- if (!dependency.profile.isEmpty() && dependency.profile != candidate->profileName)
- continue;
- if (dependency.limitToSubProject && !haveSameSubProject(product, *candidate))
- continue;
- productDep = candidate;
- break;
- }
- if (!productDep) {
- // If we can tell that this is supposed to be a product dependency, we can skip
- // the module look-up.
- if (!dependency.profile.isEmpty() || !dependency.multiplexId.isEmpty()) {
- if (dependency.requiredGlobally) {
- if (!dependency.profile.isEmpty()) {
- throw ErrorInfo(Tr::tr("Product '%1' depends on '%2', which does not exist "
- "for the requested profile '%3'.")
- .arg(displayName(product), dependency.displayName(),
- dependency.profile),
- product.item->location());
- }
- throw ErrorInfo(Tr::tr("Product '%1' depends on '%2', which does not exist.")
- .arg(displayName(product), dependency.displayName()),
- product.item->location());
- }
- } else {
- // No matching product found, look for a "real" module.
- const ModuleLoader::ProductContext loaderContext{
- product.item, product.project->item, product.name, product.uniqueName(),
- product.profileName, product.multiplexConfigurationId, product.moduleProperties,
- product.profileModuleProperties};
- const ModuleLoader::Result loaderResult = moduleLoader.searchAndLoadModuleFile(
- loaderContext, dependency.location(), dependency.name, dependency.fallbackMode,
- dependency.requiredGlobally);
- moduleItem = loaderResult.moduleItem;
- product.info.probes << loaderResult.providerProbes;
-
- if (moduleItem) {
- Item * const proto = moduleItem;
- moduleItem = moduleItem->clone();
- moduleItem->setPrototype(proto); // For parameter declarations.
- } else if (dependency.requiredGlobally) {
- throw ErrorInfo(Tr::tr("Dependency '%1' not found for product '%2'.")
- .arg(dependency.name.toString(), displayName(product)),
- dependency.location());
- }
- }
- }
- }
-
- if (productDep && dependency.checkProduct) {
- if (productDep == &product) {
- throw ErrorInfo(Tr::tr("Dependency '%1' refers to itself.")
- .arg(dependency.name.toString()),
- dependency.location());
- }
-
- if (any_of(product.project->topLevelProject->productsToHandle, [productDep](const auto &e) {
- return e.first == productDep;
- })) {
- if (deferral == Deferral::Allowed)
- return {nullptr, productDep, HandleDependency::Defer};
- ErrorInfo e;
- e.append(Tr::tr("Cyclic dependencies detected:"));
- e.append(Tr::tr("First product is '%1'.")
- .arg(displayName(product)), product.item->location());
- e.append(Tr::tr("Second product is '%1'.")
- .arg(displayName(*productDep)), productDep->item->location());
- e.append(Tr::tr("Requested here."), dependency.location());
- throw e;
- }
-
- // This covers both the case of user-disabled products and products with errors.
- // The latter are force-disabled in handleProductError().
- if (disabledItems.contains(productDep->item)) {
- if (dependency.requiredGlobally) {
- ErrorInfo e;
- e.append(Tr::tr("Product '%1' depends on '%2',")
- .arg(displayName(product), displayName(*productDep)),
- product.item->location());
- e.append(Tr::tr("but product '%1' is disabled.").arg(displayName(*productDep)),
- productDep->item->location());
- throw e;
- }
- productDep = nullptr;
- }
- }
-
- if (productDep) {
- QBS_CHECK(productDep->mergedExportItem);
- moduleItem = productDep->mergedExportItem->clone();
- moduleItem->setParent(nullptr);
-
- // Needed for isolated Export item evaluation.
- moduleItem->setPrototype(productDep->mergedExportItem);
- }
-
- if (moduleItem) {
- for (auto it = product.resolveDependenciesState.begin();
- it != product.resolveDependenciesState.end(); ++it) {
- Item *itemToCheck = moduleItem;
- if (it->loadingItem != itemToCheck) {
- if (!productDep)
- continue;
- itemToCheck = productDep->item;
- }
- if (it->loadingItem != itemToCheck)
- continue;
- ErrorInfo e;
- e.append(Tr::tr("Cyclic dependencies detected:"));
- while (true) {
- e.append(it->loadingItemOrigin.name.toString(),
- it->loadingItemOrigin.location());
- if (it->loadingItem->type() == ItemType::ModuleInstance) {
- createNonPresentModule(itemPool, it->loadingItemOrigin.name.toString(),
- QLatin1String("cyclic dependency"), it->loadingItem);
- }
- if (it == product.resolveDependenciesState.begin())
- break;
- --it;
- }
- e.append(dependency.name.toString(), dependency.location());
- throw e;
- }
- checkForModuleNamePrefixCollision(product.item, dependency);
- }
-
- // Can only happen with multiplexing.
- if (moduleWithSameName && moduleWithSameName != moduleItem)
- QBS_CHECK(productDep);
-
- QString loadingName;
- if (loadingItem == product.item) {
- loadingName = product.name;
- } else if (!product.resolveDependenciesState.empty()) {
- const auto &loadingItemOrigin = product.resolveDependenciesState.front().loadingItemOrigin;
- loadingName = loadingItemOrigin.name.toString() + loadingItemOrigin.multiplexId
- + loadingItemOrigin.profile;
- }
- moduleInstantiator.instantiate({
- product.item, product.name, loadingItem, loadingName, moduleItem, moduleWithSameName,
- productDep ? productDep->item : nullptr, product.scope, product.project->scope,
- dependency.name, dependency.id(), bool(existingModule)});
-
- // At this point, a null module item is only possible for a non-required dependency.
- // Note that we still needed to to the instantiation above, as that injects the module
- // name into the surrounding item for the ".present" check.
- if (!moduleItem) {
- QBS_CHECK(!dependency.requiredGlobally);
- return {nullptr, nullptr, HandleDependency::Ignore};
- }
-
- const auto createModule = [&] {
- Item::Module m;
- m.item = moduleItem;
- if (productDep) {
- m.productInfo.emplace(productDep->item, productDep->multiplexConfigurationId,
- productDep->profileName);
- }
- m.name = dependency.name;
- m.required = dependency.requiredLocally;
- m.versionRange = dependency.versionRange;
- return m;
- };
- const auto addLocalModule = [&] {
- if (loadingItem->type() == ItemType::ModuleInstance
- && !findExistingModule(loadingItem).first) {
- loadingItem->addModule(createModule());
- }
- };
-
- // The module has already been loaded, so we don't need to add it to the product's list of
- // modules, nor do we need to handle its dependencies. The only thing we might need to
- // do is to add it to the "local" list of modules of the loading item, if it isn't in there yet.
- if (existingModule) {
- addLocalModule();
- return {nullptr, nullptr, HandleDependency::Ignore};
- }
-
- qCDebug(lcModuleLoader) << "module loaded:" << dependency.name.toString();
- if (product.item) {
- Item::Module module = createModule();
-
- if (module.name.toString() != StringConstants::qbsModule()) {
- // TODO: Why do we have default parameters only for Export items and
- // property declarations only for modules? Does that make any sense?
- if (productDep)
- module.parameters = productDep->defaultParameters;
- mergeParameters(module.parameters, dependency.parameters);
- }
- module.required = dependency.requiredGlobally;
- module.loadingItems.push_back(loadingItem);
- module.maxDependsChainLength = product.resolveDependenciesState.size();
- product.item->addModule(module);
- addLocalModule();
- }
- return {moduleItem, nullptr, HandleDependency::Use};
-}
-
-bool ProjectTreeBuilder::Private::haveSameSubProject(const ProductContext &p1,
- const ProductContext &p2)
-{
- for (const Item *otherParent = p2.item->parent(); otherParent;
- otherParent = otherParent->parent()) {
- if (otherParent == p1.item->parent())
- return true;
- }
- return false;
-}
-
-static Item::PropertyMap filterItemProperties(const Item::PropertyMap &properties)
-{
- Item::PropertyMap result;
- for (auto it = properties.begin(); it != properties.end(); ++it) {
- if (it.value()->type() == Value::ItemValueType)
- result.insert(it.key(), it.value());
- }
- return result;
-}
-
-static QVariantMap safeToVariant(JSContext *ctx, const JSValue &v)
-{
- QVariantMap result;
- handleJsProperties(ctx, v, [&](const JSAtom &prop, const JSPropertyDescriptor &desc) {
- const JSValue u = desc.value;
- if (JS_IsError(ctx, u))
- throw ErrorInfo(getJsString(ctx, u));
- const QString name = getJsString(ctx, prop);
- result[name] = (JS_IsObject(u) && !JS_IsArray(ctx, u) && !JS_IsRegExp(ctx, u))
- ? safeToVariant(ctx, u) : getJsVariant(ctx, u);
- });
- return result;
-}
-
-QVariantMap ProjectTreeBuilder::Private::extractParameters(Item *dependsItem) const
-{
- QVariantMap result;
- const Item::PropertyMap &itemProperties = filterItemProperties(dependsItem->properties());
- if (itemProperties.empty())
- return result;
- auto origProperties = dependsItem->properties();
-
- // TODO: This is not exception-safe. Also, can't we do the item value check along the
- // way, without allocationg an extra map and exchanging the list of children?
- dependsItem->setProperties(itemProperties);
-
- JSValue sv = evaluator.scriptValue(dependsItem);
- try {
- result = safeToVariant(evaluator.engine()->context(), 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;
-}
-
-std::optional<ProductContext::ResolvedDependsItem>
-ProjectTreeBuilder::Private::resolveDependsItem(const ProductContext &product, Item *dependsItem)
-{
- if (!checkItemCondition(dependsItem)) {
- qCDebug(lcModuleLoader) << "Depends item disabled, ignoring.";
- return {};
- }
-
- const QString name = evaluator.stringValue(dependsItem, StringConstants::nameProperty());
- if (name == StringConstants::qbsModule()) // Redundant
- return {};
-
- bool submodulesPropertySet;
- const QStringList submodules = 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());
- }
- bool productTypesWasSet;
- const QStringList productTypes = evaluator.stringListValue(
- dependsItem, StringConstants::productTypesProperty(), &productTypesWasSet);
- if (!name.isEmpty() && !productTypes.isEmpty()) {
- throw ErrorInfo(Tr::tr("The name and productTypes are mutually exclusive "
- "in a Depends item"), dependsItem->location());
- }
- if (productTypes.isEmpty() && productTypesWasSet) {
- qCDebug(lcModuleLoader) << "Ignoring Depends item with empty productTypes list.";
- return {};
- }
- if (name.isEmpty() && !productTypesWasSet) {
- throw ErrorInfo(Tr::tr("Either name or productTypes must be set in a Depends item"),
- dependsItem->location());
- }
-
- const FallbackMode fallbackMode = parameters.fallbackProviderEnabled()
- && evaluator.boolValue(dependsItem, StringConstants::enableFallbackProperty())
- ? FallbackMode::Enabled : FallbackMode::Disabled;
-
- bool profilesPropertyWasSet = false;
- std::optional<QStringList> profiles;
- bool required = true;
- if (productTypes.isEmpty()) {
- const QStringList profileList = evaluator.stringListValue(
- dependsItem, StringConstants::profilesProperty(), &profilesPropertyWasSet);
- if (profilesPropertyWasSet)
- profiles.emplace(profileList);
- required = evaluator.boolValue(dependsItem, StringConstants::requiredProperty());
- }
- const Version minVersion = Version::fromString(
- evaluator.stringValue(dependsItem, StringConstants::versionAtLeastProperty()));
- const Version maxVersion = Version::fromString(
- evaluator.stringValue(dependsItem, StringConstants::versionBelowProperty()));
- const bool limitToSubProject = evaluator.boolValue(
- dependsItem, StringConstants::limitToSubProjectProperty());
- const QStringList multiplexIds = evaluator.stringListValue(
- dependsItem, StringConstants::multiplexConfigurationIdsProperty());
- adjustParametersScopes(dependsItem, dependsItem);
- moduleLoader.forwardParameterDeclarations(dependsItem, product.item->modules());
- const QVariantMap parameters = extractParameters(dependsItem);
-
- return ProductContext::ResolvedDependsItem{dependsItem, QualifiedId::fromString(name),
- submodules, FileTags::fromStringList(productTypes), multiplexIds, profiles,
- {minVersion, maxVersion}, parameters, limitToSubProject, fallbackMode, required};
-}
-
-// Potentially multiplexes a dependency along Depends.productTypes, Depends.subModules and
-// Depends.profiles, as well as internally set up multiplexing axes.
-// Each entry in the resulting queue corresponds to exactly one product or module to pull in.
-std::queue<ProductContext::ResolvedAndMultiplexedDependsItem>
-ProjectTreeBuilder::Private::multiplexDependency(
- const ProductContext &product, const ProductContext::ResolvedDependsItem &dependency)
-{
- std::queue<ProductContext::ResolvedAndMultiplexedDependsItem> dependencies;
- if (!dependency.productTypes.empty()) {
- std::vector<ProductContext *> matchingProducts;
- for (const FileTag &typeTag : dependency.productTypes) {
- const auto range = productsByType.equal_range(typeTag);
- for (auto it = range.first; it != range.second; ++it) {
- if (it->second != &product
- && it->second->name != product.name
- && (!dependency.limitToSubProject
- || haveSameSubProject(product, *it->second))) {
- matchingProducts.push_back(it->second);
- }
- }
- }
- if (matchingProducts.empty()) {
- qCDebug(lcModuleLoader) << "Depends.productTypes does not match anything."
- << dependency.item->location();
- return {};
- }
- for (ProductContext * const match : matchingProducts)
- dependencies.emplace(match, dependency);
- return dependencies;
- }
-
- const QStringList profiles = dependency.profiles && !dependency.profiles->isEmpty()
- ? *dependency.profiles : QStringList(QString());
- const QStringList multiplexIds = !dependency.multiplexIds.isEmpty()
- ? dependency.multiplexIds : QStringList(QString());
- const QStringList subModules = !dependency.subModules.isEmpty()
- ? dependency.subModules : QStringList(QString());
- for (const QString &profile : profiles) {
- for (const QString &multiplexId : multiplexIds) {
- for (const QString &subModule : subModules) {
- QualifiedId name = dependency.name;
- if (!subModule.isEmpty())
- name << subModule.split(QLatin1Char('.'));
- dependencies.emplace(dependency, name, profile, multiplexId);
- }
- }
- }
- return dependencies;
-}
-
-QString ProductContext::uniqueName() const
-{
- return ResolvedProduct::uniqueName(name, multiplexConfigurationId);
-}
-
-QString ProductContext::ResolvedAndMultiplexedDependsItem::id() const
-{
- if (!item) {
- QBS_CHECK(name.toString() == StringConstants::qbsModule());
- return {};
- }
- return item->id();
-}
-
-CodeLocation ProductContext::ResolvedAndMultiplexedDependsItem::location() const
-{
- if (!item) {
- QBS_CHECK(name.toString() == StringConstants::qbsModule());
- return {};
- }
- return item->location();
-}
-
-QString ProductContext::ResolvedAndMultiplexedDependsItem::displayName() const
-{
- return ProductItemMultiplexer::fullProductDisplayName(name.toString(), multiplexId);
-}
-
-ProjectTreeBuilder::Private::TempBaseModuleAttacher::TempBaseModuleAttacher(
- ProjectTreeBuilder::Private *d, ProductContext &product)
- : m_productItem(product.item)
-{
- const ValuePtr qbsValue = m_productItem->property(StringConstants::qbsModule());
-
- // Cloning is necessary because the original value will get "instantiated" now.
- if (qbsValue)
- m_origQbsValue = qbsValue->clone();
-
- m_tempBaseModule = d->loadBaseModule(product, m_productItem);
-}
-
-void ProjectTreeBuilder::Private::TempBaseModuleAttacher::drop()
-{
- if (!m_tempBaseModule)
- return;
-
- // "Unload" the qbs module again.
- if (m_origQbsValue)
- m_productItem->setProperty(StringConstants::qbsModule(), m_origQbsValue);
- else
- m_productItem->removeProperty(StringConstants::qbsModule());
- m_productItem->removeModules();
- m_tempBaseModule = nullptr;
-}
-
-} // namespace qbs::Internal
diff --git a/src/lib/corelib/language/projecttreebuilder.h b/src/lib/corelib/language/projecttreebuilder.h
deleted file mode 100644
index 5f7869978..000000000
--- a/src/lib/corelib/language/projecttreebuilder.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2023 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#pragma once
-
-#include "forward_decls.h"
-#include "moduleproviderinfo.h"
-#include "moduleproviderloader.h"
-#include "qualifiedid.h"
-
-#include <QString>
-#include <QVariant>
-
-namespace qbs {
-class SetupProjectParameters;
-namespace Internal {
-class Evaluator;
-class FileTime;
-class Item;
-class ItemPool;
-class ProgressObserver;
-
-using ModulePropertiesPerGroup = std::unordered_map<const Item *, QualifiedIdSet>;
-
-// TODO: This class only needs to be known inside the ProjectResolver; no need to
-// instantiate them separately when they always appear together.
-// Possibly we can get rid of the Loader class altogether.
-class ProjectTreeBuilder
-{
-public:
- ProjectTreeBuilder(const SetupProjectParameters &parameters, ItemPool &itemPool,
- Evaluator &evaluator, Logger &logger);
- ~ProjectTreeBuilder();
-
- struct Result
- {
- struct ProductInfo
- {
- std::vector<ProbeConstPtr> probes;
- ModulePropertiesPerGroup modulePropertiesSetInGroups;
- ErrorInfo delayedError;
- };
-
- Item *root = nullptr;
- std::unordered_map<Item *, ProductInfo> productInfos;
- std::vector<ProbeConstPtr> projectProbes;
- StoredModuleProviderInfo storedModuleProviderInfo;
- Set<QString> qbsFiles;
- QVariantMap profileConfigs;
- };
- Result load();
-
- void setProgressObserver(ProgressObserver *progressObserver);
- void setSearchPaths(const QStringList &searchPaths);
- void setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes);
- void setOldProductProbes(const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes);
- void setLastResolveTime(const FileTime &time);
- void setStoredProfiles(const QVariantMap &profiles);
- void setStoredModuleProviderInfo(const StoredModuleProviderInfo &moduleProviderInfo);
-
-private:
- class Private;
- Private * const d;
-};
-
-} // namespace Internal
-} // namespace qbs