diff options
Diffstat (limited to 'src/app/qbs-setup-toolchains/sdccprobe.cpp')
-rw-r--r-- | src/app/qbs-setup-toolchains/sdccprobe.cpp | 161 |
1 files changed, 127 insertions, 34 deletions
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()) |