diff options
Diffstat (limited to 'src/lib/corelib/tools')
81 files changed, 3108 insertions, 823 deletions
diff --git a/src/lib/corelib/tools/applecodesignutils.cpp b/src/lib/corelib/tools/applecodesignutils.cpp index feae266bf..de74e9206 100644 --- a/src/lib/corelib/tools/applecodesignutils.cpp +++ b/src/lib/corelib/tools/applecodesignutils.cpp @@ -95,12 +95,12 @@ QVariantMap certificateInfo(const QByteArray &data) map.insert(QString::fromUtf8(attr), cert.subjectInfo(attr).front()); return map; }; - + const auto sha1 = QString::fromLatin1(cert.digest(QCryptographicHash::Sha1).toHex().toUpper()); return { - {QStringLiteral("SHA1"), cert.digest(QCryptographicHash::Sha1).toHex().toUpper()}, + {QStringLiteral("SHA1"), sha1}, {QStringLiteral("subjectInfo"), subjectInfo(cert)}, - {QStringLiteral("validBefore"), cert.effectiveDate()}, - {QStringLiteral("validAfter"), cert.expiryDate()} + {QStringLiteral("validAfter"), cert.effectiveDate()}, + {QStringLiteral("validBefore"), cert.expiryDate()} }; } diff --git a/src/lib/corelib/tools/architectures.cpp b/src/lib/corelib/tools/architectures.cpp index cf9fec27b..f139509e4 100644 --- a/src/lib/corelib/tools/architectures.cpp +++ b/src/lib/corelib/tools/architectures.cpp @@ -55,7 +55,7 @@ QString canonicalTargetArchitecture(const QString &architecture, const QString &system, const QString &abi) { - const QString arch = canonicalArchitecture(architecture); + QString arch = canonicalArchitecture(architecture); const bool isApple = (vendor == QStringLiteral("apple") || system == QStringLiteral("darwin") || system == QStringLiteral("macosx") diff --git a/src/lib/corelib/tools/buildoptions.cpp b/src/lib/corelib/tools/buildoptions.cpp index e4e9ba17f..cc3ef7557 100644 --- a/src/lib/corelib/tools/buildoptions.cpp +++ b/src/lib/corelib/tools/buildoptions.cpp @@ -413,8 +413,8 @@ template<> JobLimits fromJson(const QJsonValue &limitsData) { JobLimits limits; const QJsonArray &limitsArray = limitsData.toArray(); - for (const QJsonValue &v : limitsArray) { - const QJsonObject limitData = v.toObject(); + for (const auto &value : limitsArray) { + const QJsonObject limitData = value.toObject(); QString pool; int limit = 0; setValueFromJson(pool, limitData, "pool"); diff --git a/src/lib/corelib/tools/buildoptions.h b/src/lib/corelib/tools/buildoptions.h index bd0fb22cb..59b87237d 100644 --- a/src/lib/corelib/tools/buildoptions.h +++ b/src/lib/corelib/tools/buildoptions.h @@ -45,10 +45,10 @@ #include "joblimits.h" #include <QtCore/qshareddata.h> +#include <QtCore/qstringlist.h> QT_BEGIN_NAMESPACE class QJsonObject; -class QStringList; QT_END_NAMESPACE namespace qbs { diff --git a/src/lib/corelib/tools/clangclinfo.cpp b/src/lib/corelib/tools/clangclinfo.cpp new file mode 100644 index 000000000..fd907ebf1 --- /dev/null +++ b/src/lib/corelib/tools/clangclinfo.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "clangclinfo.h" + +#include "hostosinfo.h" +#include "msvcinfo.h" +#include "stlutils.h" + +namespace qbs { +namespace Internal { + +static std::vector<MSVCInstallInfo> compatibleMsvcs(Logger &logger) +{ + auto msvcs = MSVCInstallInfo::installedMSVCs(logger); + auto filter = [](const MSVCInstallInfo &info) + { + const auto versions = info.version.split(QLatin1Char('.')); + if (versions.empty()) + return true; + bool ok = false; + const int major = versions.at(0).toInt(&ok); + return !ok || major < 15; // support MSVC2017 and above + }; + Internal::removeIf(msvcs, filter); + for (const auto &msvc: msvcs) { + auto vcvarsallPath = msvc.findVcvarsallBat(); + if (vcvarsallPath.isEmpty()) + continue; + } + return msvcs; +} + +static QString findCompatibleVcsarsallBat(const std::vector<MSVCInstallInfo> &msvcs) +{ + for (const auto &msvc: msvcs) { + auto vcvarsallPath = msvc.findVcvarsallBat(); + if (!vcvarsallPath.isEmpty()) + return vcvarsallPath; + } + return {}; +} + +static QString wow6432Key() +{ +#ifdef Q_OS_WIN64 + return QStringLiteral("\\Wow6432Node"); +#else + return {}; +#endif +} + +static QString getToolchainInstallPath(const QFileInfo &compiler) +{ + return compiler.path(); // 1 level up +} + +QVariantMap ClangClInfo::toVariantMap() const +{ + return { + {QStringLiteral("toolchainInstallPath"), toolchainInstallPath}, + {QStringLiteral("vcvarsallPath"), vcvarsallPath}, + }; +} + +ClangClInfo ClangClInfo::fromCompilerFilePath(const QString &path, Logger &logger) +{ + const auto compilerName = QStringLiteral("clang-cl"); + const auto vcvarsallPath = findCompatibleVcsarsallBat(compatibleMsvcs(logger)); + if (vcvarsallPath.isEmpty()) { + logger.qbsWarning() + << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.") + .arg(compilerName); + return {}; + } + + return {getToolchainInstallPath(QFileInfo(path)), vcvarsallPath}; +} + +std::vector<ClangClInfo> ClangClInfo::installedCompilers( + const std::vector<QString> &extraPaths, Logger &logger) +{ + std::vector<QString> compilerPaths; + compilerPaths.reserve(extraPaths.size()); + std::copy_if(extraPaths.begin(), extraPaths.end(), + std::back_inserter(compilerPaths), + [](const QString &path){ return !path.isEmpty(); }); + const auto compilerName = HostOsInfo::appendExecutableSuffix(QStringLiteral("clang-cl")); + + const QSettings registry( + QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\LLVM\\LLVM").arg(wow6432Key()), + QSettings::NativeFormat); + const auto key = QStringLiteral("."); + if (registry.contains(key)) { + const auto compilerPath = QDir::fromNativeSeparators(registry.value(key).toString()) + + QStringLiteral("/bin/") + compilerName; + if (QFileInfo::exists(compilerPath) && !contains(compilerPaths, compilerPath)) + compilerPaths.push_back(compilerPath); + } + + // this branch can be useful in case user had two LLVM installations (e.g. 32bit & 64bit) + // but uninstalled one - in that case, registry will be empty + static const char * const envVarCandidates[] = {"ProgramFiles", "ProgramFiles(x86)"}; + for (const auto &envVar : envVarCandidates) { + const auto value + = QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv(envVar))); + const auto compilerPath = value + QStringLiteral("/LLVM/bin/") + compilerName; + if (QFileInfo::exists(compilerPath) && !contains(compilerPaths, compilerPath)) + compilerPaths.push_back(compilerPath); + } + + const auto msvcs = compatibleMsvcs(logger); + const auto vcvarsallPath = findCompatibleVcsarsallBat(msvcs); + if (vcvarsallPath.isEmpty()) { + logger.qbsWarning() + << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.") + .arg(compilerName); + return {}; + } + + std::vector<ClangClInfo> result; + result.reserve(compilerPaths.size() + msvcs.size()); + transform(compilerPaths, result, [&vcvarsallPath](const auto &path) { + return ClangClInfo{getToolchainInstallPath(QFileInfo(path)), vcvarsallPath}; }); + + // If we didn't find custom LLVM installation, try to find if it's installed with Visual Studio + for (const auto &msvc : msvcs) { + const auto compilerPath = QStringLiteral("%1/VC/Tools/Llvm/bin/%2") + .arg(msvc.installDir, compilerName); + if (QFileInfo::exists(compilerPath)) { + const auto vcvarsallPath = msvc.findVcvarsallBat(); + if (vcvarsallPath.isEmpty()) { + logger.qbsWarning() + << Tr::tr("Found LLVM in %1, but vcvarsall.bat is missing.") + .arg(msvc.installDir); + } + + result.push_back({getToolchainInstallPath(QFileInfo(compilerPath)), vcvarsallPath}); + } + } + + return result; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/clangclinfo.h b/src/lib/corelib/tools/clangclinfo.h new file mode 100644 index 000000000..76ae169f2 --- /dev/null +++ b/src/lib/corelib/tools/clangclinfo.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QBS_CLANGCLINFO_H +#define QBS_CLANGCLINFO_H + +#include "logging/logger.h" + +#include <QtCore/qstring.h> + +#include <vector> + +namespace qbs { +namespace Internal { + +class ClangClInfo +{ +public: + QString toolchainInstallPath; + QString vcvarsallPath; + + bool isEmpty() const { return toolchainInstallPath.isEmpty() && vcvarsallPath.isEmpty(); } + + QBS_EXPORT QVariantMap toVariantMap() const; + + QBS_EXPORT static ClangClInfo fromCompilerFilePath(const QString &path, Logger &logger); + QBS_EXPORT static std::vector<ClangClInfo> installedCompilers( + const std::vector<QString> &extraPaths, qbs::Internal::Logger &logger); +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_CLANGCLINFO_H diff --git a/src/lib/corelib/tools/cleanoptions.cpp b/src/lib/corelib/tools/cleanoptions.cpp index cef77468e..affc9b3f7 100644 --- a/src/lib/corelib/tools/cleanoptions.cpp +++ b/src/lib/corelib/tools/cleanoptions.cpp @@ -58,7 +58,7 @@ public: bool logElapsedTime; }; -} +} // namespace Internal /*! * \class CleanOptions diff --git a/src/lib/corelib/tools/codelocation.cpp b/src/lib/corelib/tools/codelocation.cpp index 542408795..f04041e14 100644 --- a/src/lib/corelib/tools/codelocation.cpp +++ b/src/lib/corelib/tools/codelocation.cpp @@ -47,7 +47,7 @@ #include <QtCore/qdir.h> #include <QtCore/qjsonobject.h> #include <QtCore/qjsonvalue.h> -#include <QtCore/qregexp.h> +#include <QtCore/qregularexpression.h> #include <QtCore/qshareddata.h> #include <QtCore/qstring.h> @@ -87,8 +87,9 @@ CodeLocation::CodeLocation(const QString &aFilePath, int aLine, int aColumn, boo } CodeLocation::CodeLocation(const CodeLocation &other) = default; - +CodeLocation::CodeLocation(CodeLocation &&other) noexcept = default; CodeLocation &CodeLocation::operator=(const CodeLocation &other) = default; +CodeLocation &CodeLocation::operator=(CodeLocation &&other) noexcept = default; CodeLocation::~CodeLocation() = default; @@ -118,9 +119,9 @@ QString CodeLocation::toString() const if (isValid()) { str = QDir::toNativeSeparators(filePath()); QString lineAndColumn; - if (line() > 0 && !str.contains(QRegExp(QStringLiteral(":[0-9]+$")))) + if (line() > 0 && !str.contains(QRegularExpression(QStringLiteral(":[0-9]+$")))) lineAndColumn += QLatin1Char(':') + QString::number(line()); - if (column() > 0 && !str.contains(QRegExp(QStringLiteral(":[0-9]+:[0-9]+$")))) + if (column() > 0 && !str.contains(QRegularExpression(QStringLiteral(":[0-9]+:[0-9]+$")))) lineAndColumn += QLatin1Char(':') + QString::number(column()); str += lineAndColumn; } @@ -181,4 +182,44 @@ bool operator<(const CodeLocation &cl1, const CodeLocation &cl2) return cl1.toString() < cl2.toString(); } +void CodePosition::load(Internal::PersistentPool &pool) { pool.load(m_line, m_column); } +void CodePosition::store(Internal::PersistentPool &pool) const { pool.store(m_line, m_column); } + +bool operator==(const CodePosition &pos1, const CodePosition &pos2) +{ + return pos1.line() == pos2.line() && pos1.column() == pos2.column(); +} +bool operator!=(const CodePosition &pos1, const CodePosition &pos2) { return !(pos1 == pos2); } + +bool operator<(const CodePosition &pos1, const CodePosition &pos2) +{ + const int lineDiff = pos1.line() - pos2.line(); + if (lineDiff < 0) + return true; + if (lineDiff > 0) + return false; + return pos1.column() < pos2.column(); +} +bool operator>(const CodePosition &pos1, const CodePosition &pos2) { return pos2 < pos1; } +bool operator<=(const CodePosition &pos1, const CodePosition &pos2) { return !(pos1 > pos2); } +bool operator>=(const CodePosition &pos1, const CodePosition &pos2) { return !(pos1 < pos2); } + +CodeRange::CodeRange(const CodePosition &start, const CodePosition &end) + : m_start(start), m_end(end) {} + +void CodeRange::load(Internal::PersistentPool &pool) { pool.load(m_start, m_end); } +void CodeRange::store(Internal::PersistentPool &pool) const { pool.store(m_start, m_end); } + +bool CodeRange::contains(const CodePosition &pos) const +{ + return start() <= pos && end() > pos; +} + +bool operator==(const CodeRange &r1, const CodeRange &r2) +{ + return r1.start() == r2.start() && r1.end() == r2.end(); +} +bool operator!=(const CodeRange &r1, const CodeRange &r2) { return !(r1 == r2); } +bool operator<(const CodeRange &r1, const CodeRange &r2) { return r1.start() < r2.start(); } + } // namespace qbs diff --git a/src/lib/corelib/tools/codelocation.h b/src/lib/corelib/tools/codelocation.h index 3e84ce2d1..afcd2e075 100644 --- a/src/lib/corelib/tools/codelocation.h +++ b/src/lib/corelib/tools/codelocation.h @@ -62,7 +62,9 @@ public: explicit CodeLocation(const QString &aFilePath, int aLine = -1, int aColumn = -1, bool checkPath = true); CodeLocation(const CodeLocation &other); + CodeLocation(CodeLocation &&other) noexcept; CodeLocation &operator=(const CodeLocation &other); + CodeLocation &operator=(CodeLocation &&other) noexcept; ~CodeLocation(); QString filePath() const; @@ -84,10 +86,78 @@ private: QBS_EXPORT bool operator==(const CodeLocation &cl1, const CodeLocation &cl2); QBS_EXPORT bool operator!=(const CodeLocation &cl1, const CodeLocation &cl2); QBS_EXPORT bool operator<(const CodeLocation &cl1, const CodeLocation &cl2); +inline auto qHash(const CodeLocation &cl) { return qHash(cl.toString()); } +QDebug operator<<(QDebug debug, const CodeLocation &location); -inline uint qHash(const CodeLocation &cl) { return qHash(cl.toString()); } +class QBS_EXPORT CodePosition +{ +public: + CodePosition(int line, int column) : m_line(line), m_column(column) {} -QDebug operator<<(QDebug debug, const CodeLocation &location); + CodePosition() = default; + CodePosition(const CodePosition &other) = default; + CodePosition(CodePosition &&other) = default; + CodePosition &operator=(const CodePosition &other) = default; + CodePosition &operator=(CodePosition &&other) = default; + + int line() const { return m_line; } + void setLine(int newLine) { m_line = newLine; } + + int column() const { return m_column; } + void setColumn(int newColumn) { m_column = newColumn; } + + void load(Internal::PersistentPool &pool); + void store(Internal::PersistentPool &pool) const; + +private: + int m_line = 0; + int m_column = 0; +}; + +QBS_EXPORT bool operator==(const CodePosition &pos1, const CodePosition &pos2); +QBS_EXPORT bool operator!=(const CodePosition &pos1, const CodePosition &pos2); +QBS_EXPORT bool operator<(const CodePosition &pos1, const CodePosition &pos2); +QBS_EXPORT bool operator>(const CodePosition &pos1, const CodePosition &pos2); +QBS_EXPORT bool operator<=(const CodePosition &pos1, const CodePosition &pos2); +QBS_EXPORT bool operator>=(const CodePosition &pos1, const CodePosition &pos2); +inline auto qHash(const CodePosition &pos) +{ + return QT_PREPEND_NAMESPACE(qHash)(pos.line()) ^ QT_PREPEND_NAMESPACE(qHash)(pos.column()); +} + +class QBS_EXPORT CodeRange +{ +public: + CodeRange(const CodePosition &start, const CodePosition &end); + + CodeRange() = default; + CodeRange(const CodeRange &other) = default; + CodeRange(CodeRange &&other) = default; + CodeRange &operator=(const CodeRange &other) = default; + CodeRange &operator=(CodeRange &&other) = default; + + const CodePosition &start() const & { return m_start; } + const CodePosition &end() const & { return m_end; } + CodePosition start() && { return std::move(m_start); } + CodePosition end() && { return std::move(m_end); } + + bool contains(const CodePosition &pos) const; + + void load(Internal::PersistentPool &pool); + void store(Internal::PersistentPool &pool) const; + +private: + CodePosition m_start; + CodePosition m_end; +}; + +QBS_EXPORT bool operator==(const CodeRange &r1, const CodeRange &r2); +QBS_EXPORT bool operator!=(const CodeRange &r1, const CodeRange &r2); +QBS_EXPORT bool operator<(const CodeRange &r1, const CodeRange &r2); +inline auto qHash(const CodeRange &range) { return qHash(range.start()) ^ qHash(range.end()); } + +using CodeLinksInFile = QHash<CodeRange, QList<CodeLocation>>; +using CodeLinks = QHash<QString, CodeLinksInFile>; } // namespace qbs diff --git a/src/lib/corelib/tools/commandechomode.h b/src/lib/corelib/tools/commandechomode.h index 88d8377ad..e7b315563 100644 --- a/src/lib/corelib/tools/commandechomode.h +++ b/src/lib/corelib/tools/commandechomode.h @@ -43,11 +43,7 @@ #include "qbs_export.h" #include <QtCore/qglobal.h> - -QT_BEGIN_NAMESPACE -class QString; -class QStringList; -QT_END_NAMESPACE +#include <QtCore/qstringlist.h> namespace qbs { diff --git a/src/lib/corelib/tools/deprecationwarningmode.cpp b/src/lib/corelib/tools/deprecationwarningmode.cpp new file mode 100644 index 000000000..e140e3e49 --- /dev/null +++ b/src/lib/corelib/tools/deprecationwarningmode.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2022 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 "deprecationwarningmode.h" + +/*! + * \enum DeprecationWarningMode + * This enum type specifies how \QBS should behave on encountering deprecated items or properties. + * \value DeprecationWarningMode::Error Project resolving will stop with an error message. + * \value DeprecationWarningMode::On A warning will be printed. + * \value DeprecationWarningMode::BeforeRemoval A warning will be printed if and only if this is + * the last \QBS version before the removal version. This is the default behavior. + * \note If the removal version's minor version number is zero, the behavior is + * the same as for ErrorHandlingMode::On. + * \value DeprecationWarningMode::Off No warnings will be emitted for deprecated constructs. + */ + +namespace qbs { + +DeprecationWarningMode defaultDeprecationWarningMode() +{ + return DeprecationWarningMode::BeforeRemoval; +} + +QString deprecationWarningModeName(DeprecationWarningMode mode) +{ + switch (mode) { + case DeprecationWarningMode::Error: + return QStringLiteral("error"); + case DeprecationWarningMode::On: + return QStringLiteral("on"); + case DeprecationWarningMode::BeforeRemoval: + return QStringLiteral("before-removal"); + case DeprecationWarningMode::Off: + return QStringLiteral("off"); + default: + break; + } + return {}; +} + +DeprecationWarningMode deprecationWarningModeFromName(const QString &name) +{ + DeprecationWarningMode mode = defaultDeprecationWarningMode(); + for (int i = 0; i <= int(DeprecationWarningMode::Sentinel); ++i) { + if (deprecationWarningModeName(static_cast<DeprecationWarningMode>(i)) == name) { + mode = static_cast<DeprecationWarningMode>(i); + break; + } + } + return mode; +} + +QStringList allDeprecationWarningModeStrings() +{ + QStringList result; + for (int i = 0; i <= int(DeprecationWarningMode::Sentinel); ++i) + result << deprecationWarningModeName(static_cast<DeprecationWarningMode>(i)); + return result; +} + +} // namespace qbs diff --git a/src/lib/corelib/tools/deprecationwarningmode.h b/src/lib/corelib/tools/deprecationwarningmode.h new file mode 100644 index 000000000..bb2a14155 --- /dev/null +++ b/src/lib/corelib/tools/deprecationwarningmode.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2022 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_DEPRECATIONWARNINGMODE_H +#define QBS_DEPRECATIONWARNINGMODE_H + +#include "qbs_export.h" + +#include <QtCore/qstringlist.h> + +namespace qbs { + +enum class DeprecationWarningMode { Error, On, BeforeRemoval, Off, Sentinel = Off }; + +QBS_EXPORT DeprecationWarningMode defaultDeprecationWarningMode(); +QBS_EXPORT QString deprecationWarningModeName(DeprecationWarningMode mode); +QBS_EXPORT DeprecationWarningMode deprecationWarningModeFromName(const QString &name); +QBS_EXPORT QStringList allDeprecationWarningModeStrings(); + +} // namespace qbs + +#endif // QBS_DEPRECATIONWARNINGMODE_H + diff --git a/src/lib/corelib/tools/error.cpp b/src/lib/corelib/tools/error.cpp index ff7ce46cf..f1b90b71e 100644 --- a/src/lib/corelib/tools/error.cpp +++ b/src/lib/corelib/tools/error.cpp @@ -40,8 +40,11 @@ #include "error.h" #include "persistence.h" -#include "qttools.h" #include "stringconstants.h" +#include "setupprojectparameters.h" +#include "logging/logger.h" + +#include <tools/stlutils.h> #include <QtCore/qjsonarray.h> #include <QtCore/qjsonobject.h> @@ -205,15 +208,21 @@ ErrorInfo::ErrorInfo(const QString &description, const QStringList &backtrace) { append(description); for (const QString &traceLine : backtrace) { - static const std::regex regexp("^(.+) at (.+):(\\-?[0-9]+)$"); + if (traceLine.contains(QStringLiteral("<eval>"))) + continue; + static const std::regex regexpWithFunc("^(.+) at [^(]*\\((.+):(\\-?[0-9]+)\\)$"); + static const std::regex regexpWithoutFunc("^(.+) at (.+):(\\-?[0-9]+)$"); std::smatch match; const std::string tl = traceLine.toStdString(); - if (std::regex_match(tl, match, regexp)) { - const QString message = QString::fromStdString(match[1]), - file = QString::fromStdString(match[2]), - line = QString::fromStdString(match[3]); + bool hasMatch = std::regex_match(tl, match, regexpWithFunc); + if (!hasMatch) + hasMatch = std::regex_match(tl, match, regexpWithoutFunc); + if (hasMatch) { + const QString message = QString::fromStdString(match[1]).trimmed(); + const QString file = QString::fromStdString(match[2]); + const QString line = QString::fromStdString(match[3]); const CodeLocation location(file, line.toInt()); - appendBacktrace(message, location); + appendBacktrace(message, CodeLocation(file, line.toInt())); } } } @@ -267,7 +276,7 @@ void ErrorInfo::clear() QString ErrorInfo::toString() const { QStringList lines; - for (const ErrorItem &e : qAsConst(d->items)) { + for (const ErrorItem &e : std::as_const(d->items)) { if (e.isBacktraceItem()) { QString line; if (!e.description().isEmpty()) @@ -304,8 +313,15 @@ bool ErrorInfo::isInternalError() const bool ErrorInfo::hasLocation() const { - return std::any_of(d->items.cbegin(), d->items.cend(), - [](const ErrorItem &ei) { return ei.codeLocation().isValid(); }); + return Internal::any_of(d->items, [](const ErrorItem &ei) { + return ei.codeLocation().isValid(); }); +} + +bool ErrorInfo::isCancelException() const +{ + return Internal::any_of(d->items, [](const ErrorItem &ei) { + return ei.description() == QLatin1String("interrupted"); + }); } void ErrorInfo::load(Internal::PersistentPool &pool) @@ -325,4 +341,12 @@ void appendError(ErrorInfo &dst, const ErrorInfo &src) dst.append(item); } +void handlePropertyError( + const ErrorInfo &error, const SetupProjectParameters ¶ms, Internal::Logger &logger) +{ + if (params.propertyCheckingMode() == ErrorHandlingMode::Strict) + throw error; + logger.printWarning(error); +} + } // namespace qbs diff --git a/src/lib/corelib/tools/error.h b/src/lib/corelib/tools/error.h index 36cf5e0ea..ba600a558 100644 --- a/src/lib/corelib/tools/error.h +++ b/src/lib/corelib/tools/error.h @@ -45,18 +45,21 @@ #include <QtCore/qhash.h> #include <QtCore/qmetatype.h> #include <QtCore/qshareddata.h> +#include <QtCore/qstringlist.h> QT_BEGIN_NAMESPACE class QJsonObject; -template <class T> class QList; -class QString; -class QStringList; QT_END_NAMESPACE namespace qbs { -namespace Internal { class PersistentPool; } +namespace Internal { +class PersistentPool; +class Logger; +} // namespace Internal class CodeLocation; +class SetupProjectParameters; + class QBS_EXPORT ErrorItem { friend class ErrorInfo; @@ -110,6 +113,7 @@ public: QJsonObject toJson() const; bool isInternalError() const; bool hasLocation() const; + bool isCancelException() const; void load(Internal::PersistentPool &pool); void store(Internal::PersistentPool &pool) const; @@ -120,7 +124,10 @@ private: }; void appendError(ErrorInfo &dst, const ErrorInfo &src); -inline uint qHash(const ErrorInfo &e) { return qHash(e.toString()); } +void handlePropertyError( + const ErrorInfo &error, const SetupProjectParameters ¶ms, Internal::Logger &logger); + +inline auto qHash(const ErrorInfo &e) { return qHash(e.toString()); } inline bool operator==(const ErrorInfo &e1, const ErrorInfo &e2) { return e1.toString() == e2.toString(); } diff --git a/src/lib/corelib/tools/executablefinder.cpp b/src/lib/corelib/tools/executablefinder.cpp index 0dc06dd86..b5d9c0151 100644 --- a/src/lib/corelib/tools/executablefinder.cpp +++ b/src/lib/corelib/tools/executablefinder.cpp @@ -76,7 +76,7 @@ QString ExecutableFinder::findExecutable(const QString &path, const QString &wor //if (FileInfo::fileName(filePath) == filePath) if (!FileInfo::isAbsolute(filePath)) return findInPath(filePath, workingDirPath); - else if (HostOsInfo::isWindowsHost()) + if (HostOsInfo::isWindowsHost()) return findBySuffix(filePath); return filePath; } @@ -99,7 +99,7 @@ QString ExecutableFinder::findBySuffix(const QString &filePath) const bool ExecutableFinder::candidateCheck(const QString &directory, const QString &program, QString &fullProgramPath) const { - for (const QString &suffix : qAsConst(m_executableSuffixes)) { + for (const QString &suffix : std::as_const(m_executableSuffixes)) { QString candidate = directory + program + suffix; qCDebug(lcExec) << "candidate:" << candidate; QFileInfo fi(candidate); @@ -120,10 +120,10 @@ QString ExecutableFinder::findInPath(const QString &filePath, const QString &wor fullProgramPath = filePath; qCDebug(lcExec) << "looking for executable in PATH" << fullProgramPath; QStringList pathEnv = m_environment.value(StringConstants::pathEnvVar()) - .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts); + .split(HostOsInfo::pathListSeparator(), Qt::SkipEmptyParts); if (HostOsInfo::isWindowsHost()) pathEnv.prepend(StringConstants::dot()); - for (QString directory : qAsConst(pathEnv)) { + for (QString directory : std::as_const(pathEnv)) { if (directory == StringConstants::dot()) directory = workingDirPath; if (!directory.isEmpty()) { diff --git a/src/lib/corelib/tools/fileinfo.cpp b/src/lib/corelib/tools/fileinfo.cpp index 1918117d6..f56762c04 100644 --- a/src/lib/corelib/tools/fileinfo.cpp +++ b/src/lib/corelib/tools/fileinfo.cpp @@ -41,13 +41,14 @@ #include <logging/translator.h> #include <tools/qbsassert.h> +#include <tools/stlutils.h> #include <tools/stringconstants.h> #include <QtCore/qcoreapplication.h> #include <QtCore/qdatetime.h> #include <QtCore/qdir.h> #include <QtCore/qfileinfo.h> -#include <QtCore/qregexp.h> +#include <QtCore/qregularexpression.h> #if defined(Q_OS_UNIX) #include <cerrno> @@ -133,16 +134,16 @@ void FileInfo::splitIntoDirectoryAndFileName(const QString &filePath, QString *d *fileName = filePath.mid(idx + 1); } -void FileInfo::splitIntoDirectoryAndFileName(const QString &filePath, QStringRef *dirPath, QStringRef *fileName) +void FileInfo::splitIntoDirectoryAndFileName(const QString &filePath, QStringView *dirPath, QStringView *fileName) { int idx = filePath.lastIndexOf(QLatin1Char('/')); if (idx < 0) { - dirPath->clear(); - *fileName = QStringRef(&filePath); + *dirPath = QStringView(); + *fileName = QStringView(filePath); return; } - *dirPath = filePath.leftRef(idx); - *fileName = filePath.midRef(idx + 1); + *dirPath = QStringView(filePath).left(idx); + *fileName = QStringView(filePath).mid(idx + 1); } bool FileInfo::exists(const QString &fp) @@ -180,20 +181,12 @@ bool FileInfo::isAbsolute(const QString &path, HostOsInfo::HostOs hostOs) return false; } -bool FileInfo::isPattern(const QString &str) -{ - return isPattern(QStringRef(&str)); -} - -bool FileInfo::isPattern(const QStringRef &str) +bool FileInfo::isPattern(QStringView str) { - for (const QChar &ch : str) { - if (ch == QLatin1Char('*') || ch == QLatin1Char('?') - || ch == QLatin1Char(']') || ch == QLatin1Char('[')) { - return true; - } - } - return false; + return Internal::any_of(str, [](const auto &ch) { + return (ch == QLatin1Char('*') || ch == QLatin1Char('?') || ch == QLatin1Char(']') + || ch == QLatin1Char('[')); + }); } /** @@ -242,17 +235,6 @@ QString FileInfo::resolvePath(const QString &base, const QString &rel, HostOsInf return r; } -bool FileInfo::globMatches(const QRegExp ®exp, const QString &fileName) -{ - const QString pattern = regexp.pattern(); - // May be it's simple wildcard, i.e. "*.cpp"? - if (pattern.startsWith(QLatin1Char('*')) && !isPattern(pattern.midRef(1))) { - // Yes, it's rather simple to just check the extension - return fileName.endsWith(pattern.midRef(1)); - } - return regexp.exactMatch(fileName); -} - #ifdef Q_OS_WIN static QString prependLongPathPrefix(const QString &absolutePath) { @@ -353,9 +335,70 @@ QString applicationDirPath() #elif defined(Q_OS_UNIX) +namespace GetFileTimes { + +enum TimeType { LastModified, LastStatusChanged }; + +// SFINAE magic inspired by +// https://github.com/qt/qtbase/blob/5.13/src/corelib/io/qfilesystemengine_unix.cpp +template< + TimeType type, + typename T, + std::enable_if_t<(&T::st_mtim, &T::st_ctim, true), int> = 0> +static inline FileTime get(const T &stat) +{ + if constexpr (type == LastModified) + return stat.st_mtim; + else if constexpr (type == LastStatusChanged) + return stat.st_ctim; +} + +// we are interested in real members, not compatibility macros +# if defined(st_mtimespec) +# undef st_mtimespec +# endif + +# if defined(st_ctimespec) +# undef st_ctimespec +# endif + +template< + TimeType type, + typename T, + std::enable_if_t<(&T::st_mtimespec, &T::st_ctimespec, true), int> = 0> +static inline FileTime get(const T &stat) +{ + if constexpr (type == LastModified) + return stat.st_mtimespec; + else if constexpr (type == LastStatusChanged) + return stat.st_ctimespec; +} + +# if defined(st_mtimensec) +# undef st_mtimensec +# endif + +# if defined(st_ctimensec) +# undef st_ctimensec +# endif + +template< + TimeType type, + typename T, + std::enable_if_t<(&T::st_mtimensec, &T::st_ctimensec, true), int> = 0> +static inline FileTime get(const T &stat) +{ + if constexpr (type == LastModified) + return FileTime::InternalType{stat.st_mtime, stat.st_mtimensec}; + else if constexpr (type == LastStatusChanged) + return FileTime::InternalType{stat.st_ctime, stat.st_ctimensec}; +} + +} // namespace GetFileTimes + FileInfo::FileInfo(const QString &fileName) { - if (stat(fileName.toLocal8Bit(), &m_stat) == -1) { + if (stat(fileName.toLocal8Bit().constData(), &m_stat) == -1) { m_stat.st_mtime = 0; m_stat.st_mode = 0; } @@ -368,24 +411,12 @@ bool FileInfo::exists() const FileTime FileInfo::lastModified() const { -#if APPLE_STAT_TIMESPEC - return m_stat.st_mtimespec; -#elif HAS_CLOCK_GETTIME - return m_stat.st_mtim; -#else - return m_stat.st_mtime; -#endif + return GetFileTimes::get<GetFileTimes::LastModified>(m_stat); } FileTime FileInfo::lastStatusChange() const { -#if APPLE_STAT_TIMESPEC - return m_stat.st_ctimespec; -#elif HAS_CLOCK_GETTIME - return m_stat.st_ctim; -#else - return m_stat.st_ctime; -#endif + return GetFileTimes::get<GetFileTimes::LastStatusChanged>(m_stat); } bool FileInfo::isDir() const @@ -450,7 +481,7 @@ static QByteArray storedLinkTarget(const QString &filePath) const QByteArray nativeFilePath = QFile::encodeName(filePath); ssize_t len; while (true) { - struct stat sb; + struct stat sb{}; if (lstat(nativeFilePath.constData(), &sb)) { qWarning("storedLinkTarget: lstat for %s failed with error code %d", nativeFilePath.constData(), errno); diff --git a/src/lib/corelib/tools/fileinfo.h b/src/lib/corelib/tools/fileinfo.h index 9813b69a7..3ad585f74 100644 --- a/src/lib/corelib/tools/fileinfo.h +++ b/src/lib/corelib/tools/fileinfo.h @@ -72,14 +72,12 @@ public: static QString completeSuffix(const QString &fp); static QString path(const QString &fp, HostOsInfo::HostOs hostOs = HostOsInfo::hostOs()); static void splitIntoDirectoryAndFileName(const QString &filePath, QString *dirPath, QString *fileName); - static void splitIntoDirectoryAndFileName(const QString &filePath, QStringRef *dirPath, QStringRef *fileName); + static void splitIntoDirectoryAndFileName(const QString &filePath, QStringView *dirPath, QStringView *fileName); static bool exists(const QString &fp); static bool isAbsolute(const QString &fp, HostOsInfo::HostOs hostOs = HostOsInfo::hostOs()); - static bool isPattern(const QStringRef &str); - static bool isPattern(const QString &str); + static bool isPattern(QStringView str); static QString resolvePath(const QString &base, const QString &rel, HostOsInfo::HostOs hostOs = HostOsInfo::hostOs()); - static bool globMatches(const QRegExp &pattern, const QString &subject); static bool isFileCaseCorrect(const QString &filePath); // Symlink-correct check. @@ -101,10 +99,10 @@ private: bool removeFileRecursion(const QFileInfo &f, QString *errorMessage); -// FIXME: Used by tests. -bool QBS_EXPORT removeDirectoryWithContents(const QString &path, QString *errorMessage); -bool QBS_EXPORT copyFileRecursion(const QString &sourcePath, const QString &targetPath, - bool preserveSymLinks, bool copyDirectoryContents, QString *errorMessage); +bool QBS_AUTOTEST_EXPORT removeDirectoryWithContents(const QString &path, QString *errorMessage); +bool QBS_AUTOTEST_EXPORT copyFileRecursion( + const QString &sourcePath, const QString &targetPath, bool preserveSymLinks, + bool copyDirectoryContents, QString *errorMessage); } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/tools/filesaver.cpp b/src/lib/corelib/tools/filesaver.cpp index 5a0a68c1f..3459ac558 100644 --- a/src/lib/corelib/tools/filesaver.cpp +++ b/src/lib/corelib/tools/filesaver.cpp @@ -107,14 +107,9 @@ bool FileSaver::commit() return true; } -bool FileSaver::write(const std::vector<char> &data) +bool FileSaver::write(std::string_view data) { - return fwrite(data, device()); -} - -bool FileSaver::write(const std::string &data) -{ - return fwrite(data, device()); + return Internal::fwrite(data.data(), data.size(), device()); } } // namespace Internal diff --git a/src/lib/corelib/tools/filesaver.h b/src/lib/corelib/tools/filesaver.h index 8b4c01669..06f11eba0 100644 --- a/src/lib/corelib/tools/filesaver.h +++ b/src/lib/corelib/tools/filesaver.h @@ -60,8 +60,7 @@ public: std::ostream *device(); bool open(); bool commit(); - bool write(const std::vector<char> &data); - bool write(const std::string &data); + bool write(std::string_view data); private: std::string m_oldFileContents; diff --git a/src/lib/corelib/tools/filetime.cpp b/src/lib/corelib/tools/filetime.cpp index d115075a7..4ccb5731e 100644 --- a/src/lib/corelib/tools/filetime.cpp +++ b/src/lib/corelib/tools/filetime.cpp @@ -50,78 +50,29 @@ namespace qbs { namespace Internal { -#ifdef APPLE_CUSTOM_CLOCK_GETTIME -#include <sys/time.h> - -#ifndef CLOCK_REALTIME -#define CLOCK_REALTIME 0 -#endif - -// clk_id isn't used, only the CLOCK_REALTIME case is implemented. -int clock_gettime(int /*clk_id*/, struct timespec *t) -{ - struct timeval tv; - // Resolution of gettimeofday is 1000nsecs = 1 microsecond. - int ret = gettimeofday(&tv, NULL); - t->tv_sec = tv.tv_sec; - t->tv_nsec = tv.tv_usec * 1000; - return ret; -} -#endif +#if defined(Q_OS_WIN) FileTime::FileTime() { -#ifdef Q_OS_WIN static_assert(sizeof(FileTime::InternalType) == sizeof(FILETIME), "FileTime::InternalType has wrong size."); m_fileTime = 0; -#elif HAS_CLOCK_GETTIME - m_fileTime = {0, 0}; -#else - m_fileTime = 0; -#endif } FileTime::FileTime(const FileTime::InternalType &ft) : m_fileTime(ft) { -#if HAS_CLOCK_GETTIME - if (m_fileTime.tv_sec == 0) - m_fileTime.tv_nsec = 0; // stat() sets only the first member to 0 for non-existing files. -#endif } int FileTime::compare(const FileTime &other) const { -#ifdef Q_OS_WIN auto const t1 = reinterpret_cast<const FILETIME *>(&m_fileTime); auto const t2 = reinterpret_cast<const FILETIME *>(&other.m_fileTime); return CompareFileTime(t1, t2); -#elif HAS_CLOCK_GETTIME - if (m_fileTime.tv_sec < other.m_fileTime.tv_sec) - return -1; - if (m_fileTime.tv_sec > other.m_fileTime.tv_sec) - return 1; - if (m_fileTime.tv_nsec < other.m_fileTime.tv_nsec) - return -1; - if (m_fileTime.tv_nsec > other.m_fileTime.tv_nsec) - return 1; - return 0; -#else - if (m_fileTime < other.m_fileTime) - return -1; - if (m_fileTime > other.m_fileTime) - return 1; - return 0; -#endif } void FileTime::clear() { -#if HAS_CLOCK_GETTIME - m_fileTime = { 0, 0 }; -#else m_fileTime = 0; -#endif } bool FileTime::isValid() const @@ -131,32 +82,16 @@ bool FileTime::isValid() const FileTime FileTime::currentTime() { -#ifdef Q_OS_WIN FileTime result; SYSTEMTIME st; GetSystemTime(&st); auto const ft = reinterpret_cast<FILETIME *>(&result.m_fileTime); SystemTimeToFileTime(&st, ft); return result; -#elif defined APPLE_CUSTOM_CLOCK_GETTIME - InternalType t; - // Explicitly use our custom version, so that we don't get an additional unresolved symbol on a - // system that actually provides one, but isn't used due to the minimium deployment target - // being lower. - qbs::Internal::clock_gettime(CLOCK_REALTIME, &t); - return t; -#elif HAS_CLOCK_GETTIME - InternalType t; - clock_gettime(CLOCK_REALTIME, &t); - return t; -#else - return time(nullptr); -#endif } FileTime FileTime::oldestTime() { -#ifdef Q_OS_WIN SYSTEMTIME st = { 1601, 1, @@ -171,25 +106,15 @@ FileTime FileTime::oldestTime() auto const ft = reinterpret_cast<FILETIME *>(&result.m_fileTime); SystemTimeToFileTime(&st, ft); return result; -#elif HAS_CLOCK_GETTIME - return FileTime({1, 0}); -#else - return 1; -#endif } double FileTime::asDouble() const { -#if HAS_CLOCK_GETTIME - return static_cast<double>(m_fileTime.tv_sec); -#else return static_cast<double>(m_fileTime); -#endif } QString FileTime::toString() const { -#ifdef Q_OS_WIN auto const ft = reinterpret_cast<const FILETIME *>(&m_fileTime); SYSTEMTIME stUTC, stLocal; FileTimeToSystemTime(ft, &stUTC); @@ -198,16 +123,93 @@ QString FileTime::toString() const .arg(stLocal.wDay, 2, 10, QLatin1Char('0')).arg(stLocal.wMonth, 2, 10, QLatin1Char('0')).arg(stLocal.wYear) .arg(stLocal.wHour, 2, 10, QLatin1Char('0')).arg(stLocal.wMinute, 2, 10, QLatin1Char('0')).arg(stLocal.wSecond, 2, 10, QLatin1Char('0')); return result; -#else +} + +#else // defined(Q_OS_WIN) + +// based on https://github.com/qt/qtbase/blob/5.13/src/corelib/kernel/qelapsedtimer_unix.cpp +// for details why it is implemented this way, see Qt source code +# if !defined(CLOCK_REALTIME) +# define CLOCK_REALTIME 0 +static inline void qbs_clock_gettime(int, struct timespec *ts) +{ + // support clock_gettime with gettimeofday + struct timeval tv; + gettimeofday(&tv, 0); + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; +} + +# ifdef _POSIX_MONOTONIC_CLOCK +# undef _POSIX_MONOTONIC_CLOCK +# define _POSIX_MONOTONIC_CLOCK -1 +# endif +# else +static inline void qbs_clock_gettime(clockid_t clock, struct timespec *ts) +{ + clock_gettime(clock, ts); +} +# endif + +FileTime::FileTime() +{ + m_fileTime = {0, 0}; +} + +FileTime::FileTime(const FileTime::InternalType &ft) : m_fileTime(ft) +{ + if (m_fileTime.tv_sec == 0) + m_fileTime.tv_nsec = 0; // stat() sets only the first member to 0 for non-existing files. +} + +int FileTime::compare(const FileTime &other) const +{ + if (m_fileTime.tv_sec < other.m_fileTime.tv_sec) + return -1; + if (m_fileTime.tv_sec > other.m_fileTime.tv_sec) + return 1; + if (m_fileTime.tv_nsec < other.m_fileTime.tv_nsec) + return -1; + if (m_fileTime.tv_nsec > other.m_fileTime.tv_nsec) + return 1; + return 0; +} + +void FileTime::clear() +{ + m_fileTime = { 0, 0 }; +} + +bool FileTime::isValid() const +{ + return *this != FileTime(); +} + +FileTime FileTime::currentTime() +{ + InternalType t; + qbs_clock_gettime(CLOCK_REALTIME, &t); + return t; +} + +FileTime FileTime::oldestTime() +{ + return FileTime({1, 0}); +} + +double FileTime::asDouble() const +{ + return static_cast<double>(m_fileTime.tv_sec); +} + +QString FileTime::toString() const +{ QDateTime dt; -#if HAS_CLOCK_GETTIME - dt.setMSecsSinceEpoch(m_fileTime.tv_sec * 1000 + m_fileTime.tv_nsec / 1000000); -#else - dt.setTime_t(m_fileTime); -#endif + dt.setMSecsSinceEpoch(m_fileTime.tv_sec * 1000 + m_fileTime.tv_nsec / 1000000); return dt.toString(Qt::ISODateWithMs); -#endif } +#endif // defined(Q_OS_WIN) + } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/tools/filetime.h b/src/lib/corelib/tools/filetime.h index f9a15f794..a0d4d2d7d 100644 --- a/src/lib/corelib/tools/filetime.h +++ b/src/lib/corelib/tools/filetime.h @@ -45,28 +45,10 @@ #include <QtCore/qdatastream.h> #include <QtCore/qdebug.h> -#if defined(Q_OS_UNIX) && !defined(__APPLE__) +#if defined(Q_OS_UNIX) #include <time.h> -#define HAS_CLOCK_GETTIME (_POSIX_C_SOURCE >= 199309L) #endif // Q_OS_UNIX -#ifdef __APPLE__ - -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200 -// macOS 10.12+ ships clock_gettime. -#else -// We implement our own clock_gettime. -#define APPLE_CUSTOM_CLOCK_GETTIME 1 -#endif // __MAC_OS_X_VERSION_MIN_REQUIRED - -// Either way we have a clock_gettime in the end. -#define HAS_CLOCK_GETTIME 1 - -// Apple stat struct has slightly different names for time fields. -#define APPLE_STAT_TIMESPEC 1 - -#endif // __APPLE__ - namespace qbs { namespace Internal { @@ -74,11 +56,7 @@ class QBS_AUTOTEST_EXPORT FileTime { public: #if defined(Q_OS_UNIX) -#if HAS_CLOCK_GETTIME using InternalType = timespec; -#else - using InternalType = time_t; -#endif // HAS_CLOCK_GETTIME #elif defined(Q_OS_WIN) using InternalType = quint64; #else @@ -107,7 +85,7 @@ public: template<PersistentPool::OpType opType> void completeSerializationOp(PersistentPool &pool) { -#if HAS_CLOCK_GETTIME +#if !defined(Q_OS_WIN) pool.serializationOp<opType>(m_fileTime.tv_sec, m_fileTime.tv_nsec); #else pool.serializationOp<opType>(m_fileTime); diff --git a/src/lib/corelib/tools/hostosinfo.h b/src/lib/corelib/tools/hostosinfo.h index d7f718c19..32817f731 100644 --- a/src/lib/corelib/tools/hostosinfo.h +++ b/src/lib/corelib/tools/hostosinfo.h @@ -67,16 +67,16 @@ namespace qbs { namespace Internal { -class QBS_EXPORT HostOsInfo // Exported for use by command-line tools. +class HostOsInfo { public: // Add more as needed. enum HostOs { HostOsWindows, HostOsLinux, HostOsMacos, HostOsOtherUnix, HostOsOther }; - static inline std::string hostOSIdentifier(); - static inline std::string hostOSArchitecture(); - static inline std::vector<std::string> hostOSIdentifiers(); - static inline std::vector<std::string> canonicalOSIdentifiers(const std::string &os); + static inline QString hostOSIdentifier(); + static inline QString hostOSArchitecture(); + static inline std::vector<QString> hostOSIdentifiers(); + static inline std::vector<QString> canonicalOSIdentifiers(const QString &os); static inline HostOs hostOs(); static inline Version hostOsVersion() { @@ -98,6 +98,28 @@ public: return v; } + static QString hostOsBuildVersion() { + QString v; + if (HostOsInfo::isWindowsHost()) { + QSettings settings(QStringLiteral("HKEY_LOCAL_MACHINE\\Software\\" + "Microsoft\\Windows NT\\CurrentVersion"), + QSettings::NativeFormat); + v = settings.value(QStringLiteral("CurrentBuildNumber")).toString(); + } else if (HostOsInfo::isMacosHost()) { + QSettings serverSettings(QStringLiteral( + "/System/Library/CoreServices/ServerVersion.plist"), + QSettings::NativeFormat); + v = serverSettings.value(QStringLiteral("ProductBuildVersion")).toString(); + if (v.isNull()) { + QSettings systemSettings(QStringLiteral( + "/System/Library/CoreServices/SystemVersion.plist"), + QSettings::NativeFormat); + v = systemSettings.value(QStringLiteral("ProductBuildVersion")).toString(); + } + } + return v; + } + static bool isWindowsHost() { return hostOs() == HostOsWindows; } static bool isLinuxHost() { return hostOs() == HostOsLinux; } static bool isMacosHost() { return hostOs() == HostOsMacos; } @@ -112,6 +134,13 @@ public: return finalName; } + static QString stripExecutableSuffix(const QString &executable) + { + constexpr QLatin1String suffix(QBS_HOST_EXE_SUFFIX, sizeof(QBS_HOST_EXE_SUFFIX) - 1); + return !suffix.isEmpty() && executable.endsWith(suffix) + ? executable.chopped(suffix.size()) : executable; + } + static QString dynamicLibraryName(const QString &libraryBaseName) { return QLatin1String(QBS_HOST_DYNAMICLIB_PREFIX) + libraryBaseName @@ -148,69 +177,68 @@ public: } }; -std::string HostOsInfo::hostOSIdentifier() +QString HostOsInfo::hostOSIdentifier() { #if defined(__APPLE__) - return "macos"; + return QStringLiteral("macos"); #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) - return "windows"; + return QStringLiteral("windows"); #elif defined(_AIX) - return "aix"; + return QStringLiteral("aix"); #elif defined(hpux) || defined(__hpux) - return "hpux"; + return QStringLiteral("hpux"); #elif defined(__sun) || defined(sun) - return "solaris"; + return QStringLiteral("solaris"); #elif defined(__linux__) || defined(__linux) - return "linux"; + return QStringLiteral("linux"); #elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) - return "freebsd"; + return QStringLiteral("freebsd"); #elif defined(__NetBSD__) - return "netbsd"; + return QStringLiteral("netbsd"); #elif defined(__OpenBSD__) - return "openbsd"; + return QStringLiteral("openbsd"); #elif defined(__GNU__) - return "hurd"; + return QStringLiteral("hurd"); #elif defined(__HAIKU__) - return "haiku"; + return QStringLiteral("haiku"); #else #warning "Qbs has not been ported to this OS - see http://qbs.io/" - return ""; + return {}; #endif } -std::string HostOsInfo::hostOSArchitecture() +QString HostOsInfo::hostOSArchitecture() { const auto cpuArch = QSysInfo::currentCpuArchitecture(); if (cpuArch == QLatin1String("i386")) - return "x86"; - return cpuArch.toStdString(); + return QStringLiteral("x86"); + return cpuArch; } -std::vector<std::string> HostOsInfo::hostOSIdentifiers() +std::vector<QString> HostOsInfo::hostOSIdentifiers() { return canonicalOSIdentifiers(hostOSIdentifier()); } -std::vector<std::string> HostOsInfo::canonicalOSIdentifiers(const std::string &name) +std::vector<QString> HostOsInfo::canonicalOSIdentifiers(const QString &name) { - std::vector<std::string> list { name }; - if (contains(std::vector<std::string> {"ios-simulator"}, name)) - list << canonicalOSIdentifiers("ios"); - if (contains(std::vector<std::string> {"tvos-simulator"}, name)) - list << canonicalOSIdentifiers("tvos"); - if (contains(std::vector<std::string> {"watchos-simulator"}, name)) - list << canonicalOSIdentifiers("watchos"); - if (contains(std::vector<std::string> {"macos", "ios", "tvos", "watchos"}, name)) - list << canonicalOSIdentifiers("darwin"); - if (contains(std::vector<std::string> {"darwin", "freebsd", "netbsd", "openbsd"}, name)) - list << canonicalOSIdentifiers("bsd"); - if (contains(std::vector<std::string> {"android"}, name)) - list << canonicalOSIdentifiers("linux"); + std::vector<QString> list { name }; + if (contains({u"ios-simulator"}, name)) + list << canonicalOSIdentifiers(QStringLiteral("ios")); + if (contains({u"tvos-simulator"}, name)) + list << canonicalOSIdentifiers(QStringLiteral("tvos")); + if (contains({u"watchos-simulator"}, name)) + list << canonicalOSIdentifiers(QStringLiteral("watchos")); + if (contains({u"macos", u"ios", u"tvos", u"watchos"}, name)) + list << canonicalOSIdentifiers(QStringLiteral("darwin")); + if (contains({u"darwin", u"freebsd", u"netbsd", u"openbsd"}, name)) + list << canonicalOSIdentifiers(QStringLiteral("bsd")); + if (contains({u"android"}, name)) + list << canonicalOSIdentifiers(QStringLiteral("linux")); // Note: recognized non-Unix platforms include: windows, haiku, vxworks - if (contains(std::vector<std::string> { - "bsd", "aix", "hpux", "solaris", "linux", "hurd", "qnx", "integrity"}, name)) - list << canonicalOSIdentifiers("unix"); + if (contains({u"bsd", u"aix", u"hpux", u"solaris", u"linux", u"hurd", u"qnx", u"integrity"}, name)) + list << canonicalOSIdentifiers(QStringLiteral("unix")); return list; } diff --git a/src/lib/corelib/tools/id.cpp b/src/lib/corelib/tools/id.cpp index 6dd1147f2..cca755512 100644 --- a/src/lib/corelib/tools/id.cpp +++ b/src/lib/corelib/tools/id.cpp @@ -75,7 +75,7 @@ public: : n(length), str(s) { if (!n) - length = n = qstrlen(s); + length = n = int(qstrlen(s)); h = 0; while (length--) { h = (h << 4) + *s++; @@ -95,7 +95,7 @@ static bool operator==(const StringHolder &sh1, const StringHolder &sh2) } -static uint qHash(const StringHolder &sh) +QHashValueType qHash(const StringHolder &sh) { return sh.h; } @@ -269,11 +269,10 @@ Id Id::withPrefix(const char *prefix) const bool Id::operator==(const char *name) const { - const char *string = getStringFromId(m_id); - if (string && name) - return strcmp(string, name) == 0; - else - return false; + const auto string = getStringFromId(m_id); + if (!string.isNull() && name) + return strcmp(string.data(), name) == 0; + return false; } bool Id::alphabeticallyBefore(Id other) const diff --git a/src/lib/corelib/tools/id.h b/src/lib/corelib/tools/id.h index aa327833f..845ed60df 100644 --- a/src/lib/corelib/tools/id.h +++ b/src/lib/corelib/tools/id.h @@ -41,6 +41,7 @@ #define QBS_TOOLS_ID_H #include "qbs_export.h" +#include <tools/porting.h> #include <QtCore/qmetatype.h> #include <QtCore/qstring.h> @@ -84,7 +85,7 @@ private: int m_id; }; -inline uint qHash(const Id &id) { return id.uniqueIdentifier(); } +inline QHashValueType qHash(const Id &id) { return id.uniqueIdentifier(); } } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/tools/installoptions.cpp b/src/lib/corelib/tools/installoptions.cpp index 5e112e6de..563f04a7b 100644 --- a/src/lib/corelib/tools/installoptions.cpp +++ b/src/lib/corelib/tools/installoptions.cpp @@ -68,7 +68,7 @@ public: QString effectiveInstallRoot(const InstallOptions &options, const TopLevelProject *project) { - const QString installRoot = options.installRoot(); + QString installRoot = options.installRoot(); if (!installRoot.isEmpty()) return installRoot; diff --git a/src/lib/corelib/tools/iosutils.h b/src/lib/corelib/tools/iosutils.h index 1a5faf3c3..69957a82e 100644 --- a/src/lib/corelib/tools/iosutils.h +++ b/src/lib/corelib/tools/iosutils.h @@ -45,7 +45,7 @@ #include <ostream> #if defined(_WIN32) && defined(_MSC_VER) -#include <codecvt> +#include <windows.h> #include <locale> #define QBS_RENAME_IMPL ::_wrename #define QBS_UNLINK_IMPL ::_wunlink @@ -82,8 +82,10 @@ static inline bool fwrite(const char *s, std::ostream *stream) static inline qbs_filesystem_path_string_type utf8_to_native_path(const std::string &str) { #if defined(_WIN32) && defined(_MSC_VER) - std::wstring_convert<std::codecvt_utf8<wchar_t>> converter; - return converter.from_bytes(str); + const int size = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); + std::wstring result(size, 0); + MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), result.data(), size); + return result; #else return str; #endif diff --git a/src/lib/corelib/tools/jsliterals.cpp b/src/lib/corelib/tools/jsliterals.cpp index 74328006c..647bdc640 100644 --- a/src/lib/corelib/tools/jsliterals.cpp +++ b/src/lib/corelib/tools/jsliterals.cpp @@ -40,8 +40,9 @@ #include "jsliterals.h" #include <tools/stringconstants.h> +#include <tools/qttools.h> -#include <QtCore/qregexp.h> +#include <QtCore/qregularexpression.h> namespace qbs { @@ -53,7 +54,7 @@ QString toJSLiteral(const bool b) QString toJSLiteral(const QString &str) { QString js = str; - js.replace(QRegExp(QLatin1String("([\\\\\"])")), QLatin1String("\\\\1")); + js.replace(QRegularExpression(QLatin1String("([\\\\\"])")), QLatin1String("\\\\1")); js.prepend(QLatin1Char('"')); js.append(QLatin1Char('"')); return js; @@ -75,7 +76,7 @@ QString toJSLiteral(const QVariant &val) { if (!val.isValid()) return Internal::StringConstants::undefinedValue(); - if (val.type() == QVariant::List || val.type() == QVariant::StringList) { + if (val.userType() == QMetaType::QVariantList || val.userType() == QMetaType::QStringList) { QString res; const auto list = val.toList(); for (const QVariant &child : list) { @@ -86,7 +87,7 @@ QString toJSLiteral(const QVariant &val) res.append(QLatin1Char(']')); return res; } - if (val.type() == QVariant::Map) { + if (val.userType() == QMetaType::QVariantMap) { const QVariantMap &vm = val.toMap(); QString str = QStringLiteral("{"); for (QVariantMap::const_iterator it = vm.begin(); it != vm.end(); ++it) { @@ -97,9 +98,9 @@ QString toJSLiteral(const QVariant &val) str += QLatin1Char('}'); return str; } - if (val.type() == QVariant::Bool) + if (val.userType() == QMetaType::Bool) return toJSLiteral(val.toBool()); - if (val.canConvert(QVariant::String)) + if (qVariantCanConvert(val, QMetaType::QString)) return toJSLiteral(val.toString()); return QStringLiteral("Unconvertible type %1").arg(QLatin1String(val.typeName())); } diff --git a/src/lib/corelib/tools/jsonhelper.h b/src/lib/corelib/tools/jsonhelper.h index d87802c0a..cb911c45a 100644 --- a/src/lib/corelib/tools/jsonhelper.h +++ b/src/lib/corelib/tools/jsonhelper.h @@ -40,6 +40,8 @@ #ifndef QBS_JSON_HELPER_H #define QBS_JSON_HELPER_H +#include <tools/stlutils.h> + #include <QtCore/qjsonarray.h> #include <QtCore/qjsonobject.h> #include <QtCore/qjsonvalue.h> @@ -60,10 +62,7 @@ template<> inline QString fromJson(const QJsonValue &v) { return v.toString(); } template<> inline QStringList fromJson(const QJsonValue &v) { const QJsonArray &jsonList = v.toArray(); - QStringList stringList; - std::transform(jsonList.begin(), jsonList.end(), std::back_inserter(stringList), - [](const QVariant &v) { return v.toString(); }); - return stringList; + return transformed<QStringList>(jsonList, [](const auto &v) { return v.toString(); }); } template<> inline QVariantMap fromJson(const QJsonValue &v) { return v.toObject().toVariantMap(); } template<> inline QProcessEnvironment fromJson(const QJsonValue &v) @@ -78,9 +77,9 @@ template<> inline QProcessEnvironment fromJson(const QJsonValue &v) template<typename T> inline void setValueFromJson(T &targetValue, const QJsonObject &data, const char *jsonProperty) { - const QJsonValue v = data.value(QLatin1String(jsonProperty)); - if (!v.isNull()) - targetValue = fromJson<T>(v); + const auto it = data.find(QLatin1String(jsonProperty)); + if (it != data.end()) + targetValue = fromJson<T>(*it); } } // namespace Internal diff --git a/src/lib/corelib/tools/launcherinterface.cpp b/src/lib/corelib/tools/launcherinterface.cpp index f4e80c5e2..cd877c154 100644 --- a/src/lib/corelib/tools/launcherinterface.cpp +++ b/src/lib/corelib/tools/launcherinterface.cpp @@ -61,11 +61,23 @@ namespace Internal { class LauncherProcess : public QProcess { public: - LauncherProcess(QObject *parent) : QProcess(parent) { } + LauncherProcess(QObject *parent) : QProcess(parent) + { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) && defined(Q_OS_UNIX) + setChildProcessModifier([this] { setupChildProcess_impl(); }); +#endif + } private: +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) void setupChildProcess() override { + setupChildProcess_impl(); + } +#endif + + void setupChildProcess_impl() + { #ifdef Q_OS_UNIX const auto pid = static_cast<pid_t>(processId()); setpgid(pid, pid); @@ -108,9 +120,7 @@ void LauncherInterface::doStart() return; } m_process = new LauncherProcess(this); - connect(m_process, - static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error), - this, &LauncherInterface::handleProcessError); + connect(m_process, &QProcess::errorOccurred, this, &LauncherInterface::handleProcessError); connect(m_process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &LauncherInterface::handleProcessFinished); @@ -130,8 +140,7 @@ void LauncherInterface::doStop() if (!m_process) return; m_process->disconnect(); - if (m_socket->isReady()) - m_socket->shutdown(); + m_socket->shutdown(); m_process->waitForFinished(3000); m_process->deleteLater(); m_process = nullptr; diff --git a/src/lib/corelib/tools/launcherpackets.h b/src/lib/corelib/tools/launcherpackets.h index 4306718fd..b3eac4320 100644 --- a/src/lib/corelib/tools/launcherpackets.h +++ b/src/lib/corelib/tools/launcherpackets.h @@ -145,7 +145,7 @@ class ProcessErrorPacket : public LauncherPacket public: ProcessErrorPacket(quintptr token); - QProcess::ProcessError error; + QProcess::ProcessError error = QProcess::UnknownError; QString errorString; private: diff --git a/src/lib/corelib/tools/launchersocket.cpp b/src/lib/corelib/tools/launchersocket.cpp index 4373b10b8..7d4788ee3 100644 --- a/src/lib/corelib/tools/launchersocket.cpp +++ b/src/lib/corelib/tools/launchersocket.cpp @@ -68,21 +68,21 @@ void LauncherSocket::sendData(const QByteArray &data) void LauncherSocket::shutdown() { - QBS_ASSERT(m_socket, return); - m_socket->disconnect(); - m_socket->write(ShutdownPacket().serialize()); - m_socket->waitForBytesWritten(1000); - m_socket->deleteLater(); - m_socket = nullptr; + const auto socket = m_socket.exchange(nullptr); + if (!socket) + return; + socket->disconnect(); + socket->write(ShutdownPacket().serialize()); + socket->waitForBytesWritten(1000); + socket->deleteLater(); } void LauncherSocket::setSocket(QLocalSocket *socket) { QBS_ASSERT(!m_socket, return); - m_socket = socket; + m_socket.store(socket); m_packetParser.setDevice(m_socket); - connect(m_socket, - static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), + connect(m_socket, &QLocalSocket::errorOccurred, this, &LauncherSocket::handleSocketError); connect(m_socket, &QLocalSocket::readyRead, this, &LauncherSocket::handleSocketDataAvailable); @@ -93,8 +93,9 @@ void LauncherSocket::setSocket(QLocalSocket *socket) void LauncherSocket::handleSocketError() { - if (m_socket->error() != QLocalSocket::PeerClosedError) - handleError(Tr::tr("Socket error: %1").arg(m_socket->errorString())); + auto socket = m_socket.load(); + if (socket->error() != QLocalSocket::PeerClosedError) + handleError(Tr::tr("Socket error: %1").arg(socket->errorString())); } void LauncherSocket::handleSocketDataAvailable() @@ -127,18 +128,19 @@ void LauncherSocket::handleSocketDisconnected() void LauncherSocket::handleError(const QString &error) { - m_socket->disconnect(); - m_socket->deleteLater(); - m_socket = nullptr; + const auto socket = m_socket.exchange(nullptr); + socket->disconnect(); + socket->deleteLater(); emit errorOccurred(error); } void LauncherSocket::handleRequests() { - QBS_ASSERT(isReady(), return); + const auto socket = m_socket.load(); + QBS_ASSERT(socket, return); std::lock_guard<std::mutex> locker(m_requestsMutex); - for (const QByteArray &request : qAsConst(m_requests)) - m_socket->write(request); + for (const QByteArray &request : std::as_const(m_requests)) + socket->write(request); m_requests.clear(); } diff --git a/src/lib/corelib/tools/launchersocket.h b/src/lib/corelib/tools/launchersocket.h index a9a1af800..2eb2c3f63 100644 --- a/src/lib/corelib/tools/launchersocket.h +++ b/src/lib/corelib/tools/launchersocket.h @@ -60,7 +60,7 @@ class LauncherSocket : public QObject Q_OBJECT friend class LauncherInterface; public: - bool isReady() const { return m_socket; } + bool isReady() const { return m_socket.load(); } void sendData(const QByteArray &data); signals: @@ -81,7 +81,7 @@ private: void handleError(const QString &error); void handleRequests(); - QLocalSocket *m_socket = nullptr; + std::atomic<QLocalSocket *> m_socket{nullptr}; PacketParser m_packetParser; std::vector<QByteArray> m_requests; std::mutex m_requestsMutex; diff --git a/src/lib/corelib/tools/msvcinfo.cpp b/src/lib/corelib/tools/msvcinfo.cpp index cffec85b2..77b83023a 100644 --- a/src/lib/corelib/tools/msvcinfo.cpp +++ b/src/lib/corelib/tools/msvcinfo.cpp @@ -38,14 +38,21 @@ ****************************************************************************/ #include "msvcinfo.h" +#include "visualstudioversioninfo.h" +#include <logging/logger.h> #include <tools/error.h> #include <tools/profile.h> +#include <tools/stlutils.h> #include <tools/stringconstants.h> #include <QtCore/qbytearray.h> #include <QtCore/qdir.h> +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsondocument.h> +#include <QtCore/qjsonobject.h> #include <QtCore/qprocess.h> +#include <QtCore/qsettings.h> #include <QtCore/qstringlist.h> #include <QtCore/qtemporaryfile.h> @@ -54,6 +61,7 @@ #endif #include <algorithm> +#include <memory> #include <mutex> using namespace qbs; @@ -129,16 +137,18 @@ public: #ifdef Q_OS_WIN static QStringList parseCommandLine(const QString &commandLine) { - QStringList list; - const auto buf = new wchar_t[commandLine.size() + 1]; - buf[commandLine.toWCharArray(buf)] = 0; + const auto buf = std::make_unique<wchar_t[]>(size_t(commandLine.size()) + 1); + buf[size_t(commandLine.toWCharArray(buf.get()))] = 0; int argCount = 0; - LPWSTR *args = CommandLineToArgvW(buf, &argCount); + const auto argsDeleter = [](LPWSTR *p){ LocalFree(p); }; + const auto args = std::unique_ptr<LPWSTR[], decltype(argsDeleter)>( + CommandLineToArgvW(buf.get(), &argCount), argsDeleter); if (!args) throw ErrorInfo(mkStr("Could not parse command line arguments: ") + commandLine); + QStringList list; + list.reserve(argCount); for (int i = 0; i < argCount; ++i) - list.push_back(QString::fromWCharArray(args[i])); - delete[] buf; + list.push_back(QString::fromWCharArray(args[size_t(i)])); return list; } #endif @@ -163,9 +173,10 @@ static QVariantMap getMsvcDefines(const QString &compilerFilePath, QStringList out = QString::fromLocal8Bit(runProcess(compilerFilePath, QStringList() << QStringLiteral("/nologo") << backendSwitch - << QString::fromWCharArray(_wgetenv(L"COMSPEC")) + << qEnvironmentVariable("COMSPEC") << QStringLiteral("/c") << languageSwitch + << QStringLiteral("/Zs") << QStringLiteral("NUL"), compilerEnv, true, commands)).split(QLatin1Char('\n')); @@ -206,30 +217,32 @@ static QVariantMap getMsvcDefines(const QString &compilerFilePath, static QVariantMap getClangClDefines( const QString &compilerFilePath, const QProcessEnvironment &compilerEnv, - MSVC::CompilerLanguage language) + MSVC::CompilerLanguage language, + const QString &arch) { #ifdef Q_OS_WIN QFileInfo clInfo(compilerFilePath); - QFileInfo clangInfo(clInfo.absolutePath() + QLatin1String("/clang.exe")); + QFileInfo clangInfo(clInfo.absolutePath() + QLatin1String("/clang-cl.exe")); if (!clangInfo.exists()) throw ErrorInfo(QStringLiteral("%1 does not exist").arg(clangInfo.absoluteFilePath())); - QString languageSwitch; - switch (language) { - case MSVC::CLanguage: - languageSwitch = QStringLiteral("c"); - break; - case MSVC::CPlusPlusLanguage: - languageSwitch = QStringLiteral("c++"); - break; - } QStringList args = { - QStringLiteral("-dM"), - QStringLiteral("-E"), - QStringLiteral("-x"), - languageSwitch, - QStringLiteral("NUL"), + QStringLiteral("/d1PP"), // dump macros + QStringLiteral("/E") // preprocess to stdout }; + + if (language == MSVC::CLanguage) + args.append(QStringLiteral("/TC")); + else if (language == MSVC::CPlusPlusLanguage) + args.append(QStringLiteral("/TP")); + + if (arch == QLatin1String("x86")) + args.append(QStringLiteral("-m32")); + else if (arch == QLatin1String("x86_64")) + args.append(QStringLiteral("-m64")); + + args.append(QStringLiteral("NUL")); // filename + const auto lines = QString::fromLocal8Bit( runProcess( clangInfo.absoluteFilePath(), @@ -239,10 +252,8 @@ static QVariantMap getClangClDefines( QVariantMap result; for (const auto &line: lines) { static const auto defineString = QLatin1String("#define "); - if (!line.startsWith(defineString)) { - throw ErrorInfo(QStringLiteral("Unexpected compiler frontend output: ") - + lines.join(QLatin1Char('\n'))); - } + if (!line.startsWith(defineString)) + continue; QStringView view(line.data() + defineString.size()); const auto it = std::find(view.begin(), view.end(), QLatin1Char(' ')); if (it == view.end()) { @@ -253,15 +264,240 @@ static QVariantMap getClangClDefines( QStringView value(it + 1, view.end()); result.insert(key.toString(), value.isEmpty() ? QVariant() : QVariant(value.toString())); } + if (result.isEmpty()) { + throw ErrorInfo(QStringLiteral("Cannot determine macroses from compiler frontend output: ") + + lines.join(QLatin1Char('\n'))); + } return result; #else Q_UNUSED(compilerFilePath); Q_UNUSED(compilerEnv); Q_UNUSED(language); + Q_UNUSED(arch); + return {}; +#endif +} + +static QString formatVswhereOutput(const QString &out, const QString &err) +{ + QString ret; + if (!out.isEmpty()) { + ret.append(Tr::tr("stdout")).append(QLatin1String(":\n")); + const auto lines = out.split(QLatin1Char('\n')); + for (const QString &line : lines) + ret.append(QLatin1Char('\t')).append(line).append(QLatin1Char('\n')); + } + if (!err.isEmpty()) { + ret.append(Tr::tr("stderr")).append(QLatin1String(":\n")); + const auto lines = err.split(QLatin1Char('\n')); + for (const QString &line : lines) + ret.append(QLatin1Char('\t')).append(line).append(QLatin1Char('\n')); + } + return ret; +} + +static QString wow6432Key() +{ +#ifdef Q_OS_WIN64 + return QStringLiteral("\\Wow6432Node"); +#else return {}; #endif } +static QString vswhereFilePath() +{ + static const std::vector<const char *> envVarCandidates{"ProgramFiles", "ProgramFiles(x86)"}; + for (const char * const envVar : envVarCandidates) { + const QString value = QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv(envVar))); + QString cmd = value + + QStringLiteral("/Microsoft Visual Studio/Installer/vswhere.exe"); + if (QFileInfo(cmd).exists()) + return cmd; + } + return {}; +} + +enum class ProductType { VisualStudio, BuildTools }; +static std::vector<MSVCInstallInfo> retrieveInstancesFromVSWhere( + ProductType productType, Logger &logger) +{ + std::vector<MSVCInstallInfo> result; + const QString cmd = vswhereFilePath(); + if (cmd.isEmpty()) + return result; + QProcess vsWhere; + QStringList args = productType == ProductType::VisualStudio + ? QStringList({QStringLiteral("-all"), QStringLiteral("-legacy"), + QStringLiteral("-prerelease")}) + : QStringList({QStringLiteral("-products"), + QStringLiteral("Microsoft.VisualStudio.Product.BuildTools")}); + args << QStringLiteral("-format") << QStringLiteral("json") << QStringLiteral("-utf8"); + vsWhere.start(cmd, args); + if (!vsWhere.waitForStarted(-1)) + return result; + if (!vsWhere.waitForFinished(-1)) { + logger.qbsWarning() << Tr::tr("The vswhere tool failed to run").append(QLatin1String(": ")) + .append(vsWhere.errorString()); + return result; + } + if (vsWhere.exitCode() != 0) { + const QString stdOut = QString::fromLocal8Bit(vsWhere.readAllStandardOutput()); + const QString stdErr = QString::fromLocal8Bit(vsWhere.readAllStandardError()); + logger.qbsWarning() << Tr::tr("The vswhere tool failed to run").append(QLatin1String(".\n")) + .append(formatVswhereOutput(stdOut, stdErr)); + return result; + } + QJsonParseError parseError{}; + QJsonDocument jsonOutput = QJsonDocument::fromJson(vsWhere.readAllStandardOutput(), + &parseError); + if (parseError.error != QJsonParseError::NoError) { + logger.qbsWarning() << Tr::tr("The vswhere tool produced invalid JSON output: %1") + .arg(parseError.errorString()); + return result; + } + const auto jsonArray = jsonOutput.array(); + for (const auto &value : jsonArray) { + const QJsonObject o = value.toObject(); + MSVCInstallInfo info; + info.version = o.value(QStringLiteral("installationVersion")).toString(); + if (productType == ProductType::BuildTools) { + // For build tools, the version is e.g. "15.8.28010.2036", rather than "15.0". + const int dotIndex = info.version.indexOf(QLatin1Char('.')); + if (dotIndex != -1) + info.version = info.version.left(dotIndex); + } + info.installDir = o.value(QStringLiteral("installationPath")).toString(); + if (!info.version.isEmpty() && !info.installDir.isEmpty()) + result.push_back(info); + } + return result; +} + +static std::vector<MSVCInstallInfo> installedMSVCsFromVsWhere(Logger &logger) +{ + const std::vector<MSVCInstallInfo> vsInstallations + = retrieveInstancesFromVSWhere(ProductType::VisualStudio, logger); + const std::vector<MSVCInstallInfo> buildToolInstallations + = retrieveInstancesFromVSWhere(ProductType::BuildTools, logger); + std::vector<MSVCInstallInfo> all; + std::copy(vsInstallations.begin(), vsInstallations.end(), std::back_inserter(all)); + std::copy(buildToolInstallations.begin(), buildToolInstallations.end(), + std::back_inserter(all)); + return all; +} + +static std::vector<MSVCInstallInfo> installedMSVCsFromRegistry() +{ + std::vector<MSVCInstallInfo> result; + + // Detect Visual Studio + const QSettings vsRegistry( + QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE") + wow6432Key() + + QStringLiteral("\\Microsoft\\VisualStudio\\SxS\\VS7"), + QSettings::NativeFormat); + const auto vsNames = vsRegistry.childKeys(); + for (const QString &vsName : vsNames) { + MSVCInstallInfo entry; + entry.version = vsName; + entry.installDir = vsRegistry.value(vsName).toString(); + result.push_back(entry); + } + + // Detect Visual C++ Build Tools + QSettings vcbtRegistry( + QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE") + wow6432Key() + + QStringLiteral("\\Microsoft\\VisualCppBuildTools"), + QSettings::NativeFormat); + const QStringList &vcbtRegistryChildGroups = vcbtRegistry.childGroups(); + for (const QString &childGroup : vcbtRegistryChildGroups) { + vcbtRegistry.beginGroup(childGroup); + bool ok; + int installed = vcbtRegistry.value(QStringLiteral("Installed")).toInt(&ok); + if (ok && installed) { + MSVCInstallInfo entry; + entry.version = childGroup; + const QSettings vsRegistry( + QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE") + wow6432Key() + + QStringLiteral("\\Microsoft\\VisualStudio\\") + childGroup + + QStringLiteral("\\Setup\\VC"), + QSettings::NativeFormat); + entry.installDir = vsRegistry.value(QStringLiteral("ProductDir")).toString(); + result.push_back(entry); + } + vcbtRegistry.endGroup(); + } + + return result; +} + +/* + Returns the list of compilers present in all MSVC installations + (Visual Studios or Build Tools) without the architecture, e.g. + [VC\Tools\MSVC\14.16.27023, VC\Tools\MSVC\14.14.26428, ...] +*/ +static std::vector<MSVC> installedCompilersHelper(Logger &logger) +{ + std::vector<MSVC> msvcs; + std::vector<MSVCInstallInfo> installInfos = installedMSVCsFromVsWhere(logger); + if (installInfos.empty()) + installInfos = installedMSVCsFromRegistry(); + for (const MSVCInstallInfo &installInfo : installInfos) { + MSVC msvc; + msvc.internalVsVersion = Version::fromString(installInfo.version, true); + if (!msvc.internalVsVersion.isValid()) + continue; + + QDir vsInstallDir(installInfo.installDir); + msvc.vsInstallPath = vsInstallDir.absolutePath(); + if (vsInstallDir.dirName() != QStringLiteral("VC") + && !vsInstallDir.cd(QStringLiteral("VC"))) { + continue; + } + + msvc.version = QString::number(Internal::VisualStudioVersionInfo( + msvc.internalVsVersion).marketingVersion()); + if (msvc.version.isEmpty()) { + logger.qbsWarning() + << Tr::tr("Unknown MSVC version %1 found.").arg(installInfo.version); + continue; + } + + if (msvc.internalVsVersion.majorVersion() < 15) { + QDir vcInstallDir = vsInstallDir; + if (!vcInstallDir.cd(QStringLiteral("bin"))) + continue; + msvc.vcInstallPath = vcInstallDir.absolutePath(); + msvcs.push_back(msvc); + } else { + QDir vcInstallDir = vsInstallDir; + vcInstallDir.cd(QStringLiteral("Tools/MSVC")); + const auto vcVersionStrs = vcInstallDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + std::vector<Version> vcVersions; + vcVersions.reserve(vcVersionStrs.size()); + for (const QString &vcVersionStr : vcVersionStrs) { + const Version vcVersion = Version::fromString(vcVersionStr); + if (!vcVersion.isValid()) + continue; + vcVersions.push_back(vcVersion); + } + // sort the versions so the new one comes first + std::sort(vcVersions.begin(), vcVersions.end(), std::greater<>()); + + for (const Version &vcVersion : vcVersions) { + QDir specificVcInstallDir = vcInstallDir; + if (!specificVcInstallDir.cd(vcVersion.toString()) + || !specificVcInstallDir.cd(QStringLiteral("bin"))) { + continue; + } + msvc.vcInstallPath = specificVcInstallDir.absolutePath(); + msvcs.push_back(msvc); + } + } + } + return msvcs; +} + void MSVC::init() { determineCompilerVersion(); @@ -274,12 +510,55 @@ void MSVC::init() QString MSVC::architectureFromClPath(const QString &clPath) { const auto parentDir = QFileInfo(clPath).absolutePath(); - const auto parentDirName = QFileInfo(parentDir).fileName().toLower(); + auto parentDirName = QFileInfo(parentDir).fileName().toLower(); + // can be the case when cl.exe is present within the Windows SDK installation... but can it? if (parentDirName == QLatin1String("bin")) return QStringLiteral("x86"); return parentDirName; } +QString MSVC::vcVariablesVersionFromBinPath(const QString &binPath) +{ + const auto binDirName = QFileInfo(binPath).fileName().toLower(); + // the case when cl.exe is present within the Windows SDK installation + if (binDirName == QLatin1String("bin")) + return {}; + // binPath is something like + // Microsoft Visual Studio 14.0/VC/bin/amd64_x86 + // or + // Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.28.29910/bin/Hostx64/x64 + QDir dir(binPath); + dir.cdUp(); + // older Visual Studios do not support multiple compiler versions + if (dir.dirName().toLower() == QLatin1String("bin")) + return {}; + dir.cdUp(); + dir.cdUp(); + return dir.dirName(); +} + +QString MSVC::canonicalArchitecture(const QString &arch) +{ + if (arch == QLatin1String("x64") || arch == QLatin1String("amd64")) + return QStringLiteral("x86_64"); + return arch; +} + +std::pair<QString, QString> MSVC::getHostTargetArchPair(const QString &arch) +{ + QString hostArch; + QString targetArch; + const int index = arch.indexOf(QLatin1Char('_')); + if (index != -1) { + hostArch = arch.mid(0, index); + targetArch = arch.mid(index); + } else { + hostArch = arch; + targetArch = arch; + } + return {canonicalArchitecture(hostArch), canonicalArchitecture(targetArch)}; +} + QString MSVC::binPathForArchitecture(const QString &arch) const { QString archSubDir; @@ -301,10 +580,106 @@ QVariantMap MSVC::compilerDefines(const QString &compilerFilePath, { const auto compilerName = QFileInfo(compilerFilePath).fileName().toLower(); if (compilerName == QLatin1String("clang-cl.exe")) - return getClangClDefines(compilerFilePath, environment, language); + return getClangClDefines(compilerFilePath, environment, language, architecture); return getMsvcDefines(compilerFilePath, environment, language); } +std::vector<MSVCArchInfo> MSVC::findSupportedArchitectures(const MSVC &msvc) +{ + std::vector<MSVCArchInfo> result; + auto addResult = [&result](const MSVCArchInfo &ai) { + if (QFile::exists(ai.binPath + QLatin1String("/cl.exe"))) + result.push_back(ai); + }; + if (msvc.internalVsVersion.majorVersion() < 15) { + static const QStringList knownArchitectures = QStringList() + << QStringLiteral("x86") + << QStringLiteral("amd64_x86") + << QStringLiteral("amd64") + << QStringLiteral("x86_amd64") + << QStringLiteral("ia64") + << QStringLiteral("x86_ia64") + << QStringLiteral("x86_arm") + << QStringLiteral("amd64_arm"); + for (const QString &knownArchitecture : knownArchitectures) { + MSVCArchInfo ai; + ai.arch = knownArchitecture; + ai.binPath = msvc.binPathForArchitecture(knownArchitecture); + addResult(ai); + } + } else { + QDir vcInstallDir(msvc.vcInstallPath); + const auto hostArchs = vcInstallDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const QString &hostArch : hostArchs) { + QDir subdir = vcInstallDir; + if (!subdir.cd(hostArch)) + continue; + const QString shortHostArch = hostArch.mid(4).toLower(); + const auto archs = subdir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const QString &arch : archs) { + MSVCArchInfo ai; + ai.binPath = subdir.absoluteFilePath(arch); + if (shortHostArch == arch) + ai.arch = arch; + else + ai.arch = shortHostArch + QLatin1Char('_') + arch; + addResult(ai); + } + } + } + return result; +} + +QVariantMap MSVC::toVariantMap() const +{ + return { + {QStringLiteral("version"), version}, + {QStringLiteral("internalVsVersion"), internalVsVersion.toString()}, + {QStringLiteral("vsInstallPath"), vsInstallPath}, + {QStringLiteral("vcInstallPath"), vcInstallPath}, + {QStringLiteral("binPath"), binPath}, + {QStringLiteral("architecture"), architecture}, + }; +} + +/*! + \internal + Returns the list of all compilers present in all MSVC installations + separated by host/target arch, e.g. + [ + VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64, + VC\Tools\MSVC\14.16.27023\bin\Hostx64\x86, + VC\Tools\MSVC\14.16.27023\bin\Hostx64\arm, + VC\Tools\MSVC\14.16.27023\bin\Hostx64\arm64, + VC\Tools\MSVC\14.16.27023\bin\Hostx86\x64, + ... + ] + \note that MSVC.architecture can be either "x64" or "amd64" (depending on the MSVC version) + in case of 64-bit platform (but we use the "x86_64" name...) +*/ +std::vector<MSVC> MSVC::installedCompilers(Logger &logger) +{ + std::vector<MSVC> msvcs; + const auto instMsvcs = installedCompilersHelper(logger); + for (const MSVC &msvc : instMsvcs) { + if (msvc.internalVsVersion.majorVersion() < 15) { + // Check existence of various install scripts + const QString vcvars32bat = msvc.vcInstallPath + QLatin1String("/vcvars32.bat"); + if (!QFileInfo(vcvars32bat).isFile()) + continue; + } + + const auto ais = findSupportedArchitectures(msvc); + transform(ais, msvcs, [&msvc](const auto &ai) { + MSVC specificMSVC = msvc; + specificMSVC.architecture = ai.arch; + specificMSVC.binPath = ai.binPath; + return specificMSVC; + }); + } + return msvcs; +} + void MSVC::determineCompilerVersion() { QString cppFilePath; @@ -332,3 +707,24 @@ void MSVC::determineCompilerVersion() compilerVersion = Version(versionStr.mid(0, 2).toInt(), versionStr.mid(2, 2).toInt(), versionStr.mid(4).toInt()); } + +QString MSVCInstallInfo::findVcvarsallBat() const +{ + static const auto vcvarsall2017 = QStringLiteral("VC/Auxiliary/Build/vcvarsall.bat"); + // 2015, 2013 and 2012 + static const auto vcvarsallOld = QStringLiteral("VC/vcvarsall.bat"); + QDir dir(installDir); + if (dir.exists(vcvarsall2017)) + return dir.absoluteFilePath(vcvarsall2017); + if (dir.exists(vcvarsallOld)) + return dir.absoluteFilePath(vcvarsallOld); + return {}; +} + +std::vector<MSVCInstallInfo> MSVCInstallInfo::installedMSVCs(Logger &logger) +{ + auto installInfos = installedMSVCsFromVsWhere(logger); + if (installInfos.empty()) + return installedMSVCsFromRegistry(); + return installInfos; +} diff --git a/src/lib/corelib/tools/msvcinfo.h b/src/lib/corelib/tools/msvcinfo.h index 171afeb29..efaf0b6c2 100644 --- a/src/lib/corelib/tools/msvcinfo.h +++ b/src/lib/corelib/tools/msvcinfo.h @@ -53,6 +53,14 @@ namespace qbs { namespace Internal { +class Logger; + +struct MSVCArchInfo +{ + QString arch; + QString binPath; +}; + /** * Represents one MSVC installation for one specific target architecture. * There are potentially multiple MSVCs in one Visual Studio installation. @@ -73,12 +81,14 @@ public: QString binPath; QString pathPrefix; QString architecture; + QString sdkVersion; QProcessEnvironment environment; MSVC() = default; - MSVC(const QString &clPath, QString arch): - architecture(std::move(arch)) + MSVC(const QString &clPath, QString arch, QString sdkVersion = {}): + architecture(std::move(arch)), + sdkVersion(std::move(sdkVersion)) { QDir parentDir = QFileInfo(clPath).dir(); binPath = parentDir.absolutePath(); @@ -90,11 +100,20 @@ public: QBS_EXPORT void init(); QBS_EXPORT static QString architectureFromClPath(const QString &clPath); + QBS_EXPORT static QString vcVariablesVersionFromBinPath(const QString &binPath); + QBS_EXPORT static QString canonicalArchitecture(const QString &arch); + QBS_EXPORT static std::pair<QString, QString> getHostTargetArchPair(const QString &arch); QBS_EXPORT QString binPathForArchitecture(const QString &arch) const; QBS_EXPORT QString clPathForArchitecture(const QString &arch) const; QBS_EXPORT QVariantMap compilerDefines(const QString &compilerFilePath, CompilerLanguage language) const; + QBS_EXPORT static std::vector<MSVCArchInfo> findSupportedArchitectures(const MSVC &msvc); + + QBS_EXPORT QVariantMap toVariantMap() const; + + QBS_EXPORT static std::vector<MSVC> installedCompilers(Logger &logger); + private: void determineCompilerVersion(); }; @@ -110,6 +129,16 @@ public: } }; +struct QBS_EXPORT MSVCInstallInfo +{ + QString version; + QString installDir; + + QString findVcvarsallBat() const; + + static std::vector<MSVCInstallInfo> installedMSVCs(Logger &logger); +}; + } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/tools/mutexdata.h b/src/lib/corelib/tools/mutexdata.h new file mode 100644 index 000000000..160f6dd26 --- /dev/null +++ b/src/lib/corelib/tools/mutexdata.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2023 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$ +** +****************************************************************************/ + +#pragma once + +#include <shared_mutex> + +namespace qbs { +namespace Internal { + +// adapted version of https://github.com/dragazo/rustex/blob/master/rustex.h + +// a data with a mutually exclusive access +template<typename DataType, typename MutexType = std::shared_mutex> +class MutexData +{ +public: + template<typename T, template<typename> typename LockType> + class ReferenceGuard + { + friend class MutexData; + template<typename U> + ReferenceGuard(U &&data) noexcept + : m_ptr(std::addressof(data.m_data)) + , m_lock(data.m_mutex) + {} + public: + ReferenceGuard(const ReferenceGuard &) = delete; + ReferenceGuard(ReferenceGuard &&) = default; + ReferenceGuard &operator=(const ReferenceGuard &) = delete; + ReferenceGuard &operator=(ReferenceGuard &&) = default; + + T &get() const noexcept { return *m_ptr; } + T &data() const noexcept { return *m_ptr; } + operator T &() const noexcept { return *m_ptr; } + + private: + T *m_ptr; + LockType<MutexType> m_lock; + }; + + using UniqueMutableGuard = ReferenceGuard<DataType, std::unique_lock>; + using UniqueConstGuard = ReferenceGuard<const DataType, std::unique_lock>; + using SharedGuard = ReferenceGuard<const DataType, std::shared_lock>; + + template< + typename ...Args, + std::enable_if_t<std::is_constructible_v<DataType, Args...>, int> = 0 + > + explicit MutexData(Args &&...args) : m_data(std::forward<Args>(args)...) {} + + [[nodiscard]] UniqueMutableGuard lock() noexcept { return UniqueMutableGuard{*this}; } + [[nodiscard]] UniqueConstGuard lock() const noexcept { return UniqueConstGuard{*this}; } + + template< + typename U = MutexType, + std::enable_if_t<std::is_same_v<U, std::shared_mutex>, int> = 0 + > + [[nodiscard]] SharedGuard lock_shared() const noexcept + { return SharedGuard{*this}; } + +private: + DataType m_data; + mutable MutexType m_mutex; +}; + +} // namespace qbs +} // namespace Internal diff --git a/src/lib/corelib/tools/pathutils.h b/src/lib/corelib/tools/pathutils.h index a2fad9aa4..55dbc1c6d 100644 --- a/src/lib/corelib/tools/pathutils.h +++ b/src/lib/corelib/tools/pathutils.h @@ -46,7 +46,7 @@ namespace qbs { namespace Internal { -class QBS_EXPORT PathUtils +class PathUtils { public: static QString toNativeSeparators(const QString &s, diff --git a/src/lib/corelib/tools/persistence.cpp b/src/lib/corelib/tools/persistence.cpp index c30d16518..0e545377a 100644 --- a/src/lib/corelib/tools/persistence.cpp +++ b/src/lib/corelib/tools/persistence.cpp @@ -48,7 +48,7 @@ namespace qbs { namespace Internal { -static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-127"; +static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-133"; NoBuildGraphError::NoBuildGraphError(const QString &filePath) : ErrorInfo(Tr::tr("Build graph not found for configuration '%1'. Expected location was '%2'.") @@ -140,7 +140,12 @@ void PersistentPool::finalizeWriteStream() void PersistentPool::storeVariant(const QVariant &variant) { - const auto type = static_cast<quint32>(variant.type()); + if (variant.isNull()) { + m_stream << quint32(QMetaType::User); + m_stream << variant; + return; + } + const auto type = static_cast<quint32>(variant.userType()); m_stream << type; switch (type) { case QMetaType::QString: @@ -206,7 +211,7 @@ void PersistentPool::doLoadValue(QStringList &l) void PersistentPool::doLoadValue(QProcessEnvironment &env) { - const QStringList keys = load<QStringList>(); + const auto keys = load<QStringList>(); for (const QString &key : keys) env.insert(key, load<QString>()); } @@ -218,7 +223,7 @@ void PersistentPool::doStoreValue(const QString &s) void PersistentPool::doStoreValue(const QStringList &l) { - m_stream << l.size(); + m_stream << int(l.size()); for (const QString &s : l) store(s); } @@ -231,8 +236,5 @@ void PersistentPool::doStoreValue(const QProcessEnvironment &env) store(env.value(key)); } -const PersistentPool::PersistentObjectId PersistentPool::ValueNotFoundId; -const PersistentPool::PersistentObjectId PersistentPool::EmptyValueId; - } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/tools/persistence.h b/src/lib/corelib/tools/persistence.h index e00310321..86365c993 100644 --- a/src/lib/corelib/tools/persistence.h +++ b/src/lib/corelib/tools/persistence.h @@ -48,8 +48,9 @@ #include <QtCore/qdatastream.h> #include <QtCore/qflags.h> #include <QtCore/qprocess.h> -#include <QtCore/qregexp.h> +#include <QtCore/qregularexpression.h> #include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> #include <QtCore/qvariant.h> #include <memory> @@ -81,16 +82,14 @@ public: QVariantMap projectConfig; }; - template<typename T, typename ...Types> void store(const T &value, const Types &...args) + template<typename ...Types> void store(const Types &...args) { - PPHelper<T>::store(value, this); - store(args...); + (... , PPHelper<Types>::store(args, this)); } - template<typename T, typename ...Types> void load(T &value, Types &...args) + template<typename ...Types> void load(Types &...args) { - PPHelper<T>::load(value, this); - load(args...); + (... , PPHelper<Types>::load(args, this)); } template<typename T> T load() { T tmp; @@ -99,30 +98,17 @@ public: } enum OpType { Store, Load }; - template<OpType type, typename T, typename ...Types> struct OpTypeHelper { }; - template<typename T, typename ...Types> struct OpTypeHelper<Store, T, Types...> - { - static void serializationOp(PersistentPool *pool, const T &value, const Types &...args) - { - pool->store(value, args...); - } - }; - template<typename T, typename ...Types> struct OpTypeHelper<Load, T, Types...> + template<OpType type, typename ...Types> void serializationOp(const Types &...args) { - static void serializationOp(PersistentPool *pool, T &value, Types &...args) - { - pool->load(value, args...); - } - }; - template<OpType type, typename T, typename ...Types> void serializationOp(const T &value, - const Types &...args) - { - OpTypeHelper<type, T, Types...>::serializationOp(this, value, args...); + static_assert(type == Store); + store(args...); } - template<OpType type, typename T, typename ...Types> void serializationOp(T &value, - Types &...args) + template<OpType type, typename ...Types> void serializationOp(Types &...args) { - OpTypeHelper<type, T, Types...>::serializationOp(this, value, args...); + if constexpr(type == Store) + store(args...); + else + load(args...); } void load(const QString &filePath); @@ -159,12 +145,9 @@ private: template<typename T> QHash<T, PersistentObjectId> &idMap(); template<typename T> PersistentObjectId &lastStoredId(); - // Recursion termination - void store() {} - void load() {} - - static const PersistentObjectId ValueNotFoundId = -1; - static const PersistentObjectId EmptyValueId = -2; + static const inline PersistentObjectId ValueNotFoundId = -1; + static const inline PersistentObjectId EmptyValueId = -2; + static const inline PersistentObjectId NullValueId = -3; std::unique_ptr<QIODevice> m_file; QDataStream m_stream; @@ -289,8 +272,13 @@ template<typename T> inline T PersistentPool::idLoadValue() { int id; m_stream >> id; - if (id == EmptyValueId) + if (id == NullValueId) + return T(); + if (id == EmptyValueId) { + if constexpr (std::is_same_v<T, QString>) + return QString(0, QChar()); return T(); + } QBS_CHECK(id >= 0); if (id >= static_cast<int>(idStorage<T>().size())) { T value; @@ -305,6 +293,12 @@ template<typename T> inline T PersistentPool::idLoadValue() template<typename T> void PersistentPool::idStoreValue(const T &value) { + if constexpr (std::is_same_v<T, QString>) { + if (value.isNull()) { + m_stream << NullValueId; + return; + } + } if (value.isEmpty()) { m_stream << EmptyValueId; return; @@ -340,8 +334,8 @@ struct PPHelper /***** Specializations of Helper class *****/ template<typename T> -struct PPHelper<T, std::enable_if_t<std::is_member_function_pointer< - decltype(&T::template completeSerializationOp<PersistentPool::Load>)>::value>> +struct PPHelper<T, std::enable_if_t<std::is_member_function_pointer_v< + decltype(&T::template completeSerializationOp<PersistentPool::Load>)>>> { static void store(const T &value, PersistentPool *pool) { @@ -353,7 +347,7 @@ struct PPHelper<T, std::enable_if_t<std::is_member_function_pointer< } }; -template<typename T> struct PPHelper<T, std::enable_if_t<std::is_integral<T>::value>> +template<typename T> struct PPHelper<T, std::enable_if_t<std::is_integral_v<T>>> { static void store(const T &value, PersistentPool *pool) { pool->m_stream << value; } static void load(T &value, PersistentPool *pool) { pool->m_stream >> value; } @@ -370,7 +364,7 @@ template<> struct PPHelper<long> } }; -template<typename T> struct PPHelper<T, std::enable_if_t<std::is_enum<T>::value>> +template<typename T> struct PPHelper<T, std::enable_if_t<std::is_enum_v<T>>> { using U = std::underlying_type_t<T>; static void store(const T &value, PersistentPool *pool) @@ -413,8 +407,8 @@ template<typename T> struct PPHelper<T *> static void load(T* &value, PersistentPool *pool) { value = pool->idLoad<T>(); } }; -template<typename T> struct PPHelper<T, std::enable_if_t<std::is_same<T, QString>::value - || std::is_same<T, QStringList>::value || std::is_same<T, QProcessEnvironment>::value>> +template<typename T> struct PPHelper<T, std::enable_if_t<std::is_same_v<T, QString> + || std::is_same_v<T, QStringList> || std::is_same_v<T, QProcessEnvironment>>> { static void store(const T &v, PersistentPool *pool) { pool->idStoreValue(v); } static void load(T &v, PersistentPool *pool) { v = pool->idLoadValue<T>(); } @@ -426,10 +420,16 @@ template<> struct PPHelper<QVariant> static void load(QVariant &v, PersistentPool *pool) { v = pool->loadVariant(); } }; -template<> struct PPHelper<QRegExp> +template<> struct PPHelper<QRegularExpression> { - static void store(const QRegExp &re, PersistentPool *pool) { pool->store(re.pattern()); } - static void load(QRegExp &re, PersistentPool *pool) { re.setPattern(pool->load<QString>()); } + static void store(const QRegularExpression &re, PersistentPool *pool) + { + pool->store(re.pattern()); + } + static void load(QRegularExpression &re, PersistentPool *pool) + { + re.setPattern(pool->load<QString>()); + } }; template<typename T, typename U> struct PPHelper<std::pair<T, U>> @@ -446,6 +446,33 @@ template<typename T, typename U> struct PPHelper<std::pair<T, U>> } }; +template<typename... Args> struct PPHelper<std::tuple<Args...>> +{ + template<std::size_t... Ns> + static void storeHelper( + std::index_sequence<Ns...>, const std::tuple<Args...> &tuple, PersistentPool *pool) + { + (pool->store(std::get<Ns>(tuple)), ...); + } + + static void store(const std::tuple<Args...> &tuple, PersistentPool *pool) + { + storeHelper(std::make_index_sequence<sizeof...(Args)>(), tuple, pool); + } + + template<std::size_t... Ns> + static void loadHelper( + std::index_sequence<Ns...>, std::tuple<Args...> &tuple, PersistentPool *pool) + { + (pool->load(std::get<Ns>(tuple)), ...); + } + + static void load(std::tuple<Args...> &tuple, PersistentPool * pool) + { + loadHelper(std::make_index_sequence<sizeof...(Args)>(), tuple, pool); + } +}; + template<typename T> struct PPHelper<QFlags<T>> { using Int = typename QFlags<T>::Int; @@ -461,6 +488,7 @@ template<typename T> struct PPHelper<QFlags<T>> template<typename T> struct IsSimpleContainer : std::false_type { }; template<typename T> struct IsSimpleContainer<QList<T>> : std::true_type { }; +template<> struct IsSimpleContainer<QStringList> : std::false_type { }; template<typename T> struct IsSimpleContainer<std::vector<T>> : std::true_type { }; template<typename T> struct PPHelper<T, std::enable_if_t<IsSimpleContainer<T>::value>> @@ -490,7 +518,7 @@ struct PPHelper<T, std::enable_if_t<IsKeyValueContainer<T>::value>> { static void store(const T &container, PersistentPool *pool) { - pool->store(container.size()); + pool->store(int(container.size())); for (auto it = container.cbegin(); it != container.cend(); ++it) { pool->store(it.key()); pool->store(it.value()); diff --git a/src/lib/corelib/tools/pimpl.h b/src/lib/corelib/tools/pimpl.h new file mode 100644 index 000000000..ab672aabe --- /dev/null +++ b/src/lib/corelib/tools/pimpl.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2023 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 PIMPL_H +#define PIMPL_H + +#include <tools/propagate_const.h> +#include <memory> + +namespace qbs { +namespace Internal { + +template<typename T> +using Pimpl = KDToolBox::propagate_const<std::unique_ptr<T>>; + +template<typename T, typename... Args> +Pimpl<T> makePimpl(Args&&... args) +{ + return Pimpl<T>(std::make_unique<T>(std::forward<Args>(args)...)); +} + +} // namespace Internal +} // namespace qbs + +#endif // PIMPL_H diff --git a/src/lib/corelib/tools/porting.h b/src/lib/corelib/tools/porting.h new file mode 100644 index 000000000..37582e8d3 --- /dev/null +++ b/src/lib/corelib/tools/porting.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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_TOOLS_PORTING_H +#define QBS_TOOLS_PORTING_H + +#include <QtCore/qglobal.h> + +namespace qbs { + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +using QHashValueType = uint; +#else +using QHashValueType = size_t; +#endif + +} + +#endif diff --git a/src/lib/corelib/tools/preferences.cpp b/src/lib/corelib/tools/preferences.cpp index 4db271758..fe669e163 100644 --- a/src/lib/corelib/tools/preferences.cpp +++ b/src/lib/corelib/tools/preferences.cpp @@ -63,7 +63,6 @@ Preferences::Preferences(Settings *settings, QVariantMap profileContents) { } - /*! * \brief Returns true <=> colored output should be used for printing messages. * This is only relevant for command-line frontends. diff --git a/src/lib/corelib/tools/processresult.h b/src/lib/corelib/tools/processresult.h index 92408aa31..3870a3ae5 100644 --- a/src/lib/corelib/tools/processresult.h +++ b/src/lib/corelib/tools/processresult.h @@ -44,11 +44,10 @@ #include <QtCore/qshareddata.h> #include <QtCore/qmetatype.h> #include <QtCore/qprocess.h> +#include <QtCore/qstringlist.h> QT_BEGIN_NAMESPACE class QJsonObject; -class QString; -class QStringList; QT_END_NAMESPACE namespace qbs { diff --git a/src/lib/corelib/tools/processutils.cpp b/src/lib/corelib/tools/processutils.cpp index b27592f88..02608cc9f 100644 --- a/src/lib/corelib/tools/processutils.cpp +++ b/src/lib/corelib/tools/processutils.cpp @@ -91,7 +91,7 @@ QString processNameByPid(qint64 pid) char exePath[64]; char buf[PATH_MAX]; memset(buf, 0, sizeof(buf)); - sprintf(exePath, "/proc/%lld/exe", pid); + std::sprintf(exePath, "/proc/%lld/exe", pid); if (readlink(exePath, buf, sizeof(buf)) < 0) return {}; return FileInfo::fileName(QString::fromUtf8(buf)); diff --git a/src/lib/corelib/tools/profile.cpp b/src/lib/corelib/tools/profile.cpp index 7e594fd2d..0fe0ba19d 100644 --- a/src/lib/corelib/tools/profile.cpp +++ b/src/lib/corelib/tools/profile.cpp @@ -63,13 +63,13 @@ namespace qbs { /*! * \brief Creates an object giving access to the settings for profile \c name. */ -Profile::Profile(const QString &name, Settings *settings, const QVariantMap &profiles) - : m_name(name), +Profile::Profile(QString name, Settings *settings, QVariantMap profiles) + : m_name(std::move(name)), m_settings(settings), - m_values(profiles.value(name).toMap()), - m_profiles(profiles) + m_values(profiles.value(m_name).toMap()), + m_profiles(std::move(profiles)) { - QBS_ASSERT(name == cleanName(name), return); + QBS_ASSERT(m_name == cleanName(m_name), return); } bool Profile::exists() const @@ -213,7 +213,7 @@ QVariant Profile::possiblyInheritedValue(const QString &key, const QVariant &def QStringList profileChain) const { extendAndCheckProfileChain(profileChain); - const QVariant v = localValue(key); + QVariant v = localValue(key); if (v.isValid()) return v; const QString baseProfileName = baseProfile(); diff --git a/src/lib/corelib/tools/profile.h b/src/lib/corelib/tools/profile.h index aa8b7ef10..b7fc7ff67 100644 --- a/src/lib/corelib/tools/profile.h +++ b/src/lib/corelib/tools/profile.h @@ -42,12 +42,9 @@ #include "qbs_export.h" #include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> #include <QtCore/qvariant.h> -QT_BEGIN_NAMESPACE -class QStringList; -QT_END_NAMESPACE - namespace qbs { class ErrorInfo; class Settings; @@ -55,7 +52,7 @@ class Settings; class QBS_EXPORT Profile { public: - Profile(const QString &name, Settings *settings, const QVariantMap &profiles = QVariantMap()); + Profile(QString name, Settings *settings, QVariantMap profiles = QVariantMap()); bool exists() const; QVariant value(const QString &key, const QVariant &defaultValue = QVariant(), diff --git a/src/lib/corelib/tools/profiling.cpp b/src/lib/corelib/tools/profiling.cpp index 7e3559b54..b93d3fa17 100644 --- a/src/lib/corelib/tools/profiling.cpp +++ b/src/lib/corelib/tools/profiling.cpp @@ -61,7 +61,7 @@ TimedActivityLogger::TimedActivityLogger(const Logger &logger, const QString &ac { if (!enabled) return; - d = new TimedActivityLoggerPrivate; + d = std::make_unique<TimedActivityLoggerPrivate>(); d->logger = logger; d->activity = activity; d->logger.qbsLog(LoggerInfo, true) << Tr::tr("Starting activity '%2'.").arg(activity); @@ -72,11 +72,10 @@ void TimedActivityLogger::finishActivity() { if (!d) return; - const QString timeString = elapsedTimeString(d->timer.elapsed()); + const QString timeString = elapsedTimeString(d->timer.nsecsElapsed()); d->logger.qbsLog(LoggerInfo, true) << Tr::tr("Activity '%2' took %3.").arg(d->activity, timeString); - delete d; - d = nullptr; + d.reset(); } TimedActivityLogger::~TimedActivityLogger() @@ -99,13 +98,13 @@ void AccumulatingTimer::stop() { if (!m_timer.isValid()) return; - *m_elapsedTime += m_timer.elapsed(); + *m_elapsedTime += m_timer.nsecsElapsed(); m_timer.invalidate(); } -QString elapsedTimeString(qint64 elapsedTimeInMs) +QString elapsedTimeString(qint64 elapsedTimeInNs) { - qint64 ms = elapsedTimeInMs; + qint64 ms = elapsedTimeInNs / (1000ll * 1000ll); qint64 s = ms/1000; ms -= s*1000; qint64 m = s/60; diff --git a/src/lib/corelib/tools/profiling.h b/src/lib/corelib/tools/profiling.h index 89f862ff9..3220c2a82 100644 --- a/src/lib/corelib/tools/profiling.h +++ b/src/lib/corelib/tools/profiling.h @@ -42,6 +42,8 @@ #include <QtCore/qelapsedtimer.h> +#include <memory> + QT_BEGIN_NAMESPACE class QString; QT_END_NAMESPACE @@ -50,7 +52,7 @@ namespace qbs { namespace Internal { class Logger; -QString elapsedTimeString(qint64 elapsedTimeInMs); +QString elapsedTimeString(qint64 elapsedTimeInNs); class TimedActivityLogger { @@ -61,7 +63,7 @@ public: private: class TimedActivityLoggerPrivate; - TimedActivityLoggerPrivate *d; + std::unique_ptr<TimedActivityLoggerPrivate> d; }; class AccumulatingTimer diff --git a/src/lib/corelib/tools/progressobserver.h b/src/lib/corelib/tools/progressobserver.h index fc49d9eed..73a61de37 100644 --- a/src/lib/corelib/tools/progressobserver.h +++ b/src/lib/corelib/tools/progressobserver.h @@ -41,12 +41,15 @@ #include <QtCore/qglobal.h> +#include <vector> + QT_BEGIN_NAMESPACE class QString; QT_END_NAMESPACE namespace qbs { namespace Internal { +class ScriptEngine; class ProgressObserver { @@ -64,6 +67,14 @@ public: // Call this to ensure that the progress bar always goes to 100%. void setFinished(); + + void addScriptEngine(ScriptEngine *engine) { m_scriptEngines.push_back(engine); } + +protected: + const std::vector<ScriptEngine *> &scriptEngines() const { return m_scriptEngines; } + +private: + std::vector<ScriptEngine *> m_scriptEngines; }; } // namespace Internal diff --git a/src/lib/corelib/tools/projectgeneratormanager.h b/src/lib/corelib/tools/projectgeneratormanager.h index 469d650b9..1efe593fc 100644 --- a/src/lib/corelib/tools/projectgeneratormanager.h +++ b/src/lib/corelib/tools/projectgeneratormanager.h @@ -45,10 +45,7 @@ #include <QtCore/qmap.h> #include <QtCore/qstring.h> - -QT_BEGIN_NAMESPACE -class QStringList; -QT_END_NAMESPACE +#include <QtCore/qstringlist.h> namespace qbs { class ProjectGenerator; diff --git a/src/lib/corelib/tools/propagate_const.h b/src/lib/corelib/tools/propagate_const.h new file mode 100644 index 000000000..65e35fc8c --- /dev/null +++ b/src/lib/corelib/tools/propagate_const.h @@ -0,0 +1,418 @@ +/**************************************************************************** +** MIT License +** +** Copyright (C) 2022-2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com +** Author: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> +** +** This file is part of KDToolBox (https://github.com/KDAB/KDToolBox). +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, ** and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice (including the next paragraph) +** shall be included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF ** CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +****************************************************************************/ + +#ifndef KDTOOLBOX_PROPAGATE_CONST +#define KDTOOLBOX_PROPAGATE_CONST + +#include <functional> +#include <type_traits> +#include <utility> + +// Our SFINAE is too noisy and clang-format gets confused. +// clang-format off + +namespace KDToolBox +{ + +template <typename T> +class propagate_const; + +namespace detail { +// Type traits to detect propagate_const specializations +template <typename T> +struct is_propagate_const : std::false_type +{ +}; + +template <typename T> +struct is_propagate_const<propagate_const<T>> : std::true_type +{ +}; + +// Using SFINAE in a base class to constrain the conversion operators. +// Otherwise, if we make them function templates and apply SFINAE directly on +// them, we would not be able to do certain conversions (cf. +// [over.ics.user/3]). +template <typename T> +using propagate_const_element_type = std::remove_reference_t<decltype(*std::declval<T &>())>; + +// Const conversion. +// NON-STANDARD: checks that `const T` is convertible, not `T`. +// See https://wg21.link/lwg3812 +template <typename T, + bool = std::disjunction_v< + std::is_pointer<T>, + std::is_convertible<const T, const propagate_const_element_type<T> *> + > + > +struct propagate_const_const_conversion_operator_base +{ +}; + +template <typename T> +struct propagate_const_const_conversion_operator_base<T, true> +{ + constexpr operator const propagate_const_element_type<T> *() const; +}; + +// Non-const conversion +template <typename T, + bool = std::disjunction_v< + std::is_pointer<T>, + std::is_convertible<T, propagate_const_element_type<T> *> + > + > +struct propagate_const_non_const_conversion_operator_base +{ +}; + +template <typename T> +struct propagate_const_non_const_conversion_operator_base<T, true> +{ + constexpr operator propagate_const_element_type<T> *(); +}; + +template <typename T> +struct propagate_const_base + : propagate_const_const_conversion_operator_base<T>, + propagate_const_non_const_conversion_operator_base<T> +{}; + +} // namespace detail + +/* + TODO: This code could benefit from a C++20 overhaul: + * concepts + * three-way comparisons + * explicit(bool) + + However we can't depend on C++20 yet... +*/ + +template <typename T> +class propagate_const : public detail::propagate_const_base<T> +{ +public: + using element_type = detail::propagate_const_element_type<T>; + + // Special member functions + propagate_const() = default; + propagate_const(propagate_const &&) = default; + propagate_const &operator=(propagate_const &&) = default; + propagate_const(const propagate_const &) = delete; + propagate_const &operator=(const propagate_const &) = delete; + ~propagate_const() = default; + + // Constructor from values + + template < + typename U, + std::enable_if_t< // This constructor is enabled if: + std::conjunction_v< + std::is_constructible<T, U>, // 1) we can build a T from a U, + std::negation<detail::is_propagate_const<std::decay_t<U>>>, // 2) we are not making a + // converting constructor, + std::is_convertible<U, T> // 3) and the conversion from U to T is implicit; + >, + bool> = true> + /* implicit */ // then, this constructor is implicit too. + propagate_const(U &&other) + : m_t(std::forward<U>(other)) + { + } + + template < + typename U, + std::enable_if_t< // This constructor is enabled if: + std::conjunction_v< + std::is_constructible<T, U>, // 1) we can build a T from a U, + std::negation<detail::is_propagate_const<std::decay_t<U>>>, // 2) we are not making a + // converting constructor, + std::negation<std::is_convertible<U, T>> // 3) and the conversion from U to T is + // explicit; + >, + bool> = true> + explicit // then, this constructor is explicit. + propagate_const(U &&other) + : m_t(std::forward<U>(other)) + { + } + + // Constructors from other propagate_const (converting constructors) + template <typename U, + std::enable_if_t< // This constructor is enabled if: + std::conjunction_v<std::is_constructible<T, U>, // 1) we can do the conversion, + std::is_convertible<U, T> // 2) and the conversion is implicit; + >, + bool> = true> + /* implicit */ // then, this constructor is implicit. + constexpr propagate_const(propagate_const<U> &&other) + : m_t(std::move(get_underlying(other))) + { + } + + template <typename U, + std::enable_if_t< // This constructor is enabled if: + std::conjunction_v<std::is_constructible<T, U>, // 1) we can do the conversion, + std::negation<std::is_convertible<U, T>> // 2) and the + // conversion is + // explicit; + >, + bool> = true> + explicit // then, this constructor is explicit. + constexpr propagate_const(propagate_const<U> &&other) + : m_t(std::move(get_underlying(other))) + { + } + + // Converting assignment + template <typename U, + std::enable_if_t< // This assignment operator is enabled if + std::is_convertible_v<U, T>, // the conversion from U to T is implicit + bool> = true> + constexpr propagate_const &operator=(propagate_const<U> &&other) + { + m_t = std::move(get_underlying(other)); + return *this; + } + + template <typename U, + std::enable_if_t< // This assignment operator is enabled if: + std::conjunction_v< + std::is_convertible<U, T>, // 1) the conversion from U to T is implicit, + std::negation<detail::is_propagate_const<std::decay_t<U>>> // 2) and U is not a + // propagate_const + >, + bool> = true> + constexpr propagate_const &operator=(U &&other) + { + m_t = std::forward<U>(other); + return *this; + } + + // Swap + constexpr void swap(propagate_const &other) noexcept(std::is_nothrow_swappable_v<T>) + { + using std::swap; + swap(m_t, other.m_t); + } + + // Const observers + constexpr explicit operator bool() const { return static_cast<bool>(m_t); } + + constexpr const element_type *get() const { return get_impl(m_t); } + + constexpr const element_type &operator*() const { return *get(); } + + constexpr const element_type *operator->() const { return get(); } + + // Non-const observers + constexpr element_type *get() { return get_impl(m_t); } + + constexpr element_type &operator*() { return *get(); } + + constexpr element_type *operator->() { return get(); } + + // Non-member utilities: extract the contained object + template <typename U> + friend constexpr auto &get_underlying(propagate_const<U> &p); + + template <typename U> + friend constexpr const auto &get_underlying(const propagate_const<U> &p); + +private: + // Implementation of get() that works with raw pointers and smart + // pointers. Similar to std::to_address, but to_address is C++20, + // and propagate_const spec does not match it. + template <typename U> + static constexpr element_type *get_impl(U *u) + { + return u; + } + + template <typename U> + static constexpr element_type *get_impl(U &u) + { + return u.get(); + } + + template <typename U> + static constexpr const element_type *get_impl(const U *u) + { + return u; + } + + template <typename U> + static constexpr const element_type *get_impl(const U &u) + { + return u.get(); + } + + T m_t; +}; + +// Swap +template <typename T, + std::enable_if_t<std::is_swappable_v<T>, bool> = true> +constexpr void swap(propagate_const<T> &lhs, propagate_const<T> &rhs) + noexcept(noexcept(lhs.swap(rhs))) +{ + lhs.swap(rhs); +} + +// Implementation of get_underlying +template <typename T> +constexpr auto &get_underlying(propagate_const<T> &p) +{ + return p.m_t; +} + +template <typename T> +constexpr const auto &get_underlying(const propagate_const<T> &p) +{ + return p.m_t; +} + +// Implementation of the conversion operators +template <typename T> +constexpr detail::propagate_const_const_conversion_operator_base<T, true> + ::operator const detail::propagate_const_element_type<T> *() const +{ + return static_cast<const propagate_const<T> *>(this)->get(); +} + +template <typename T> +constexpr detail::propagate_const_non_const_conversion_operator_base<T, true> + ::operator detail::propagate_const_element_type<T> *() +{ + return static_cast<propagate_const<T> *>(this)->get(); +} + +// Comparisons. As per spec, they're free function templates. + +// Comparisons against nullptr. +template <typename T> +constexpr bool operator==(const propagate_const<T> &p, std::nullptr_t) +{ + return get_underlying(p) == nullptr; +} + +template <typename T> +constexpr bool operator!=(const propagate_const<T> &p, std::nullptr_t) +{ + return get_underlying(p) != nullptr; +} + +template <typename T> +constexpr bool operator==(std::nullptr_t, const propagate_const<T> &p) +{ + return nullptr == get_underlying(p); +} + +template <typename T> +constexpr bool operator!=(std::nullptr_t, const propagate_const<T> &p) +{ + return nullptr == get_underlying(p); +} + +// Comparisons between propagate_const +#define DEFINE_PROPAGATE_CONST_COMPARISON_OP(op) \ +template <typename T, typename U> \ + constexpr bool operator op (const propagate_const<T> &lhs, const propagate_const<U> &rhs) \ +{ \ + return get_underlying(lhs) op get_underlying(rhs); \ +} \ + + DEFINE_PROPAGATE_CONST_COMPARISON_OP(==) + DEFINE_PROPAGATE_CONST_COMPARISON_OP(!=) + DEFINE_PROPAGATE_CONST_COMPARISON_OP(<) + DEFINE_PROPAGATE_CONST_COMPARISON_OP(<=) + DEFINE_PROPAGATE_CONST_COMPARISON_OP(>) + DEFINE_PROPAGATE_CONST_COMPARISON_OP(>=) + +#undef DEFINE_PROPAGATE_CONST_COMPARISON_OP + +// Comparisons against other (smart) pointers +#define DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(op) \ + template <typename T, typename U> \ + constexpr bool operator op (const propagate_const<T> &lhs, const U &rhs) \ +{ \ + return get_underlying(lhs) op rhs; \ +} \ + template <typename T, typename U> \ + constexpr bool operator op (const T &lhs, const propagate_const<U> &rhs) \ +{ \ + return lhs op get_underlying(rhs); \ +} \ + + DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(==) + DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(!=) + DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(<) + DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(<=) + DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(>) + DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(>=) + +#undef DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP + +} // namespace KDToolBox + +// std::hash specialization +namespace std +{ +template <typename T> +struct hash<KDToolBox::propagate_const<T>> +{ + constexpr size_t operator()(const KDToolBox::propagate_const<T> &t) const + noexcept(noexcept(hash<T>{}(get_underlying(t)))) + { + return hash<T>{}(get_underlying(t)); + } +}; + +#define DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(COMP) \ +template <typename T> \ + struct COMP<KDToolBox::propagate_const<T>> \ +{ \ + constexpr bool operator()(const KDToolBox::propagate_const<T> &lhs, \ + const KDToolBox::propagate_const<T> &rhs) \ + { \ + return COMP<T>{}(get_underlying(lhs), get_underlying(rhs)); \ + } \ +}; \ + + DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(equal_to) + DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(not_equal_to) + DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(less) + DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(greater) + DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(less_equal) + DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(greater_equal) + +#undef DEFINE_COMP_OBJECT_SPECIALIZATION + +} // namespace std + +#endif // KDTOOLBOX_PROPAGATE_CONST diff --git a/src/lib/corelib/tools/qbs_export.h b/src/lib/corelib/tools/qbs_export.h index 164aa4184..2cfe1cd82 100644 --- a/src/lib/corelib/tools/qbs_export.h +++ b/src/lib/corelib/tools/qbs_export.h @@ -53,14 +53,14 @@ #else # ifdef QBS_LIBRARY # define QBS_EXPORT QBS_DECL_EXPORT -# ifdef QBS_ENABLE_UNIT_TESTS +# ifdef QBS_WITH_TESTS # define QBS_AUTOTEST_EXPORT QBS_DECL_EXPORT # else # define QBS_AUTOTEST_EXPORT # endif # else # define QBS_EXPORT QBS_DECL_IMPORT -# ifdef QBS_ENABLE_UNIT_TESTS +# ifdef QBS_WITH_TESTS # define QBS_AUTOTEST_EXPORT QBS_DECL_IMPORT # else # define QBS_AUTOTEST_EXPORT diff --git a/src/lib/corelib/tools/qbsassert.h b/src/lib/corelib/tools/qbsassert.h index e8dfcacf8..65352fdb3 100644 --- a/src/lib/corelib/tools/qbsassert.h +++ b/src/lib/corelib/tools/qbsassert.h @@ -59,6 +59,11 @@ QBS_EXPORT void writeAssertLocation(const char *condition, const char *file, int // The do {} while (0) is here to enforce the use of a semicolon after QBS_ASSERT. // action can also be continue or break. Copied from qtcassert.h in Qt Creator. +#define QBS_GUARD(cond) \ + (Q_LIKELY(cond) \ + ? true \ + : (::qbs::Internal::writeAssertLocation(#cond, __FILE__, __LINE__), false)) + #define QBS_CHECK(cond)\ do {\ if (Q_LIKELY(cond)) {} else {\ diff --git a/src/lib/corelib/tools/qbspluginmanager.cpp b/src/lib/corelib/tools/qbspluginmanager.cpp index d4e92e22a..0816d9d25 100644 --- a/src/lib/corelib/tools/qbspluginmanager.cpp +++ b/src/lib/corelib/tools/qbspluginmanager.cpp @@ -79,7 +79,7 @@ QbsPluginManager::~QbsPluginManager() { unloadStaticPlugins(); - for (QLibrary * const lib : qAsConst(d->libs)) { + for (QLibrary * const lib : std::as_const(d->libs)) { auto unload = reinterpret_cast<QbsPluginUnloadFunction>(lib->resolve("QbsPluginUnload")); if (unload) unload(); diff --git a/src/lib/corelib/tools/qbsprocess.cpp b/src/lib/corelib/tools/qbsprocess.cpp index 52ce3f25a..3fdc05afe 100644 --- a/src/lib/corelib/tools/qbsprocess.cpp +++ b/src/lib/corelib/tools/qbsprocess.cpp @@ -65,7 +65,7 @@ void QbsProcess::start(const QString &command, const QStringList &arguments) { if (m_socketError) { m_error = QProcess::FailedToStart; - emit error(m_error); + emit errorOccurred(m_error); return; } m_command = command; @@ -95,7 +95,7 @@ void QbsProcess::cancel() m_errorString = Tr::tr("Process canceled before it was started."); m_error = QProcess::FailedToStart; m_state = QProcess::NotRunning; - emit error(m_error); + emit errorOccurred(m_error); break; case QProcess::Running: sendPacket(StopProcessPacket(token())); @@ -120,7 +120,7 @@ void QbsProcess::sendPacket(const LauncherPacket &packet) QByteArray QbsProcess::readAndClear(QByteArray &data) { - const QByteArray tmp = data; + QByteArray tmp = data; data.clear(); return tmp; } @@ -155,7 +155,7 @@ void QbsProcess::handleSocketError(const QString &message) if (m_state != QProcess::NotRunning) { m_state = QProcess::NotRunning; m_error = QProcess::FailedToStart; - emit error(m_error); + emit errorOccurred(m_error); } } @@ -166,7 +166,7 @@ void QbsProcess::handleErrorPacket(const QByteArray &packetData) m_error = packet.error; m_errorString = packet.errorString; m_state = QProcess::NotRunning; - emit error(m_error); + emit errorOccurred(m_error); } void QbsProcess::handleFinishedPacket(const QByteArray &packetData) diff --git a/src/lib/corelib/tools/qbsprocess.h b/src/lib/corelib/tools/qbsprocess.h index 2181818f0..3314e3d3c 100644 --- a/src/lib/corelib/tools/qbsprocess.h +++ b/src/lib/corelib/tools/qbsprocess.h @@ -69,7 +69,7 @@ public: QString errorString() const { return m_errorString; } signals: - void error(QProcess::ProcessError error); + void errorOccurred(QProcess::ProcessError error); void finished(int exitCode); private: diff --git a/src/lib/corelib/tools/qttools.cpp b/src/lib/corelib/tools/qttools.cpp index ffd336d56..40e0d0778 100644 --- a/src/lib/corelib/tools/qttools.cpp +++ b/src/lib/corelib/tools/qttools.cpp @@ -38,20 +38,42 @@ ****************************************************************************/ #include "qttools.h" +#include "porting.h" #include <QtCore/qprocess.h> +namespace std { + +size_t hash<QVariant>::operator()(const QVariant &v) const noexcept +{ + switch (v.userType()) { + case QMetaType::UnknownType: return 0; + case QMetaType::Bool: return std::hash<bool>()(v.toBool()); + case QMetaType::Int: return std::hash<int>()(v.toInt()); + case QMetaType::UInt: return std::hash<uint>()(v.toUInt()); + case QMetaType::QString: return std::hash<QString>()(v.toString()); + case QMetaType::QStringList: return std::hash<QStringList>()(v.toStringList()); + case QMetaType::QVariantList: return std::hash<QVariantList>()(v.toList()); + case QMetaType::QVariantMap: return std::hash<QVariantMap>()(v.toMap()); + case QMetaType::QVariantHash: return std::hash<QVariantHash>()(v.toHash()); + default: + QBS_ASSERT("Unsupported variant type" && false, return 0); + } +} + +} // namespace std + QT_BEGIN_NAMESPACE -uint qHash(const QStringList &list) +qbs::QHashValueType qHash(const QStringList &list) { - uint s = 0; + qbs::QHashValueType s = 0; for (const QString &n : list) s ^= qHash(n) + 0x9e3779b9 + (s << 6) + (s >> 2); return s; } -uint qHash(const QProcessEnvironment &env) +qbs::QHashValueType qHash(const QProcessEnvironment &env) { return qHash(env.toStringList()); } diff --git a/src/lib/corelib/tools/qttools.h b/src/lib/corelib/tools/qttools.h index c3b4d3a9f..029948be4 100644 --- a/src/lib/corelib/tools/qttools.h +++ b/src/lib/corelib/tools/qttools.h @@ -40,8 +40,14 @@ #ifndef QBSQTTOOLS_H #define QBSQTTOOLS_H +#include <tools/qbsassert.h> +#include <tools/porting.h> +#include <tools/stlutils.h> + #include <QtCore/qhash.h> #include <QtCore/qstringlist.h> +#include <QtCore/qtextstream.h> +#include <QtCore/qvariant.h> #include <functional> @@ -50,12 +56,6 @@ class QProcessEnvironment; QT_END_NAMESPACE namespace std { -#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) -template<> struct hash<QString> { - std::size_t operator()(const QString &s) const { return qHash(s); } -}; -#endif - template<typename T1, typename T2> struct hash<std::pair<T1, T2>> { size_t operator()(const pair<T1, T2> &x) const @@ -63,11 +63,92 @@ template<typename T1, typename T2> struct hash<std::pair<T1, T2>> return std::hash<T1>()(x.first) ^ std::hash<T2>()(x.second); } }; + +template <typename... Ts> +struct hash<std::tuple<Ts...>> +{ +private: + template<std::size_t... Ns> + static size_t helper(std::index_sequence<Ns...>, const std::tuple<Ts...> &tuple) noexcept + { + size_t seed = 0; + (qbs::Internal::hashCombineHelper(seed, std::get<Ns>(tuple)), ...); + return seed; + } + +public: + size_t operator()(const std::tuple<Ts...> & tuple) const noexcept + { + return helper(std::make_index_sequence<sizeof...(Ts)>(), tuple); + } +}; + + +template<> struct hash<QStringList> +{ + std::size_t operator()(const QStringList &s) const noexcept + { + return qbs::Internal::hashRange(s); + } +}; + +template<> struct hash<QVariant> +{ + size_t operator()(const QVariant &v) const noexcept; +}; + +template<> struct hash<QVariantList> +{ + size_t operator()(const QVariantList &v) const noexcept + { + return qbs::Internal::hashRange(v); + } +}; + +template<> struct hash<QVariantMap> +{ + size_t operator()(const QVariantMap &v) const noexcept + { + return qbs::Internal::hashRange(v); + } +}; + +template<> struct hash<QVariantHash> +{ + size_t operator()(const QVariantHash &v) const noexcept + { + return qbs::Internal::hashRange(v); + } +}; + } // namespace std QT_BEGIN_NAMESPACE -uint qHash(const QStringList &list); -uint qHash(const QProcessEnvironment &env); + +qbs::QHashValueType qHash(const QStringList &list); +qbs::QHashValueType qHash(const QProcessEnvironment &env); + +template<typename... Args> +qbs::QHashValueType qHash(const std::tuple<Args...> &tuple) +{ + return std::hash<std::tuple<Args...>>()(tuple) % std::numeric_limits<uint>::max(); +} + +inline qbs::QHashValueType qHash(const QVariant &v) +{ + return std::hash<QVariant>()(v) % std::numeric_limits<uint>::max(); +} + +inline qbs::QHashValueType qHash(const QVariantMap &v) +{ + return std::hash<QVariantMap>()(v) % std::numeric_limits<uint>::max(); +} + +inline qbs::QHashValueType qHash(const QVariantHash &v) +{ + return std::hash<QVariantHash>()(v) % std::numeric_limits<uint>::max(); +} + QT_END_NAMESPACE namespace qbs { @@ -75,14 +156,94 @@ namespace qbs { template <class T> QSet<T> toSet(const QList<T> &list) { -#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) - return list.toSet(); -#else return QSet<T>(list.begin(), list.end()); +} + +template<class T> +QList<T> toList(const QSet<T> &set) +{ + return QList<T>(set.begin(), set.end()); +} + +template<typename K, typename V> +QHash<K, V> &unite(QHash<K, V> &h, const QHash<K, V> &other) +{ + h.insert(other); + return h; +} + +inline void setupDefaultCodec(QTextStream &stream) +{ +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + stream.setCodec("UTF-8"); +#else + Q_UNUSED(stream); #endif } -} // namespace qbs +inline bool qVariantCanConvert(const QVariant &variant, int typeId) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + return variant.canConvert(QMetaType(typeId)); +#else + return variant.canConvert(typeId); // deprecated in Qt6 +#endif +} +inline bool qVariantConvert(QVariant &variant, int typeId) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + return variant.convert(QMetaType(typeId)); +#else + return variant.convert(typeId); // deprecated in Qt6 +#endif +} + +inline QMetaType::Type qVariantType(const QVariant &v) +{ + return static_cast<QMetaType::Type>( +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + v.metaType().id() +#else + v.type() +#endif + ); +} + +template<typename T> +inline QVariant typedNullVariant() +{ + const auto metaType = QMetaType::fromType<T>(); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + return QVariant(metaType, nullptr); +#else + return QVariant(static_cast<QVariant::Type>(metaType.id())); +#endif +} + +inline bool qVariantsEqual(const QVariant &v1, const QVariant &v2) +{ + return v1.isNull() == v2.isNull() && v1 == v2; +} + +inline bool qVariantMapsEqual(const QVariantMap &m1, const QVariantMap &m2) +{ + if (m1.size() != m2.size()) + return false; + if (m1.isSharedWith(m2)) + return true; + + auto it1 = m1.cbegin(); + auto it2 = m2.cbegin(); + while (it1 != m1.cend()) { + if (it1.key() != it2.key() || !qVariantsEqual(it1.value(), it2.value())) + return false; + ++it2; + ++it1; + } + return true; +} + +} // namespace qbs #endif // QBSQTTOOLS_H diff --git a/src/lib/corelib/tools/scripttools.cpp b/src/lib/corelib/tools/scripttools.cpp index adf930cf0..e89e4a0ad 100644 --- a/src/lib/corelib/tools/scripttools.cpp +++ b/src/lib/corelib/tools/scripttools.cpp @@ -39,9 +39,14 @@ #include "scripttools.h" -#include <QtCore/qdatastream.h> +#include <language/scriptengine.h> +#include <tools/error.h> -#include <QtScript/qscriptengine.h> +#include <QtCore/qdatastream.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsondocument.h> +#include <QtCore/qjsonobject.h> namespace qbs { namespace Internal { @@ -62,20 +67,262 @@ QVariant getConfigProperty(const QVariantMap &cfg, const QStringList &name) { if (name.length() == 1) return cfg.value(name.front()); - else - return getConfigProperty(cfg.value(name.front()).toMap(), name.mid(1)); + return getConfigProperty(cfg.value(name.front()).toMap(), name.mid(1)); } -TemporaryGlobalObjectSetter::TemporaryGlobalObjectSetter(const QScriptValue &object) +TemporaryGlobalObjectSetter::TemporaryGlobalObjectSetter( + ScriptEngine *engine, const JSValue &object) + : m_engine(engine), m_oldGlobalObject(engine->globalObject()) { - QScriptEngine *engine = object.engine(); - m_oldGlobalObject = engine->globalObject(); engine->setGlobalObject(object); } TemporaryGlobalObjectSetter::~TemporaryGlobalObjectSetter() { - m_oldGlobalObject.engine()->setGlobalObject(m_oldGlobalObject); + m_engine->setGlobalObject(m_oldGlobalObject); +} + +JsException::JsException(JsException &&other) noexcept + : m_ctx(other.m_ctx), m_exception(other.m_exception), + m_fallbackLocation(std::move(other.m_fallbackLocation)) +{ + other.m_exception = JS_NULL; +} + +JsException::~JsException() { JS_FreeValue(m_ctx, m_exception); } + +QString JsException::message() const +{ + if (JS_IsError(m_ctx, m_exception)) + return getJsStringProperty(m_ctx, m_exception, QStringLiteral("message")); + const QVariant v = getJsVariant(m_ctx, m_exception); + switch (static_cast<QMetaType::Type>(v.userType())) { + case QMetaType::QVariantMap: + return QString::fromUtf8(QJsonDocument(QJsonObject::fromVariantMap(v.toMap())) + .toJson(QJsonDocument::Indented)); + case QMetaType::QStringList: + return QString::fromUtf8(QJsonDocument(QJsonArray::fromStringList(v.toStringList())) + .toJson(QJsonDocument::Indented)); + case QMetaType::QVariantList: + return QString::fromUtf8(QJsonDocument(QJsonArray::fromVariantList(v.toList())) + .toJson(QJsonDocument::Indented)); + default: + return v.toString(); + } +} + +const QStringList JsException::stackTrace() const +{ + return getJsStringProperty(m_ctx, m_exception, QLatin1String("stack")) + .split(QLatin1Char('\n'), Qt::SkipEmptyParts); +} + +ErrorInfo JsException::toErrorInfo() const +{ + const QString msg = message(); + ErrorInfo e(msg, stackTrace()); + if (e.hasLocation() || !m_fallbackLocation.isValid()) + return e; + return {msg, m_fallbackLocation}; +} + +void defineJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop, JSValue val) +{ + JS_DefinePropertyValueStr(ctx, obj, prop.toUtf8().constData(), val, 0); +} + +JSValue getJsProperty(JSContext *ctx, JSValue obj, const QString &prop) +{ + return JS_GetPropertyStr(ctx, obj, prop.toUtf8().constData()); +} + +void setJsProperty(JSContext *ctx, JSValue obj, const QString &prop, JSValue val) +{ + if (JS_SetPropertyStr(ctx, obj, prop.toUtf8().constData(), val) <= 0) + qDebug() << "Oje!"; +} + +void setJsProperty(JSContext *ctx, JSValue obj, const QString &prop, const QString &val) +{ + setJsProperty(ctx, obj, prop, makeJsString(ctx, val)); +} + +void handleJsProperties(JSContext *ctx, JSValue obj, + const std::function<void (const JSAtom &, + const JSPropertyDescriptor &)> &handler) +{ + struct PropsHolder { + PropsHolder(JSContext *ctx) : ctx(ctx) {} + ~PropsHolder() { + for (uint32_t i = 0; i < count; ++i) + JS_FreeAtom(ctx, props[i].atom); + js_free(ctx, props); + } + JSContext * const ctx; + JSPropertyEnum *props = nullptr; + uint32_t count = 0; + } propsHolder(ctx); + JS_GetOwnPropertyNames(ctx, &propsHolder.props, &propsHolder.count, obj, JS_GPN_STRING_MASK); + for (uint32_t i = 0; i < propsHolder.count; ++i) { + JSPropertyDescriptor desc{0, JS_UNDEFINED, JS_UNDEFINED, JS_UNDEFINED}; + JS_GetOwnProperty(ctx, &desc, obj, propsHolder.props[i].atom); + const ScopedJsValueList valueHolder(ctx, {desc.value, desc.getter, desc.setter}); + handler(propsHolder.props[i].atom, desc); + } +} + +QString getJsString(JSContext *ctx, JSValueConst val) +{ + size_t strLen; + const char * const str = JS_ToCStringLen(ctx, &strLen, val); + QString s = QString::fromUtf8(str, strLen); + JS_FreeCString(ctx, str); + return s; +} + +QString getJsStringProperty(JSContext *ctx, JSValue obj, const QString &prop) +{ + const ScopedJsValue sv(ctx, getJsProperty(ctx, obj, prop)); + return getJsString(ctx, sv); +} + +int getJsIntProperty(JSContext *ctx, JSValue obj, const QString &prop) +{ + return JS_VALUE_GET_INT(getJsProperty(ctx, obj, prop)); +} + +bool getJsBoolProperty(JSContext *ctx, JSValue obj, const QString &prop) +{ + return JS_VALUE_GET_BOOL(getJsProperty(ctx, obj, prop)); +} + +JSValue makeJsArrayBuffer(JSContext *ctx, const QByteArray &s) +{ + return ScriptEngine::engineForContext(ctx)->asJsValue(s); +} + +JSValue makeJsString(JSContext *ctx, const QString &s) +{ + return ScriptEngine::engineForContext(ctx)->asJsValue(s); +} + +JSValue makeJsStringList(JSContext *ctx, const QStringList &l) +{ + return ScriptEngine::engineForContext(ctx)->asJsValue(l); +} + +JSValue throwError(JSContext *ctx, const QString &message) +{ + return JS_Throw(ctx, makeJsString(ctx, message)); +} + +QStringList getJsStringList(JSContext *ctx, JSValue val) +{ + if (JS_IsString(val)) + return {getJsString(ctx, val)}; + if (!JS_IsArray(ctx, val)) + return {}; + const int size = getJsIntProperty(ctx, val, QLatin1String("length")); + QStringList l; + for (int i = 0; i < size; ++i) { + const ScopedJsValue elem(ctx, JS_GetPropertyUint32(ctx, val, i)); + l << getJsString(ctx, elem); + } + return l; +} + +JSValue makeJsVariant(JSContext *ctx, const QVariant &v, quintptr id) +{ + return ScriptEngine::engineForContext(ctx)->asJsValue(v, id); +} + +JSValue makeJsVariantList(JSContext *ctx, const QVariantList &l, quintptr id) +{ + return ScriptEngine::engineForContext(ctx)->asJsValue(l, id); +} + +JSValue makeJsVariantMap(JSContext *ctx, const QVariantMap &m, quintptr id) +{ + return ScriptEngine::engineForContext(ctx)->asJsValue(m, id); +} + +static QVariant getJsVariantImpl(JSContext *ctx, JSValue val, QList<JSValue> path) +{ + if (JS_IsString(val)) + return getJsString(ctx, val); + if (JS_IsBool(val)) + return bool(JS_VALUE_GET_BOOL(val)); + if (JS_IsArrayBuffer(val)) { + size_t size = 0; + const auto data = JS_GetArrayBuffer(ctx, &size, val); + if (!data || !size) + return QByteArray(); + return QByteArray(reinterpret_cast<const char *>(data), size); + } + if (JS_IsArray(ctx, val)) { + if (path.contains(val)) + return {}; + path << val; + QVariantList l; + const int size = getJsIntProperty(ctx, val, QLatin1String("length")); + for (int i = 0; i < size; ++i) { + const ScopedJsValue sv(ctx, JS_GetPropertyUint32(ctx, val, i)); + l << getJsVariantImpl(ctx, sv, path); + } + return l; + } + if (JS_IsDate(val)) { + ScopedJsValue toString(ctx, getJsProperty(ctx, val, QLatin1String("toISOString"))); + if (!JS_IsFunction(ctx, toString)) + return {}; + ScopedJsValue dateString(ctx, JS_Call(ctx, toString, val, 0, nullptr)); + if (!JS_IsString(dateString)) + return {}; + return QDateTime::fromString(getJsString(ctx, dateString), Qt::ISODateWithMs); + } + if (JS_IsObject(val)) { + if (path.contains(val)) + return {}; + path << val; + QVariantMap map; + handleJsProperties(ctx, val, [ctx, &map, path](const JSAtom &prop, const JSPropertyDescriptor &desc) { + map.insert(getJsString(ctx, prop), getJsVariantImpl(ctx, desc.value, path)); + }); + return map; + } + const auto tag = JS_VALUE_GET_TAG(val); + if (tag == JS_TAG_INT) + return JS_VALUE_GET_INT(val); + else if (JS_TAG_IS_FLOAT64(tag)) + return JS_VALUE_GET_FLOAT64(val); + return {}; +} + +QVariant getJsVariant(JSContext *ctx, JSValue val) +{ + return getJsVariantImpl(ctx, val, {}); +} + +QStringList getJsStringListProperty(JSContext *ctx, JSValue obj, const QString &prop) +{ + JSValue array = getJsProperty(ctx, obj, prop); + QStringList list = getJsStringList(ctx, array); + JS_FreeValue(ctx, array); + return list; +} + +QVariant getJsVariantProperty(JSContext *ctx, JSValue obj, const QString &prop) +{ + const JSValue vv = getJsProperty(ctx, obj, prop); + QVariant v = getJsVariant(ctx, vv); + JS_FreeValue(ctx, vv); + return v; +} + +QString getJsString(JSContext *ctx, JSAtom atom) +{ + const ScopedJsValue atomString(ctx, JS_AtomToString(ctx, atom)); + return getJsString(ctx, atomString); } } // namespace Internal diff --git a/src/lib/corelib/tools/scripttools.h b/src/lib/corelib/tools/scripttools.h index 4a258b98f..ac7ed9928 100644 --- a/src/lib/corelib/tools/scripttools.h +++ b/src/lib/corelib/tools/scripttools.h @@ -40,57 +40,172 @@ #ifndef QBS_SCRIPTTOOLS_H #define QBS_SCRIPTTOOLS_H -#include <tools/qbs_export.h> +#include "codelocation.h" +#include "porting.h" +#include "qbs_export.h" +#include <quickjs.h> + +#include <QtCore/qhash.h> #include <QtCore/qstringlist.h> #include <QtCore/qvariant.h> -#include <QtScript/qscriptengine.h> -#include <QtScript/qscriptprogram.h> -#include <QtScript/qscriptvalue.h> +#include <utility> +#include <vector> namespace qbs { +class ErrorInfo; namespace Internal { +class ScriptEngine; -template <typename C> -QScriptValue toScriptValue(QScriptEngine *scriptEngine, const C &container) -{ - QScriptValue v = scriptEngine->newArray(container.size()); - int i = 0; - for (const typename C::value_type &item : container) - v.setProperty(i++, scriptEngine->toScriptValue(item)); - return v; -} +using JSValueList = std::vector<JSValue>; -void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value); -QVariant QBS_AUTOTEST_EXPORT getConfigProperty(const QVariantMap &cfg, const QStringList &name); +void defineJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop, JSValue val); +JSValue getJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop); +void setJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop, JSValue val); +void setJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop, const QString &val); +QString getJsStringProperty(JSContext *ctx, JSValueConst obj, const QString &prop); +QStringList getJsStringListProperty(JSContext *ctx, JSValueConst obj, const QString &prop); +int getJsIntProperty(JSContext *ctx, JSValueConst obj, const QString &prop); +bool getJsBoolProperty(JSContext *ctx, JSValueConst obj, const QString &prop); +QVariant getJsVariantProperty(JSContext *ctx, JSValueConst obj, const QString &prop); +QString getJsString(JSContext *ctx, JSValueConst val); +QString getJsString(JSContext *ctx, JSAtom atom); +QBS_AUTOTEST_EXPORT QVariant getJsVariant(JSContext *ctx, JSValueConst val); +JSValue makeJsArrayBuffer(JSContext *ctx, const QByteArray &s); +JSValue makeJsString(JSContext *ctx, const QString &s); +JSValue makeJsStringList(JSContext *ctx, const QStringList &l); +JSValue makeJsVariant(JSContext *ctx, const QVariant &v, quintptr id = 0); +JSValue makeJsVariantList(JSContext *ctx, const QVariantList &l, quintptr id = 0); +JSValue makeJsVariantMap(JSContext *ctx, const QVariantMap &m, quintptr id = 0); +QStringList getJsStringList(JSContext *ctx, JSValueConst val); +JSValue throwError(JSContext *ctx, const QString &message); +using PropertyHandler = std::function<void(const JSAtom &, const JSPropertyDescriptor &)>; +void handleJsProperties(JSContext *ctx, JSValueConst obj, const PropertyHandler &handler); +inline quintptr jsObjectId(const JSValue &val) { return quintptr(JS_VALUE_GET_OBJ(val)); } template <class T> -void attachPointerTo(QScriptValue &scriptValue, T *ptr) +void attachPointerTo(JSValue &scriptValue, T *ptr) { - QVariant v; - v.setValue<quintptr>(reinterpret_cast<quintptr>(ptr)); - scriptValue.setData(scriptValue.engine()->newVariant(v)); + JS_SetOpaque(scriptValue, const_cast<std::remove_const_t<T> *>(ptr)); } template <class T> -T *attachedPointer(const QScriptValue &scriptValue) +T *attachedPointer(const JSValue &scriptValue, JSClassID id) { - const auto ptr = scriptValue.data().toVariant().value<quintptr>(); - return reinterpret_cast<T *>(ptr); + return reinterpret_cast<T *>(JS_GetOpaque(scriptValue, id)); } class TemporaryGlobalObjectSetter { public: - TemporaryGlobalObjectSetter(const QScriptValue &object); + TemporaryGlobalObjectSetter(ScriptEngine *engine, const JSValue &object); ~TemporaryGlobalObjectSetter(); private: - QScriptValue m_oldGlobalObject; + ScriptEngine * const m_engine; + const JSValue m_oldGlobalObject; +}; + +class ScopedJsValue +{ +public: + ScopedJsValue(JSContext *ctx, JSValue v) : m_context(ctx), m_value(v) {} + void setValue(JSValue v) { JS_FreeValue(m_context, m_value); m_value = v; } + ~ScopedJsValue() { JS_FreeValue(m_context, m_value); } + operator JSValue() const { return m_value; } + JSValue release() { const JSValue v = m_value; m_value = JS_UNDEFINED; return v; } + void reset(JSValue v) { JS_FreeValue(m_context, m_value); m_value = v; } + + ScopedJsValue(const ScopedJsValue &) = delete; + ScopedJsValue &operator=(const ScopedJsValue &) = delete; + + ScopedJsValue(ScopedJsValue && other) : m_context(other.m_context), m_value(other.m_value) + { + other.m_value = JS_UNDEFINED; + } + +private: + JSContext * const m_context; + JSValue m_value; }; +class ScopedJsValueList +{ +public: + ScopedJsValueList(JSContext *ctx, const JSValueList &l) : m_context(ctx), m_list(l) {} + ~ScopedJsValueList() { for (const JSValue v : m_list) JS_FreeValue(m_context, v); } + operator JSValueList() const { return m_list; } + + ScopedJsValueList(const ScopedJsValueList &) = delete; + ScopedJsValueList &operator=(const ScopedJsValueList &) = delete; + + ScopedJsValueList(ScopedJsValueList && other) noexcept + : m_context(other.m_context), m_list(std::move(other.m_list)) + { + other.m_list.clear(); + } + +private: + JSContext * const m_context; + JSValueList m_list; +}; + +class ScopedJsAtom +{ +public: + ScopedJsAtom(JSContext *ctx, JSAtom a) : m_context(ctx), m_atom(a) {} + ScopedJsAtom(JSContext *ctx, const QByteArray &s) + : ScopedJsAtom(ctx, JS_NewAtom(ctx, s.constData())) {} + ScopedJsAtom(JSContext *ctx, const QString &s) : ScopedJsAtom(ctx, s.toUtf8()) {} + ~ScopedJsAtom() { JS_FreeAtom(m_context, m_atom); } + operator JSAtom() const { return m_atom; } + + ScopedJsAtom(const ScopedJsAtom &) = delete; + ScopedJsAtom&operator=(const ScopedJsAtom &) = delete; + + ScopedJsAtom(ScopedJsAtom &&other) : m_context(other.m_context), m_atom(other.m_atom) + { + other.m_atom = 0; + } + +private: + JSContext * const m_context; + JSAtom m_atom; +}; + +class QBS_AUTOTEST_EXPORT JsException +{ +public: + JsException(JSContext *ctx, JSValue ex, const CodeLocation &fallbackLocation) + : m_ctx(ctx), m_exception(ex), m_fallbackLocation(fallbackLocation) {} + JsException(JsException && other) noexcept; + ~JsException(); + JsException(const JsException &) = delete; + JsException &operator=(const JsException &) = delete; + + operator bool() const { return !JS_IsNull(m_exception); } + QString message() const; + const QStringList stackTrace() const; + ErrorInfo toErrorInfo() const; +private: + JSContext *m_ctx; + JSValue m_exception; + CodeLocation m_fallbackLocation; +}; + +void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value); +QVariant QBS_AUTOTEST_EXPORT getConfigProperty(const QVariantMap &cfg, const QStringList &name); + } // namespace Internal } // namespace qbs +// Only to be used for objects! +#ifndef JS_NAN_BOXING +inline bool operator==(JSValue v1, JSValue v2) { return v1.u.ptr == v2.u.ptr; } +inline bool operator!=(JSValue v1, JSValue v2) { return !(v1 == v2); } +inline bool operator<(JSValue v1, JSValue v2) { return v1.u.ptr < v2.u.ptr; } +inline qbs::QHashValueType qHash(const JSValue &v) { return QT_PREPEND_NAMESPACE(qHash)(v.u.ptr); } +#endif + #endif // QBS_SCRIPTTOOLS_H diff --git a/src/lib/corelib/tools/set.h b/src/lib/corelib/tools/set.h index d2af77d73..461175615 100644 --- a/src/lib/corelib/tools/set.h +++ b/src/lib/corelib/tools/set.h @@ -42,6 +42,7 @@ #include <tools/dynamictypecheck.h> #include <tools/persistence.h> +#include <tools/stlutils.h> #ifdef QT_CORE_LIB #include <QtCore/qstringlist.h> @@ -100,6 +101,8 @@ public: Set() = default; Set(const std::initializer_list<T> &list); + template<typename InputIterator> + Set(InputIterator first, InputIterator last); Set &unite(const Set &other); Set &operator+=(const Set &other) { return unite(other); } @@ -112,8 +115,8 @@ public: Set &operator&=(const Set &other) { return intersect(other); } Set &operator&=(const T &v) { return intersect(Set{ v }); } - iterator find(const T &v) { return std::find(m_data.begin(), m_data.end(), v); } - const_iterator find(const T &v) const { return std::find(m_data.cbegin(), m_data.cend(), v); } + iterator find(const T &v) { return binaryFind(m_data.begin(), m_data.end(), v); } + const_iterator find(const T &v) const { return binaryFind(m_data.cbegin(), m_data.cend(), v); } std::pair<iterator, bool> insert(const T &v); Set &operator+=(const T &v) { insert(v); return *this; } Set &operator|=(const T &v) { return operator+=(v); } @@ -143,15 +146,8 @@ public: QStringList toStringList() const; QString toString(const T& value) const { return value.toString(); } QString toString() const; - - static Set<T> fromList(const QList<T> &list); - QList<T> toList() const; #endif - static Set<T> fromStdVector(const std::vector<T> &vector); - static Set<T> fromStdSet(const std::set<T> &set); - std::set<T> toStdSet() const; - template<typename U> static Set<T> filtered(const Set<U> &s); bool operator==(const Set &other) const { return m_data == other.m_data; } @@ -177,6 +173,15 @@ template<typename T> Set<T>::Set(const std::initializer_list<T> &list) : m_data( m_data.erase(last, m_data.end()); } +template<typename T> +template<typename InputIterator> +Set<T>::Set(InputIterator first, InputIterator last) +{ + reserveIfForwardIterator(&m_data, first, last); + std::copy(first, last, std::back_inserter(m_data)); + sort(); +} + template<typename T> Set<T> &Set<T>::intersect(const Set<T> &other) { auto it = begin(); @@ -287,11 +292,7 @@ template<typename T> void Set<T>::store(PersistentPool &pool) const #ifdef QT_CORE_LIB template<typename T> QStringList Set<T>::toStringList() const { - QStringList sl; - sl.reserve(int(size())); - std::transform(cbegin(), cend(), std::back_inserter(sl), - [this](const T &e) { return toString(e); }); - return sl; + return transformed<QStringList>(*this, [this](const T &e) { return toString(e); }); } template<typename T> QString Set<T>::toString() const @@ -301,45 +302,8 @@ template<typename T> QString Set<T>::toString() const template<> inline QString Set<QString>::toString(const QString &value) const { return value; } -template<typename T> Set<T> Set<T>::fromList(const QList<T> &list) -{ - Set<T> s; - std::copy(list.cbegin(), list.cend(), std::back_inserter(s.m_data)); - s.sort(); - return s; -} - -template<typename T> QList<T> Set<T>::toList() const -{ - QList<T> list; - std::copy(m_data.cbegin(), m_data.cend(), std::back_inserter(list)); - return list; -} #endif -template<typename T> Set<T> Set<T>::fromStdVector(const std::vector<T> &vector) -{ - Set<T> s; - std::copy(vector.cbegin(), vector.cend(), std::back_inserter(s.m_data)); - s.sort(); - return s; -} - -template<typename T> Set<T> Set<T>::fromStdSet(const std::set<T> &set) -{ - Set<T> s; - std::copy(set.cbegin(), set.cend(), std::back_inserter(s.m_data)); - return s; -} - -template<typename T> std::set<T> Set<T>::toStdSet() const -{ - std::set<T> set; - for (auto it = cbegin(); it != cend(); ++it) - set.insert(*it); - return set; -} - template<typename T> typename Set<T>::iterator Set<T>::asMutableIterator(typename Set<T>::const_iterator cit) { @@ -349,8 +313,8 @@ typename Set<T>::iterator Set<T>::asMutableIterator(typename Set<T>::const_itera template<typename T> template<typename U> Set<T> Set<T>::filtered(const Set<U> &s) { - static_assert(std::is_pointer<T>::value, "Set::filtered() assumes pointer types"); - static_assert(std::is_pointer<U>::value, "Set::filtered() assumes pointer types"); + static_assert(std::is_pointer_v<T>, "Set::filtered() assumes pointer types"); + static_assert(std::is_pointer_v<U>, "Set::filtered() assumes pointer types"); Set<T> filteredSet; for (auto &u : s) { if (hasDynamicType<std::remove_pointer_t<T>>(u)) diff --git a/src/lib/corelib/tools/settings.cpp b/src/lib/corelib/tools/settings.cpp index 7312cd622..4a3d9d727 100644 --- a/src/lib/corelib/tools/settings.cpp +++ b/src/lib/corelib/tools/settings.cpp @@ -91,8 +91,8 @@ Settings::Settings(const QString &baseDir) : Settings(baseDir, systemSettingsBas Settings::Settings(const QString &baseDir, const QString &systemBaseDir) : m_settings(SettingsCreator(baseDir).getQSettings()), - m_systemSettings(new QSettings(systemBaseDir + QStringLiteral("/qbs.conf"), - QSettings::IniFormat)), + m_systemSettings(std::make_unique<QSettings>(systemBaseDir + QStringLiteral("/qbs.conf"), + QSettings::IniFormat)), m_baseDir(baseDir) { // Actual qbs settings are stored transparently within a group, because QSettings @@ -100,11 +100,7 @@ Settings::Settings(const QString &baseDir, const QString &systemBaseDir) m_settings->beginGroup(QStringLiteral("org/qt-project/qbs")); } -Settings::~Settings() -{ - delete m_settings; - delete m_systemSettings; -} +Settings::~Settings() = default; QVariant Settings::value(const QString &key, Scopes scopes, const QVariant &defaultValue) const { @@ -121,9 +117,9 @@ QVariant Settings::value(const QString &key, Scopes scopes, const QVariant &defa } if (!systemValue.isValid()) return userValue; - if (static_cast<QMetaType::Type>(userValue.type()) == QMetaType::QStringList) + if (static_cast<QMetaType::Type>(userValue.userType()) == QMetaType::QStringList) return userValue.toStringList() + systemValue.toStringList(); - if (static_cast<QMetaType::Type>(userValue.type()) == QMetaType::QVariantList) + if (static_cast<QMetaType::Type>(userValue.userType()) == QMetaType::QVariantList) return userValue.toList() + systemValue.toList(); return userValue; } @@ -139,13 +135,22 @@ QStringList Settings::allKeys(Scopes scopes) const return keys; } -QStringList Settings::directChildren(const QString &parentGroup, Scope scope) const +QStringList Settings::directChildren(const QString &parentGroup, Scopes scopes) const { - QSettings * const settings = settingsForScope(scope); - settings->beginGroup(internalRepresentation(parentGroup)); - QStringList children = settings->childGroups(); - children << settings->childKeys(); - settings->endGroup(); + auto helper = [this, &parentGroup](const Scope scope) { + QSettings * const settings = settingsForScope(scope); + settings->beginGroup(internalRepresentation(parentGroup)); + QStringList children = settings->childGroups(); + children << settings->childKeys(); + settings->endGroup(); + return children; + }; + + QStringList children; + if (scopes & UserScope) + children += helper(UserScope); + if (scopes & SystemScope) + children += helper(SystemScope); fixupKeys(children); return children; } @@ -241,7 +246,7 @@ void Settings::fixupKeys(QStringList &keys) const QSettings *Settings::settingsForScope(Settings::Scope scope) const { - return scope == UserScope ? m_settings : m_systemSettings; + return scope == UserScope ? m_settings.get() : m_systemSettings.get(); } QSettings *Settings::targetForWriting() const diff --git a/src/lib/corelib/tools/settings.h b/src/lib/corelib/tools/settings.h index d4de08490..7c9c52c9e 100644 --- a/src/lib/corelib/tools/settings.h +++ b/src/lib/corelib/tools/settings.h @@ -43,11 +43,13 @@ #include "qbs_export.h" #include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> #include <QtCore/qvariant.h> +#include <memory> + QT_BEGIN_NAMESPACE class QSettings; -class QStringList; QT_END_NAMESPACE namespace qbs { @@ -68,7 +70,7 @@ public: QVariant value(const QString &key, Scopes scopes, const QVariant &defaultValue = QVariant()) const; QStringList allKeys(Scopes scopes) const; - QStringList directChildren(const QString &parentGroup, Scope scope) const; // Keys and groups. + QStringList directChildren(const QString &parentGroup, Scopes scopes) const; // Keys and groups. QStringList allKeysWithPrefix(const QString &group, Scopes scopes) const; void setValue(const QString &key, const QVariant &value); void remove(const QString &key); @@ -94,8 +96,8 @@ private: QSettings *targetForWriting() const; void checkForWriteError(); - QSettings * const m_settings; - QSettings * const m_systemSettings; + const std::unique_ptr<QSettings> m_settings; + const std::unique_ptr<QSettings> m_systemSettings; const QString m_baseDir; Scope m_scopeForWriting = UserScope; }; diff --git a/src/lib/corelib/tools/settingscreator.cpp b/src/lib/corelib/tools/settingscreator.cpp index b7c727cd0..2cef0e5ce 100644 --- a/src/lib/corelib/tools/settingscreator.cpp +++ b/src/lib/corelib/tools/settingscreator.cpp @@ -53,23 +53,35 @@ namespace qbs { namespace Internal { +namespace { +QString getBaseDir(QString baseDir) { + if (!baseDir.isEmpty()) + return baseDir; + + const char key[] = "QBS_SETTINGS_DIR"; + if (qEnvironmentVariableIsSet(key)) + return QLatin1String(qgetenv(key)); + + return {}; +} +} // namespace + static QSettings::Format format() { return HostOsInfo::isWindowsHost() ? QSettings::IniFormat : QSettings::NativeFormat; } - SettingsCreator::SettingsCreator(QString baseDir) - : m_settingsBaseDir(std::move(baseDir)) + : m_settingsBaseDir(getBaseDir(std::move(baseDir))) , m_qbsVersion(Version::fromString(QLatin1String(QBS_VERSION))) { } -QSettings *SettingsCreator::getQSettings() +std::unique_ptr<QSettings> SettingsCreator::getQSettings() { createQSettings(); migrate(); - return m_settings.release(); + return std::move(m_settings); } void SettingsCreator::migrate() @@ -84,46 +96,15 @@ void SettingsCreator::migrate() QString oldSettingsDir = m_settingsBaseDir; if (thePredecessor.isValid()) oldSettingsDir.append(QLatin1String("/qbs/")).append(thePredecessor.toString()); - QString oldProfilesDir = oldSettingsDir; - if (!thePredecessor.isValid()) - oldProfilesDir += QLatin1String("/qbs"); - oldProfilesDir += QLatin1String("/profiles"); - const QString newProfilesDir = m_newSettingsDir + QLatin1String("/profiles"); - QString errorMessage; - if (QFileInfo(oldProfilesDir).exists() - && !copyFileRecursion(oldProfilesDir, newProfilesDir, false, true, &errorMessage)) { - qWarning() << "Error in settings migration: " << errorMessage; - } const QString oldSettingsFilePath = oldSettingsDir + QLatin1Char('/') + m_settingsFileName; - if (QFileInfo(oldSettingsFilePath).exists() + if (QFileInfo::exists(oldSettingsFilePath) && (!QDir::root().mkpath(m_newSettingsDir) || !QFile::copy(oldSettingsFilePath, m_newSettingsFilePath))) { qWarning() << "Error in settings migration: Could not copy" << oldSettingsFilePath << "to" << m_newSettingsFilePath; } - // Adapt all paths in settings that point to the old location. At the time of this writing, - // that's only preferences.qbsSearchPaths as written by libqtprofilesetup, but we don't want - // to hardcode that here. m_settings = std::make_unique<QSettings>(m_newSettingsFilePath, format()); - const auto allKeys = m_settings->allKeys(); - for (const QString &key : allKeys) { - QVariant v = m_settings->value(key); - if (v.type() == QVariant::String) { - QString s = v.toString(); - if (s.contains(oldProfilesDir)) - m_settings->setValue(key, s.replace(oldProfilesDir, newProfilesDir)); - } else if (v.type() == QVariant::StringList) { - const QStringList oldList = v.toStringList(); - QStringList newList; - for (const QString &oldString : oldList) { - QString newString = oldString; - newList << newString.replace(oldProfilesDir, newProfilesDir); - } - if (newList != oldList) - m_settings->setValue(key, newList); - } - } } void SettingsCreator::createQSettings() diff --git a/src/lib/corelib/tools/settingscreator.h b/src/lib/corelib/tools/settingscreator.h index 39da80a7f..ab491105c 100644 --- a/src/lib/corelib/tools/settingscreator.h +++ b/src/lib/corelib/tools/settingscreator.h @@ -58,7 +58,7 @@ class SettingsCreator public: SettingsCreator(QString baseDir); - QSettings *getQSettings(); + std::unique_ptr<QSettings> getQSettings(); private: void migrate(); diff --git a/src/lib/corelib/tools/settingsmodel.cpp b/src/lib/corelib/tools/settingsmodel.cpp index 7283e959c..4fa0e14da 100644 --- a/src/lib/corelib/tools/settingsmodel.cpp +++ b/src/lib/corelib/tools/settingsmodel.cpp @@ -42,6 +42,7 @@ #include <tools/profile.h> #include <tools/qttools.h> #include <tools/settings.h> +#include <tools/stlutils.h> #include <tools/stringconstants.h> #include <QtCore/qlist.h> @@ -77,7 +78,7 @@ QString Node::uniqueChildName() const bool unique; do { unique = true; - for (const Node *childNode : qAsConst(children)) { + for (const Node *childNode : std::as_const(children)) { if (childNode->name == newName) { unique = false; newName += QLatin1Char('_'); @@ -90,11 +91,9 @@ QString Node::uniqueChildName() const bool Node::hasDirectChildWithName(const QString &name) const { - for (const Node * const child : qAsConst(children)) { - if (child->name == name) - return true; - } - return false; + return Internal::any_of(children, [&name](const auto &child){ + return child->name == name; + }); } } // namespace Internal @@ -123,17 +122,15 @@ public: }; SettingsModel::SettingsModel(const QString &settingsDir, Settings::Scope scope, QObject *parent) - : QAbstractItemModel(parent), d(new SettingsModelPrivate) + : QAbstractItemModel(parent), + d(std::make_unique<SettingsModelPrivate>()) { d->settings = std::make_unique<qbs::Settings>(settingsDir); d->settings->setScopeForWriting(scope); d->readSettings(); } -SettingsModel::~SettingsModel() -{ - delete d; -} +SettingsModel::~SettingsModel() = default; void SettingsModel::reload() { @@ -284,10 +281,10 @@ bool SettingsModel::setData(const QModelIndex &index, const QVariant &value, int const QString valueString = value.toString(); QString *toChange = nullptr; if (index.column() == keyColumn() && !valueString.isEmpty() - && !node->parent->hasDirectChildWithName(valueString) - && !(node->parent->parent == &d->rootNode - && node->parent->name == Internal::StringConstants::profilesSettingsKey() - && valueString == Profile::fallbackName())) { + && !node->parent->hasDirectChildWithName(valueString) + && (node->parent->parent != &d->rootNode + || node->parent->name != Internal::StringConstants::profilesSettingsKey() + || valueString != Profile::fallbackName())) { toChange = &node->name; } else if (index.column() == valueColumn() && valueString != node->value) { toChange = &node->value; @@ -332,7 +329,7 @@ void SettingsModel::SettingsModelPrivate::readSettings() addNodeFromSettings(&rootNode, topLevelKey); for (QVariantMap::ConstIterator it = additionalProperties.constBegin(); it != additionalProperties.constEnd(); ++it) { - const QStringList nameAsList = it.key().split(QLatin1Char('.'), QString::SkipEmptyParts); + const QStringList nameAsList = it.key().split(QLatin1Char('.'), Qt::SkipEmptyParts); addNode(&rootNode, nameAsList.front(), nameAsList.mid(1), it.value()); } dirty = false; @@ -364,7 +361,7 @@ void SettingsModel::SettingsModelPrivate::addNode(qbs::Internal::Node *parentNod const QString ¤tNamePart, const QStringList &restOfName, const QVariant &value) { Node *currentNode = nullptr; - for (Node * const n : qAsConst(parentNode->children)) { + for (Node * const n : std::as_const(parentNode->children)) { if (n->name == currentNamePart) { currentNode = n; break; @@ -388,7 +385,7 @@ void SettingsModel::SettingsModelPrivate::doSave(const Node *node, const QString } const QString newPrefix = prefix + node->name + QLatin1Char('.'); - for (const Node * const child : qAsConst(node->children)) + for (const Node * const child : std::as_const(node->children)) doSave(child, newPrefix); } diff --git a/src/lib/corelib/tools/settingsmodel.h b/src/lib/corelib/tools/settingsmodel.h index 1bd59737c..63651d300 100644 --- a/src/lib/corelib/tools/settingsmodel.h +++ b/src/lib/corelib/tools/settingsmodel.h @@ -46,6 +46,8 @@ #include <QtCore/qabstractitemmodel.h> #include <QtCore/qvariant.h> +#include <memory> + namespace qbs { class QBS_EXPORT SettingsModel : public QAbstractItemModel @@ -81,7 +83,7 @@ public: private: class SettingsModelPrivate; - SettingsModelPrivate * const d; + const std::unique_ptr<SettingsModelPrivate> d; }; } // namespace qbs diff --git a/src/lib/corelib/tools/settingsrepresentation.cpp b/src/lib/corelib/tools/settingsrepresentation.cpp index 256c60c0e..6dfd81beb 100644 --- a/src/lib/corelib/tools/settingsrepresentation.cpp +++ b/src/lib/corelib/tools/settingsrepresentation.cpp @@ -41,8 +41,8 @@ #include "jsliterals.h" -#include <QtScript/qscriptengine.h> -#include <QtScript/qscriptvalue.h> +#include <language/scriptengine.h> +#include <logging/logger.h> namespace qbs { @@ -54,11 +54,19 @@ QString settingsValueToRepresentation(const QVariant &value) static QVariant variantFromString(const QString &str, bool &ok) { // ### use Qt5's JSON reader at some point. - QScriptEngine engine; - QScriptValue sv = engine.evaluate(QLatin1String("(function(){return ") - + str + QLatin1String(";})()")); - ok = !sv.isError(); - return sv.toVariant(); + class DummyLogSink : public ILogSink { + void doPrintMessage(LoggerLevel, const QString &, const QString &) override { } + } logSink; + Internal::Logger logger(&logSink); + + const auto engine = Internal::ScriptEngine::create(logger, {}); + Internal::ScopedJsValue sv( + engine->context(), + engine->evaluate(Internal::JsValueOwner::Caller, + QLatin1String("(function(){return ") + str + + QLatin1String(";})()"))); + ok = !engine->checkAndClearException({}); + return Internal::getJsVariant(engine->context(), sv); } QVariant representationToSettingsValue(const QString &representation) @@ -67,8 +75,8 @@ QVariant representationToSettingsValue(const QString &representation) QVariant variant = variantFromString(representation, ok); // We have no floating-point properties, so this is most likely intended to be a string. - if (static_cast<QMetaType::Type>(variant.type()) == QMetaType::Float - || static_cast<QMetaType::Type>(variant.type()) == QMetaType::Double) { + if (static_cast<QMetaType::Type>(variant.userType()) == QMetaType::Float + || static_cast<QMetaType::Type>(variant.userType()) == QMetaType::Double) { variant = variantFromString(QLatin1Char('"') + representation + QLatin1Char('"'), ok); } diff --git a/src/lib/corelib/tools/setupprojectparameters.cpp b/src/lib/corelib/tools/setupprojectparameters.cpp index 996f6b273..e9212c165 100644 --- a/src/lib/corelib/tools/setupprojectparameters.cpp +++ b/src/lib/corelib/tools/setupprojectparameters.cpp @@ -38,6 +38,8 @@ ****************************************************************************/ #include "setupprojectparameters.h" +#include "buildoptions.h" + #include <logging/logger.h> #include <logging/translator.h> #include <tools/buildgraphlocker.h> @@ -45,8 +47,10 @@ #include <tools/jsonhelper.h> #include <tools/profile.h> #include <tools/qbsassert.h> +#include <tools/qttools.h> #include <tools/scripttools.h> #include <tools/settings.h> +#include <tools/stringconstants.h> #include <QtCore/qdir.h> #include <QtCore/qfileinfo.h> @@ -89,21 +93,24 @@ public: mutable QVariantMap buildConfigurationTree; mutable QVariantMap overriddenValuesTree; mutable QVariantMap finalBuildConfigTree; + int maxJobCount = 0; bool overrideBuildGraphData; bool dryRun; bool logElapsedTime; bool forceProbeExecution; bool waitLockBuildGraph; - bool fallbackProviderEnabled = true; SetupProjectParameters::RestoreBehavior restoreBehavior; ErrorHandlingMode propertyCheckingMode; ErrorHandlingMode productErrorMode; + DeprecationWarningMode deprecationWarningMode = defaultDeprecationWarningMode(); QProcessEnvironment environment; }; } // namespace Internal -SetupProjectParameters::SetupProjectParameters() : d(new Internal::SetupProjectParametersPrivate) +using namespace Internal; + +SetupProjectParameters::SetupProjectParameters() : d(new SetupProjectParametersPrivate) { } @@ -125,6 +132,11 @@ template<> ErrorHandlingMode fromJson(const QJsonValue &v) return ErrorHandlingMode::Strict; } +template<> DeprecationWarningMode fromJson(const QJsonValue &v) +{ + return deprecationWarningModeFromName(v.toString()); +} + template<> SetupProjectParameters::RestoreBehavior fromJson(const QJsonValue &v) { const QString value = v.toString(); @@ -145,15 +157,18 @@ SetupProjectParameters SetupProjectParameters::fromJson(const QJsonObject &data) setValueFromJson(params.d->projectFilePath, data, "project-file-path"); setValueFromJson(params.d->buildRoot, data, "build-root"); setValueFromJson(params.d->settingsBaseDir, data, "settings-directory"); + setValueFromJson(params.d->maxJobCount, data, "max-job-count"); + if (params.maxJobCount() <= 0) + params.setMaxJobCount(BuildOptions::defaultMaxJobCount()); setValueFromJson(params.d->overriddenValues, data, "overridden-properties"); setValueFromJson(params.d->dryRun, data, "dry-run"); setValueFromJson(params.d->logElapsedTime, data, "log-time"); setValueFromJson(params.d->forceProbeExecution, data, "force-probe-execution"); setValueFromJson(params.d->waitLockBuildGraph, data, "wait-lock-build-graph"); - setValueFromJson(params.d->fallbackProviderEnabled, data, "fallback-provider-enabled"); setValueFromJson(params.d->environment, data, "environment"); setValueFromJson(params.d->restoreBehavior, data, "restore-behavior"); setValueFromJson(params.d->propertyCheckingMode, data, "error-handling-mode"); + setValueFromJson(params.d->deprecationWarningMode, data, "deprecation-warning-mode"); params.d->productErrorMode = params.d->propertyCheckingMode; return params; } @@ -219,6 +234,44 @@ void SetupProjectParameters::setProjectFilePath(const QString &projectFilePath) d->projectFilePath = canonicalProjectFilePath; } +void SetupProjectParameters::finalizeProjectFilePath() +{ + QString filePath = projectFilePath(); + if (filePath.isEmpty()) + filePath = QDir::currentPath(); + const QFileInfo projectFileInfo(filePath); + if (!projectFileInfo.exists()) + throw ErrorInfo(Tr::tr("Project file '%1' cannot be found.").arg(filePath)); + if (projectFileInfo.isRelative()) + filePath = projectFileInfo.absoluteFilePath(); + if (projectFileInfo.isFile()) { + setProjectFilePath(filePath); + return; + } + if (!projectFileInfo.isDir()) + throw ErrorInfo(Tr::tr("Project file '%1' has invalid type.").arg(filePath)); + + const QStringList &actualFileNames + = QDir(filePath).entryList(StringConstants::qbsFileWildcards(), QDir::Files); + if (actualFileNames.empty()) { + QString error; + if (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(filePath); + throw ErrorInfo(error); + } + if (actualFileNames.size() > 1) { + throw ErrorInfo(Tr::tr("More than one project file found in directory '%1'.") + .arg(filePath)); + } + filePath.append(QLatin1Char('/')).append(actualFileNames.front()); + + filePath = QDir::current().filePath(filePath); + filePath = QDir::cleanPath(filePath); + setProjectFilePath(filePath); +} + /*! * \brief Returns the base path of where to put the build artifacts and store the build graph. */ @@ -243,7 +296,7 @@ void SetupProjectParameters::setBuildRoot(const QString &buildRoot) // Calling mkpath() may be necessary to get the canonical build root, but if we do it, // it must be reverted immediately afterwards as not to create directories needlessly, // e.g in the case of a dry run build. - Internal::DirectoryManager dirManager(buildRoot, Internal::Logger()); + DirectoryManager dirManager(buildRoot, Logger()); // We don't do error checking here, as this is not a convenient place to report an error. // If creation of the build directory is not possible, we will get sensible error messages @@ -325,6 +378,27 @@ void SetupProjectParameters::setSettingsDirectory(const QString &settingsBaseDir } /*! + * \brief Returns the maximum number of threads to employ when resolving the project. + * If the value is not valid (i.e. <= 0), a sensible one will be derived from the number of + * available processor cores. + * The default is 0. + * \sa BuildOptions::defaultMaxJobCount + */ +int SetupProjectParameters::maxJobCount() const +{ + return d->maxJobCount; +} + +/*! + * \brief Controls how many threads to employ when resolving the project. + * A value <= 0 leaves the decision to qbs. + */ +void SetupProjectParameters::setMaxJobCount(int jobCount) +{ + d->maxJobCount = jobCount; +} + +/*! * Returns the overridden values of the build configuration. */ QVariantMap SetupProjectParameters::overriddenValues() const @@ -349,12 +423,12 @@ static void provideValuesTree(const QVariantMap &values, QVariantMap *valueTree) valueTree->clear(); for (QVariantMap::const_iterator it = values.constBegin(); it != values.constEnd(); ++it) { - const QString name = it.key(); + const QString &name = it.key(); int idx = name.lastIndexOf(QLatin1Char('.')); const QStringList nameElements = (idx == -1) ? QStringList() << name : QStringList() << name.left(idx) << name.mid(idx + 1); - Internal::setConfigProperty(*valueTree, nameElements, it.value()); + setConfigProperty(*valueTree, nameElements, it.value()); } } @@ -395,7 +469,7 @@ static QVariantMap expandedBuildConfigurationInternal(const Profile &profile, if (err.hasError()) throw err; if (profileKeys.empty()) - throw ErrorInfo(Internal::Tr::tr("Unknown or empty profile '%1'.").arg(profile.name())); + throw ErrorInfo(Tr::tr("Unknown or empty profile '%1'.").arg(profile.name())); for (const QString &profileKey : profileKeys) { buildConfig.insert(profileKey, profile.value(profileKey, QVariant(), &err)); if (err.hasError()) @@ -405,7 +479,7 @@ static QVariantMap expandedBuildConfigurationInternal(const Profile &profile, // (2) Build configuration name. if (configurationName.isEmpty()) - throw ErrorInfo(Internal::Tr::tr("No build configuration name set.")); + throw ErrorInfo(Tr::tr("No build configuration name set.")); buildConfig.insert(QStringLiteral("qbs.configurationName"), configurationName); return buildConfig; } @@ -442,7 +516,7 @@ ErrorInfo SetupProjectParameters::expandBuildConfiguration() QVariantMap expandedConfig = expandedBuildConfiguration(profile, configurationName(), &err); if (err.hasError()) return err; - if (d->buildConfiguration != expandedConfig) { + if (!qVariantMapsEqual(d->buildConfiguration, expandedConfig)) { d->buildConfigurationTree.clear(); d->buildConfiguration = expandedConfig; } @@ -547,22 +621,6 @@ void SetupProjectParameters::setWaitLockBuildGraph(bool wait) } /*! - * \brief Returns true if qbs should fall back to pkg-config if a dependency is not found. - */ -bool SetupProjectParameters::fallbackProviderEnabled() const -{ - return d->fallbackProviderEnabled; -} - -/*! - * Controls whether to fall back to pkg-config if a dependency is not found. - */ -void SetupProjectParameters::setFallbackProviderEnabled(bool enable) -{ - d->fallbackProviderEnabled = enable; -} - -/*! * \brief Gets the environment used while resolving the project. */ QProcessEnvironment SetupProjectParameters::environment() const @@ -688,4 +746,20 @@ void SetupProjectParameters::setProductErrorMode(ErrorHandlingMode mode) d->productErrorMode = mode; } +/*! + * \brief Indicates how deprecated constructs are handled. + */ +DeprecationWarningMode SetupProjectParameters::deprecationWarningMode() const +{ + return d->deprecationWarningMode; +} + +/*! + * \brief Specifies the behavior on encountering deprecated constructs. + */ +void SetupProjectParameters::setDeprecationWarningMode(DeprecationWarningMode mode) +{ + d->deprecationWarningMode = mode; +} + } // namespace qbs diff --git a/src/lib/corelib/tools/setupprojectparameters.h b/src/lib/corelib/tools/setupprojectparameters.h index a4d090ec5..5cd9700a9 100644 --- a/src/lib/corelib/tools/setupprojectparameters.h +++ b/src/lib/corelib/tools/setupprojectparameters.h @@ -41,13 +41,14 @@ #include "qbs_export.h" +#include <tools/deprecationwarningmode.h> #include <tools/error.h> #include <QtCore/qshareddata.h> +#include <QtCore/qstringlist.h> QT_BEGIN_NAMESPACE class QProcessEnvironment; -class QStringList; using QVariantMap = QMap<QString, QVariant>; QT_END_NAMESPACE @@ -81,6 +82,7 @@ public: QString projectFilePath() const; void setProjectFilePath(const QString &projectFilePath); + void finalizeProjectFilePath(); QString buildRoot() const; void setBuildRoot(const QString &buildRoot); @@ -97,6 +99,9 @@ public: QString settingsDirectory() const; void setSettingsDirectory(const QString &settingsBaseDir); + int maxJobCount() const; + void setMaxJobCount(int jobCount); + QVariantMap overriddenValues() const; void setOverriddenValues(const QVariantMap &values); QVariantMap overriddenValuesTree() const; @@ -127,9 +132,6 @@ public: bool waitLockBuildGraph() const; void setWaitLockBuildGraph(bool wait); - bool fallbackProviderEnabled() const; - void setFallbackProviderEnabled(bool enable); - QProcessEnvironment environment() const; void setEnvironment(const QProcessEnvironment &env); QProcessEnvironment adjustedEnvironment() const; @@ -144,6 +146,9 @@ public: ErrorHandlingMode productErrorMode() const; void setProductErrorMode(ErrorHandlingMode mode); + DeprecationWarningMode deprecationWarningMode() const; + void setDeprecationWarningMode(DeprecationWarningMode mode); + private: QSharedDataPointer<Internal::SetupProjectParametersPrivate> d; }; diff --git a/src/lib/corelib/tools/shellutils.cpp b/src/lib/corelib/tools/shellutils.cpp index dae98f337..5fff254f6 100644 --- a/src/lib/corelib/tools/shellutils.cpp +++ b/src/lib/corelib/tools/shellutils.cpp @@ -39,9 +39,12 @@ ****************************************************************************/ #include "shellutils.h" + #include "pathutils.h" +#include "qttools.h" + #include <QtCore/qfile.h> -#include <QtCore/qregexp.h> +#include <QtCore/qregularexpression.h> #include <QtCore/qtextstream.h> namespace qbs { @@ -53,8 +56,8 @@ QString shellInterpreter(const QString &filePath) { QTextStream ts(&file); const QString shebang = ts.readLine(); if (shebang.startsWith(QLatin1String("#!"))) { - return (shebang.mid(2).split(QRegExp(QStringLiteral("\\s")), - QString::SkipEmptyParts) << QString()).front(); + return (shebang.mid(2).split(QRegularExpression(QStringLiteral("\\s")), + Qt::SkipEmptyParts) << QString()).front(); } } @@ -66,9 +69,7 @@ QString shellInterpreter(const QString &filePath) { inline static bool isSpecialChar(ushort c, const uchar (&iqm)[16]) { - if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)))) - return true; - return false; + return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))); } inline static bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16]) @@ -124,9 +125,9 @@ static QString shellQuoteWin(const QString &arg) // The process-level standard quoting allows escaping quotes with backslashes (note // that backslashes don't escape themselves, unless they are followed by a quote). // Consequently, quotes are escaped and their preceding backslashes are doubled. - ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\"")); + ret.replace(QRegularExpression(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\"")); // Trailing backslashes must be doubled as well, as they are followed by a quote. - ret.replace(QRegExp(QLatin1String("(\\\\+)$")), QLatin1String("\\1\\1")); + ret.replace(QRegularExpression(QLatin1String("(\\\\+)$")), QLatin1String("\\1\\1")); // However, the shell also interprets the command, and no backslash-escaping exists // there - a quote always toggles the quoting state, but is nonetheless passed down // to the called process verbatim. In the unquoted state, the circumflex escapes diff --git a/src/lib/corelib/tools/shellutils.h b/src/lib/corelib/tools/shellutils.h index 6f1d82afb..f4ad35044 100644 --- a/src/lib/corelib/tools/shellutils.h +++ b/src/lib/corelib/tools/shellutils.h @@ -77,7 +77,7 @@ public: private: struct Argument { - Argument(const QString &value = QString()) : value(value) { } + Argument(QString value = QString()) : value(std::move(value)) { } QString value; bool isFilePath = false; bool shouldQuote = true; diff --git a/src/lib/corelib/tools/stlutils.h b/src/lib/corelib/tools/stlutils.h index ad00070cf..306f37157 100644 --- a/src/lib/corelib/tools/stlutils.h +++ b/src/lib/corelib/tools/stlutils.h @@ -46,19 +46,58 @@ namespace qbs { namespace Internal { -template <class C> -C sorted(const C &container) +template <typename C> +auto sorted(C &&container) { - C result = container; + using R = std::remove_cv_t<std::remove_reference_t<C>>; + R result(std::forward<C>(container)); std::sort(std::begin(result), std::end(result)); return result; } -template <class C> -bool contains(const C &container, const typename C::value_type &v) +template <typename C, typename Pred> +auto sorted(C &&container, Pred &&pred) { - const auto &end = container.cend(); - return std::find(container.cbegin(), end, v) != end; + using R = std::remove_cv_t<std::remove_reference_t<C>>; + R result(std::forward<C>(container)); + std::sort(std::begin(result), std::end(result), std::forward<Pred>(pred)); + return result; +} + +template <typename To, typename From, typename Op> +To transformed(const From &from, Op op) +{ + To to; + to.reserve(int(from.size())); + std::transform(std::cbegin(from), std::cend(from), std::back_inserter(to), std::move(op)); + return to; +} + +template <typename C, typename Op> +void transform(C &&container, Op op) +{ + std::transform(std::begin(container), std::end(container), std::begin(container), + std::move(op)); +} + +template <typename To, typename From, typename Op> +void transform(const From &from, To &&to, Op op) +{ + std::transform(std::cbegin(from), std::cend(from), std::back_inserter(to), std::move(op)); +} + +template <class C, class T> +bool contains(const C &container, const T &v) +{ + const auto &end = std::cend(container); + return std::find(std::cbegin(container), end, v) != end; +} + +template <class T, size_t N, class U> +bool contains(const T (&container)[N], const U &v) +{ + const auto &end = std::cend(container); + return std::find(std::cbegin(container), end, v) != end; } template <class C> @@ -68,6 +107,17 @@ bool containsKey(const C &container, const typename C::key_type &v) return container.find(v) != end; } +template <class C> +typename C::mapped_type mapValue( + const C &container, + const typename C::key_type &key, + const typename C::mapped_type &value = typename C::mapped_type()) +{ + const auto end = container.cend(); + const auto it = container.find(key); + return it != end ? it->second : value; +} + template <typename C> bool removeOne(C &container, const typename C::value_type &v) { @@ -86,6 +136,13 @@ void removeAll(C &container, const typename C::value_type &v) std::end(container)); } +template <typename C, typename Pred> +void removeIf(C &container, const Pred &pred) +{ + container.erase(std::remove_if(std::begin(container), std::end(container), pred), + std::end(container)); +} + template <class Container, class UnaryPredicate> bool any_of(const Container &container, const UnaryPredicate &predicate) { @@ -93,11 +150,32 @@ bool any_of(const Container &container, const UnaryPredicate &predicate) } template <class Container, class UnaryPredicate> +bool all_of(const Container &container, const UnaryPredicate &predicate) +{ + return std::all_of(std::begin(container), std::end(container), predicate); +} + +template <class Container, class UnaryPredicate> bool none_of(const Container &container, const UnaryPredicate &predicate) { return std::none_of(std::begin(container), std::end(container), predicate); } +template <class It, class T, class Compare> +It binaryFind(It begin, It end, const T &value, Compare comp) +{ + const auto it = std::lower_bound(begin, end, value, comp); + if (it == end || comp(value, *it)) + return end; + return it; +} + +template <class It, class T> +It binaryFind(It begin, It end, const T &value) +{ + return binaryFind(begin, end, value, std::less<T>()); +} + template <class C> C &operator<<(C &container, const typename C::value_type &v) { @@ -112,6 +190,79 @@ C &operator<<(C &container, const C &other) return container; } +// based on http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0814r0.pdf +template<typename T> +void hashCombineHelper(size_t &seed, const T &val) +{ + seed ^= std::hash<T>()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +template<typename... Types> +size_t hashCombine(const Types &... args) +{ + size_t seed = 0; + (hashCombineHelper(seed, args), ...); // create hash value with seed over all args return seed; + return seed; +} + +template<typename It> +size_t hashRange(It first, It last) +{ + size_t seed = 0; + for (; first != last; ++first) + hashCombineHelper(seed, *first); + + return seed; +} + +template<typename R> +size_t hashRange(R &&range) +{ + return hashRange(std::begin(range), std::end(range)); +} + +// based on qcontainertools_impl.h +template <typename Iterator> +using IfIsForwardIterator_t = typename std::enable_if_t< + std::is_convertible_v< + typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>, + bool>; + +template <typename Iterator> +using IfIsNotForwardIterator = typename std::enable_if_t< + !std::is_convertible_v< + typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>, + bool>; + +template <typename Container, + typename InputIterator, + IfIsNotForwardIterator<InputIterator> = true> +void reserveIfForwardIterator(Container *, InputIterator, InputIterator) +{ +} + +template <typename Container, + typename ForwardIterator, + IfIsForwardIterator_t<ForwardIterator> = true> +void reserveIfForwardIterator(Container *c, ForwardIterator f, ForwardIterator l) +{ + c->reserve(static_cast<typename Container::size_type>(std::distance(f, l))); +} + +// similar to ranges::to proposal +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1206r1.pdf +template <class C, class R> +C rangeTo(R &&r) +{ + return C(std::begin(r), std::end(r)); +} + +template<class Enum> +constexpr std::underlying_type_t<Enum> toUnderlying(Enum e) noexcept +{ + return static_cast<std::underlying_type_t<Enum>>(e); +} + } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/tools/stringconstants.h b/src/lib/corelib/tools/stringconstants.h index 79cbcd125..799a140d9 100644 --- a/src/lib/corelib/tools/stringconstants.h +++ b/src/lib/corelib/tools/stringconstants.h @@ -110,6 +110,7 @@ public: QBS_STRING_CONSTANT(installDirProperty, "installDir") QBS_STRING_CONSTANT(installSourceBaseProperty, "installSourceBase") QBS_STRING_CONSTANT(isEnabledKey, "is-enabled") + QBS_STRING_CONSTANT(isEagerProperty, "isEager") QBS_STRING_CONSTANT(jobCountProperty, "jobCount") QBS_STRING_CONSTANT(jobPoolProperty, "jobPool") QBS_STRING_CONSTANT(lengthProperty, "length") @@ -143,6 +144,7 @@ public: static const QString &profilesProperty() { return profiles(); } QBS_STRING_CONSTANT(productTypesProperty, "productTypes") QBS_STRING_CONSTANT(productsKey, "products") + QBS_STRING_CONSTANT(qbsModuleProviders, "qbsModuleProviders") QBS_STRING_CONSTANT(qbsSearchPathsProperty, "qbsSearchPaths") QBS_STRING_CONSTANT(referencesProperty, "references") QBS_STRING_CONSTANT(recursiveProperty, "recursive") @@ -153,6 +155,7 @@ public: QBS_STRING_CONSTANT(searchPathsProperty, "searchPaths") QBS_STRING_CONSTANT(setupBuildEnvironmentProperty, "setupBuildEnvironment") QBS_STRING_CONSTANT(setupRunEnvironmentProperty, "setupRunEnvironment") + QBS_STRING_CONSTANT(shadowProductPrefix, "__shadow__") QBS_STRING_CONSTANT(sourceCodeProperty, "sourceCode") QBS_STRING_CONSTANT(sourceDirectoryProperty, "sourceDirectory") QBS_STRING_CONSTANT(submodulesProperty, "submodules") @@ -166,6 +169,7 @@ public: QBS_STRING_CONSTANT(importScopeNamePropertyInternal, "_qbs_importScopeName") QBS_STRING_CONSTANT(modulePropertyInternal, "__module") + QBS_STRING_CONSTANT(dataPropertyInternal, "_qbs_data") QBS_STRING_CONSTANT(qbsSourceDirPropertyInternal, "_qbs_sourceDir") static const char *qbsProcEnvVarInternal() { return "_qbs_procenv"; } diff --git a/src/lib/corelib/tools/toolchains.cpp b/src/lib/corelib/tools/toolchains.cpp index 0d793f8aa..6263fb199 100644 --- a/src/lib/corelib/tools/toolchains.cpp +++ b/src/lib/corelib/tools/toolchains.cpp @@ -49,9 +49,11 @@ namespace qbs { namespace Internal { static const QString clangToolchain() { return QStringLiteral("clang"); } +static const QString clangClToolchain() { return QStringLiteral("clang-cl"); } static const QString gccToolchain() { return QStringLiteral("gcc"); } static const QString llvmToolchain() { return QStringLiteral("llvm"); } static const QString mingwToolchain() { return QStringLiteral("mingw"); } +static const QString msvcToolchain() { return QStringLiteral("msvc"); } } using namespace Internal; @@ -64,7 +66,8 @@ QStringList canonicalToolchain(const QStringList &toolchain) llvmToolchain(), mingwToolchain(), gccToolchain(), - QStringLiteral("msvc") + clangClToolchain(), + msvcToolchain() }; // Canonicalize each toolchain in the toolchain list, @@ -110,6 +113,8 @@ QStringList canonicalToolchain(const QString &name) else if (toolchainName == llvmToolchain() || toolchainName == mingwToolchain()) { toolchains << canonicalToolchain(QStringLiteral("gcc")); + } else if (toolchainName == clangClToolchain()) { + toolchains << canonicalToolchain(msvcToolchain()); } return toolchains; } diff --git a/src/lib/corelib/tools/tools.pri b/src/lib/corelib/tools/tools.pri deleted file mode 100644 index 89d752671..000000000 --- a/src/lib/corelib/tools/tools.pri +++ /dev/null @@ -1,144 +0,0 @@ -include(../../../install_prefix.pri) - -INCLUDEPATH += $$PWD/../.. # for plugins - -QBS_SYSTEM_SETTINGS_DIR = $$(QBS_SYSTEM_SETTINGS_DIR) -!isEmpty(QBS_SYSTEM_SETTINGS_DIR) { - DEFINES += QBS_SYSTEM_SETTINGS_DIR=\\\"$$QBS_SYSTEM_SETTINGS_DIR\\\" -} - -HEADERS += \ - $$PWD/architectures.h \ - $$PWD/buildgraphlocker.h \ - $$PWD/codelocation.h \ - $$PWD/commandechomode.h \ - $$PWD/dynamictypecheck.h \ - $$PWD/error.h \ - $$PWD/executablefinder.h \ - $$PWD/fileinfo.h \ - $$PWD/filesaver.h \ - $$PWD/filetime.h \ - $$PWD/generateoptions.h \ - $$PWD/id.h \ - $$PWD/iosutils.h \ - $$PWD/joblimits.h \ - $$PWD/jsliterals.h \ - $$PWD/jsonhelper.h \ - $$PWD/launcherinterface.h \ - $$PWD/launcherpackets.h \ - $$PWD/launchersocket.h \ - $$PWD/msvcinfo.h \ - $$PWD/persistence.h \ - $$PWD/scannerpluginmanager.h \ - $$PWD/scripttools.h \ - $$PWD/set.h \ - $$PWD/settings.h \ - $$PWD/settingsmodel.h \ - $$PWD/settingsrepresentation.h \ - $$PWD/pathutils.h \ - $$PWD/preferences.h \ - $$PWD/profile.h \ - $$PWD/profiling.h \ - $$PWD/processresult.h \ - $$PWD/processresult_p.h \ - $$PWD/processutils.h \ - $$PWD/progressobserver.h \ - $$PWD/projectgeneratormanager.h \ - $$PWD/qbspluginmanager.h \ - $$PWD/qbsprocess.h \ - $$PWD/shellutils.h \ - $$PWD/stlutils.h \ - $$PWD/stringutils.h \ - $$PWD/toolchains.h \ - $$PWD/hostosinfo.h \ - $$PWD/buildoptions.h \ - $$PWD/installoptions.h \ - $$PWD/cleanoptions.h \ - $$PWD/setupprojectparameters.h \ - $$PWD/weakpointer.h \ - $$PWD/qbs_export.h \ - $$PWD/qbsassert.h \ - $$PWD/qttools.h \ - $$PWD/settingscreator.h \ - $$PWD/stringconstants.h \ - $$PWD/version.h \ - $$PWD/visualstudioversioninfo.h \ - $$PWD/vsenvironmentdetector.h - -SOURCES += \ - $$PWD/architectures.cpp \ - $$PWD/buildgraphlocker.cpp \ - $$PWD/codelocation.cpp \ - $$PWD/commandechomode.cpp \ - $$PWD/error.cpp \ - $$PWD/executablefinder.cpp \ - $$PWD/fileinfo.cpp \ - $$PWD/filesaver.cpp \ - $$PWD/filetime.cpp \ - $$PWD/generateoptions.cpp \ - $$PWD/id.cpp \ - $$PWD/joblimits.cpp \ - $$PWD/jsliterals.cpp \ - $$PWD/launcherinterface.cpp \ - $$PWD/launcherpackets.cpp \ - $$PWD/launchersocket.cpp \ - $$PWD/msvcinfo.cpp \ - $$PWD/persistence.cpp \ - $$PWD/scannerpluginmanager.cpp \ - $$PWD/scripttools.cpp \ - $$PWD/settings.cpp \ - $$PWD/settingsmodel.cpp \ - $$PWD/settingsrepresentation.cpp \ - $$PWD/preferences.cpp \ - $$PWD/processresult.cpp \ - $$PWD/processutils.cpp \ - $$PWD/profile.cpp \ - $$PWD/profiling.cpp \ - $$PWD/progressobserver.cpp \ - $$PWD/projectgeneratormanager.cpp \ - $$PWD/qbspluginmanager.cpp \ - $$PWD/qbsprocess.cpp \ - $$PWD/shellutils.cpp \ - $$PWD/buildoptions.cpp \ - $$PWD/installoptions.cpp \ - $$PWD/cleanoptions.cpp \ - $$PWD/setupprojectparameters.cpp \ - $$PWD/qbsassert.cpp \ - $$PWD/qttools.cpp \ - $$PWD/settingscreator.cpp \ - $$PWD/toolchains.cpp \ - $$PWD/version.cpp \ - $$PWD/visualstudioversioninfo.cpp \ - $$PWD/vsenvironmentdetector.cpp - -osx { - HEADERS += $$PWD/applecodesignutils.h - SOURCES += $$PWD/applecodesignutils.cpp - LIBS += -framework Security -} - -!qbs_no_dev_install { - tools_headers.files = \ - $$PWD/architectures.h \ - $$PWD/buildoptions.h \ - $$PWD/cleanoptions.h \ - $$PWD/codelocation.h \ - $$PWD/commandechomode.h \ - $$PWD/error.h \ - $$PWD/generateoptions.h \ - $$PWD/installoptions.h \ - $$PWD/joblimits.h \ - $$PWD/preferences.h \ - $$PWD/processresult.h \ - $$PWD/profile.h \ - $$PWD/projectgeneratormanager.h \ - $$PWD/qbs_export.h \ - $$PWD/settings.h \ - $$PWD/settingsmodel.h \ - $$PWD/settingsrepresentation.h \ - $$PWD/setupprojectparameters.h \ - $$PWD/toolchains.h \ - $$PWD/version.h - tools_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/tools - INSTALLS += tools_headers -} diff --git a/src/lib/corelib/tools/version.cpp b/src/lib/corelib/tools/version.cpp index f653256b3..719bc386f 100644 --- a/src/lib/corelib/tools/version.cpp +++ b/src/lib/corelib/tools/version.cpp @@ -39,7 +39,7 @@ #include "version.h" -#include <QtCore/qregexp.h> +#include <QtCore/qregularexpression.h> #include <QtCore/qstring.h> namespace qbs { @@ -51,13 +51,14 @@ Version Version::fromString(const QString &versionString, bool buildNumberAllowe pattern += QStringLiteral("(?:\\.(\\d+))?"); // Followed by a dot and a number up to two times. if (buildNumberAllowed) pattern += QStringLiteral("(?:[-.](\\d+))?"); // And possibly a dash or dot followed by the build number. - QRegExp rex(pattern); - if (!rex.exactMatch(versionString)) + const QRegularExpression rex(QRegularExpression::anchoredPattern(pattern)); + const QRegularExpressionMatch match = rex.match(versionString); + if (!match.hasMatch()) return Version{}; - const int majorNr = rex.cap(1).toInt(); - const int minorNr = rex.captureCount() >= 2 ? rex.cap(2).toInt() : 0; - const int patchNr = rex.captureCount() >= 3 ? rex.cap(3).toInt() : 0; - const int buildNr = rex.captureCount() >= 4 ? rex.cap(4).toInt() : 0; + const int majorNr = match.captured(1).toInt(); + const int minorNr = match.lastCapturedIndex() >= 2 ? match.captured(2).toInt() : 0; + const int patchNr = match.lastCapturedIndex() >= 3 ? match.captured(3).toInt() : 0; + const int buildNr = match.lastCapturedIndex() >= 4 ? match.captured(4).toInt() : 0; return Version{majorNr, minorNr, patchNr, buildNr}; } diff --git a/src/lib/corelib/tools/version.h b/src/lib/corelib/tools/version.h index 63ad3f88c..7b2d23ebb 100644 --- a/src/lib/corelib/tools/version.h +++ b/src/lib/corelib/tools/version.h @@ -51,7 +51,7 @@ QT_END_NAMESPACE namespace qbs { -class QBS_EXPORT Version +class Version { public: constexpr explicit Version(int majorVersion = 0, int minorVersion = 0, int patchLevel = 0, @@ -73,9 +73,9 @@ public: constexpr int buildNumber() const { return m_build; } constexpr void setBuildNumber(int nr) { m_build = nr; } - static Version fromString(const QString &versionString, bool buildNumberAllowed = false); - QString toString(const QChar &separator = QLatin1Char('.'), - const QChar &buildSeparator = QLatin1Char('-')) const; + static QBS_EXPORT Version fromString(const QString &versionString, bool buildNumberAllowed = false); + QString QBS_EXPORT toString(const QChar &separator = QLatin1Char('.'), + const QChar &buildSeparator = QLatin1Char('-')) const; private: int m_major; diff --git a/src/lib/corelib/tools/visualstudioversioninfo.cpp b/src/lib/corelib/tools/visualstudioversioninfo.cpp index 02e5ef495..9ea86aaed 100644 --- a/src/lib/corelib/tools/visualstudioversioninfo.cpp +++ b/src/lib/corelib/tools/visualstudioversioninfo.cpp @@ -58,8 +58,8 @@ VisualStudioVersionInfo::VisualStudioVersionInfo(const Version &version) std::set<VisualStudioVersionInfo> VisualStudioVersionInfo::knownVersions() { static const std::set<VisualStudioVersionInfo> known = { - Version(16), Version(15), Version(14), Version(12), Version(11), Version(10), Version(9), - Version(8), Version(7, 1), Version(7), Version(6) + Version(17), Version(16), Version(15), Version(14), Version(12), Version(11), + Version(10), Version(9), Version(8), Version(7, 1), Version(7), Version(6) }; return known; } @@ -125,6 +125,8 @@ int VisualStudioVersionInfo::marketingVersion() const return 2017; case 16: return 2019; + case 17: + return 2022; default: qWarning() << QStringLiteral("unrecognized Visual Studio version: ") << m_version.toString(); @@ -171,17 +173,18 @@ QString VisualStudioVersionInfo::toolsVersion() const QString VisualStudioVersionInfo::platformToolsetVersion() const { static std::pair<int, QString> table[] = { + {17, QStringLiteral("v143")}, // VS 2022 {16, QStringLiteral("v142")}, // VS 2019 {15, QStringLiteral("v141")} // VS 2017 }; - for (auto p : table) { + for (const auto &p : table) { if (p.first == m_version.majorVersion()) return p.second; } return QStringLiteral("v%1").arg(m_version.majorVersion() * 10); } -quint32 qHash(const VisualStudioVersionInfo &info) +QHashValueType qHash(const VisualStudioVersionInfo &info) { return qHash(info.version().toString()); } diff --git a/src/lib/corelib/tools/visualstudioversioninfo.h b/src/lib/corelib/tools/visualstudioversioninfo.h index d4b226623..92eecb388 100644 --- a/src/lib/corelib/tools/visualstudioversioninfo.h +++ b/src/lib/corelib/tools/visualstudioversioninfo.h @@ -43,6 +43,7 @@ #include "qbs_export.h" +#include <tools/porting.h> #include <tools/version.h> #include <QtCore/qstring.h> @@ -78,7 +79,7 @@ private: Version m_version; }; -quint32 qHash(const VisualStudioVersionInfo &info); +QHashValueType qHash(const VisualStudioVersionInfo &info); } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/tools/vsenvironmentdetector.cpp b/src/lib/corelib/tools/vsenvironmentdetector.cpp index f8f98e7b7..5bcbd93b6 100644 --- a/src/lib/corelib/tools/vsenvironmentdetector.cpp +++ b/src/lib/corelib/tools/vsenvironmentdetector.cpp @@ -64,7 +64,7 @@ static QString windowsSystem32Path() #ifdef Q_OS_WIN wchar_t str[UNICODE_STRING_MAX_CHARS]; if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_SYSTEM, NULL, 0, str))) - return QString::fromUtf16(reinterpret_cast<ushort*>(str)); + return QString::fromUtf16(reinterpret_cast<char16_t*>(str)); #endif return {}; } @@ -124,14 +124,12 @@ QString VsEnvironmentDetector::findVcVarsAllBat(const MSVC &msvc, QString fullPath = dir.absoluteFilePath(path); if (dir.exists(path)) return fullPath; - else - searchedPaths.push_back(fullPath); + searchedPaths.push_back(fullPath); path = QStringLiteral("Auxiliary/Build/") + vcvarsallbat; fullPath = dir.absoluteFilePath(path); if (dir.exists(path)) return fullPath; - else - searchedPaths.push_back(fullPath); + searchedPaths.push_back(fullPath); return {}; } @@ -193,13 +191,13 @@ bool VsEnvironmentDetector::startDetection(const std::vector<MSVC *> &compatible static void batClearVars(QTextStream &s, const QStringList &varnames) { for (const QString &varname : varnames) - s << "set " << varname << '=' << endl; + s << "set " << varname << '=' << Qt::endl; } static void batPrintVars(QTextStream &s, const QStringList &varnames) { for (const QString &varname : varnames) - s << "echo " << varname << "=%" << varname << '%' << endl; + s << "echo " << varname << "=%" << varname << '%' << Qt::endl; } static QString vcArchitecture(const MSVC *msvc) @@ -232,14 +230,22 @@ void VsEnvironmentDetector::writeBatchFile(QIODevice *device, const QString &vcv << QStringLiteral("INCLUDE") << QStringLiteral("LIB") << QStringLiteral("WindowsSdkDir") << QStringLiteral("WindowsSDKVersion") << QStringLiteral("VSINSTALLDIR"); QTextStream s(device); + using Qt::endl; s << "@echo off" << endl; + // Avoid execution of powershell (in vsdevcmd.bat), which is not in the cleared PATH + s << "set VSCMD_SKIP_SENDTELEMETRY=1" << endl; for (const MSVC *msvc : msvcs) { s << "echo --" << msvc->architecture << "--" << endl << "setlocal" << endl; batClearVars(s, varnames); s << "set PATH=" << m_windowsSystemDirPath << endl; // vcvarsall.bat needs tools from here - s << "call \"" << vcvarsallbat << "\" " << vcArchitecture(msvc) - << " || exit /b 1" << endl; + s << "call \"" << vcvarsallbat << "\" " << vcArchitecture(msvc); + if (!msvc->sdkVersion.isEmpty()) + s << " " << msvc->sdkVersion; + const auto vcVarsVer = MSVC::vcVariablesVersionFromBinPath(msvc->binPath); + if (!vcVarsVer.isEmpty()) + s << " -vcvars_ver=" << vcVarsVer; + s << " || exit /b 1" << endl; batPrintVars(s, varnames); s << "endlocal" << endl; } @@ -272,8 +278,6 @@ void VsEnvironmentDetector::parseBatOutput(const QByteArray &output, std::vector value.remove(m_windowsSystemDirPath); if (value.endsWith(QLatin1Char(';'))) value.chop(1); - if (value.endsWith(QLatin1Char('\\'))) - value.chop(1); targetEnv->insert(name, value); } } diff --git a/src/lib/corelib/tools/vsenvironmentdetector.h b/src/lib/corelib/tools/vsenvironmentdetector.h index 7fa152cb6..39bea07d6 100644 --- a/src/lib/corelib/tools/vsenvironmentdetector.h +++ b/src/lib/corelib/tools/vsenvironmentdetector.h @@ -57,7 +57,7 @@ class MSVC; class QBS_EXPORT VsEnvironmentDetector { public: - explicit VsEnvironmentDetector(QString vcvarsallPath = QString()); + explicit VsEnvironmentDetector(QString vcvarsallPath = {}); bool start(MSVC *msvc); bool start(std::vector<MSVC *> msvcs); @@ -71,6 +71,7 @@ private: const QString m_windowsSystemDirPath; const QString m_vcvarsallPath; + const QString m_vcVariablesVersion; QString m_errorString; }; |