diff options
Diffstat (limited to 'src/app/qbs-setup-toolchains/gccprobe.cpp')
-rw-r--r-- | src/app/qbs-setup-toolchains/gccprobe.cpp | 265 |
1 files changed, 186 insertions, 79 deletions
diff --git a/src/app/qbs-setup-toolchains/gccprobe.cpp b/src/app/qbs-setup-toolchains/gccprobe.cpp index 2f2a22465..e72e39bbb 100644 --- a/src/app/qbs-setup-toolchains/gccprobe.cpp +++ b/src/app/qbs-setup-toolchains/gccprobe.cpp @@ -85,10 +85,10 @@ static QStringList validMinGWMachines() QStringLiteral("amd64-mingw32msvc")}; } -static QString gccMachineName(const QString &compilerFilePath) +static QString gccMachineName(const QFileInfo &compiler) { - return qsystem(compilerFilePath, QStringList() - << QStringLiteral("-dumpmachine")).trimmed(); + return qsystem(compiler.absoluteFilePath(), {QStringLiteral("-dumpmachine")}) + .trimmed(); } static QStringList standardCompilerFileNames() @@ -99,63 +99,116 @@ static QStringList standardCompilerFileNames() HostOsInfo::appendExecutableSuffix(QStringLiteral("clang++"))}; } -static void setCommonProperties(Profile &profile, const QString &compilerFilePath, - const QStringList &toolchainTypes) +class ToolchainDetails { - const QFileInfo cfi(compilerFilePath); - const QString compilerName = cfi.fileName(); +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")); - const QString prefix = compilerName.left( - compilerName.lastIndexOf(QLatin1Char('-')) + 1); - if (!prefix.isEmpty()) - profile.setValue(QStringLiteral("cpp.toolchainPrefix"), prefix); + if (!details.prefix.isEmpty()) + profile.setValue(QStringLiteral("cpp.toolchainPrefix"), details.prefix); profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), - cfi.absolutePath()); + compiler.absolutePath()); profile.setValue(QStringLiteral("qbs.toolchain"), toolchainTypes); - const QString suffix = compilerName.right(compilerName.size() - prefix.size()); - if (!standardCompilerFileNames().contains(suffix)) + 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(compilerName))); + "manually").arg(compiler.fileName()))); + } } class ToolPathSetup { public: - explicit ToolPathSetup(Profile *profile, QString path, QString toolchainPrefix) + explicit ToolPathSetup(Profile *profile, QString path, ToolchainDetails details) : m_profile(profile), m_compilerDirPath(std::move(path)), - m_toolchainPrefix(std::move(toolchainPrefix)) + m_details(std::move(details)) { } 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()) { + // 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' exists neither in '%2' nor in PATH.") - .arg(toolFileName, m_compilerDirPath))); + QStringLiteral("'%1' not found in '%2'. " + "Qbs will try to find it in PATH at build time.") + .arg(toolName, m_compilerDirPath))); } - m_profile->setValue(propertyName, toolFilePath); + + 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; - QString m_toolchainPrefix; + ToolchainDetails m_details; }; static bool doesProfileTargetOS(const Profile &profile, const QString &os) @@ -169,12 +222,48 @@ static bool doesProfileTargetOS(const Profile &profile, const QString &os) 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, + // "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; +} + Profile createGccProfile(const QFileInfo &compiler, Settings *settings, const QStringList &toolchainTypes, const QString &profileName) { - const auto compilerFilePath = compiler.absoluteFilePath(); - const QString machineName = gccMachineName(compilerFilePath); + const QString machineName = gccMachineName(compiler); if (toolchainTypes.contains(QLatin1String("mingw"))) { if (!validMinGWMachines().contains(machineName)) { @@ -185,29 +274,31 @@ Profile createGccProfile(const QFileInfo &compiler, Settings *settings, 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 = compiler.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")); + + const ToolchainDetails details(compiler); + + setCommonProperties(profile, compiler, toolchainTypes, details); + + 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(), compilerFilePath); + .arg(profile.name(), compiler.absoluteFilePath()); return profile; } @@ -215,38 +306,54 @@ void gccProbe(Settings *settings, QList<Profile> &profiles, const QString &compi { qbsInfo() << Tr::tr("Trying to detect %1...").arg(compilerName); - const QString crossCompilePrefix = QString::fromLocal8Bit(qgetenv("CROSS_COMPILE")); - const QString compilerFilePath = findExecutable(crossCompilePrefix + compilerName); - const QFileInfo cfi(compilerFilePath); - if (!cfi.exists()) { - qbsError() << Tr::tr("%1 not found.").arg(compilerName); - return; - } - const QString profileName = cfi.completeBaseName(); - const QStringList toolchainTypes = toolchainTypeFromCompilerName(compilerName); - profiles.push_back(createGccProfile(compilerFilePath, settings, - toolchainTypes, profileName)); -} + const QStringList searchPaths = systemSearchPaths(); -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"); + 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; + } } } - 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")))); + if (candidates.empty()) { + qbsInfo() << Tr::tr("No %1 toolchains found.").arg(compilerName); + return; + } + + 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)); } } |