diff options
Diffstat (limited to 'src/app')
18 files changed, 1127 insertions, 321 deletions
diff --git a/src/app/qbs-setup-toolchains/clangclprobe.cpp b/src/app/qbs-setup-toolchains/clangclprobe.cpp index 5c2a87731..816d28546 100644 --- a/src/app/qbs-setup-toolchains/clangclprobe.cpp +++ b/src/app/qbs-setup-toolchains/clangclprobe.cpp @@ -60,9 +60,9 @@ using qbs::Internal::Tr; namespace { -QString getToolchainInstallPath(const QString &compilerFilePath) +QString getToolchainInstallPath(const QFileInfo &compiler) { - return QFileInfo(compilerFilePath).path(); // 1 level up + return compiler.path(); // 1 level up } Profile createProfileHelper( @@ -159,19 +159,19 @@ QString findClangCl() } // namespace -void createClangClProfile( - const QString &profileName, const QString &compilerFilePath, Settings *settings) +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); + .arg(compilerName); return; } - const auto toolchainInstallPath = getToolchainInstallPath(compilerFilePath); + const auto toolchainInstallPath = getToolchainInstallPath(compiler); const auto hostArch = QString::fromStdString(HostOsInfo::hostOSArchitecture()); createProfileHelper(settings, profileName, toolchainInstallPath, vcvarsallPath, hostArch); } @@ -184,12 +184,12 @@ void clangClProbe(Settings *settings, QList<Profile> &profiles) { const auto compilerName = QStringLiteral("clang-cl"); qbsInfo() << Tr::tr("Trying to detect %1...").arg(compilerName); - const auto compilerFilePath = findClangCl(); + const QString compilerFilePath = findClangCl(); if (compilerFilePath.isEmpty()) { qbsInfo() << Tr::tr("%1 was not found.").arg(compilerName); return; } - + const QFileInfo compiler(compilerFilePath); const auto vcvarsallPath = findCompatibleVcsarsallBat(); if (vcvarsallPath.isEmpty()) { qbsWarning() @@ -202,7 +202,7 @@ void clangClProbe(Settings *settings, QList<Profile> &profiles) QStringLiteral("x86_64"), QStringLiteral("x86") }; - const auto toolchainInstallPath = getToolchainInstallPath(compilerFilePath); + const auto toolchainInstallPath = getToolchainInstallPath(compiler); for (const auto &arch: architectures) { const auto profileName = QStringLiteral("clang-cl-%1").arg(arch); auto profile = createProfileHelper( diff --git a/src/app/qbs-setup-toolchains/clangclprobe.h b/src/app/qbs-setup-toolchains/clangclprobe.h index 1e7724fbf..1afbbb1b8 100644 --- a/src/app/qbs-setup-toolchains/clangclprobe.h +++ b/src/app/qbs-setup-toolchains/clangclprobe.h @@ -37,19 +37,23 @@ ** ****************************************************************************/ -#ifndef CLANGCLPROBE_H -#define CLANGCLPROBE_H +#ifndef QBS_SETUPTOOLCHAINS_CLANGCLPROBE_H +#define QBS_SETUPTOOLCHAINS_CLANGCLPROBE_H #include <QtCore/qlist.h> +QT_BEGIN_NAMESPACE +class QFileInfo; +QT_END_NAMESPACE + namespace qbs { class Profile; class Settings; } -void createClangClProfile( - const QString &profileName, const QString &compilerFilePath, qbs::Settings *settings); +void createClangClProfile(const QFileInfo &compiler, qbs::Settings *settings, + const QString &profileName); void clangClProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles); -#endif // CLANGCLPROBE_H +#endif // Header guard diff --git a/src/app/qbs-setup-toolchains/commandlineparser.h b/src/app/qbs-setup-toolchains/commandlineparser.h index 9d5b0c246..0616f068e 100644 --- a/src/app/qbs-setup-toolchains/commandlineparser.h +++ b/src/app/qbs-setup-toolchains/commandlineparser.h @@ -36,6 +36,7 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + #ifndef QBS_SETUPTOOLCHAINS_COMMANDLINEPARSER_H #define QBS_SETUPTOOLCHAINS_COMMANDLINEPARSER_H @@ -75,4 +76,4 @@ private: QString m_command; }; -#endif // Include guard +#endif // Header guard diff --git a/src/app/qbs-setup-toolchains/gccprobe.cpp b/src/app/qbs-setup-toolchains/gccprobe.cpp new file mode 100644 index 000000000..a8482f8d0 --- /dev/null +++ b/src/app/qbs-setup-toolchains/gccprobe.cpp @@ -0,0 +1,515 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "probe.h" +#include "gccprobe.h" + +#include "../shared/logging/consolelogger.h" + +#include <logging/translator.h> + +#include <tools/hostosinfo.h> +#include <tools/profile.h> +#include <tools/settings.h> +#include <tools/toolchains.h> + +#include <QtCore/qdir.h> +#include <QtCore/qprocess.h> + +#include <algorithm> + +using namespace qbs; +using Internal::HostOsInfo; +using Internal::Tr; + +static QString qsystem(const QString &exe, const QStringList &args = QStringList()) +{ + QProcess p; + p.setProcessChannelMode(QProcess::MergedChannels); + p.start(exe, args); + if (!p.waitForStarted()) { + throw qbs::ErrorInfo(Tr::tr("Failed to start compiler '%1': %2") + .arg(exe, p.errorString())); + } + if (!p.waitForFinished(-1) || p.exitCode() != 0) + throw qbs::ErrorInfo(Tr::tr("Failed to run compiler '%1': %2") + .arg(exe, p.errorString())); + return QString::fromLocal8Bit(p.readAll()); +} + +static QStringList validMinGWMachines() +{ + // List of MinGW machine names (gcc -dumpmachine) recognized by Qbs + return {QStringLiteral("mingw32"), + QStringLiteral("mingw64"), + QStringLiteral("i686-w64-mingw32"), + QStringLiteral("x86_64-w64-mingw32"), + QStringLiteral("i686-w64-mingw32.shared"), + QStringLiteral("x86_64-w64-mingw32.shared"), + QStringLiteral("i686-w64-mingw32.static"), + QStringLiteral("x86_64-w64-mingw32.static"), + QStringLiteral("i586-mingw32msvc"), + QStringLiteral("amd64-mingw32msvc")}; +} + +static QString gccMachineName(const QFileInfo &compiler) +{ + return qsystem(compiler.absoluteFilePath(), {QStringLiteral("-dumpmachine")}) + .trimmed(); +} + +static QStringList standardCompilerFileNames() +{ + return {HostOsInfo::appendExecutableSuffix(QStringLiteral("gcc")), + HostOsInfo::appendExecutableSuffix(QStringLiteral("g++")), + HostOsInfo::appendExecutableSuffix(QStringLiteral("clang")), + HostOsInfo::appendExecutableSuffix(QStringLiteral("clang++"))}; +} + +class ToolchainDetails +{ +public: + explicit ToolchainDetails(const QFileInfo &compiler) + { + auto baseName = compiler.completeBaseName(); + if (baseName.contains(QRegExp(QLatin1String("\\d$")))) { + const auto dashIndex = baseName.lastIndexOf(QLatin1Char('-')); + version = baseName.mid(dashIndex + 1); + baseName = baseName.left(dashIndex); + } + + const auto dashIndex = baseName.lastIndexOf(QLatin1Char('-')); + suffix = baseName.mid(dashIndex + 1); + prefix = baseName.left(dashIndex + 1); + } + + QString prefix; + QString suffix; + QString version; +}; + +static void setCommonProperties(Profile &profile, const QFileInfo &compiler, + const QStringList &toolchainTypes, const ToolchainDetails &details) +{ + if (toolchainTypes.contains(QStringLiteral("mingw"))) + profile.setValue(QStringLiteral("qbs.targetPlatform"), + QStringLiteral("windows")); + + if (!details.prefix.isEmpty()) + profile.setValue(QStringLiteral("cpp.toolchainPrefix"), details.prefix); + + profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), + compiler.absolutePath()); + profile.setValue(QStringLiteral("qbs.toolchain"), toolchainTypes); + + if (!standardCompilerFileNames().contains( + HostOsInfo::appendExecutableSuffix(details.suffix))) { + qWarning("%s", qPrintable( + QStringLiteral("'%1' is not a standard compiler file name; " + "you must set the cpp.cCompilerName and " + "cpp.cxxCompilerName properties of this profile " + "manually").arg(compiler.fileName()))); + } +} + +class ToolPathSetup +{ +public: + explicit ToolPathSetup(Profile *profile, QString path, ToolchainDetails details) + : m_profile(profile), + m_compilerDirPath(std::move(path)), + m_details(std::move(details)) + { + } + + void apply(const QString &toolName, const QString &propertyName) const + { + // Check for full tool name at first (includes suffix and version). + QString filePath = toolFilePath(toolName, UseFullToolName); + if (filePath.isEmpty()) { + // Check for base tool name at second (without of suffix and version). + filePath = toolFilePath(toolName, UseBaseToolName); + if (filePath.isEmpty()) { + // Check for short tool name at third (only a tool name). + filePath = toolFilePath(toolName, UseOnlyShortToolName); + } + } + + if (filePath.isEmpty()) { + qWarning("%s", qPrintable( + QStringLiteral("'%1' not found in '%2'. " + "Qbs will try to find it in PATH at build time.") + .arg(toolName, m_compilerDirPath))); + } else { + m_profile->setValue(propertyName, filePath); + } + } + +private: + enum ToolNameParts : quint8 { + UseOnlyShortToolName = 0x0, + UseToolPrefix = 0x01, + UseToolSuffix = 0x02, + UseToolVersion = 0x04, + UseFullToolName = UseToolPrefix | UseToolSuffix | UseToolVersion, + UseBaseToolName = UseToolPrefix, + }; + + QString toolFilePath(const QString &toolName, int parts) const + { + QString fileName; + if ((parts & UseToolPrefix) && !m_details.prefix.isEmpty()) + fileName += m_details.prefix; + if ((parts & UseToolSuffix) && !m_details.suffix.isEmpty()) + fileName += m_details.suffix + QLatin1Char('-'); + fileName += toolName; + if ((parts & UseToolVersion) && !m_details.version.isEmpty()) + fileName += QLatin1Char('-') + m_details.version; + + fileName = HostOsInfo::appendExecutableSuffix(fileName); + QString filePath = QDir(m_compilerDirPath).absoluteFilePath(fileName); + if (QFile::exists(filePath)) + return filePath; + return {}; + } + + Profile * const m_profile; + QString m_compilerDirPath; + ToolchainDetails m_details; +}; + +static bool doesProfileTargetOS(const Profile &profile, const QString &os) +{ + const auto target = profile.value(QStringLiteral("qbs.targetPlatform")); + if (target.isValid()) { + return Internal::contains(HostOsInfo::canonicalOSIdentifiers( + target.toString().toStdString()), + os.toStdString()); + } + return Internal::contains(HostOsInfo::hostOSIdentifiers(), os.toStdString()); +} + +static QString buildProfileName(const QFileInfo &cfi) +{ + // We need to replace a dot-separated compiler version string + // with a underscore-separated string, because the profile + // name does not allow a dots. + auto result = cfi.completeBaseName(); + result.replace(QLatin1Char('.'), QLatin1Char('_')); + return result; +} + +static QStringList buildCompilerNameFilters(const QString &compilerName) +{ + QStringList filters = { + // "clang", "gcc" + compilerName, + // "clang-8", "gcc-5" + compilerName + QLatin1String("-[1-9]*"), + // "avr-gcc" + QLatin1String("*-") + compilerName, + // "avr-gcc-5.4.0" + QLatin1String("*-") + compilerName + QLatin1String("-[1-9]*"), + // "arm-none-eabi-gcc" + QLatin1String("*-*-*-") + compilerName, + // "arm-none-eabi-gcc-9.1.0" + QLatin1String("*-*-*-") + compilerName + QLatin1String("-[1-9]*"), + // "x86_64-pc-linux-gnu-gcc" + QLatin1String("*-*-*-*-") + compilerName, + // "x86_64-pc-linux-gnu-gcc-7.4.1" + QLatin1String("*-*-*-*-") + compilerName + QLatin1String("-[1-9]*") + }; + + std::transform(filters.begin(), filters.end(), filters.begin(), + [](const auto &filter) { + return HostOsInfo::appendExecutableSuffix(filter); + }); + + return filters; +} + +static QStringList gnuRegistrySearchPaths() +{ + if (!HostOsInfo::isWindowsHost()) + return {}; + + // Registry token for the "GNU Tools for ARM Embedded Processors". + static const char kRegistryToken[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\" \ + "Windows\\CurrentVersion\\Uninstall\\"; + + QStringList searchPaths; + + QSettings registry(QLatin1String(kRegistryToken), QSettings::NativeFormat); + const auto productGroups = registry.childGroups(); + for (const QString &productKey : productGroups) { + if (!productKey.startsWith( + QLatin1String("GNU Tools for ARM Embedded Processors"))) { + continue; + } + registry.beginGroup(productKey); + QString uninstallFilePath = registry.value( + QLatin1String("UninstallString")).toString(); + if (uninstallFilePath.startsWith(QLatin1Char('"'))) + uninstallFilePath.remove(0, 1); + if (uninstallFilePath.endsWith(QLatin1Char('"'))) + uninstallFilePath.remove(uninstallFilePath.size() - 1, 1); + registry.endGroup(); + + const QString toolkitRootPath = QFileInfo(uninstallFilePath).path(); + const QString toolchainPath = toolkitRootPath + QLatin1String("/bin"); + searchPaths.push_back(toolchainPath); + } + + return searchPaths; +} + +static QStringList atmelRegistrySearchPaths() +{ + if (!HostOsInfo::isWindowsHost()) + return {}; + + // Registry token for the "Atmel" toolchains, e.g. provided by installed + // "Atmel Studio" IDE. + static const char kRegistryToken[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Atmel\\"; + + QStringList searchPaths; + QSettings registry(QLatin1String(kRegistryToken), QSettings::NativeFormat); + + // This code enumerate the installed toolchains provided + // by the Atmel Studio v6.x. + const auto toolchainGroups = registry.childGroups(); + for (const QString &toolchainKey : toolchainGroups) { + if (!toolchainKey.endsWith(QLatin1String("GCC"))) + continue; + registry.beginGroup(toolchainKey); + const auto entries = registry.childGroups(); + for (const auto &entryKey : entries) { + registry.beginGroup(entryKey); + const QString installDir = registry.value( + QStringLiteral("Native/InstallDir")).toString(); + const QString version = registry.value( + QStringLiteral("Native/Version")).toString(); + registry.endGroup(); + + QString toolchainPath = installDir + + QLatin1String("/Atmel Toolchain/") + + toolchainKey + QLatin1String("/Native/") + + version; + if (toolchainKey.startsWith(QLatin1String("ARM"))) + toolchainPath += QLatin1String("/arm-gnu-toolchain"); + else if (toolchainKey.startsWith(QLatin1String("AVR32"))) + toolchainPath += QLatin1String("/avr32-gnu-toolchain"); + else if (toolchainKey.startsWith(QLatin1String("AVR8)"))) + toolchainPath += QLatin1String("/avr8-gnu-toolchain"); + else + break; + + toolchainPath += QLatin1String("/bin"); + + if (QFileInfo::exists(toolchainPath)) { + searchPaths.push_back(toolchainPath); + break; + } + } + registry.endGroup(); + } + + // This code enumerate the installed toolchains provided + // by the Atmel Studio v7. + registry.beginGroup(QStringLiteral("AtmelStudio")); + const auto productVersions = registry.childGroups(); + for (const auto &productVersionKey : productVersions) { + registry.beginGroup(productVersionKey); + const QString installDir = registry.value( + QStringLiteral("InstallDir")).toString(); + registry.endGroup(); + + const QStringList knownToolchainSubdirs = { + QStringLiteral("/toolchain/arm/arm-gnu-toolchain/bin/"), + QStringLiteral("/toolchain/avr8/avr8-gnu-toolchain/bin/"), + QStringLiteral("/toolchain/avr32/avr32-gnu-toolchain/bin/"), + }; + + for (const auto &subdir : knownToolchainSubdirs) { + const QString toolchainPath = installDir + subdir; + if (!QFileInfo::exists(toolchainPath)) + continue; + searchPaths.push_back(toolchainPath); + } + } + registry.endGroup(); + + return searchPaths; +} + +Profile createGccProfile(const QFileInfo &compiler, Settings *settings, + const QStringList &toolchainTypes, + const QString &profileName) +{ + const QString machineName = gccMachineName(compiler); + + if (toolchainTypes.contains(QLatin1String("mingw"))) { + if (!validMinGWMachines().contains(machineName)) { + throw ErrorInfo(Tr::tr("Detected gcc platform '%1' is not supported.") + .arg(machineName)); + } + } + + Profile profile(!profileName.isEmpty() ? profileName : machineName, settings); + profile.removeProfile(); + + const ToolchainDetails details(compiler); + + setCommonProperties(profile, compiler, toolchainTypes, details); + + if (HostOsInfo::isWindowsHost() && toolchainTypes.contains(QLatin1String("clang"))) { + const QStringList profileNames = settings->profiles(); + bool foundMingw = false; + for (const QString &profileName : profileNames) { + const Profile otherProfile(profileName, settings); + if (otherProfile.value(QLatin1String("qbs.toolchainType")).toString() + == QLatin1String("mingw") + || otherProfile.value(QLatin1String("qbs.toolchain")) + .toStringList().contains(QLatin1String("mingw"))) { + const QFileInfo tcDir(otherProfile.value(QLatin1String("cpp.toolchainInstallPath")) + .toString()); + if (!tcDir.fileName().isEmpty() && tcDir.exists()) { + profile.setValue(QLatin1String("qbs.sysroot"), tcDir.path()); + foundMingw = true; + break; + } + } + } + if (!foundMingw) { + qbsWarning() << Tr::tr("Using clang on Windows requires a mingw installation. " + "Please set qbs.sysroot accordingly for profile '%1'.") + .arg(profile.name()); + } + } + + if (!toolchainTypes.contains(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(); + const ToolPathSetup toolPathSetup(&profile, compilerDirPath, details); + toolPathSetup.apply(QStringLiteral("ar"), QStringLiteral("cpp.archiverPath")); + toolPathSetup.apply(QStringLiteral("as"), QStringLiteral("cpp.assemblerPath")); + toolPathSetup.apply(QStringLiteral("nm"), QStringLiteral("cpp.nmPath")); + if (doesProfileTargetOS(profile, QStringLiteral("darwin"))) + toolPathSetup.apply(QStringLiteral("dsymutil"), + QStringLiteral("cpp.dsymutilPath")); + else + toolPathSetup.apply(QStringLiteral("objcopy"), + QStringLiteral("cpp.objcopyPath")); + toolPathSetup.apply(QStringLiteral("strip"), + QStringLiteral("cpp.stripPath")); + } + + qbsInfo() << Tr::tr("Profile '%1' created for '%2'.") + .arg(profile.name(), compiler.absoluteFilePath()); + return profile; +} + +void gccProbe(Settings *settings, QList<Profile> &profiles, const QString &compilerName) +{ + qbsInfo() << Tr::tr("Trying to detect %1...").arg(compilerName); + + QStringList searchPaths; + searchPaths << systemSearchPaths() << gnuRegistrySearchPaths() + << atmelRegistrySearchPaths(); + + std::vector<QFileInfo> candidates; + const auto filters = buildCompilerNameFilters(compilerName); + for (const auto &searchPath : searchPaths) { + const QDir dir(searchPath); + const QStringList fileNames = dir.entryList( + filters, QDir::Files | QDir::Executable); + for (const QString &fileName : fileNames) { + // Ignore unexpected compiler names. + if (fileName.startsWith(QLatin1String("c89-gcc")) + || fileName.startsWith(QLatin1String("c99-gcc"))) { + continue; + } + const QFileInfo candidate = dir.filePath(fileName); + // Filter duplicates. + const auto existingEnd = candidates.end(); + const auto existingIt = std::find_if( + candidates.begin(), existingEnd, + [candidate](const QFileInfo &existing) { + return isSameExecutable(candidate.absoluteFilePath(), + existing.absoluteFilePath()); + }); + if (existingIt == existingEnd) { + // No duplicates are found, just add a new candidate. + candidates.push_back(candidate); + } else { + // Replace the existing entry if a candidate name more than + // an existing name. + const auto candidateName = candidate.completeBaseName(); + const auto existingName = existingIt->completeBaseName(); + if (candidateName > existingName) + *existingIt = candidate; + } + } + } + + if (candidates.empty()) { + qbsInfo() << Tr::tr("No %1 toolchains found.").arg(compilerName); + return; + } + + // Sort candidates so that mingw comes first. Information from mingw profiles is potentially + // used for setting up clang profiles. + if (HostOsInfo::isWindowsHost()) { + std::sort(candidates.begin(), candidates.end(), + [](const QFileInfo &fi1, const QFileInfo &fi2) { + return fi1.absoluteFilePath().contains(QLatin1String("mingw")) + && !fi2.absoluteFilePath().contains(QLatin1String("mingw")); + }); + } + + for (const auto &candidate : qAsConst(candidates)) { + const QStringList toolchainTypes = toolchainTypeFromCompilerName( + candidate.baseName()); + const QString profileName = buildProfileName(candidate); + auto profile = createGccProfile(candidate, settings, + toolchainTypes, profileName); + profiles.push_back(std::move(profile)); + } +} diff --git a/src/app/qbs-setup-toolchains/gccprobe.h b/src/app/qbs-setup-toolchains/gccprobe.h new file mode 100644 index 000000000..4344c5836 --- /dev/null +++ b/src/app/qbs-setup-toolchains/gccprobe.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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_SETUPTOOLCHAINS_GCCPROBE_H +#define QBS_SETUPTOOLCHAINS_GCCPROBE_H + +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE +class QFileInfo; +QT_END_NAMESPACE + +namespace qbs { +class Profile; +class Settings; +} + +qbs::Profile createGccProfile(const QFileInfo &compiler, + qbs::Settings *settings, + const QStringList &toolchainTypes, + const QString &profileName = QString()); + +void gccProbe(qbs::Settings *settings, QList<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 5d3785759..47592b9c0 100644 --- a/src/app/qbs-setup-toolchains/iarewprobe.cpp +++ b/src/app/qbs-setup-toolchains/iarewprobe.cpp @@ -47,9 +47,9 @@ #include <tools/hostosinfo.h> #include <tools/profile.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qlist.h> +#include <QtCore/qprocess.h> #include <QtCore/qsettings.h> +#include <QtCore/qstandardpaths.h> using namespace qbs; using Internal::Tr; @@ -57,7 +57,9 @@ using Internal::HostOsInfo; static QStringList knownIarCompilerNames() { - return {QStringLiteral("icc8051"), QStringLiteral("iccarm"), QStringLiteral("iccavr")}; + return {QStringLiteral("icc8051"), QStringLiteral("iccarm"), + QStringLiteral("iccavr"), QStringLiteral("iccstm8"), + QStringLiteral("icc430")}; } static QString guessIarArchitecture(const QFileInfo &compiler) @@ -69,17 +71,31 @@ static QString guessIarArchitecture(const QFileInfo &compiler) return QStringLiteral("arm"); if (baseName == QLatin1String("iccavr")) return QStringLiteral("avr"); + if (baseName == QLatin1String("iccstm8")) + return QStringLiteral("stm8"); + if (baseName == QLatin1String("icc430")) + return QStringLiteral("msp430"); return {}; } -static Profile createIarProfileHelper(const QFileInfo &compiler, Settings *settings, +static Profile createIarProfileHelper(const ToolchainInstallInfo &info, + Settings *settings, QString profileName = QString()) { + const QFileInfo compiler = info.compilerPath; const QString architecture = guessIarArchitecture(compiler); // In case the profile is auto-detected. - if (profileName.isEmpty()) - profileName = QLatin1String("iar-") + architecture; + if (profileName.isEmpty()) { + if (!info.compilerVersion.isValid()) { + profileName = QStringLiteral("iar-unknown-%1").arg(architecture); + } else { + const QString version = info.compilerVersion.toString(QLatin1Char('_'), + QLatin1Char('_')); + profileName = QStringLiteral("iar-%1-%2").arg( + version, architecture); + } + } Profile profile(profileName, settings); profile.setValue(QLatin1String("cpp.toolchainInstallPath"), compiler.absolutePath()); @@ -92,9 +108,53 @@ static Profile createIarProfileHelper(const QFileInfo &compiler, Settings *setti return profile; } -static std::vector<IarInstallInfo> installedIarsFromPath() +static Version dumpIarCompilerVersion(const QFileInfo &compiler) +{ + const QString outFilePath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + + QLatin1String("/macros.dump"); + const QStringList args = {QStringLiteral("."), + QStringLiteral("--predef_macros"), + outFilePath}; + 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{}; + } + + QByteArray dump; + QFile out(outFilePath); + if (out.open(QIODevice::ReadOnly)) + dump = out.readAll(); + out.remove(); + + const int verCode = extractVersion(dump, "__VER__ "); + if (verCode < 0) { + qbsWarning() << Tr::tr("No '__VER__' token was found in a compiler dump:\n%1") + .arg(QString::fromUtf8(dump)); + return Version{}; + } + + const QString arch = guessIarArchitecture(compiler); + if (arch == QLatin1String("arm")) { + return Version{verCode / 1000000, (verCode / 1000) % 1000, verCode % 1000}; + } else if (arch == QLatin1String("avr") + || arch == QLatin1String("mcs51") + || arch == QLatin1String("stm8") + || arch == QLatin1String("msp430")) { + return Version{verCode / 100, verCode % 100}; + } + + return Version{}; +} + +static std::vector<ToolchainInstallInfo> installedIarsFromPath() { - std::vector<IarInstallInfo> infos; + std::vector<ToolchainInstallInfo> infos; const auto compilerNames = knownIarCompilerNames(); for (const QString &compilerName : compilerNames) { const QFileInfo iarPath( @@ -102,14 +162,16 @@ static std::vector<IarInstallInfo> installedIarsFromPath() HostOsInfo::appendExecutableSuffix(compilerName))); if (!iarPath.exists()) continue; - infos.push_back({iarPath.absoluteFilePath(), {}}); + const Version version = dumpIarCompilerVersion(iarPath); + infos.push_back({iarPath, version}); } + std::sort(infos.begin(), infos.end()); return infos; } -static std::vector<IarInstallInfo> installedIarsFromRegistry() +static std::vector<ToolchainInstallInfo> installedIarsFromRegistry() { - std::vector<IarInstallInfo> infos; + std::vector<ToolchainInstallInfo> infos; if (HostOsInfo::isWindowsHost()) { @@ -127,6 +189,8 @@ static std::vector<IarInstallInfo> installedIarsFromRegistry() {QStringLiteral("EWARM"), QStringLiteral("\\arm\\bin\\iccarm.exe")}, {QStringLiteral("EWAVR"), QStringLiteral("\\avr\\bin\\iccavr.exe")}, {QStringLiteral("EW8051"), QStringLiteral("\\8051\\bin\\icc8051.exe")}, + {QStringLiteral("EWSTM8"), QStringLiteral("\\stm8\\bin\\iccstm8.exe")}, + {QStringLiteral("EW430"), QStringLiteral("\\430\\bin\\icc430.exe")}, }; QSettings registry(QLatin1String(kRegistryNode), QSettings::NativeFormat); @@ -148,7 +212,7 @@ static std::vector<IarInstallInfo> installedIarsFromRegistry() if (iarPath.exists()) { // Note: threeLevelKey is a guessed toolchain version. const QString version = threeLevelKey; - infos.push_back({iarPath.absoluteFilePath(), version}); + infos.push_back({iarPath, Version::fromString(version)}); } } registry.endGroup(); @@ -161,6 +225,7 @@ static std::vector<IarInstallInfo> installedIarsFromRegistry() } + std::sort(infos.begin(), infos.end()); return infos; } @@ -175,19 +240,25 @@ bool isIarCompiler(const QString &compilerName) void createIarProfile(const QFileInfo &compiler, Settings *settings, QString profileName) { - createIarProfileHelper(compiler, settings, profileName); + const ToolchainInstallInfo info = {compiler, Version{}}; + createIarProfileHelper(info, settings, profileName); } void iarProbe(Settings *settings, QList<Profile> &profiles) { qbsInfo() << Tr::tr("Trying to detect IAR toolchains..."); - std::vector<IarInstallInfo> allInfos = installedIarsFromRegistry(); - const std::vector<IarInstallInfo> pathInfos = installedIarsFromPath(); - allInfos.insert(std::end(allInfos), std::begin(pathInfos), std::end(pathInfos)); + // Make sure that a returned infos are sorted before using the std::set_union! + const std::vector<ToolchainInstallInfo> regInfos = installedIarsFromRegistry(); + const std::vector<ToolchainInstallInfo> pathInfos = installedIarsFromPath(); + std::vector<ToolchainInstallInfo> allInfos; + allInfos.reserve(regInfos.size() + pathInfos.size()); + std::set_union(regInfos.cbegin(), regInfos.cend(), + pathInfos.cbegin(), pathInfos.cend(), + std::back_inserter(allInfos)); - for (const IarInstallInfo &info : allInfos) { - const auto profile = createIarProfileHelper(info.compilerPath, settings); + for (const ToolchainInstallInfo &info : allInfos) { + const auto profile = createIarProfileHelper(info, settings); profiles.push_back(profile); } diff --git a/src/app/qbs-setup-toolchains/iarewprobe.h b/src/app/qbs-setup-toolchains/iarewprobe.h index f5caf61ed..b604d6c6b 100644 --- a/src/app/qbs-setup-toolchains/iarewprobe.h +++ b/src/app/qbs-setup-toolchains/iarewprobe.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef IAREWPROBE_H -#define IAREWPROBE_H +#ifndef QBS_SETUPTOOLCHAINS_IAREWPROBE_H +#define QBS_SETUPTOOLCHAINS_IAREWPROBE_H #include <QtCore/qlist.h> @@ -51,12 +51,6 @@ class Profile; class Settings; } -struct IarInstallInfo -{ - QString compilerPath; - QString version; -}; - bool isIarCompiler(const QString &compilerName); void createIarProfile(const QFileInfo &compiler, qbs::Settings *settings, @@ -64,4 +58,4 @@ void createIarProfile(const QFileInfo &compiler, qbs::Settings *settings, void iarProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles); -#endif // IAREWPROBE_H +#endif // Header guard diff --git a/src/app/qbs-setup-toolchains/keilprobe.cpp b/src/app/qbs-setup-toolchains/keilprobe.cpp index dc327d2ee..08f0f2167 100644 --- a/src/app/qbs-setup-toolchains/keilprobe.cpp +++ b/src/app/qbs-setup-toolchains/keilprobe.cpp @@ -47,9 +47,9 @@ #include <tools/hostosinfo.h> #include <tools/profile.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qlist.h> +#include <QtCore/qprocess.h> #include <QtCore/qsettings.h> +#include <QtCore/qtemporaryfile.h> using namespace qbs; using Internal::Tr; @@ -70,14 +70,24 @@ static QString guessKeilArchitecture(const QFileInfo &compiler) return {}; } -static Profile createKeilProfileHelper(const QFileInfo &compiler, Settings *settings, +static Profile createKeilProfileHelper(const ToolchainInstallInfo &info, + Settings *settings, QString profileName = QString()) { + const QFileInfo compiler = info.compilerPath; const QString architecture = guessKeilArchitecture(compiler); // In case the profile is auto-detected. - if (profileName.isEmpty()) - profileName = QLatin1String("keil-") + architecture; + if (profileName.isEmpty()) { + if (!info.compilerVersion.isValid()) { + profileName = QStringLiteral("keil-unknown-%1").arg(architecture); + } else { + const QString version = info.compilerVersion.toString(QLatin1Char('_'), + QLatin1Char('_')); + profileName = QStringLiteral("keil-%1-%2").arg( + version, architecture); + } + } Profile profile(profileName, settings); profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), compiler.absolutePath()); @@ -90,9 +100,80 @@ static Profile createKeilProfileHelper(const QFileInfo &compiler, Settings *sett return profile; } -static std::vector<KeilInstallInfo> installedKeilsFromPath() +static Version dumpKeilCompilerVersion(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{}; + } + + 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{}; + } + + 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}; + } + + return Version{}; +} + +static std::vector<ToolchainInstallInfo> installedKeilsFromPath() { - std::vector<KeilInstallInfo> infos; + std::vector<ToolchainInstallInfo> infos; const auto compilerNames = knownKeilCompilerNames(); for (const QString &compilerName : compilerNames) { const QFileInfo keilPath( @@ -100,14 +181,16 @@ static std::vector<KeilInstallInfo> installedKeilsFromPath() HostOsInfo::appendExecutableSuffix(compilerName))); if (!keilPath.exists()) continue; - infos.push_back({keilPath.absoluteFilePath(), {}}); + const Version version = dumpKeilCompilerVersion(keilPath); + infos.push_back({keilPath, version}); } + std::sort(infos.begin(), infos.end()); return infos; } -static std::vector<KeilInstallInfo> installedKeilsFromRegistry() +static std::vector<ToolchainInstallInfo> installedKeilsFromRegistry() { - std::vector<KeilInstallInfo> infos; + std::vector<ToolchainInstallInfo> infos; if (HostOsInfo::isWindowsHost()) { @@ -148,7 +231,7 @@ static std::vector<KeilInstallInfo> installedKeilsFromRegistry() .toString(); if (version.startsWith(QLatin1Char('V'))) version.remove(0, 1); - infos.push_back({keilPath.absoluteFilePath(), version}); + infos.push_back({keilPath, Version::fromString(version)}); } } registry.endGroup(); @@ -156,6 +239,7 @@ static std::vector<KeilInstallInfo> installedKeilsFromRegistry() } + std::sort(infos.begin(), infos.end()); return infos; } @@ -170,19 +254,25 @@ bool isKeilCompiler(const QString &compilerName) void createKeilProfile(const QFileInfo &compiler, Settings *settings, QString profileName) { - createKeilProfileHelper(compiler, settings, profileName); + const ToolchainInstallInfo info = {compiler, Version{}}; + createKeilProfileHelper(info, settings, profileName); } void keilProbe(Settings *settings, QList<Profile> &profiles) { qbsInfo() << Tr::tr("Trying to detect KEIL toolchains..."); - std::vector<KeilInstallInfo> allInfos = installedKeilsFromRegistry(); - const std::vector<KeilInstallInfo> pathInfos = installedKeilsFromPath(); - allInfos.insert(std::end(allInfos), std::begin(pathInfos), std::end(pathInfos)); + // Make sure that a returned infos are sorted before using the std::set_union! + const std::vector<ToolchainInstallInfo> regInfos = installedKeilsFromRegistry(); + const std::vector<ToolchainInstallInfo> pathInfos = installedKeilsFromPath(); + std::vector<ToolchainInstallInfo> allInfos; + allInfos.reserve(regInfos.size() + pathInfos.size()); + std::set_union(regInfos.cbegin(), regInfos.cend(), + pathInfos.cbegin(), pathInfos.cend(), + std::back_inserter(allInfos)); - for (const KeilInstallInfo &info : allInfos) { - const auto profile = createKeilProfileHelper(info.compilerPath, settings); + for (const ToolchainInstallInfo &info : allInfos) { + const auto profile = createKeilProfileHelper(info, settings); profiles.push_back(profile); } diff --git a/src/app/qbs-setup-toolchains/keilprobe.h b/src/app/qbs-setup-toolchains/keilprobe.h index 6a897b84b..c7e66ee24 100644 --- a/src/app/qbs-setup-toolchains/keilprobe.h +++ b/src/app/qbs-setup-toolchains/keilprobe.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef KEILPROBE_H -#define KEILPROBE_H +#ifndef QBS_SETUPTOOLCHAINS_KEILPROBE_H +#define QBS_SETUPTOOLCHAINS_KEILPROBE_H #include <QtCore/qlist.h> @@ -51,12 +51,6 @@ class Profile; class Settings; } -struct KeilInstallInfo -{ - QString compilerPath; - QString version; -}; - bool isKeilCompiler(const QString &compilerName); void createKeilProfile(const QFileInfo &compiler, qbs::Settings *settings, @@ -64,4 +58,4 @@ void createKeilProfile(const QFileInfo &compiler, qbs::Settings *settings, void keilProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles); -#endif // KEILPROBE_H +#endif // Header guard diff --git a/src/app/qbs-setup-toolchains/msvcprobe.cpp b/src/app/qbs-setup-toolchains/msvcprobe.cpp index d0b60a7fe..c8108bfd2 100644 --- a/src/app/qbs-setup-toolchains/msvcprobe.cpp +++ b/src/app/qbs-setup-toolchains/msvcprobe.cpp @@ -464,9 +464,10 @@ void msvcProbe(Settings *settings, QList<Profile> &profiles) } } -void createMsvcProfile(const QString &profileName, const QString &compilerFilePath, - Settings *settings) +void createMsvcProfile(const QFileInfo &compiler, Settings *settings, + const QString &profileName) { + const auto compilerFilePath = compiler.absoluteFilePath(); MSVC msvc(compilerFilePath, MSVC::architectureFromClPath(compilerFilePath)); msvc.init(); QList<Profile> dummy; diff --git a/src/app/qbs-setup-toolchains/msvcprobe.h b/src/app/qbs-setup-toolchains/msvcprobe.h index 4fa2cde48..f63dd5dc8 100644 --- a/src/app/qbs-setup-toolchains/msvcprobe.h +++ b/src/app/qbs-setup-toolchains/msvcprobe.h @@ -37,13 +37,17 @@ ** ****************************************************************************/ -#ifndef MSVCPROBE_H -#define MSVCPROBE_H +#ifndef QBS_SETUPTOOLCHAINS_MSVCPROBE_H +#define QBS_SETUPTOOLCHAINS_MSVCPROBE_H #include <QtCore/qlist.h> #include <vector> +QT_BEGIN_NAMESPACE +class QFileInfo; +QT_END_NAMESPACE + namespace qbs { class Profile; class Settings; @@ -59,9 +63,9 @@ struct MSVCInstallInfo std::vector<MSVCInstallInfo> installedMSVCs(); -void createMsvcProfile(const QString &profileName, const QString &compilerFilePath, - qbs::Settings *settings); +void createMsvcProfile(const QFileInfo &compiler, qbs::Settings *settings, + const QString &profileName); void msvcProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles); -#endif // MSVCPROBE_H +#endif // Header guard diff --git a/src/app/qbs-setup-toolchains/probe.cpp b/src/app/qbs-setup-toolchains/probe.cpp index 3fae20a6e..205306acd 100644 --- a/src/app/qbs-setup-toolchains/probe.cpp +++ b/src/app/qbs-setup-toolchains/probe.cpp @@ -39,6 +39,7 @@ #include "probe.h" #include "clangclprobe.h" +#include "gccprobe.h" #include "iarewprobe.h" #include "keilprobe.h" #include "msvcprobe.h" @@ -46,22 +47,26 @@ #include "xcodeprobe.h" #include <logging/translator.h> -#include <tools/architectures.h> #include <tools/error.h> #include <tools/hostosinfo.h> #include <tools/profile.h> -#include <tools/qttools.h> #include <tools/settings.h> #include <tools/toolchains.h> -#include <tools/stlutils.h> #include <QtCore/qdir.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qprocess.h> -#include <QtCore/qstringlist.h> +#include <QtCore/qoperatingsystemversion.h> #include <QtCore/qtextstream.h> -#include <cstdio> +#ifdef Q_OS_WIN +// We need defines for Windows 8. +#undef _WIN32_WINNT +#define _WIN32_WINNT _WIN32_WINNT_WIN8 + +#include <qt_windows.h> +#include <shlobj.h> +#else +#include <qplatformdefs.h> +#endif // Q_OS_WIN using namespace qbs; using Internal::HostOsInfo; @@ -70,6 +75,11 @@ using Internal::Tr; static QTextStream qStdout(stdout); static QTextStream qStderr(stderr); +QStringList systemSearchPaths() +{ + return QString::fromLocal8Bit(qgetenv("PATH")).split(HostOsInfo::pathListSeparator()); +} + QString findExecutable(const QString &fileName) { QString fullFileName = fileName; @@ -77,8 +87,7 @@ QString findExecutable(const QString &fileName) && !fileName.endsWith(QLatin1String(".exe"), Qt::CaseInsensitive)) { fullFileName += QLatin1String(".exe"); } - const QString path = QString::fromLocal8Bit(qgetenv("PATH")); - const auto ppaths = path.split(HostOsInfo::pathListSeparator()); + const auto ppaths = systemSearchPaths(); for (const QString &ppath : ppaths) { const QString fullPath = ppath + QLatin1Char('/') + fullFileName; if (QFileInfo::exists(fullPath)) @@ -87,31 +96,7 @@ QString findExecutable(const QString &fileName) return {}; } -static QString qsystem(const QString &exe, const QStringList &args = QStringList()) -{ - QProcess p; - p.setProcessChannelMode(QProcess::MergedChannels); - p.start(exe, args); - if (!p.waitForStarted()) { - throw qbs::ErrorInfo(Tr::tr("Failed to start compiler '%1': %2") - .arg(exe, p.errorString())); - } - if (!p.waitForFinished(-1) || p.exitCode() != 0) - throw qbs::ErrorInfo(Tr::tr("Failed to run compiler '%1': %2").arg(exe, p.errorString())); - return QString::fromLocal8Bit(p.readAll()); -} - -static QStringList validMinGWMachines() -{ - // List of MinGW machine names (gcc -dumpmachine) recognized by Qbs - return {QStringLiteral("mingw32"), QStringLiteral("mingw64"), - QStringLiteral("i686-w64-mingw32"), QStringLiteral("x86_64-w64-mingw32"), - QStringLiteral("i686-w64-mingw32.shared"), QStringLiteral("x86_64-w64-mingw32.shared"), - QStringLiteral("i686-w64-mingw32.static"), QStringLiteral("x86_64-w64-mingw32.static"), - QStringLiteral("i586-mingw32msvc"), QStringLiteral("amd64-mingw32msvc")}; -} - -static QStringList toolchainTypeFromCompilerName(const QString &compilerName) +QStringList toolchainTypeFromCompilerName(const QString &compilerName) { if (compilerName == QLatin1String("cl.exe")) return canonicalToolchain(QStringLiteral("msvc")); @@ -134,174 +119,19 @@ static QStringList toolchainTypeFromCompilerName(const QString &compilerName) return {}; } -static QString gccMachineName(const QString &compilerFilePath) -{ - return qsystem(compilerFilePath, QStringList() << QStringLiteral("-dumpmachine")).trimmed(); -} - -static QStringList standardCompilerFileNames() -{ - return {HostOsInfo::appendExecutableSuffix(QStringLiteral("gcc")), - HostOsInfo::appendExecutableSuffix(QStringLiteral("g++")), - HostOsInfo::appendExecutableSuffix(QStringLiteral("clang")), - HostOsInfo::appendExecutableSuffix(QStringLiteral("clang++"))}; -} - -static void setCommonProperties(Profile &profile, const QString &compilerFilePath, - const QStringList &toolchainTypes) -{ - const QFileInfo cfi(compilerFilePath); - const QString compilerName = cfi.fileName(); - - if (toolchainTypes.contains(QStringLiteral("mingw"))) - profile.setValue(QStringLiteral("qbs.targetPlatform"), QStringLiteral("windows")); - - const QString prefix = compilerName.left(compilerName.lastIndexOf(QLatin1Char('-')) + 1); - if (!prefix.isEmpty()) - profile.setValue(QStringLiteral("cpp.toolchainPrefix"), prefix); - - profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), cfi.absolutePath()); - profile.setValue(QStringLiteral("qbs.toolchain"), toolchainTypes); - - const QString suffix = compilerName.right(compilerName.size() - prefix.size()); - if (!standardCompilerFileNames().contains(suffix)) - qWarning("%s", qPrintable( - QStringLiteral("'%1' is not a standard compiler file name; " - "you must set the cpp.cCompilerName and " - "cpp.cxxCompilerName properties of this profile " - "manually").arg(compilerName))); -} - -class ToolPathSetup -{ -public: - ToolPathSetup(Profile *profile, QString path, QString toolchainPrefix) - : m_profile(profile), - m_compilerDirPath(std::move(path)), - m_toolchainPrefix(std::move(toolchainPrefix)) - { - } - - void apply(const QString &toolName, const QString &propertyName) const - { - QString toolFileName = m_toolchainPrefix + HostOsInfo::appendExecutableSuffix(toolName); - if (QFile::exists(m_compilerDirPath + QLatin1Char('/') + toolFileName)) - return; - const QString toolFilePath = findExecutable(toolFileName); - if (toolFilePath.isEmpty()) { - qWarning("%s", qPrintable(QStringLiteral("'%1' exists neither in '%2' nor in PATH.") - .arg(toolFileName, m_compilerDirPath))); - } - m_profile->setValue(propertyName, toolFilePath); - } - -private: - Profile * const m_profile; - QString m_compilerDirPath; - QString m_toolchainPrefix; -}; - -static bool doesProfileTargetOS(const Profile &profile, const QString &os) -{ - const auto target = profile.value(QStringLiteral("qbs.targetPlatform")); - if (target.isValid()) { - return Internal::contains(HostOsInfo::canonicalOSIdentifiers( - target.toString().toStdString()), os.toStdString()); - } - return Internal::contains(HostOsInfo::hostOSIdentifiers(), os.toStdString()); -} - -static Profile createGccProfile(const QString &compilerFilePath, Settings *settings, - const QStringList &toolchainTypes, - const QString &profileName = QString()) -{ - const QString machineName = gccMachineName(compilerFilePath); - - if (toolchainTypes.contains(QLatin1String("mingw"))) { - if (!validMinGWMachines().contains(machineName)) { - throw ErrorInfo(Tr::tr("Detected gcc platform '%1' is not supported.") - .arg(machineName)); - } - } - - Profile profile(!profileName.isEmpty() ? profileName : machineName, settings); - profile.removeProfile(); - setCommonProperties(profile, compilerFilePath, toolchainTypes); - - // 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 = QFileInfo(compilerFilePath).absolutePath(); - const ToolPathSetup toolPathSetup(&profile, compilerDirPath, - profile.value(QStringLiteral("cpp.toolchainPrefix")) - .toString()); - toolPathSetup.apply(QStringLiteral("ar"), QStringLiteral("cpp.archiverPath")); - toolPathSetup.apply(QStringLiteral("as"), QStringLiteral("cpp.assemblerPath")); - toolPathSetup.apply(QStringLiteral("nm"), QStringLiteral("cpp.nmPath")); - if (doesProfileTargetOS(profile, QStringLiteral("darwin"))) - toolPathSetup.apply(QStringLiteral("dsymutil"), QStringLiteral("cpp.dsymutilPath")); - else - toolPathSetup.apply(QStringLiteral("objcopy"), QStringLiteral("cpp.objcopyPath")); - toolPathSetup.apply(QStringLiteral("strip"), QStringLiteral("cpp.stripPath")); - - qStdout << Tr::tr("Profile '%1' created for '%2'.").arg(profile.name(), compilerFilePath) - << endl; - return profile; -} - -static void gccProbe(Settings *settings, QList<Profile> &profiles, const QString &compilerName) -{ - qStdout << Tr::tr("Trying to detect %1...").arg(compilerName) << endl; - - const QString crossCompilePrefix = QString::fromLocal8Bit(qgetenv("CROSS_COMPILE")); - const QString compilerFilePath = findExecutable(crossCompilePrefix + compilerName); - QFileInfo cfi(compilerFilePath); - if (!cfi.exists()) { - qStderr << Tr::tr("%1 not found.").arg(compilerName) << endl; - return; - } - const QString profileName = cfi.completeBaseName(); - const QStringList toolchainTypes = toolchainTypeFromCompilerName(compilerName); - profiles.push_back(createGccProfile(compilerFilePath, settings, toolchainTypes, profileName)); -} - -static void mingwProbe(Settings *settings, QList<Profile> &profiles) -{ - // List of possible compiler binary names for this platform - QStringList compilerNames; - if (HostOsInfo::isWindowsHost()) { - compilerNames << QStringLiteral("gcc"); - } else { - const auto machineNames = validMinGWMachines(); - for (const QString &machineName : machineNames) { - compilerNames << machineName + QLatin1String("-gcc"); - } - } - - for (const QString &compilerName : qAsConst(compilerNames)) { - const QString gccPath - = findExecutable(HostOsInfo::appendExecutableSuffix(compilerName)); - if (!gccPath.isEmpty()) - profiles.push_back(createGccProfile(gccPath, settings, - canonicalToolchain(QStringLiteral("mingw")))); - } -} - void probe(Settings *settings) { QList<Profile> profiles; if (HostOsInfo::isWindowsHost()) { msvcProbe(settings, profiles); clangClProbe(settings, profiles); - } else { - gccProbe(settings, profiles, QStringLiteral("gcc")); - gccProbe(settings, profiles, QStringLiteral("clang")); - - if (HostOsInfo::isMacosHost()) { - xcodeProbe(settings, profiles); - } + } else if (HostOsInfo::isMacosHost()) { + xcodeProbe(settings, profiles); } - mingwProbe(settings, profiles); + gccProbe(settings, profiles, QStringLiteral("gcc")); + gccProbe(settings, profiles, QStringLiteral("clang")); + iarProbe(settings, profiles); keilProbe(settings, profiles); sdccProbe(settings, profiles); @@ -334,11 +164,11 @@ void createProfile(const QString &profileName, const QString &toolchainType, toolchainTypes = canonicalToolchain(toolchainType); if (toolchainTypes.contains(QLatin1String("msvc"))) - createMsvcProfile(profileName, compiler.absoluteFilePath(), settings); + createMsvcProfile(compiler, settings, profileName); else if (toolchainTypes.contains(QLatin1String("clang-cl"))) - createClangClProfile(profileName, compiler.absoluteFilePath(), settings); + createClangClProfile(compiler, settings, profileName); else if (toolchainTypes.contains(QLatin1String("gcc"))) - createGccProfile(compiler.absoluteFilePath(), settings, toolchainTypes, profileName); + createGccProfile(compiler, settings, toolchainTypes, profileName); else if (toolchainTypes.contains(QLatin1String("iar"))) createIarProfile(compiler, settings, profileName); else if (toolchainTypes.contains(QLatin1String("keil"))) @@ -348,3 +178,126 @@ void createProfile(const QString &profileName, const QString &toolchainType, else throw qbs::ErrorInfo(Tr::tr("Cannot create profile: Unknown toolchain type.")); } + +int extractVersion(const QByteArray ¯oDump, const QByteArray &keyToken) +{ + const int startIndex = macroDump.indexOf(keyToken); + if (startIndex == -1) + return -1; + const int endIndex = macroDump.indexOf('\n', startIndex); + if (endIndex == -1) + return -1; + const auto keyLength = keyToken.length(); + const int version = macroDump.mid(startIndex + keyLength, + endIndex - startIndex - keyLength) + .toInt(); + return version; +} + +static QString resolveSymlinks(const QString &filePath) +{ + QFileInfo fi(filePath); + int links = 16; + while (links-- && fi.isSymLink()) + fi.setFile(fi.dir(), fi.symLinkTarget()); + if (links <= 0) + return {}; + return fi.filePath(); +} + +// Copied from qfilesystemengine_win.cpp. +#ifdef Q_OS_WIN + +// File ID for Windows up to version 7. +static inline QByteArray fileIdWin7(HANDLE handle) +{ + BY_HANDLE_FILE_INFORMATION info; + if (GetFileInformationByHandle(handle, &info)) { + char buffer[sizeof "01234567:0123456701234567\0"]; + qsnprintf(buffer, sizeof(buffer), "%lx:%08lx%08lx", + info.dwVolumeSerialNumber, + info.nFileIndexHigh, + info.nFileIndexLow); + return QByteArray(buffer); + } + return {}; +} + +// File ID for Windows starting from version 8. +static QByteArray fileIdWin8(HANDLE handle) +{ + QByteArray result; + FILE_ID_INFO infoEx = {}; + if (::GetFileInformationByHandleEx( + handle, + static_cast<FILE_INFO_BY_HANDLE_CLASS>(18), // FileIdInfo in Windows 8 + &infoEx, sizeof(FILE_ID_INFO))) { + result = QByteArray::number(infoEx.VolumeSerialNumber, 16); + result += ':'; + // Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one. + result += QByteArray(reinterpret_cast<const char *>(&infoEx.FileId), + int(sizeof(infoEx.FileId))).toHex(); + } + return result; +} + +static QByteArray fileIdWin(HANDLE fHandle) +{ + return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8 + ? fileIdWin8(HANDLE(fHandle)) + : fileIdWin7(HANDLE(fHandle)); +} + +static QByteArray fileId(const QString &filePath) +{ + QByteArray result; + const HANDLE handle = ::CreateFile( + reinterpret_cast<const wchar_t*>(filePath.utf16()), 0, + FILE_SHARE_READ, nullptr, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (handle != INVALID_HANDLE_VALUE) { + result = fileIdWin(handle); + ::CloseHandle(handle); + } + return result; +} + +static qint64 fileSize(const QString &filePath) +{ + return QFileInfo(filePath).size(); +} + +#else + +static QByteArray fileId(const QString &filePath) +{ + QByteArray result; + if (Q_UNLIKELY(filePath.isEmpty())) + return {}; + QT_STATBUF statResult = {}; + if (QT_STAT(filePath.toLocal8Bit().constData(), &statResult)) + return {}; + result = QByteArray::number(quint64(statResult.st_dev), 16); + result += ':'; + result += QByteArray::number(quint64(statResult.st_ino)); + return result; +} + +#endif // Q_OS_WIN + +bool isSameExecutable(const QString &filePath1, const QString &filePath2) +{ + if (filePath1 == filePath2) + return true; + if (resolveSymlinks(filePath1) == resolveSymlinks(filePath2)) + return true; + if (fileId(filePath1) == fileId(filePath2)) + return true; + +#ifdef Q_OS_WIN + if (fileSize(filePath1) == fileSize(filePath2)) + return true; +#endif + + return false; +} diff --git a/src/app/qbs-setup-toolchains/probe.h b/src/app/qbs-setup-toolchains/probe.h index 510747ef7..235d7a899 100644 --- a/src/app/qbs-setup-toolchains/probe.h +++ b/src/app/qbs-setup-toolchains/probe.h @@ -36,10 +36,15 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#ifndef QBS_PROBE_H -#define QBS_PROBE_H -#include <QtCore/qglobal.h> +#ifndef QBS_SETUPTOOLCHAINS_PROBE_H +#define QBS_SETUPTOOLCHAINS_PROBE_H + +#include <tools/version.h> + +#include <QtCore/qfileinfo.h> + +#include <tuple> // for std::tie QT_BEGIN_NAMESPACE class QString; @@ -48,11 +53,32 @@ QT_END_NAMESPACE namespace qbs { class Settings; } +QStringList systemSearchPaths(); + QString findExecutable(const QString &fileName); +QStringList toolchainTypeFromCompilerName(const QString &compilerName); + void createProfile(const QString &profileName, const QString &toolchainType, const QString &compilerFilePath, qbs::Settings *settings); void probe(qbs::Settings *settings); +struct ToolchainInstallInfo +{ + QFileInfo compilerPath; + qbs::Version compilerVersion; +}; + +inline bool operator<(const ToolchainInstallInfo &lhs, const ToolchainInstallInfo &rhs) +{ + const auto lp = lhs.compilerPath.absoluteFilePath(); + const auto rp = rhs.compilerPath.absoluteFilePath(); + return std::tie(lp, lhs.compilerVersion) < std::tie(rp, rhs.compilerVersion); +} + +int extractVersion(const QByteArray ¯oDump, const QByteArray &keyToken); + +bool isSameExecutable(const QString &exe1, const QString &exe2); + #endif // Header guard diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro index e83a9c716..6581bec63 100644 --- a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro @@ -5,6 +5,7 @@ TARGET = qbs-setup-toolchains HEADERS += \ clangclprobe.h \ commandlineparser.h \ + gccprobe.h \ iarewprobe.h \ keilprobe.h \ msvcprobe.h \ @@ -15,6 +16,7 @@ HEADERS += \ SOURCES += \ clangclprobe.cpp \ commandlineparser.cpp \ + gccprobe.cpp \ iarewprobe.cpp \ keilprobe.cpp \ main.cpp \ diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs index fd3043a28..554891fc4 100644 --- a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs @@ -8,6 +8,8 @@ QbsApp { "clangclprobe.h", "commandlineparser.cpp", "commandlineparser.h", + "gccprobe.cpp", + "gccprobe.h", "iarewprobe.cpp", "iarewprobe.h", "keilprobe.cpp", diff --git a/src/app/qbs-setup-toolchains/sdccprobe.cpp b/src/app/qbs-setup-toolchains/sdccprobe.cpp index 751e872ee..e916a405b 100644 --- a/src/app/qbs-setup-toolchains/sdccprobe.cpp +++ b/src/app/qbs-setup-toolchains/sdccprobe.cpp @@ -47,9 +47,9 @@ #include <tools/hostosinfo.h> #include <tools/profile.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qlist.h> +#include <QtCore/qprocess.h> #include <QtCore/qsettings.h> +#include <QtCore/qtemporaryfile.h> using namespace qbs; using Internal::Tr; @@ -60,37 +60,119 @@ static QStringList knownSdccCompilerNames() return {QStringLiteral("sdcc")}; } -static QString guessSdccArchitecture(const QFileInfo &compiler) +static QByteArray dumpSdccMacros(const QFileInfo &compiler, + const QString &targetFlag = QString()) { - const auto baseName = compiler.baseName(); - if (baseName == QLatin1String("sdcc")) - return QStringLiteral("mcs51"); - return {}; + QTemporaryFile fakeIn(QStringLiteral("XXXXXX.c")); + if (!fakeIn.open()) { + qbsWarning() << Tr::tr("Unable to open temporary file %1 for output: %2") + .arg(fakeIn.fileName(), fakeIn.errorString()); + return {}; + } + fakeIn.close(); + + const QStringList args = {QStringLiteral("-dM"), + QStringLiteral("-E"), + targetFlag, + 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 {}; + } + + return p.readAllStandardOutput(); +} + +static QString dumpSdccArchitecture(const QFileInfo &compiler, + const QString &arch) +{ + const auto targetFlag = QStringLiteral("-m%1").arg(arch); + const auto token = QStringLiteral("__SDCC_%1").arg(arch); + const auto macros = dumpSdccMacros(compiler, targetFlag); + return macros.contains(token.toLatin1()) ? arch : QString(); } -static Profile createSdccProfileHelper(const QFileInfo &compiler, Settings *settings, - QString profileName = QString()) +static std::vector<Profile> createSdccProfileHelper( + const ToolchainInstallInfo &info, + Settings *settings, + const QString &profileName = QString()) { - const QString architecture = guessSdccArchitecture(compiler); + const QFileInfo compiler = info.compilerPath; + + std::vector<Profile> profiles; + + const char *knownArchs[] = {"mcs51", "stm8"}; + for (const auto &knownArch : knownArchs) { + const auto actualArch = dumpSdccArchitecture( + compiler, QString::fromLatin1(knownArch)); + // Don't create a profile in case the compiler does + // not support the proposed architecture. + if (actualArch != QString::fromLatin1(knownArch)) + continue; - // In case the profile is auto-detected. - if (profileName.isEmpty()) - profileName = QLatin1String("sdcc-") + architecture; + QString fullProfileName = profileName; + if (fullProfileName.isEmpty()) { + // Create a full profile name in case we is + // in auto-detecting mode. + if (!info.compilerVersion.isValid()) { + fullProfileName = QStringLiteral("sdcc-unknown-%1") + .arg(actualArch); + } else { + const QString version = info.compilerVersion.toString( + QLatin1Char('_'), QLatin1Char('_')); + fullProfileName = QStringLiteral("sdcc-%1-%2").arg( + version, actualArch); + } + } else { + // Append the detected actual architecture name + // to the proposed profile name. + fullProfileName = QStringLiteral("%1-%2").arg( + fullProfileName, actualArch); + } + + Profile profile(fullProfileName, settings); + profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), + compiler.absolutePath()); + profile.setValue(QStringLiteral("qbs.toolchainType"), + QStringLiteral("sdcc")); + if (!actualArch.isEmpty()) + profile.setValue(QStringLiteral("qbs.architecture"), actualArch); + + qbsInfo() << Tr::tr("Profile '%1' created for '%2'.").arg( + profile.name(), compiler.absoluteFilePath()); + } - Profile profile(profileName, settings); - profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), compiler.absolutePath()); - profile.setValue(QStringLiteral("qbs.toolchainType"), QStringLiteral("sdcc")); - if (!architecture.isEmpty()) - profile.setValue(QStringLiteral("qbs.architecture"), architecture); + return profiles; +} + +static Version dumpSdccCompilerVersion(const QFileInfo &compiler) +{ + const QByteArray dump = dumpSdccMacros(compiler); + if (dump.isEmpty()) + return Version{}; + + const int major = extractVersion(dump, "__SDCC_VERSION_MAJOR "); + 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{}; + } - qbsInfo() << Tr::tr("Profile '%1' created for '%2'.").arg( - profile.name(), compiler.absoluteFilePath()); - return profile; + return Version{major, minor, patch}; } -static std::vector<SdccInstallInfo> installedSdccsFromPath() +static std::vector<ToolchainInstallInfo> installedSdccsFromPath() { - std::vector<SdccInstallInfo> infos; + std::vector<ToolchainInstallInfo> infos; const auto compilerNames = knownSdccCompilerNames(); for (const QString &compilerName : compilerNames) { const QFileInfo sdccPath( @@ -98,14 +180,16 @@ static std::vector<SdccInstallInfo> installedSdccsFromPath() HostOsInfo::appendExecutableSuffix(compilerName))); if (!sdccPath.exists()) continue; - infos.push_back({sdccPath.absoluteFilePath(), {}}); + const Version version = dumpSdccCompilerVersion(sdccPath); + infos.push_back({sdccPath, version}); } + std::sort(infos.begin(), infos.end()); return infos; } -static std::vector<SdccInstallInfo> installedSdccsFromRegistry() +static std::vector<ToolchainInstallInfo> installedSdccsFromRegistry() { - std::vector<SdccInstallInfo> infos; + std::vector<ToolchainInstallInfo> infos; if (HostOsInfo::isWindowsHost()) { @@ -126,11 +210,12 @@ static std::vector<SdccInstallInfo> installedSdccsFromRegistry() registry.value(QStringLiteral("VersionMajor")).toString(), registry.value(QStringLiteral("VersionMinor")).toString(), registry.value(QStringLiteral("VersionRevision")).toString()); - infos.push_back({sdccPath.absoluteFilePath(), version}); + infos.push_back({sdccPath, Version::fromString(version)}); } } } + std::sort(infos.begin(), infos.end()); return infos; } @@ -145,20 +230,28 @@ bool isSdccCompiler(const QString &compilerName) void createSdccProfile(const QFileInfo &compiler, Settings *settings, QString profileName) { - createSdccProfileHelper(compiler, settings, profileName); + const ToolchainInstallInfo info = {compiler, Version{}}; + createSdccProfileHelper(info, settings, profileName); } void sdccProbe(Settings *settings, QList<Profile> &profiles) { qbsInfo() << Tr::tr("Trying to detect SDCC toolchains..."); - std::vector<SdccInstallInfo> allInfos = installedSdccsFromRegistry(); - const std::vector<SdccInstallInfo> pathInfos = installedSdccsFromPath(); - allInfos.insert(std::end(allInfos), std::begin(pathInfos), std::end(pathInfos)); + // Make sure that a returned infos are sorted before using the std::set_union! + const std::vector<ToolchainInstallInfo> regInfos = installedSdccsFromRegistry(); + const std::vector<ToolchainInstallInfo> pathInfos = installedSdccsFromPath(); + std::vector<ToolchainInstallInfo> allInfos; + allInfos.reserve(regInfos.size() + pathInfos.size()); + std::set_union(regInfos.cbegin(), regInfos.cend(), + pathInfos.cbegin(), pathInfos.cend(), + std::back_inserter(allInfos)); - for (const SdccInstallInfo &info : allInfos) { - const auto profile = createSdccProfileHelper(info.compilerPath, settings); - profiles.push_back(profile); + for (const ToolchainInstallInfo &info : allInfos) { + const auto newProfiles = createSdccProfileHelper(info, settings); + profiles.reserve(profiles.size() + int(newProfiles.size())); + std::copy(newProfiles.cbegin(), newProfiles.cend(), + std::back_inserter(profiles)); } if (allInfos.empty()) diff --git a/src/app/qbs-setup-toolchains/sdccprobe.h b/src/app/qbs-setup-toolchains/sdccprobe.h index 7f4219b5a..5fad90e96 100644 --- a/src/app/qbs-setup-toolchains/sdccprobe.h +++ b/src/app/qbs-setup-toolchains/sdccprobe.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef SDCCPROBE_H -#define SDCCPROBE_H +#ifndef QBS_SETUPTOOLCHAINS_SDCCPROBE_H +#define QBS_SETUPTOOLCHAINS_SDCCPROBE_H #include <QtCore/qlist.h> @@ -51,12 +51,6 @@ class Profile; class Settings; } -struct SdccInstallInfo -{ - QString compilerPath; - QString version; -}; - bool isSdccCompiler(const QString &compilerName); void createSdccProfile(const QFileInfo &compiler, qbs::Settings *settings, @@ -64,4 +58,4 @@ void createSdccProfile(const QFileInfo &compiler, qbs::Settings *settings, void sdccProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles); -#endif // SDCCPROBE_H +#endif // Header guard diff --git a/src/app/qbs-setup-toolchains/xcodeprobe.h b/src/app/qbs-setup-toolchains/xcodeprobe.h index 11fc2bffa..ab501036b 100644 --- a/src/app/qbs-setup-toolchains/xcodeprobe.h +++ b/src/app/qbs-setup-toolchains/xcodeprobe.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef XCODEPROBE_H -#define XCODEPROBE_H +#ifndef QBS_SETUPTOOLCHAINS_XCODEPROBE_H +#define QBS_SETUPTOOLCHAINS_XCODEPROBE_H #include <QtCore/qlist.h> @@ -49,4 +49,4 @@ class Settings; void xcodeProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles); -#endif // XCODEPROBE_H +#endif // Header guard |