diff options
Diffstat (limited to 'src/app/qbs-setup-toolchains/keilprobe.cpp')
-rw-r--r-- | src/app/qbs-setup-toolchains/keilprobe.cpp | 124 |
1 files changed, 107 insertions, 17 deletions
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); } |