aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/corelib/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/corelib/tools')
-rw-r--r--src/lib/corelib/tools/applecodesignutils.cpp8
-rw-r--r--src/lib/corelib/tools/architectures.cpp2
-rw-r--r--src/lib/corelib/tools/buildoptions.cpp4
-rw-r--r--src/lib/corelib/tools/buildoptions.h2
-rw-r--r--src/lib/corelib/tools/clangclinfo.cpp173
-rw-r--r--src/lib/corelib/tools/clangclinfo.h61
-rw-r--r--src/lib/corelib/tools/cleanoptions.cpp2
-rw-r--r--src/lib/corelib/tools/codelocation.cpp49
-rw-r--r--src/lib/corelib/tools/codelocation.h74
-rw-r--r--src/lib/corelib/tools/commandechomode.h6
-rw-r--r--src/lib/corelib/tools/deprecationwarningmode.cpp98
-rw-r--r--src/lib/corelib/tools/deprecationwarningmode.h59
-rw-r--r--src/lib/corelib/tools/error.cpp44
-rw-r--r--src/lib/corelib/tools/error.h17
-rw-r--r--src/lib/corelib/tools/executablefinder.cpp8
-rw-r--r--src/lib/corelib/tools/fileinfo.cpp123
-rw-r--r--src/lib/corelib/tools/fileinfo.h14
-rw-r--r--src/lib/corelib/tools/filesaver.cpp9
-rw-r--r--src/lib/corelib/tools/filesaver.h3
-rw-r--r--src/lib/corelib/tools/filetime.cpp168
-rw-r--r--src/lib/corelib/tools/filetime.h26
-rw-r--r--src/lib/corelib/tools/hostosinfo.h106
-rw-r--r--src/lib/corelib/tools/id.cpp13
-rw-r--r--src/lib/corelib/tools/id.h3
-rw-r--r--src/lib/corelib/tools/installoptions.cpp2
-rw-r--r--src/lib/corelib/tools/iosutils.h8
-rw-r--r--src/lib/corelib/tools/jsliterals.cpp13
-rw-r--r--src/lib/corelib/tools/jsonhelper.h13
-rw-r--r--src/lib/corelib/tools/launcherinterface.cpp21
-rw-r--r--src/lib/corelib/tools/launcherpackets.h2
-rw-r--r--src/lib/corelib/tools/launchersocket.cpp36
-rw-r--r--src/lib/corelib/tools/launchersocket.h4
-rw-r--r--src/lib/corelib/tools/msvcinfo.cpp454
-rw-r--r--src/lib/corelib/tools/msvcinfo.h33
-rw-r--r--src/lib/corelib/tools/mutexdata.h104
-rw-r--r--src/lib/corelib/tools/pathutils.h2
-rw-r--r--src/lib/corelib/tools/persistence.cpp16
-rw-r--r--src/lib/corelib/tools/persistence.h118
-rw-r--r--src/lib/corelib/tools/pimpl.h61
-rw-r--r--src/lib/corelib/tools/porting.h55
-rw-r--r--src/lib/corelib/tools/preferences.cpp1
-rw-r--r--src/lib/corelib/tools/processresult.h3
-rw-r--r--src/lib/corelib/tools/processutils.cpp2
-rw-r--r--src/lib/corelib/tools/profile.cpp12
-rw-r--r--src/lib/corelib/tools/profile.h7
-rw-r--r--src/lib/corelib/tools/profiling.cpp13
-rw-r--r--src/lib/corelib/tools/profiling.h6
-rw-r--r--src/lib/corelib/tools/progressobserver.h11
-rw-r--r--src/lib/corelib/tools/projectgeneratormanager.h5
-rw-r--r--src/lib/corelib/tools/propagate_const.h418
-rw-r--r--src/lib/corelib/tools/qbs_export.h4
-rw-r--r--src/lib/corelib/tools/qbsassert.h5
-rw-r--r--src/lib/corelib/tools/qbspluginmanager.cpp2
-rw-r--r--src/lib/corelib/tools/qbsprocess.cpp10
-rw-r--r--src/lib/corelib/tools/qbsprocess.h2
-rw-r--r--src/lib/corelib/tools/qttools.cpp28
-rw-r--r--src/lib/corelib/tools/qttools.h185
-rw-r--r--src/lib/corelib/tools/scripttools.cpp263
-rw-r--r--src/lib/corelib/tools/scripttools.h163
-rw-r--r--src/lib/corelib/tools/set.h70
-rw-r--r--src/lib/corelib/tools/settings.cpp37
-rw-r--r--src/lib/corelib/tools/settings.h10
-rw-r--r--src/lib/corelib/tools/settingscreator.cpp53
-rw-r--r--src/lib/corelib/tools/settingscreator.h2
-rw-r--r--src/lib/corelib/tools/settingsmodel.cpp33
-rw-r--r--src/lib/corelib/tools/settingsmodel.h4
-rw-r--r--src/lib/corelib/tools/settingsrepresentation.cpp26
-rw-r--r--src/lib/corelib/tools/setupprojectparameters.cpp124
-rw-r--r--src/lib/corelib/tools/setupprojectparameters.h13
-rw-r--r--src/lib/corelib/tools/shellutils.cpp17
-rw-r--r--src/lib/corelib/tools/shellutils.h2
-rw-r--r--src/lib/corelib/tools/stlutils.h165
-rw-r--r--src/lib/corelib/tools/stringconstants.h4
-rw-r--r--src/lib/corelib/tools/toolchains.cpp7
-rw-r--r--src/lib/corelib/tools/tools.pri144
-rw-r--r--src/lib/corelib/tools/version.cpp15
-rw-r--r--src/lib/corelib/tools/version.h8
-rw-r--r--src/lib/corelib/tools/visualstudioversioninfo.cpp11
-rw-r--r--src/lib/corelib/tools/visualstudioversioninfo.h3
-rw-r--r--src/lib/corelib/tools/vsenvironmentdetector.cpp26
-rw-r--r--src/lib/corelib/tools/vsenvironmentdetector.h3
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 &params, 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 &params, 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 &regexp, 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 &currentNamePart, 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;
};