diff options
Diffstat (limited to 'src/app')
32 files changed, 468 insertions, 601 deletions
diff --git a/src/app/config-ui/main.cpp b/src/app/config-ui/main.cpp index a7c2662e6..dcf538989 100644 --- a/src/app/config-ui/main.cpp +++ b/src/app/config-ui/main.cpp @@ -47,8 +47,6 @@ #include <cstdlib> #include <iostream> -using qbs::Internal::Tr; - int main(int argc, char *argv[]) { QApplication app(argc, argv); diff --git a/src/app/config-ui/mainwindow.cpp b/src/app/config-ui/mainwindow.cpp index 2bf7fad5e..febf170a2 100644 --- a/src/app/config-ui/mainwindow.cpp +++ b/src/app/config-ui/mainwindow.cpp @@ -119,10 +119,10 @@ MainWindow::MainWindow(const QString &settingsDir, qbs::Settings::Scope scope, Q saveAction->setShortcut(QKeySequence::Save); connect(saveAction, &QAction::triggered, this, &MainWindow::saveSettings); const auto expandAllAction = new QAction(tr("&Expand All"), this); - expandAllAction->setShortcut(Qt::CTRL | Qt::Key_E); + expandAllAction->setShortcut(int(Qt::CTRL) | int(Qt::Key_E)); connect(expandAllAction, &QAction::triggered, this, &MainWindow::expandAll); const auto collapseAllAction = new QAction(tr("C&ollapse All"), this); - collapseAllAction->setShortcut(Qt::CTRL | Qt::Key_O); + collapseAllAction->setShortcut(int(Qt::CTRL) | int(Qt::Key_O)); connect(collapseAllAction, &QAction::triggered, this, &MainWindow::collapseAll); const auto exitAction = new QAction(tr("E&xit"), this); exitAction->setShortcut(QKeySequence::Quit); diff --git a/src/app/config/configcommandexecutor.cpp b/src/app/config/configcommandexecutor.cpp index 1290ba2f0..f2d9fc59e 100644 --- a/src/app/config/configcommandexecutor.cpp +++ b/src/app/config/configcommandexecutor.cpp @@ -41,8 +41,9 @@ #include "configcommand.h" #include "../shared/logging/consolelogger.h" -#include <tools/settingsrepresentation.h> #include <tools/error.h> +#include <tools/qttools.h> +#include <tools/settingsrepresentation.h> #include <QtCore/qdir.h> #include <QtCore/qfile.h> @@ -131,7 +132,7 @@ void ConfigCommandExecutor::exportSettings(const QString &filename) const auto keys = m_settings->allKeys(m_scope); for (const QString &key : keys) stream << key << ": " << qbs::settingsValueToRepresentation(m_settings->value(key, m_scope)) - << endl; + << Qt::endl; } void ConfigCommandExecutor::importSettings(const QString &filename) diff --git a/src/app/config/configcommandlineparser.h b/src/app/config/configcommandlineparser.h index b567134fd..f1f026678 100644 --- a/src/app/config/configcommandlineparser.h +++ b/src/app/config/configcommandlineparser.h @@ -60,7 +60,7 @@ public: class Error { public: - Error(const QString &message) : m_message(message) { } + Error(QString message) : m_message(std::move(message)) { } QString message() const { return m_message; } private: QString m_message; diff --git a/src/app/qbs-setup-android/android-setup.cpp b/src/app/qbs-setup-android/android-setup.cpp index 029628419..a0a9d2948 100644 --- a/src/app/qbs-setup-android/android-setup.cpp +++ b/src/app/qbs-setup-android/android-setup.cpp @@ -45,6 +45,7 @@ #include <tools/profile.h> #include <tools/settings.h> #include <tools/version.h> +#include <tools/qttools.h> #include <QtCore/qbytearraylist.h> #include <QtCore/qcoreapplication.h> @@ -94,10 +95,10 @@ static QString mapArch(const QString &androidName) } struct QtAndroidInfo { - bool isValid() const { return !arch.isEmpty(); } + bool isValid() const { return !archs.isEmpty(); } QString qmakePath; - QString arch; + QStringList archs; QString platform; }; @@ -111,19 +112,30 @@ static QtAndroidInfo getInfoForQtDir(const QString &qtDir) if (!qdevicepri.open(QIODevice::ReadOnly)) return info; while (!qdevicepri.atEnd()) { + // For Qt < 5.14 use DEFAULT_ANDROID_TARGET_ARCH (which is the abi) to compute + // the architecture + // DEFAULT_ANDROID_ABIS doesn't exit + // For Qt >= 5.14: + // DEFAULT_ANDROID_TARGET_ARCH doesn't exist, use DEFAULT_ANDROID_ABIS to compute + // the architectures const QByteArray line = qdevicepri.readLine().simplified(); const bool isArchLine = line.startsWith("DEFAULT_ANDROID_TARGET_ARCH"); + const bool isAbisLine = line.startsWith("DEFAULT_ANDROID_ABIS"); const bool isPlatformLine = line.startsWith("DEFAULT_ANDROID_PLATFORM"); - if (!isArchLine && !isPlatformLine) + if (!isArchLine && !isPlatformLine && !isAbisLine) continue; const QList<QByteArray> elems = line.split('='); if (elems.size() != 2) continue; const QString rhs = QString::fromLatin1(elems.at(1).trimmed()); - if (isArchLine) - info.arch = mapArch(rhs); - else + if (isArchLine) { + info.archs << mapArch(rhs); + } else if (isAbisLine) { + for (const QString &abi: rhs.split(QLatin1Char(' '))) + info.archs << mapArch(abi); + } else { info.platform = rhs; + } } return info; } @@ -136,13 +148,16 @@ static QtInfoPerArch getQtAndroidInfo(const QString &qtSdkDir) return archs; QStringList qtDirs(qtSdkDir); - QDirIterator dit(qtSdkDir, QStringList() << QStringLiteral("android_*"), QDir::Dirs); + const QStringList nameFilters{QStringLiteral("android_*"), QStringLiteral("android")}; + QDirIterator dit(qtSdkDir, nameFilters, QDir::Dirs); while (dit.hasNext()) qtDirs << dit.next(); for (const auto &qtDir : qtDirs) { const QtAndroidInfo info = getInfoForQtDir(qtDir); - if (info.isValid()) - archs.insert(info.arch, info); + if (info.isValid()) { + for (const QString &arch: info.archs) + archs.insert(arch, info); + } } return archs; } @@ -224,8 +239,10 @@ static void setupNdk(qbs::Settings *settings, const QString &profileName, const qmakeFilePaths << qtAndroidInfo.qmakePath; platform = maximumPlatform(platform, qtAndroidInfo.platform); } - if (!qmakeFilePaths.empty()) + if (!qmakeFilePaths.empty()) { + qmakeFilePaths.removeDuplicates(); mainProfile.setValue(qls("moduleProviders.Qt.qmakeFilePaths"), qmakeFilePaths); + } if (!platform.isEmpty()) mainProfile.setValue(qls("Android.ndk.platform"), platform); } diff --git a/src/app/qbs-setup-android/commandlineparser.cpp b/src/app/qbs-setup-android/commandlineparser.cpp index 0aecc8773..8beeeb601 100644 --- a/src/app/qbs-setup-android/commandlineparser.cpp +++ b/src/app/qbs-setup-android/commandlineparser.cpp @@ -43,10 +43,7 @@ #include <QtCore/qfileinfo.h> -CommandLineParser::CommandLineParser() -{ - -} +CommandLineParser::CommandLineParser() = default; using qbs::Internal::Tr; diff --git a/src/app/qbs-setup-qt/setupqt.cpp b/src/app/qbs-setup-qt/setupqt.cpp index 947cbc5fc..07e1a81b5 100644 --- a/src/app/qbs-setup-qt/setupqt.cpp +++ b/src/app/qbs-setup-qt/setupqt.cpp @@ -47,6 +47,7 @@ #include <tools/set.h> #include <tools/settings.h> #include <tools/stlutils.h> +#include <tools/toolchains.h> #include <tools/version.h> #include <QtCore/qbytearraymatcher.h> @@ -196,7 +197,7 @@ static bool isToolchainProfile(const Profile &profile) { const auto actual = Internal::Set<QString>::fromList( profile.allKeys(Profile::KeySelectionRecursive)); - Internal::Set<QString> expected = Internal::Set<QString> { QStringLiteral("qbs.toolchain") }; + Internal::Set<QString> expected{ QStringLiteral("qbs.toolchainType") }; if (HostOsInfo::isMacosHost()) expected.insert(QStringLiteral("qbs.targetPlatform")); // match only Xcode profiles return Internal::Set<QString>(actual).unite(expected) == actual; @@ -230,8 +231,13 @@ static Match compatibility(const QtEnvironment &env, const Profile &toolchainPro { Match match = MatchFull; - const auto toolchainNames = Internal::Set<QString>::fromList( - toolchainProfile.value(QStringLiteral("qbs.toolchain")).toStringList()); + const auto toolchainType = + toolchainProfile.value(QStringLiteral("qbs.toolchainType")).toString(); + const auto toolchain = !toolchainType.isEmpty() + ? canonicalToolchain(toolchainType) + : toolchainProfile.value(QStringLiteral("qbs.toolchain")).toStringList(); + + const auto toolchainNames = Internal::Set<QString>::fromList(toolchain); const auto qtToolchainNames = Internal::Set<QString>::fromList(env.qbsToolchain); if (areProfilePropertiesIncompatible(toolchainNames, qtToolchainNames)) { auto intersection = toolchainNames; diff --git a/src/app/qbs-setup-toolchains/clangclprobe.cpp b/src/app/qbs-setup-toolchains/clangclprobe.cpp index 816d28546..d1a3a9ac5 100644 --- a/src/app/qbs-setup-toolchains/clangclprobe.cpp +++ b/src/app/qbs-setup-toolchains/clangclprobe.cpp @@ -44,7 +44,9 @@ #include "../shared/logging/consolelogger.h" #include <logging/translator.h> +#include <tools/clangclinfo.h> #include <tools/hostosinfo.h> +#include <tools/msvcinfo.h> #include <tools/profile.h> #include <tools/qttools.h> #include <tools/settings.h> @@ -54,17 +56,13 @@ using qbs::Settings; using qbs::Profile; +using qbs::Internal::ClangClInfo; using qbs::Internal::HostOsInfo; using qbs::Internal::Tr; namespace { -QString getToolchainInstallPath(const QFileInfo &compiler) -{ - return compiler.path(); // 1 level up -} - Profile createProfileHelper( Settings *settings, const QString &profileName, @@ -75,9 +73,7 @@ Profile createProfileHelper( Profile profile(profileName, settings); profile.removeProfile(); profile.setValue(QStringLiteral("qbs.architecture"), architecture); - profile.setValue( - QStringLiteral("qbs.toolchain"), - QStringList{QStringLiteral("clang-cl"), QStringLiteral("msvc")}); + profile.setValue(QStringLiteral("qbs.toolchainType"), QStringLiteral("clang-cl")); profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), toolchainInstallPath); profile.setValue(QStringLiteral("cpp.vcvarsallPath"), vcvarsallPath); qbsInfo() << Tr::tr("Profile '%1' created for '%2'.") @@ -85,47 +81,6 @@ Profile createProfileHelper( return profile; } -std::vector<MSVCInstallInfo> compatibleMsvcs() -{ - auto msvcs = installedMSVCs(); - 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 - }; - const auto it = std::remove_if(msvcs.begin(), msvcs.end(), filter); - msvcs.erase(it, msvcs.end()); - for (const auto &msvc: msvcs) { - auto vcvarsallPath = msvc.findVcvarsallBat(); - if (vcvarsallPath.isEmpty()) - continue; - } - return msvcs; -} - -QString findCompatibleVcsarsallBat() -{ - for (const auto &msvc: compatibleMsvcs()) { - const auto vcvarsallPath = msvc.findVcvarsallBat(); - if (!vcvarsallPath.isEmpty()) - return vcvarsallPath; - } - return {}; -} - -QString wow6432Key() -{ -#ifdef Q_OS_WIN64 - return QStringLiteral("\\Wow6432Node"); -#else - return {}; -#endif -} - QString findClangCl() { const auto compilerName = HostOsInfo::appendExecutableSuffix(QStringLiteral("clang-cl")); @@ -133,27 +88,6 @@ QString findClangCl() if (!compilerFromPath.isEmpty()) return compilerFromPath; - 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(compilerPath).exists()) - return 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(compilerPath).exists()) - return compilerPath; - } return {}; } @@ -162,51 +96,39 @@ QString findClangCl() void createClangClProfile(const QFileInfo &compiler, Settings *settings, const QString &profileName) { - const auto compilerName = QStringLiteral("clang-cl"); - const auto vcvarsallPath = findCompatibleVcsarsallBat(); - if (vcvarsallPath.isEmpty()) { - qbsWarning() - << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.") - .arg(compilerName); + const auto clangCl = ClangClInfo::fromCompilerFilePath( + compiler.filePath(), ConsoleLogger::instance()); + if (clangCl.isEmpty()) return; - } - - const auto toolchainInstallPath = getToolchainInstallPath(compiler); const auto hostArch = QString::fromStdString(HostOsInfo::hostOSArchitecture()); - createProfileHelper(settings, profileName, toolchainInstallPath, vcvarsallPath, hostArch); + createProfileHelper( + settings, profileName, clangCl.toolchainInstallPath, clangCl.vcvarsallPath, hostArch); } /*! \brief Creates a clang-cl profile based on auto-detected vsversion. \internal */ -void clangClProbe(Settings *settings, QList<Profile> &profiles) +void clangClProbe(Settings *settings, std::vector<Profile> &profiles) { const auto compilerName = QStringLiteral("clang-cl"); qbsInfo() << Tr::tr("Trying to detect %1...").arg(compilerName); - const QString compilerFilePath = findClangCl(); - if (compilerFilePath.isEmpty()) { + const auto clangCls = ClangClInfo::installedCompilers( + {findClangCl()}, ConsoleLogger::instance()); + if (clangCls.empty()) { qbsInfo() << Tr::tr("%1 was not found.").arg(compilerName); return; } - const QFileInfo compiler(compilerFilePath); - const auto vcvarsallPath = findCompatibleVcsarsallBat(); - if (vcvarsallPath.isEmpty()) { - qbsWarning() - << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.") - .arg(compilerName); - return; - } + const auto clangCl = clangCls.front(); const QString architectures[] = { QStringLiteral("x86_64"), QStringLiteral("x86") }; - const auto toolchainInstallPath = getToolchainInstallPath(compiler); for (const auto &arch: architectures) { const auto profileName = QStringLiteral("clang-cl-%1").arg(arch); auto profile = createProfileHelper( - settings, profileName, toolchainInstallPath, vcvarsallPath, arch); + settings, profileName, clangCl.toolchainInstallPath, clangCl.vcvarsallPath, arch); profiles.push_back(std::move(profile)); } } diff --git a/src/app/qbs-setup-toolchains/clangclprobe.h b/src/app/qbs-setup-toolchains/clangclprobe.h index 1afbbb1b8..2de1c0a64 100644 --- a/src/app/qbs-setup-toolchains/clangclprobe.h +++ b/src/app/qbs-setup-toolchains/clangclprobe.h @@ -40,7 +40,9 @@ #ifndef QBS_SETUPTOOLCHAINS_CLANGCLPROBE_H #define QBS_SETUPTOOLCHAINS_CLANGCLPROBE_H -#include <QtCore/qlist.h> +#include <QString> + +#include <vector> QT_BEGIN_NAMESPACE class QFileInfo; @@ -54,6 +56,6 @@ class Settings; void createClangClProfile(const QFileInfo &compiler, qbs::Settings *settings, const QString &profileName); -void clangClProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles); +void clangClProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles); #endif // Header guard diff --git a/src/app/qbs-setup-toolchains/gccprobe.cpp b/src/app/qbs-setup-toolchains/gccprobe.cpp index 78439cbcb..6cbe246a5 100644 --- a/src/app/qbs-setup-toolchains/gccprobe.cpp +++ b/src/app/qbs-setup-toolchains/gccprobe.cpp @@ -111,7 +111,7 @@ class ToolchainDetails public: explicit ToolchainDetails(const QFileInfo &compiler) { - auto baseName = compiler.completeBaseName(); + auto baseName = HostOsInfo::stripExecutableSuffix(compiler.fileName()); // Extract the version sub-string if it exists. We assume that a version // sub-string located after the compiler prefix && suffix. E.g. this code // parses a version from the compiler names, like this: @@ -138,9 +138,9 @@ public: }; static void setCommonProperties(Profile &profile, const QFileInfo &compiler, - const QStringList &toolchainTypes, const ToolchainDetails &details) + const QString &toolchainType, const ToolchainDetails &details) { - if (toolchainTypes.contains(QStringLiteral("mingw"))) + if (toolchainType == QStringLiteral("mingw")) profile.setValue(QStringLiteral("qbs.targetPlatform"), QStringLiteral("windows")); @@ -149,7 +149,7 @@ static void setCommonProperties(Profile &profile, const QFileInfo &compiler, profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), compiler.absolutePath()); - profile.setValue(QStringLiteral("qbs.toolchain"), toolchainTypes); + profile.setValue(QStringLiteral("qbs.toolchainType"), toolchainType); if (!standardCompilerFileNames().contains( HostOsInfo::appendExecutableSuffix(details.suffix))) { @@ -453,12 +453,12 @@ static QStringList mplabX32RegistrySearchPaths() } Profile createGccProfile(const QFileInfo &compiler, Settings *settings, - const QStringList &toolchainTypes, + const QString &toolchainType, const QString &profileName) { const QString machineName = gccMachineName(compiler); - if (toolchainTypes.contains(QLatin1String("mingw"))) { + if (toolchainType == QLatin1String("mingw")) { if (!validMinGWMachines().contains(machineName)) { throw ErrorInfo(Tr::tr("Detected gcc platform '%1' is not supported.") .arg(machineName)); @@ -470,9 +470,9 @@ Profile createGccProfile(const QFileInfo &compiler, Settings *settings, const ToolchainDetails details(compiler); - setCommonProperties(profile, compiler, toolchainTypes, details); + setCommonProperties(profile, compiler, toolchainType, details); - if (HostOsInfo::isWindowsHost() && toolchainTypes.contains(QLatin1String("clang"))) { + if (HostOsInfo::isWindowsHost() && toolchainType == QLatin1String("clang")) { const QStringList profileNames = settings->profiles(); bool foundMingw = false; for (const QString &profileName : profileNames) { @@ -497,7 +497,7 @@ Profile createGccProfile(const QFileInfo &compiler, Settings *settings, } } - if (!toolchainTypes.contains(QLatin1String("clang"))) { + if (toolchainType != QLatin1String("clang")) { // Check whether auxiliary tools reside within the toolchain's install path. // This might not be the case when using icecc or another compiler wrapper. const QString compilerDirPath = compiler.absolutePath(); @@ -520,7 +520,7 @@ Profile createGccProfile(const QFileInfo &compiler, Settings *settings, return profile; } -void gccProbe(Settings *settings, QList<Profile> &profiles, const QString &compilerName) +void gccProbe(Settings *settings, std::vector<Profile> &profiles, const QString &compilerName) { qbsInfo() << Tr::tr("Trying to detect %1...").arg(compilerName); @@ -582,11 +582,15 @@ void gccProbe(Settings *settings, QList<Profile> &profiles, const QString &compi } for (const auto &candidate : qAsConst(candidates)) { - const QStringList toolchainTypes = toolchainTypeFromCompilerName( + const QString toolchainType = toolchainTypeFromCompilerName( candidate.baseName()); const QString profileName = buildProfileName(candidate); - auto profile = createGccProfile(candidate, settings, - toolchainTypes, profileName); - profiles.push_back(std::move(profile)); + try { + auto profile = createGccProfile(candidate, settings, + toolchainType, profileName); + profiles.push_back(std::move(profile)); + } catch (const qbs::ErrorInfo &info) { + qbsWarning() << Tr::tr("Skipping %1: %2").arg(profileName, info.toString()); + } } } diff --git a/src/app/qbs-setup-toolchains/gccprobe.h b/src/app/qbs-setup-toolchains/gccprobe.h index 4344c5836..98e7eaa1f 100644 --- a/src/app/qbs-setup-toolchains/gccprobe.h +++ b/src/app/qbs-setup-toolchains/gccprobe.h @@ -53,10 +53,10 @@ class Settings; qbs::Profile createGccProfile(const QFileInfo &compiler, qbs::Settings *settings, - const QStringList &toolchainTypes, + const QString &toolchainType, const QString &profileName = QString()); -void gccProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles, +void gccProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles, const QString &compilerName); #endif // Header guard diff --git a/src/app/qbs-setup-toolchains/iarewprobe.cpp b/src/app/qbs-setup-toolchains/iarewprobe.cpp index 26ee57da9..de7b62574 100644 --- a/src/app/qbs-setup-toolchains/iarewprobe.cpp +++ b/src/app/qbs-setup-toolchains/iarewprobe.cpp @@ -59,7 +59,9 @@ static QStringList knownIarCompilerNames() { return {QStringLiteral("icc8051"), QStringLiteral("iccarm"), QStringLiteral("iccavr"), QStringLiteral("iccstm8"), - QStringLiteral("icc430"), QStringLiteral("iccrl78")}; + QStringLiteral("icc430"), QStringLiteral("iccrl78"), + QStringLiteral("iccrx"), QStringLiteral("iccrh850"), + QStringLiteral("iccv850"), QStringLiteral("icc78k")}; } static QString guessIarArchitecture(const QFileInfo &compiler) @@ -77,6 +79,14 @@ static QString guessIarArchitecture(const QFileInfo &compiler) return QStringLiteral("msp430"); if (baseName == QLatin1String("iccrl78")) return QStringLiteral("rl78"); + if (baseName == QLatin1String("iccrx")) + return QStringLiteral("rx"); + if (baseName == QLatin1String("iccrh850")) + return QStringLiteral("rh850"); + if (baseName == QLatin1String("iccv850")) + return QStringLiteral("v850"); + if (baseName == QLatin1String("icc78k")) + return QStringLiteral("78k"); return {}; } @@ -148,7 +158,11 @@ static Version dumpIarCompilerVersion(const QFileInfo &compiler) || arch == QLatin1String("mcs51") || arch == QLatin1String("stm8") || arch == QLatin1String("msp430") - || arch == QLatin1String("rl78")) { + || arch == QLatin1String("rl78") + || arch == QLatin1String("rx") + || arch == QLatin1String("rh850") + || arch == QLatin1String("v850") + || arch == QLatin1String("78k")) { return Version{verCode / 100, verCode % 100}; } @@ -195,6 +209,10 @@ static std::vector<ToolchainInstallInfo> installedIarsFromRegistry() {QStringLiteral("EWSTM8"), QStringLiteral("\\stm8\\bin\\iccstm8.exe")}, {QStringLiteral("EW430"), QStringLiteral("\\430\\bin\\icc430.exe")}, {QStringLiteral("EWRL78"), QStringLiteral("\\rl78\\bin\\iccrl78.exe")}, + {QStringLiteral("EWRX"), QStringLiteral("\\rx\\bin\\iccrx.exe")}, + {QStringLiteral("EWRH850"), QStringLiteral("\\rh850\\bin\\iccrh850.exe")}, + {QStringLiteral("EWV850"), QStringLiteral("\\v850\\bin\\iccv850.exe")}, + {QStringLiteral("EW78K"), QStringLiteral("\\78k\\bin\\icc78k.exe")}, }; QSettings registry(QLatin1String(kRegistryNode), QSettings::NativeFormat); @@ -215,8 +233,7 @@ static std::vector<ToolchainInstallInfo> installedIarsFromRegistry() const QFileInfo iarPath(rootPath + entry.subExePath); if (iarPath.exists()) { // Note: threeLevelKey is a guessed toolchain version. - const QString version = threeLevelKey; - infos.push_back({iarPath, Version::fromString(version)}); + infos.push_back({iarPath, Version::fromString(threeLevelKey)}); } } registry.endGroup(); @@ -245,10 +262,10 @@ void createIarProfile(const QFileInfo &compiler, Settings *settings, QString profileName) { const ToolchainInstallInfo info = {compiler, Version{}}; - createIarProfileHelper(info, settings, profileName); + createIarProfileHelper(info, settings, std::move(profileName)); } -void iarProbe(Settings *settings, QList<Profile> &profiles) +void iarProbe(Settings *settings, std::vector<Profile> &profiles) { qbsInfo() << Tr::tr("Trying to detect IAR toolchains..."); diff --git a/src/app/qbs-setup-toolchains/iarewprobe.h b/src/app/qbs-setup-toolchains/iarewprobe.h index b604d6c6b..a1a51daa4 100644 --- a/src/app/qbs-setup-toolchains/iarewprobe.h +++ b/src/app/qbs-setup-toolchains/iarewprobe.h @@ -56,6 +56,6 @@ bool isIarCompiler(const QString &compilerName); void createIarProfile(const QFileInfo &compiler, qbs::Settings *settings, QString profileName); -void iarProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles); +void iarProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles); #endif // Header guard diff --git a/src/app/qbs-setup-toolchains/keilprobe.cpp b/src/app/qbs-setup-toolchains/keilprobe.cpp index 08f0f2167..67b14a802 100644 --- a/src/app/qbs-setup-toolchains/keilprobe.cpp +++ b/src/app/qbs-setup-toolchains/keilprobe.cpp @@ -47,6 +47,7 @@ #include <tools/hostosinfo.h> #include <tools/profile.h> +#include <QtCore/qdir.h> #include <QtCore/qprocess.h> #include <QtCore/qsettings.h> #include <QtCore/qtemporaryfile.h> @@ -57,7 +58,8 @@ using Internal::HostOsInfo; static QStringList knownKeilCompilerNames() { - return {QStringLiteral("c51"), QStringLiteral("armcc")}; + return {QStringLiteral("c51"), QStringLiteral("c251"), + QStringLiteral("c166"), QStringLiteral("armcc")}; } static QString guessKeilArchitecture(const QFileInfo &compiler) @@ -65,6 +67,10 @@ static QString guessKeilArchitecture(const QFileInfo &compiler) const auto baseName = compiler.baseName(); if (baseName == QLatin1String("c51")) return QStringLiteral("mcs51"); + if (baseName == QLatin1String("c251")) + return QStringLiteral("mcs251"); + if (baseName == QLatin1String("c166")) + return QStringLiteral("c166"); if (baseName == QLatin1String("armcc")) return QStringLiteral("arm"); return {}; @@ -100,74 +106,173 @@ static Profile createKeilProfileHelper(const ToolchainInstallInfo &info, return profile; } -static Version dumpKeilCompilerVersion(const QFileInfo &compiler) +static Version dumpMcsCompilerVersion(const QFileInfo &compiler) { - const QString arch = guessKeilArchitecture(compiler); - if (arch == QLatin1String("mcs51")) { - QTemporaryFile fakeIn; - if (!fakeIn.open()) { - qbsWarning() << Tr::tr("Unable to open temporary file %1 for output: %2") - .arg(fakeIn.fileName(), fakeIn.errorString()); - return Version{}; - } - fakeIn.write("#define VALUE_TO_STRING(x) #x\n"); - fakeIn.write("#define VALUE(x) VALUE_TO_STRING(x)\n"); - fakeIn.write("#define VAR_NAME_VALUE(var) \"\"\"|\"#var\"|\"VALUE(var)\n"); - fakeIn.write("#ifdef __C51__\n"); - fakeIn.write("#pragma message(VAR_NAME_VALUE(__C51__))\n"); - fakeIn.write("#endif\n"); - fakeIn.write("#ifdef __CX51__\n"); - fakeIn.write("#pragma message(VAR_NAME_VALUE(__CX51__))\n"); - fakeIn.write("#endif\n"); - fakeIn.close(); - - const QStringList args = {fakeIn.fileName()}; - QProcess p; - p.start(compiler.absoluteFilePath(), args); - p.waitForFinished(3000); - const auto es = p.exitStatus(); - if (es != QProcess::NormalExit) { - const QByteArray out = p.readAll(); - qbsWarning() << Tr::tr("Compiler dumping failed:\n%1") - .arg(QString::fromUtf8(out)); - return Version{}; - } + QTemporaryFile fakeIn; + if (!fakeIn.open()) { + qbsWarning() << Tr::tr("Unable to open temporary file %1 for output: %2") + .arg(fakeIn.fileName(), fakeIn.errorString()); + return Version{}; + } - const QByteArray dump = p.readAllStandardOutput(); - const int verCode = extractVersion(dump, "\"__C51__\"|\""); - if (verCode < 0) { - qbsWarning() << Tr::tr("No '__C51__' token was found" - " in the compiler dump:\n%1") - .arg(QString::fromUtf8(dump)); - return Version{}; - } - return Version{verCode / 100, verCode % 100}; - } else if (arch == QLatin1String("arm")) { - const QStringList args = {QStringLiteral("-E"), - QStringLiteral("--list-macros"), - QStringLiteral("nul")}; - QProcess p; - p.start(compiler.absoluteFilePath(), args); - p.waitForFinished(3000); - const auto es = p.exitStatus(); - if (es != QProcess::NormalExit) { - const QByteArray out = p.readAll(); - qbsWarning() << Tr::tr("Compiler dumping failed:\n%1") - .arg(QString::fromUtf8(out)); - return Version{}; + fakeIn.write("#define VALUE_TO_STRING(x) #x\n"); + fakeIn.write("#define VALUE(x) VALUE_TO_STRING(x)\n"); + + // Prepare for C51 compiler. + fakeIn.write("#if defined(__C51__) || defined(__CX51__)\n"); + fakeIn.write("# define VAR_NAME_VALUE(var) \"(\"\"\"\"|\"#var\"|\"VALUE(var)\"|\"\"\"\")\"\n"); + fakeIn.write("# if defined (__C51__)\n"); + fakeIn.write("# pragma message (VAR_NAME_VALUE(__C51__))\n"); + fakeIn.write("# endif\n"); + fakeIn.write("# if defined(__CX51__)\n"); + fakeIn.write("# pragma message (VAR_NAME_VALUE(__CX51__))\n"); + fakeIn.write("# endif\n"); + fakeIn.write("#endif\n"); + + // Prepare for C251 compiler. + fakeIn.write("#if defined(__C251__)\n"); + fakeIn.write("# define VAR_NAME_VALUE(var) \"\"|#var|VALUE(var)|\"\"\n"); + fakeIn.write("# if defined(__C251__)\n"); + fakeIn.write("# warning (VAR_NAME_VALUE(__C251__))\n"); + fakeIn.write("# endif\n"); + fakeIn.write("#endif\n"); + + fakeIn.close(); + + const QStringList args = {fakeIn.fileName()}; + QProcess p; + p.start(compiler.absoluteFilePath(), args); + p.waitForFinished(3000); + + const QStringList knownKeys = {QStringLiteral("__C51__"), + QStringLiteral("__CX51__"), + QStringLiteral("__C251__")}; + + auto extractVersion = [&knownKeys](const QByteArray &output) { + QTextStream stream(output); + QString line; + while (stream.readLineInto(&line)) { + if (!line.startsWith(QLatin1String("***"))) + continue; + enum { KEY_INDEX = 1, VALUE_INDEX = 2, ALL_PARTS = 4 }; + const QStringList parts = line.split(QLatin1String("\"|\"")); + if (parts.count() != ALL_PARTS) + continue; + if (!knownKeys.contains(parts.at(KEY_INDEX))) + continue; + return parts.at(VALUE_INDEX).toInt(); } + return -1; + }; + + const QByteArray dump = p.readAllStandardOutput(); + const int verCode = extractVersion(dump); + if (verCode < 0) { + qbsWarning() << Tr::tr("No %1 tokens was found" + " in the compiler dump:\n%2") + .arg(knownKeys.join(QLatin1Char(','))) + .arg(QString::fromUtf8(dump)); + return Version{}; + } + return Version{verCode / 100, verCode % 100}; +} - const QByteArray dump = p.readAll(); - const int verCode = extractVersion(dump, "__ARMCC_VERSION "); - if (verCode < 0) { - qbsWarning() << Tr::tr("No '__ARMCC_VERSION' token was found " - "in the compiler dump:\n%1") - .arg(QString::fromUtf8(dump)); - return Version{}; +static Version dumpC166CompilerVersion(const QFileInfo &compiler) +{ + QTemporaryFile fakeIn; + if (!fakeIn.open()) { + qbsWarning() << Tr::tr("Unable to open temporary file %1 for output: %2") + .arg(fakeIn.fileName(), fakeIn.errorString()); + return Version{}; + } + + fakeIn.write("#if defined(__C166__)\n"); + fakeIn.write("# warning __C166__\n"); + fakeIn.write("# pragma __C166__\n"); + fakeIn.write("#endif\n"); + + fakeIn.close(); + + const QStringList args = {fakeIn.fileName()}; + QProcess p; + p.start(compiler.absoluteFilePath(), args); + p.waitForFinished(3000); + + // Extract the compiler version pattern in the form, like: + // + // *** WARNING C320 IN LINE 41 OF c51.c: __C166__ + // *** WARNING C2 IN LINE 42 OF c51.c: '757': unknown #pragma/control, line ignored + // + // where the '__C166__' is a key, and the '757' is a value (aka version). + auto extractVersion = [](const QString &output) { + const QStringList lines = output.split(QStringLiteral("\r\n")); + for (auto it = lines.cbegin(); it != lines.cend();) { + if (it->startsWith(QLatin1String("***")) && it->endsWith(QLatin1String("__C166__"))) { + ++it; + if (it == lines.cend() || !it->startsWith(QLatin1String("***"))) + break; + const int startIndex = it->indexOf(QLatin1Char('\'')); + if (startIndex == -1) + break; + const int stopIndex = it->indexOf(QLatin1Char('\''), startIndex + 1); + if (stopIndex == -1) + break; + const QString v = it->mid(startIndex + 1, stopIndex - startIndex - 1); + return v.toInt(); + } + ++it; } - return Version{verCode / 1000000, (verCode / 10000) % 100, verCode % 10000}; + return -1; + }; + + const QByteArray dump = p.readAllStandardOutput(); + const int verCode = extractVersion(QString::fromUtf8(dump)); + if (verCode < 0) { + qbsWarning() << Tr::tr("No __C166__ token was found" + " in the compiler dump:\n%1") + .arg(QString::fromUtf8(dump)); + return Version{}; } + return Version{verCode / 100, verCode % 100}; +} +static Version dumpArmCompilerVersion(const QFileInfo &compiler) +{ + const QStringList args = {QStringLiteral("-E"), + QStringLiteral("--list-macros"), + QStringLiteral("nul")}; + QProcess p; + p.start(compiler.absoluteFilePath(), args); + p.waitForFinished(3000); + const auto es = p.exitStatus(); + if (es != QProcess::NormalExit) { + const QByteArray out = p.readAll(); + qbsWarning() << Tr::tr("Compiler dumping failed:\n%1") + .arg(QString::fromUtf8(out)); + return Version{}; + } + + const QByteArray dump = p.readAll(); + const int verCode = extractVersion(dump, "__ARMCC_VERSION "); + if (verCode < 0) { + qbsWarning() << Tr::tr("No '__ARMCC_VERSION' token was found " + "in the compiler dump:\n%1") + .arg(QString::fromUtf8(dump)); + return Version{}; + } + return Version{verCode / 1000000, (verCode / 10000) % 100, verCode % 10000}; +} + +static Version dumpKeilCompilerVersion(const QFileInfo &compiler) +{ + const QString arch = guessKeilArchitecture(compiler); + if (arch == QLatin1String("mcs51") || arch == QLatin1String("mcs251")) { + return dumpMcsCompilerVersion(compiler); + } else if (arch == QLatin1String("c166")) { + return dumpC166CompilerVersion(compiler); + } else if (arch == QLatin1String("arm")) { + return dumpArmCompilerVersion(compiler); + } return Version{}; } @@ -188,6 +293,51 @@ static std::vector<ToolchainInstallInfo> installedKeilsFromPath() return infos; } +// Parse the 'tools.ini' file to fetch a toolchain version. +// Note: We can't use QSettings here! +static QString extractVersion(const QString &toolsIniFile, const QString §ion) +{ + QFile f(toolsIniFile); + if (!f.open(QIODevice::ReadOnly)) + return {}; + QTextStream in(&f); + enum State { Enter, Lookup, Exit } state = Enter; + while (!in.atEnd()) { + const QString line = in.readLine().trimmed(); + // Search for section. + const int firstBracket = line.indexOf(QLatin1Char('[')); + const int lastBracket = line.lastIndexOf(QLatin1Char(']')); + const bool hasSection = (firstBracket == 0 && lastBracket != -1 + && (lastBracket + 1) == line.size()); + switch (state) { + case Enter: { + if (hasSection) { + const auto content = line.midRef(firstBracket + 1, + lastBracket - firstBracket - 1); + if (content == section) + state = Lookup; + } + } + break; + case Lookup: { + if (hasSection) + return {}; // Next section found. + const int versionIndex = line.indexOf(QLatin1String("VERSION=")); + if (versionIndex < 0) + continue; + QString version = line.mid(8); + if (version.startsWith(QLatin1Char('V'))) + version.remove(0, 1); + return version; + } + break; + default: + return {}; + } + } + return {}; +} + static std::vector<ToolchainInstallInfo> installedKeilsFromRegistry() { std::vector<ToolchainInstallInfo> infos; @@ -195,48 +345,49 @@ static std::vector<ToolchainInstallInfo> installedKeilsFromRegistry() if (HostOsInfo::isWindowsHost()) { #ifdef Q_OS_WIN64 - static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Keil\\Products"; + static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\" \ + "Windows\\CurrentVersion\\Uninstall\\Keil \u00B5Vision4"; #else - static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Keil\\Products"; + static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\" \ + "Windows\\CurrentVersion\\Uninstall\\Keil \u00B5Vision4"; #endif - // Dictionary for know toolchains. - static const struct Entry { - QString productKey; - QString subExePath; - } knowToolchains[] = { - {QStringLiteral("MDK"), QStringLiteral("\\ARMCC\\bin\\armcc.exe")}, - {QStringLiteral("C51"), QStringLiteral("\\BIN\\c51.exe")}, - }; - QSettings registry(QLatin1String(kRegistryNode), QSettings::NativeFormat); const auto productGroups = registry.childGroups(); for (const QString &productKey : productGroups) { - const auto entryEnd = std::end(knowToolchains); - const auto entryIt = std::find_if(std::begin(knowToolchains), entryEnd, - [productKey](const Entry &entry) { - return entry.productKey == productKey; - }); - if (entryIt == entryEnd) + if (!productKey.startsWith(QStringLiteral("App"))) continue; - registry.beginGroup(productKey); - const QString rootPath = registry.value(QStringLiteral("Path")) + const QString productPath = registry.value(QStringLiteral("ProductDir")) .toString(); - if (!rootPath.isEmpty()) { - // Build full compiler path. - const QFileInfo keilPath(rootPath + entryIt->subExePath); - if (keilPath.exists()) { - QString version = registry.value(QStringLiteral("Version")) - .toString(); - if (version.startsWith(QLatin1Char('V'))) - version.remove(0, 1); - infos.push_back({keilPath, Version::fromString(version)}); + // Fetch the toolchain executable path. + QFileInfo keilPath; + if (productPath.endsWith(QStringLiteral("ARM"))) + keilPath.setFile(productPath + QStringLiteral("\\ARMCC\\bin\\armcc.exe")); + else if (productPath.endsWith(QStringLiteral("C51"))) + keilPath.setFile(productPath + QStringLiteral("\\BIN\\c51.exe")); + else if (productPath.endsWith(QStringLiteral("C251"))) + keilPath.setFile(productPath + QStringLiteral("\\BIN\\c251.exe")); + else if (productPath.endsWith(QStringLiteral("C166"))) + keilPath.setFile(productPath + QStringLiteral("\\BIN\\c166.exe")); + + if (keilPath.exists()) { + // Fetch the toolchain version. + const QDir rootPath(registry.value(QStringLiteral("Directory")).toString()); + const QString toolsIniFilePath = rootPath.absoluteFilePath( + QStringLiteral("tools.ini")); + for (auto index = 1; index <= 2; ++index) { + const QString section = registry.value( + QStringLiteral("Section %1").arg(index)).toString(); + const QString version = extractVersion(toolsIniFilePath, section); + if (!version.isEmpty()) { + infos.push_back({keilPath, Version::fromString(version)}); + break; + } } } registry.endGroup(); } - } std::sort(infos.begin(), infos.end()); @@ -255,10 +406,10 @@ void createKeilProfile(const QFileInfo &compiler, Settings *settings, QString profileName) { const ToolchainInstallInfo info = {compiler, Version{}}; - createKeilProfileHelper(info, settings, profileName); + createKeilProfileHelper(info, settings, std::move(profileName)); } -void keilProbe(Settings *settings, QList<Profile> &profiles) +void keilProbe(Settings *settings, std::vector<Profile> &profiles) { qbsInfo() << Tr::tr("Trying to detect KEIL toolchains..."); diff --git a/src/app/qbs-setup-toolchains/keilprobe.h b/src/app/qbs-setup-toolchains/keilprobe.h index c7e66ee24..fec650ab0 100644 --- a/src/app/qbs-setup-toolchains/keilprobe.h +++ b/src/app/qbs-setup-toolchains/keilprobe.h @@ -56,6 +56,6 @@ bool isKeilCompiler(const QString &compilerName); void createKeilProfile(const QFileInfo &compiler, qbs::Settings *settings, QString profileName); -void keilProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles); +void keilProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles); #endif // Header guard diff --git a/src/app/qbs-setup-toolchains/main.cpp b/src/app/qbs-setup-toolchains/main.cpp index 87a2a842a..475bcf07b 100644 --- a/src/app/qbs-setup-toolchains/main.cpp +++ b/src/app/qbs-setup-toolchains/main.cpp @@ -49,7 +49,6 @@ #include <cstdlib> #include <iostream> -using qbs::Internal::Tr; using qbs::Settings; static void printUsage(const QString &usageString) diff --git a/src/app/qbs-setup-toolchains/msvcprobe.cpp b/src/app/qbs-setup-toolchains/msvcprobe.cpp index c8108bfd2..bb54add9f 100644 --- a/src/app/qbs-setup-toolchains/msvcprobe.cpp +++ b/src/app/qbs-setup-toolchains/msvcprobe.cpp @@ -50,15 +50,10 @@ #include <tools/qttools.h> #include <tools/settings.h> #include <tools/version.h> -#include <tools/visualstudioversioninfo.h> #include <tools/vsenvironmentdetector.h> #include <QtCore/qdir.h> #include <QtCore/qfileinfo.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> @@ -87,70 +82,18 @@ static void setQtHelperProperties(Profile &p, const MSVC *msvc) p.setValue(QStringLiteral("cpp.compilerVersion"), msvc->compilerVersion.toString()); } -static void addMSVCPlatform(Settings *settings, QList<Profile> &profiles, QString name, MSVC *msvc) +static void addMSVCPlatform(Settings *settings, std::vector<Profile> &profiles, QString name, MSVC *msvc) { qbsInfo() << Tr::tr("Setting up profile '%1'.").arg(name); - Profile p(name, settings); + Profile p(std::move(name), settings); p.removeProfile(); p.setValue(QStringLiteral("qbs.targetPlatform"), QStringLiteral("windows")); - p.setValue(QStringLiteral("qbs.toolchain"), QStringList(QStringLiteral("msvc"))); + p.setValue(QStringLiteral("qbs.toolchainType"), QStringLiteral("msvc")); p.setValue(QStringLiteral("cpp.toolchainInstallPath"), msvc->binPath); setQtHelperProperties(p, msvc); profiles.push_back(p); } -struct MSVCArchInfo -{ - QString arch; - QString binPath; -}; - -static std::vector<MSVCArchInfo> 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; -} - static QString wow6432Key() { #ifdef Q_OS_WIN64 @@ -160,203 +103,7 @@ static QString wow6432Key() #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))); - const 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) -{ - 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)) { - qbsWarning() << Tr::tr("The vswhere tool failed to run: %1").arg(vsWhere.errorString()); - return result; - } - if (vsWhere.exitCode() != 0) { - qbsWarning() << Tr::tr("The vswhere tool failed to run: %1") - .arg(QString::fromLocal8Bit(vsWhere.readAllStandardError())); - return result; - } - QJsonParseError parseError; - QJsonDocument jsonOutput = QJsonDocument::fromJson(vsWhere.readAllStandardOutput(), - &parseError); - if (parseError.error != QJsonParseError::NoError) { - qbsWarning() << Tr::tr("The vswhere tool produced invalid JSON output: %1") - .arg(parseError.errorString()); - return result; - } - const auto jsonArray = jsonOutput.array(); - for (const QJsonValue &v : jsonArray) { - const QJsonObject o = v.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() -{ - const std::vector<MSVCInstallInfo> vsInstallations - = retrieveInstancesFromVSWhere(ProductType::VisualStudio); - const std::vector<MSVCInstallInfo> buildToolInstallations - = retrieveInstancesFromVSWhere(ProductType::BuildTools); - 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; -} - -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> installedMSVCs() -{ - const auto installInfos = installedMSVCsFromVsWhere(); - if (installInfos.empty()) - return installedMSVCsFromRegistry(); - return installInfos; -} - -static std::vector<MSVC> installedCompilers() -{ - std::vector<MSVC> msvcs; - std::vector<MSVCInstallInfo> installInfos = installedMSVCsFromVsWhere(); - 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()) { - 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); - for (const QString &vcVersionStr : vcVersionStrs) { - const Version vcVersion = Version::fromString(vcVersionStr); - if (!vcVersion.isValid()) - continue; - QDir specificVcInstallDir = vcInstallDir; - if (!specificVcInstallDir.cd(vcVersionStr) - || !specificVcInstallDir.cd(QStringLiteral("bin"))) { - continue; - } - msvc.vcInstallPath = specificVcInstallDir.absolutePath(); - msvcs.push_back(msvc); - } - } - } - return msvcs; -} - -void msvcProbe(Settings *settings, QList<Profile> &profiles) +void msvcProbe(Settings *settings, std::vector<Profile> &profiles) { qbsInfo() << Tr::tr("Detecting MSVC toolchains..."); @@ -381,7 +128,7 @@ void msvcProbe(Settings *settings, QList<Profile> &profiles) sdk.vcInstallPath.chop(1); if (sdk.isDefault) defaultWinSDK = sdk; - const auto ais = findSupportedArchitectures(sdk); + const auto ais = MSVC::findSupportedArchitectures(sdk); for (const MSVCArchInfo &ai : ais) { WinSDK specificSDK = sdk; specificSDK.architecture = ai.arch; @@ -399,24 +146,7 @@ void msvcProbe(Settings *settings, QList<Profile> &profiles) } // 2) Installed MSVCs - std::vector<MSVC> msvcs; - const auto instMsvcs = installedCompilers(); - 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); - for (const MSVCArchInfo &ai : ais) { - MSVC specificMSVC = msvc; - specificMSVC.architecture = ai.arch; - specificMSVC.binPath = ai.binPath; - msvcs.push_back(specificMSVC); - } - } + std::vector<MSVC> msvcs = MSVC::installedCompilers(ConsoleLogger::instance()); for (const MSVC &msvc : qAsConst(msvcs)) { qbsInfo() << Tr::tr(" MSVC %1 (%2) detected in\n" @@ -470,7 +200,7 @@ void createMsvcProfile(const QFileInfo &compiler, Settings *settings, const auto compilerFilePath = compiler.absoluteFilePath(); MSVC msvc(compilerFilePath, MSVC::architectureFromClPath(compilerFilePath)); msvc.init(); - QList<Profile> dummy; + std::vector<Profile> dummy; addMSVCPlatform(settings, dummy, profileName, &msvc); qbsInfo() << Tr::tr("Profile '%1' created for '%2'.") .arg(profileName, QDir::toNativeSeparators(compilerFilePath)); diff --git a/src/app/qbs-setup-toolchains/msvcprobe.h b/src/app/qbs-setup-toolchains/msvcprobe.h index f63dd5dc8..981bbc561 100644 --- a/src/app/qbs-setup-toolchains/msvcprobe.h +++ b/src/app/qbs-setup-toolchains/msvcprobe.h @@ -53,19 +53,9 @@ class Profile; class Settings; } -struct MSVCInstallInfo -{ - QString version; - QString installDir; - - QString findVcvarsallBat() const; -}; - -std::vector<MSVCInstallInfo> installedMSVCs(); - void createMsvcProfile(const QFileInfo &compiler, qbs::Settings *settings, const QString &profileName); -void msvcProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles); +void msvcProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles); #endif // Header guard diff --git a/src/app/qbs-setup-toolchains/probe.cpp b/src/app/qbs-setup-toolchains/probe.cpp index 205306acd..add7ba05c 100644 --- a/src/app/qbs-setup-toolchains/probe.cpp +++ b/src/app/qbs-setup-toolchains/probe.cpp @@ -50,6 +50,7 @@ #include <tools/error.h> #include <tools/hostosinfo.h> #include <tools/profile.h> +#include <tools/qttools.h> #include <tools/settings.h> #include <tools/toolchains.h> @@ -96,32 +97,32 @@ QString findExecutable(const QString &fileName) return {}; } -QStringList toolchainTypeFromCompilerName(const QString &compilerName) +QString toolchainTypeFromCompilerName(const QString &compilerName) { if (compilerName == QLatin1String("cl.exe")) - return canonicalToolchain(QStringLiteral("msvc")); + return QStringLiteral("msvc"); if (compilerName == QLatin1String("clang-cl.exe")) - return canonicalToolchain(QLatin1String("clang-cl")); + return QStringLiteral("clang-cl"); const auto types = { QStringLiteral("clang"), QStringLiteral("llvm"), QStringLiteral("mingw"), QStringLiteral("gcc") }; for (const auto &type : types) { if (compilerName.contains(type)) - return canonicalToolchain(type); + return type; } if (compilerName == QLatin1String("g++")) - return canonicalToolchain(QStringLiteral("gcc")); + return QStringLiteral("gcc"); if (isIarCompiler(compilerName)) - return canonicalToolchain(QStringLiteral("iar")); + return QStringLiteral("iar"); if (isKeilCompiler(compilerName)) - return canonicalToolchain(QStringLiteral("keil")); + return QStringLiteral("keil"); if (isSdccCompiler(compilerName)) - return canonicalToolchain(QStringLiteral("sdcc")); + return QStringLiteral("sdcc"); return {}; } void probe(Settings *settings) { - QList<Profile> profiles; + std::vector<Profile> profiles; if (HostOsInfo::isWindowsHost()) { msvcProbe(settings, profiles); clangClProbe(settings, profiles); @@ -137,10 +138,10 @@ void probe(Settings *settings) sdccProbe(settings, profiles); if (profiles.empty()) { - qStderr << Tr::tr("Could not detect any toolchains. No profile created.") << endl; + qStderr << Tr::tr("Could not detect any toolchains. No profile created.") << Qt::endl; } else if (profiles.size() == 1 && settings->defaultProfile().isEmpty()) { const QString profileName = profiles.front().name(); - qStdout << Tr::tr("Making profile '%1' the default.").arg(profileName) << endl; + qStdout << Tr::tr("Making profile '%1' the default.").arg(profileName) << Qt::endl; settings->setValue(QStringLiteral("defaultProfile"), profileName); } } @@ -157,23 +158,22 @@ void createProfile(const QString &profileName, const QString &toolchainType, .arg(compilerFilePath)); } - QStringList toolchainTypes; - if (toolchainType.isEmpty()) - toolchainTypes = toolchainTypeFromCompilerName(compiler.fileName()); - else - toolchainTypes = canonicalToolchain(toolchainType); + const QString realToolchainType = !toolchainType.isEmpty() + ? toolchainType + : toolchainTypeFromCompilerName(compiler.fileName()); + const QStringList toolchain = canonicalToolchain(realToolchainType); - if (toolchainTypes.contains(QLatin1String("msvc"))) + if (toolchain.contains(QLatin1String("msvc"))) createMsvcProfile(compiler, settings, profileName); - else if (toolchainTypes.contains(QLatin1String("clang-cl"))) + else if (toolchain.contains(QLatin1String("clang-cl"))) createClangClProfile(compiler, settings, profileName); - else if (toolchainTypes.contains(QLatin1String("gcc"))) - createGccProfile(compiler, settings, toolchainTypes, profileName); - else if (toolchainTypes.contains(QLatin1String("iar"))) + else if (toolchain.contains(QLatin1String("gcc"))) + createGccProfile(compiler, settings, realToolchainType, profileName); + else if (toolchain.contains(QLatin1String("iar"))) createIarProfile(compiler, settings, profileName); - else if (toolchainTypes.contains(QLatin1String("keil"))) + else if (toolchain.contains(QLatin1String("keil"))) createKeilProfile(compiler, settings, profileName); - else if (toolchainTypes.contains(QLatin1String("sdcc"))) + else if (toolchain.contains(QLatin1String("sdcc"))) createSdccProfile(compiler, settings, profileName); else throw qbs::ErrorInfo(Tr::tr("Cannot create profile: Unknown toolchain type.")); diff --git a/src/app/qbs-setup-toolchains/probe.h b/src/app/qbs-setup-toolchains/probe.h index 235d7a899..bce150bd7 100644 --- a/src/app/qbs-setup-toolchains/probe.h +++ b/src/app/qbs-setup-toolchains/probe.h @@ -57,7 +57,7 @@ QStringList systemSearchPaths(); QString findExecutable(const QString &fileName); -QStringList toolchainTypeFromCompilerName(const QString &compilerName); +QString toolchainTypeFromCompilerName(const QString &compilerName); void createProfile(const QString &profileName, const QString &toolchainType, const QString &compilerFilePath, qbs::Settings *settings); diff --git a/src/app/qbs-setup-toolchains/sdccprobe.cpp b/src/app/qbs-setup-toolchains/sdccprobe.cpp index eccd3ccae..977d834c4 100644 --- a/src/app/qbs-setup-toolchains/sdccprobe.cpp +++ b/src/app/qbs-setup-toolchains/sdccprobe.cpp @@ -116,8 +116,8 @@ static std::vector<Profile> createSdccProfileHelper( if (actualArch != QString::fromLatin1(knownArch)) continue; - QString fullProfileName = profileName; - if (fullProfileName.isEmpty()) { + QString fullProfileName; + if (profileName.isEmpty()) { // Create a full profile name in case we is // in auto-detecting mode. if (!info.compilerVersion.isValid()) { @@ -133,7 +133,7 @@ static std::vector<Profile> createSdccProfileHelper( // Append the detected actual architecture name // to the proposed profile name. fullProfileName = QStringLiteral("%1-%2").arg( - fullProfileName, actualArch); + profileName, actualArch); } Profile profile(fullProfileName, settings); @@ -151,6 +151,21 @@ static std::vector<Profile> createSdccProfileHelper( return profiles; } +static Version dumpOldSddcCompilerVersion(const QByteArray ¯oDump) +{ + const auto keyToken = QByteArrayLiteral("__SDCC "); + const int startIndex = macroDump.indexOf(keyToken); + if (startIndex == -1) + return Version{}; + const int endIndex = macroDump.indexOf('\n', startIndex); + if (endIndex == -1) + return Version{}; + const auto keyLength = keyToken.length(); + return Version::fromString(QString::fromLatin1( + macroDump.mid(startIndex + keyLength, + endIndex - startIndex - keyLength).replace('_', '.'))); +} + static Version dumpSdccCompilerVersion(const QFileInfo &compiler) { const QByteArray dump = dumpSdccMacros(compiler); @@ -161,10 +176,14 @@ static Version dumpSdccCompilerVersion(const QFileInfo &compiler) const int minor = extractVersion(dump, "__SDCC_VERSION_MINOR "); const int patch = extractVersion(dump, "__SDCC_VERSION_PATCH "); if (major < 0 || minor < 0 || patch < 0) { - qbsWarning() << Tr::tr("No '__SDCC_VERSION_xxx' token was found " - "in the compiler dump:\n%1") - .arg(QString::fromUtf8(dump)); - return Version{}; + const auto version = dumpOldSddcCompilerVersion(dump); + if (!version.isValid()) { + qbsWarning() << Tr::tr("No '__SDCC_VERSION_xxx' or '__SDCC' token was found " + "in the compiler dump:\n%1") + .arg(QString::fromUtf8(dump)); + return Version{}; + } + return version; } return Version{major, minor, patch}; @@ -252,13 +271,13 @@ bool isSdccCompiler(const QString &compilerName) } void createSdccProfile(const QFileInfo &compiler, Settings *settings, - QString profileName) + const QString &profileName) { const ToolchainInstallInfo info = {compiler, Version{}}; createSdccProfileHelper(info, settings, profileName); } -void sdccProbe(Settings *settings, QList<Profile> &profiles) +void sdccProbe(Settings *settings, std::vector<Profile> &profiles) { qbsInfo() << Tr::tr("Trying to detect SDCC toolchains..."); diff --git a/src/app/qbs-setup-toolchains/sdccprobe.h b/src/app/qbs-setup-toolchains/sdccprobe.h index aa2c613f3..4c913ddeb 100644 --- a/src/app/qbs-setup-toolchains/sdccprobe.h +++ b/src/app/qbs-setup-toolchains/sdccprobe.h @@ -68,8 +68,8 @@ inline bool operator==(const SdccInstallInfo &lhs, const SdccInstallInfo &rhs) bool isSdccCompiler(const QString &compilerName); void createSdccProfile(const QFileInfo &compiler, qbs::Settings *settings, - QString profileName); + const QString &profileName); -void sdccProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles); +void sdccProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles); #endif // Header guard diff --git a/src/app/qbs-setup-toolchains/xcodeprobe.cpp b/src/app/qbs-setup-toolchains/xcodeprobe.cpp index a0f6f80d1..97b043f92 100644 --- a/src/app/qbs-setup-toolchains/xcodeprobe.cpp +++ b/src/app/qbs-setup-toolchains/xcodeprobe.cpp @@ -110,7 +110,7 @@ namespace { class XcodeProbe { public: - XcodeProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles) + XcodeProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles) : settings(settings), profiles(profiles) { } @@ -120,7 +120,7 @@ public: void detectAll(); private: qbs::Settings *settings; - QList<qbs::Profile> &profiles; + std::vector<qbs::Profile> &profiles; QStringList developerPaths; }; @@ -172,11 +172,7 @@ void XcodeProbe::setupDefaultToolchains(const QString &devPath, const QString &x Profile installationProfile(xcodeName, settings); installationProfile.removeProfile(); - installationProfile.setValue(QStringLiteral("qbs.toolchain"), QStringList() - << QStringLiteral("xcode") - << QStringLiteral("clang") - << QStringLiteral("llvm") - << QStringLiteral("gcc")); + installationProfile.setValue(QStringLiteral("qbs.toolchainType"), QStringLiteral("xcode")); if (devPath != defaultDeveloperPath) installationProfile.setValue(QStringLiteral("xcode.developerPath"), devPath); profiles.push_back(installationProfile); @@ -229,7 +225,7 @@ void XcodeProbe::detectAll() } } // end anonymous namespace -void xcodeProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles) +void xcodeProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles) { XcodeProbe probe(settings, profiles); probe.detectAll(); diff --git a/src/app/qbs-setup-toolchains/xcodeprobe.h b/src/app/qbs-setup-toolchains/xcodeprobe.h index ab501036b..fc15d177e 100644 --- a/src/app/qbs-setup-toolchains/xcodeprobe.h +++ b/src/app/qbs-setup-toolchains/xcodeprobe.h @@ -47,6 +47,6 @@ class Profile; class Settings; } -void xcodeProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles); +void xcodeProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles); #endif // Header guard diff --git a/src/app/qbs/commandlinefrontend.cpp b/src/app/qbs/commandlinefrontend.cpp index 8be06f3af..d8b4d9ca8 100644 --- a/src/app/qbs/commandlinefrontend.cpp +++ b/src/app/qbs/commandlinefrontend.cpp @@ -78,6 +78,7 @@ CommandLineFrontend::CommandLineFrontend(const CommandLineParser &parser, Settin CommandLineFrontend::~CommandLineFrontend() { m_cancelTimer->stop(); + delete m_observer; } // Called from interrupt handler. Don't do anything non-trivial here. @@ -653,7 +654,7 @@ ProductData CommandLineFrontend::getTheOneRunnableProduct() } QBS_CHECK(false); } - QBS_CHECK(m_parser.products().size() == 0); + QBS_CHECK(m_parser.products().isEmpty()); QList<ProductData> runnableProducts; const auto products = m_projects.front().projectData().allProducts(); diff --git a/src/app/qbs/parser/commandlineoption.cpp b/src/app/qbs/parser/commandlineoption.cpp index 0c13662f0..a09f36c2c 100644 --- a/src/app/qbs/parser/commandlineoption.cpp +++ b/src/app/qbs/parser/commandlineoption.cpp @@ -47,9 +47,7 @@ namespace qbs { using namespace Internal; -CommandLineOption::~CommandLineOption() -{ -} +CommandLineOption::~CommandLineOption() = default; void CommandLineOption::parse(CommandType command, const QString &representation, QStringList &input) { @@ -546,9 +544,7 @@ QString LogTimeOption::longRepresentation() const } -SettingsDirOption::SettingsDirOption() -{ -} +SettingsDirOption::SettingsDirOption() = default; QString SettingsDirOption::description(CommandType command) const { @@ -627,9 +623,7 @@ QString RespectProjectJobLimitsOption::longRepresentation() const return QStringLiteral("--enforce-project-job-limits"); } -CommandEchoModeOption::CommandEchoModeOption() -{ -} +CommandEchoModeOption::CommandEchoModeOption() = default; QString CommandEchoModeOption::description(CommandType command) const { diff --git a/src/app/qbs/parser/parsercommand.cpp b/src/app/qbs/parser/parsercommand.cpp index c7185a725..799bf5dcf 100644 --- a/src/app/qbs/parser/parsercommand.cpp +++ b/src/app/qbs/parser/parsercommand.cpp @@ -53,9 +53,7 @@ namespace qbs { using namespace Internal; -Command::~Command() -{ -} +Command::~Command() = default; void Command::parse(QStringList &input) { diff --git a/src/app/qbs/session.cpp b/src/app/qbs/session.cpp index 30e71dfd5..75f0e3bc9 100644 --- a/src/app/qbs/session.cpp +++ b/src/app/qbs/session.cpp @@ -148,7 +148,7 @@ private: struct ProductSelection { ProductSelection(Project::ProductSelection s) : selection(s) {} - ProductSelection(const QList<ProductData> &p) : products(p) {} + ProductSelection(QList<ProductData> p) : products(std::move(p)) {} Project::ProductSelection selection = Project::ProductSelectionDefaultOnly; QList<ProductData> products; @@ -186,7 +186,10 @@ Session::Session() #ifdef Q_OS_WIN32 // Make sure the line feed character appears as itself. if (_setmode(_fileno(stdout), _O_BINARY) == -1) { - std::cerr << "Failed to set stdout to binary mode: " << std::strerror(errno) << std::endl; + constexpr size_t errmsglen = FILENAME_MAX; + char errmsg[errmsglen]; + strerror_s(errmsg, errmsglen, errno); + std::cerr << "Failed to set stdout to binary mode: " << errmsg << std::endl; qApp->exit(EXIT_FAILURE); } #endif @@ -248,7 +251,7 @@ void Session::setupProject(const QJsonObject &request) if (m_currentJob) { if (qobject_cast<SetupProjectJob *>(m_currentJob) && m_currentJob->state() == AbstractJob::StateCanceling) { - m_resolveRequest = std::move(request); + m_resolveRequest = request; return; } sendErrorReply("project-resolved", @@ -258,7 +261,7 @@ void Session::setupProject(const QJsonObject &request) m_moduleProperties = modulePropertiesFromRequest(request); auto params = SetupProjectParameters::fromJson(request); const ProjectDataMode dataMode = dataModeFromRequest(request); - m_settings.reset(new Settings(params.settingsDirectory())); + m_settings = std::make_unique<Settings>(params.settingsDirectory()); const Preferences prefs(m_settings.get()); const QString appDir = QDir::cleanPath(QCoreApplication::applicationDirPath()); params.setSearchPaths(prefs.searchPaths(appDir + QLatin1String( diff --git a/src/app/qbs/sessionpacket.cpp b/src/app/qbs/sessionpacket.cpp index ce9fdaf76..dd6d1726e 100644 --- a/src/app/qbs/sessionpacket.cpp +++ b/src/app/qbs/sessionpacket.cpp @@ -99,8 +99,8 @@ QJsonObject SessionPacket::helloMessage() { return QJsonObject{ {StringConstants::type(), QLatin1String("hello")}, - {QLatin1String("api-level"), 1}, - {QLatin1String("api-compat-level"), 1} + {QLatin1String("api-level"), 2}, + {QLatin1String("api-compat-level"), 2} }; } diff --git a/src/app/qbs/sessionpacketreader.cpp b/src/app/qbs/sessionpacketreader.cpp index fe4b73f69..e99ea01ed 100644 --- a/src/app/qbs/sessionpacketreader.cpp +++ b/src/app/qbs/sessionpacketreader.cpp @@ -52,12 +52,12 @@ public: SessionPacket currentPacket; }; -SessionPacketReader::SessionPacketReader(QObject *parent) : QObject(parent), d(new Private) { } +SessionPacketReader::SessionPacketReader(QObject *parent) + : QObject(parent) + , d(std::make_unique<Private>()) +{ } -SessionPacketReader::~SessionPacketReader() -{ - delete d; -} +SessionPacketReader::~SessionPacketReader() = default; void SessionPacketReader::start() { diff --git a/src/app/qbs/sessionpacketreader.h b/src/app/qbs/sessionpacketreader.h index 87d70cf39..f186fbc8c 100644 --- a/src/app/qbs/sessionpacketreader.h +++ b/src/app/qbs/sessionpacketreader.h @@ -43,6 +43,8 @@ #include <QtCore/qjsonobject.h> #include <QtCore/qobject.h> +#include <memory> + namespace qbs { namespace Internal { @@ -51,7 +53,7 @@ class SessionPacketReader : public QObject Q_OBJECT public: explicit SessionPacketReader(QObject *parent = nullptr); - ~SessionPacketReader(); + ~SessionPacketReader() override; void start(); @@ -61,7 +63,7 @@ signals: private: class Private; - Private * const d; + const std::unique_ptr<Private> d; }; } // namespace Internal diff --git a/src/app/qbs/stdinreader.cpp b/src/app/qbs/stdinreader.cpp index 4f784505d..5f00d7de4 100644 --- a/src/app/qbs/stdinreader.cpp +++ b/src/app/qbs/stdinreader.cpp @@ -43,13 +43,13 @@ #include <QtCore/qfile.h> #include <QtCore/qsocketnotifier.h> +#include <QtCore/qtimer.h> #include <cerrno> #include <cstring> #ifdef Q_OS_WIN32 #include <qt_windows.h> -#include <QtCore/qtimer.h> #else #include <fcntl.h> #endif @@ -69,11 +69,11 @@ private: emit errorOccurred(tr("Cannot read from standard input.")); return; } +#ifdef Q_OS_UNIX const auto emitError = [this] { emit errorOccurred(tr("Failed to make standard input non-blocking: %1") .arg(QLatin1String(std::strerror(errno)))); }; -#ifdef Q_OS_UNIX const int flags = fcntl(0, F_GETFL, 0); if (flags == -1) { emitError(); @@ -87,6 +87,18 @@ private: connect(&m_notifier, &QSocketNotifier::activated, this, [this] { emit dataAvailable(m_stdIn.readAll()); }); + + // Neither the aboutToClose() nor the readChannelFinished() signals + // are triggering, so we need a timer to check whether the controlling + // process disappeared. + const auto stdinClosedChecker = new QTimer(this); + connect(stdinClosedChecker, &QTimer::timeout, this, [this, stdinClosedChecker] { + if (m_stdIn.atEnd()) { + stdinClosedChecker->stop(); + emit errorOccurred(tr("Input channel closed unexpectedly.")); + } + }); + stdinClosedChecker->start(1000); } QFile m_stdIn; @@ -112,14 +124,22 @@ private: // (how would we abort that one?), but ideally we'd like // to have a signal-based approach like in the Unix variant. const auto timer = new QTimer(this); - connect(timer, &QTimer::timeout, this, [this] { + connect(timer, &QTimer::timeout, this, [this, timer] { char buf[1024]; DWORD bytesAvail; - PeekNamedPipe(m_stdinHandle, nullptr, 0, nullptr, &bytesAvail, nullptr); + if (!PeekNamedPipe(m_stdinHandle, nullptr, 0, nullptr, &bytesAvail, nullptr)) { + timer->stop(); + emit errorOccurred(tr("Failed to read from input channel.")); + return; + } while (bytesAvail > 0) { DWORD bytesRead; - ReadFile(m_stdinHandle, buf, std::min<DWORD>(bytesAvail, sizeof buf), &bytesRead, - nullptr); + if (!ReadFile(m_stdinHandle, buf, std::min<DWORD>(bytesAvail, sizeof buf), + &bytesRead, nullptr)) { + timer->stop(); + emit errorOccurred(tr("Failed to read from input channel.")); + return; + } emit dataAvailable(QByteArray(buf, bytesRead)); bytesAvail -= bytesRead; } |