/**************************************************************************** ** ** Copyright (C) 2019 Denis Shienkov ** 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 "iarewprobe.h" #include "../shared/logging/consolelogger.h" #include #include #include #include #include #include using namespace qbs; using Internal::Tr; using Internal::HostOsInfo; static QStringList knownIarCompilerNames() { return {QStringLiteral("icc8051"), QStringLiteral("iccarm"), QStringLiteral("iccavr"), QStringLiteral("iccstm8"), QStringLiteral("icc430")}; } static QString guessIarArchitecture(const QFileInfo &compiler) { const auto baseName = compiler.baseName(); if (baseName == QLatin1String("icc8051")) return QStringLiteral("mcs51"); if (baseName == QLatin1String("iccarm")) 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 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()) { 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()); profile.setValue(QLatin1String("qbs.toolchainType"), QLatin1String("iar")); if (!architecture.isEmpty()) profile.setValue(QLatin1String("qbs.architecture"), architecture); qbsInfo() << Tr::tr("Profile '%1' created for '%2'.").arg( profile.name(), compiler.absoluteFilePath()); return profile; } 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 installedIarsFromPath() { std::vector infos; const auto compilerNames = knownIarCompilerNames(); for (const QString &compilerName : compilerNames) { const QFileInfo iarPath( findExecutable( HostOsInfo::appendExecutableSuffix(compilerName))); if (!iarPath.exists()) continue; const Version version = dumpIarCompilerVersion(iarPath); infos.push_back({iarPath, version}); } std::sort(infos.begin(), infos.end()); return infos; } static std::vector installedIarsFromRegistry() { std::vector infos; if (HostOsInfo::isWindowsHost()) { #ifdef Q_OS_WIN64 static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\IAR Systems\\Embedded Workbench"; #else static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\IAR Systems\\Embedded Workbench"; #endif // Dictionary for know toolchains. static const struct Entry { QString registryKey; QString subExePath; } knowToolchains[] = { {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); const auto oneLevelGroups = registry.childGroups(); for (const QString &oneLevelKey : oneLevelGroups) { registry.beginGroup(oneLevelKey); const auto twoLevelGroups = registry.childGroups(); for (const Entry &entry : knowToolchains) { if (twoLevelGroups.contains(entry.registryKey)) { registry.beginGroup(entry.registryKey); const auto threeLevelGroups = registry.childGroups(); for (const QString &threeLevelKey : threeLevelGroups) { registry.beginGroup(threeLevelKey); const QString rootPath = registry.value( QStringLiteral("InstallPath")).toString(); if (!rootPath.isEmpty()) { // Build full compiler path. 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)}); } } registry.endGroup(); } registry.endGroup(); } } registry.endGroup(); } } std::sort(infos.begin(), infos.end()); return infos; } bool isIarCompiler(const QString &compilerName) { return Internal::any_of(knownIarCompilerNames(), [compilerName]( const QString &knownName) { return compilerName.contains(knownName); }); } void createIarProfile(const QFileInfo &compiler, Settings *settings, QString profileName) { const ToolchainInstallInfo info = {compiler, Version{}}; createIarProfileHelper(info, settings, profileName); } void iarProbe(Settings *settings, QList &profiles) { qbsInfo() << Tr::tr("Trying to detect IAR toolchains..."); // Make sure that a returned infos are sorted before using the std::set_union! const std::vector regInfos = installedIarsFromRegistry(); const std::vector pathInfos = installedIarsFromPath(); std::vector allInfos; allInfos.reserve(regInfos.size() + pathInfos.size()); std::set_union(regInfos.cbegin(), regInfos.cend(), pathInfos.cbegin(), pathInfos.cend(), std::back_inserter(allInfos)); for (const ToolchainInstallInfo &info : allInfos) { const auto profile = createIarProfileHelper(info, settings); profiles.push_back(profile); } if (allInfos.empty()) qbsInfo() << Tr::tr("No IAR toolchains found."); }