Files
qt-creator/src/plugins/projectexplorer/gcctoolchain.cpp
hjk 509f40825a ProjectExplorer: Pass device to ToolChain::autodetect
Currently unused, will be useful to detect toolchains in docker
containers.

Change-Id: I0fd7643969ab02c05839332a436147ffb242635d
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2021-04-28 07:59:45 +00:00

2092 lines
82 KiB
C++

/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** 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 General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** 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-3.0.html.
**
****************************************************************************/
#include "gcctoolchain.h"
#include "clangparser.h"
#include "gcctoolchainfactories.h"
#include "gccparser.h"
#include "linuxiccparser.h"
#include "projectmacro.h"
#include "toolchainmanager.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/synchronousprocess.h>
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/synchronousprocess.h>
#include <QBuffer>
#include <QComboBox>
#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QFormLayout>
#include <QLineEdit>
#include <QLoggingCategory>
#include <QRegularExpression>
#include <QTimer>
#include <memory>
namespace {
static Q_LOGGING_CATEGORY(gccLog, "qtc.projectexplorer.toolchain.gcc", QtWarningMsg);
} // namespace
using namespace Utils;
namespace ProjectExplorer {
using namespace Internal;
// --------------------------------------------------------------------------
// Helpers:
// --------------------------------------------------------------------------
static const char compilerPlatformCodeGenFlagsKeyC[] = "ProjectExplorer.GccToolChain.PlatformCodeGenFlags";
static const char compilerPlatformLinkerFlagsKeyC[] = "ProjectExplorer.GccToolChain.PlatformLinkerFlags";
static const char targetAbiKeyC[] = "ProjectExplorer.GccToolChain.TargetAbi";
static const char originalTargetTripleKeyC[] = "ProjectExplorer.GccToolChain.OriginalTargetTriple";
static const char supportedAbisKeyC[] = "ProjectExplorer.GccToolChain.SupportedAbis";
static const char parentToolChainIdKeyC[] = "ProjectExplorer.ClangToolChain.ParentToolChainId";
static const char binaryRegexp[] = "(?:^|-|\\b)(?:gcc|g\\+\\+|clang(?:\\+\\+)?)(?:-([\\d.]+))?$";
static QByteArray runGcc(const FilePath &gcc, const QStringList &arguments, const QStringList &env)
{
if (gcc.isEmpty() || !gcc.toFileInfo().isExecutable())
return QByteArray();
SynchronousProcess cpp;
QStringList environment(env);
Utils::Environment::setupEnglishOutput(&environment);
cpp.setEnvironment(environment);
cpp.setTimeoutS(10);
CommandLine cmdLine(gcc, arguments);
SynchronousProcessResponse response = cpp.runBlocking(cmdLine);
if (response.result != SynchronousProcessResponse::Finished ||
response.exitCode != 0) {
Core::MessageManager::writeFlashing({"Compiler feature detection failure!",
response.exitMessage(cmdLine.toUserOutput(), 10),
QString::fromUtf8(response.allRawOutput())});
return QByteArray();
}
return response.allOutput().toUtf8();
}
static ProjectExplorer::Macros gccPredefinedMacros(const FilePath &gcc,
const QStringList &args,
const QStringList &env)
{
QStringList arguments = args;
arguments << "-";
ProjectExplorer::Macros predefinedMacros = Macro::toMacros(runGcc(gcc, arguments, env));
// Sanity check in case we get an error message instead of real output:
QTC_CHECK(predefinedMacros.isEmpty()
|| predefinedMacros.front().type == ProjectExplorer::MacroType::Define);
if (HostOsInfo::isMacHost()) {
// Turn off flag indicating Apple's blocks support
const ProjectExplorer::Macro blocksDefine("__BLOCKS__", "1");
const ProjectExplorer::Macro blocksUndefine("__BLOCKS__", ProjectExplorer::MacroType::Undefine);
const int idx = predefinedMacros.indexOf(blocksDefine);
if (idx != -1)
predefinedMacros[idx] = blocksUndefine;
// Define __strong and __weak (used for Apple's GC extension of C) to be empty
predefinedMacros.append({"__strong"});
predefinedMacros.append({"__weak"});
}
return predefinedMacros;
}
HeaderPaths GccToolChain::gccHeaderPaths(const FilePath &gcc, const QStringList &arguments,
const QStringList &env)
{
HeaderPaths builtInHeaderPaths;
QByteArray line;
QByteArray data = runGcc(gcc, arguments, env);
QBuffer cpp(&data);
cpp.open(QIODevice::ReadOnly);
while (cpp.canReadLine()) {
line = cpp.readLine();
if (line.startsWith("#include"))
break;
}
if (!line.isEmpty() && line.startsWith("#include")) {
auto kind = HeaderPathType::User;
while (cpp.canReadLine()) {
line = cpp.readLine();
if (line.startsWith("#include")) {
kind = HeaderPathType::BuiltIn;
} else if (! line.isEmpty() && QChar(line.at(0)).isSpace()) {
HeaderPathType thisHeaderKind = kind;
line = line.trimmed();
const int index = line.indexOf(" (framework directory)");
if (index != -1) {
line.truncate(index);
thisHeaderKind = HeaderPathType::Framework;
}
const QString headerPath = QFileInfo(QFile::decodeName(line)).canonicalFilePath();
builtInHeaderPaths.append({headerPath, thisHeaderKind});
} else if (line.startsWith("End of search list.")) {
break;
} else {
qWarning("%s: Ignoring line: %s", __FUNCTION__, line.constData());
}
}
}
return builtInHeaderPaths;
}
static Abis guessGccAbi(const QString &m, const ProjectExplorer::Macros &macros)
{
Abis abiList;
Abi guessed = Abi::abiFromTargetTriplet(m);
if (guessed.isNull())
return abiList;
Abi::Architecture arch = guessed.architecture();
Abi::OS os = guessed.os();
Abi::OSFlavor flavor = guessed.osFlavor();
Abi::BinaryFormat format = guessed.binaryFormat();
int width = guessed.wordWidth();
const Macro sizeOfMacro = Utils::findOrDefault(macros, [](const Macro &m) { return m.key == "__SIZEOF_SIZE_T__"; });
if (sizeOfMacro.isValid() && sizeOfMacro.type == MacroType::Define)
width = sizeOfMacro.value.toInt() * 8;
const Macro &mscVerMacro = Utils::findOrDefault(macros, [](const Macro &m) { return m.key == "_MSC_VER"; });
if (mscVerMacro.type == MacroType::Define) {
const int msvcVersion = mscVerMacro.value.toInt();
flavor = Abi::flavorForMsvcVersion(msvcVersion);
}
if (os == Abi::DarwinOS) {
// Apple does PPC and x86!
abiList << Abi(arch, os, flavor, format, width);
abiList << Abi(arch, os, flavor, format, width == 64 ? 32 : 64);
} else if (arch == Abi::X86Architecture && (width == 0 || width == 64)) {
abiList << Abi(arch, os, flavor, format, 64);
if (width != 64 || (!m.contains("mingw")
&& ToolChainManager::detectionSettings().detectX64AsX32)) {
abiList << Abi(arch, os, flavor, format, 32);
}
} else {
abiList << Abi(arch, os, flavor, format, width);
}
return abiList;
}
static GccToolChain::DetectedAbisResult guessGccAbi(const FilePath &path, const QStringList &env,
const ProjectExplorer::Macros &macros,
const QStringList &extraArgs = QStringList())
{
if (path.isEmpty())
return GccToolChain::DetectedAbisResult();
QStringList arguments = extraArgs;
arguments << "-dumpmachine";
QString machine = QString::fromLocal8Bit(runGcc(path, arguments, env)).trimmed();
if (machine.isEmpty()) {
// ICC does not implement the -dumpmachine option on macOS.
if (HostOsInfo::isMacHost() && (path.fileName() == "icc" || path.fileName() == "icpc"))
return GccToolChain::DetectedAbisResult({Abi::hostAbi()});
return GccToolChain::DetectedAbisResult(); // no need to continue if running failed once...
}
return GccToolChain::DetectedAbisResult(guessGccAbi(machine, macros), machine);
}
static QString gccVersion(const FilePath &path, const QStringList &env,
const QStringList &extraArgs)
{
QStringList arguments = extraArgs;
arguments << "-dumpversion";
return QString::fromLocal8Bit(runGcc(path, arguments, env)).trimmed();
}
static Utils::FilePath gccInstallDir(const FilePath &path, const QStringList &env,
const QStringList &extraArgs = {})
{
QStringList arguments = extraArgs;
arguments << "-print-search-dirs";
QString output = QString::fromLocal8Bit(runGcc(path, arguments, env)).trimmed();
// Expected output looks like this:
// install: /usr/lib/gcc/x86_64-linux-gnu/7/
// ...
// Note that clang also supports "-print-search-dirs". However, the
// install dir is not part of the output (tested with clang-8/clang-9).
const QString prefix = "install: ";
const QString line = QTextStream(&output).readLine();
if (!line.startsWith(prefix))
return {};
return Utils::FilePath::fromString(QDir::cleanPath(line.mid(prefix.size())));
}
// --------------------------------------------------------------------------
// GccToolChain
// --------------------------------------------------------------------------
GccToolChain::GccToolChain(Utils::Id typeId) :
ToolChain(typeId)
{
setTypeDisplayName(tr("GCC"));
setTargetAbiKey(targetAbiKeyC);
setCompilerCommandKey("ProjectExplorer.GccToolChain.Path");
}
void GccToolChain::setSupportedAbis(const Abis &abis)
{
if (m_supportedAbis == abis)
return;
m_supportedAbis = abis;
toolChainUpdated();
}
void GccToolChain::setOriginalTargetTriple(const QString &targetTriple)
{
if (m_originalTargetTriple == targetTriple)
return;
m_originalTargetTriple = targetTriple;
toolChainUpdated();
}
void GccToolChain::setInstallDir(const Utils::FilePath &installDir)
{
if (m_installDir == installDir)
return;
m_installDir = installDir;
toolChainUpdated();
}
QString GccToolChain::defaultDisplayName() const
{
QString type = typeDisplayName();
const QRegularExpression regexp(binaryRegexp);
const QRegularExpressionMatch match = regexp.match(compilerCommand().fileName());
if (match.lastCapturedIndex() >= 1)
type += ' ' + match.captured(1);
const Abi abi = targetAbi();
if (abi.architecture() == Abi::UnknownArchitecture || abi.wordWidth() == 0)
return type;
return tr("%1 (%2, %3 %4 in %5)").arg(type,
ToolChainManager::displayNameOfLanguageId(language()),
Abi::toString(abi.architecture()),
Abi::toString(abi.wordWidth()),
compilerCommand().parentDir().toUserOutput());
}
LanguageExtensions GccToolChain::defaultLanguageExtensions() const
{
return LanguageExtension::Gnu;
}
QString GccToolChain::originalTargetTriple() const
{
if (m_originalTargetTriple.isEmpty())
m_originalTargetTriple = detectSupportedAbis().originalTargetTriple;
return m_originalTargetTriple;
}
QString GccToolChain::version() const
{
if (m_version.isEmpty())
m_version = detectVersion();
return m_version;
}
FilePath GccToolChain::installDir() const
{
if (m_installDir.isEmpty())
m_installDir = detectInstallDir();
return m_installDir;
}
Abis GccToolChain::supportedAbis() const
{
return m_supportedAbis;
}
static bool isNetworkCompiler(const QString &dirPath)
{
return dirPath.contains("icecc") || dirPath.contains("distcc");
}
static Utils::FilePath findLocalCompiler(const Utils::FilePath &compilerPath,
const Environment &env)
{
// Find the "real" compiler if icecc, distcc or similar are in use. Ignore ccache, since that
// is local already.
// Get the path to the compiler, ignoring direct calls to icecc and distcc as we cannot
// do anything about those.
if (!isNetworkCompiler(compilerPath.parentDir().toString()))
return compilerPath;
// Filter out network compilers
const FilePaths pathComponents = Utils::filtered(env.path(), [] (const FilePath &dirPath) {
return !isNetworkCompiler(dirPath.toString());
});
// This effectively searches the PATH twice, once via pathComponents and once via PATH itself:
// searchInPath filters duplicates, so that will not hurt.
const Utils::FilePath path = env.searchInPath(compilerPath.fileName(), pathComponents);
return path.isEmpty() ? compilerPath : path;
}
// For querying operations such as -dM
static QStringList filteredFlags(const QStringList &allFlags, bool considerSysroot)
{
QStringList filtered;
for (int i = 0; i < allFlags.size(); ++i) {
const QString &a = allFlags.at(i);
if (a.startsWith("--gcc-toolchain=")) {
filtered << a;
} else if (a == "-arch") {
if (++i < allFlags.length() && !filtered.contains(a))
filtered << a << allFlags.at(i);
} else if ((considerSysroot && (a == "--sysroot" || a == "-isysroot"))
|| a == "-D" || a == "-U"
|| a == "-gcc-toolchain" || a == "-target" || a == "-mllvm" || a == "-isystem") {
if (++i < allFlags.length())
filtered << a << allFlags.at(i);
} else if (a.startsWith("-m") || a == "-Os" || a == "-O0" || a == "-O1" || a == "-O2"
|| a == "-O3" || a == "-ffinite-math-only" || a == "-fshort-double"
|| a == "-fshort-wchar" || a == "-fsignaling-nans" || a == "-fno-inline"
|| a == "-fno-exceptions" || a == "-fstack-protector"
|| a == "-fstack-protector-all" || a == "-fsanitize=address"
|| a == "-fno-rtti" || a.startsWith("-std=") || a.startsWith("-stdlib=")
|| a.startsWith("-specs=") || a == "-ansi" || a == "-undef"
|| a.startsWith("-D") || a.startsWith("-U") || a == "-fopenmp"
|| a == "-Wno-deprecated" || a == "-fPIC" || a == "-fpic" || a == "-fPIE"
|| a == "-fpie" || a.startsWith("-stdlib=") || a.startsWith("-B")
|| a.startsWith("--target=")
|| (a.startsWith("-isystem") && a.length() > 8)
|| a == "-nostdinc" || a == "-nostdinc++") {
filtered << a;
}
}
return filtered;
}
ToolChain::MacroInspectionRunner GccToolChain::createMacroInspectionRunner() const
{
// Using a clean environment breaks ccache/distcc/etc.
Environment env = Environment::systemEnvironment();
addToEnvironment(env);
const QStringList platformCodeGenFlags = m_platformCodeGenFlags;
OptionsReinterpreter reinterpretOptions = m_optionsReinterpreter;
QTC_CHECK(reinterpretOptions);
MacrosCache macroCache = predefinedMacrosCache();
Utils::Id lang = language();
/*
* Asks compiler for set of predefined macros
* flags are the compiler flags collected from project settings
* returns the list of defines, one per line, e.g. "#define __GXX_WEAK__ 1"
* Note: changing compiler flags sometimes changes macros set, e.g. -fopenmp
* adds _OPENMP macro, for full list of macro search by word "when" on this page:
* http://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
*
* This runner must be thread-safe!
*/
return [env, compilerCommand = compilerCommand(),
platformCodeGenFlags, reinterpretOptions, macroCache, lang]
(const QStringList &flags) {
QStringList allFlags = platformCodeGenFlags + flags; // add only cxxflags is empty?
QStringList arguments = gccPredefinedMacrosOptions(lang) + filteredFlags(allFlags, true);
arguments = reinterpretOptions(arguments);
const Utils::optional<MacroInspectionReport> cachedMacros = macroCache->check(arguments);
if (cachedMacros)
return cachedMacros.value();
const Macros macros = gccPredefinedMacros(findLocalCompiler(compilerCommand, env),
arguments,
env.toStringList());
const auto report = MacroInspectionReport{macros, languageVersion(lang, macros)};
macroCache->insert(arguments, report);
qCDebug(gccLog) << "MacroInspectionReport for code model:";
qCDebug(gccLog) << "Language version:" << static_cast<int>(report.languageVersion);
for (const Macro &m : macros) {
qCDebug(gccLog) << compilerCommand.toUserOutput()
<< (lang == Constants::CXX_LANGUAGE_ID ? ": C++ [" : ": C [")
<< arguments.join(", ") << "]"
<< QString::fromUtf8(m.toByteArray());
}
return report;
};
}
/**
* @brief Parses gcc flags -std=*, -fopenmp, -fms-extensions.
* @see http://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html
*/
Utils::LanguageExtensions GccToolChain::languageExtensions(const QStringList &cxxflags) const
{
LanguageExtensions extensions = defaultLanguageExtensions();
const QStringList allCxxflags = m_platformCodeGenFlags + cxxflags; // add only cxxflags is empty?
foreach (const QString &flag, allCxxflags) {
if (flag.startsWith("-std=")) {
const QByteArray std = flag.mid(5).toLatin1();
if (std.startsWith("gnu"))
extensions |= LanguageExtension::Gnu;
else
extensions &= ~LanguageExtensions(LanguageExtension::Gnu);
} else if (flag == "-fopenmp") {
extensions |= LanguageExtension::OpenMP;
} else if (flag == "-fms-extensions") {
extensions |= LanguageExtension::Microsoft;
}
}
return extensions;
}
WarningFlags GccToolChain::warningFlags(const QStringList &cflags) const
{
// based on 'LC_ALL="en" gcc -Q --help=warnings | grep enabled'
WarningFlags flags(WarningFlags::Deprecated | WarningFlags::IgnoredQualifiers
| WarningFlags::SignedComparison | WarningFlags::UninitializedVars);
WarningFlags groupWall(WarningFlags::All | WarningFlags::UnknownPragma | WarningFlags::UnusedFunctions
| WarningFlags::UnusedLocals | WarningFlags::UnusedResult | WarningFlags::UnusedValue
| WarningFlags::SignedComparison | WarningFlags::UninitializedVars);
WarningFlags groupWextra(WarningFlags::Extra | WarningFlags::IgnoredQualifiers | WarningFlags::UnusedParams);
foreach (const QString &flag, cflags) {
if (flag == "--all-warnings")
flags |= groupWall;
else if (flag == "--extra-warnings")
flags |= groupWextra;
WarningFlagAdder add(flag, flags);
if (add.triggered())
continue;
// supported by clang too
add("error", WarningFlags::AsErrors);
add("all", groupWall);
add("extra", groupWextra);
add("deprecated", WarningFlags::Deprecated);
add("effc++", WarningFlags::EffectiveCxx);
add("ignored-qualifiers", WarningFlags::IgnoredQualifiers);
add("non-virtual-dtor", WarningFlags::NonVirtualDestructor);
add("overloaded-virtual", WarningFlags::OverloadedVirtual);
add("shadow", WarningFlags::HiddenLocals);
add("sign-compare", WarningFlags::SignedComparison);
add("unknown-pragmas", WarningFlags::UnknownPragma);
add("unused", WarningFlags::UnusedFunctions | WarningFlags::UnusedLocals | WarningFlags::UnusedParams
| WarningFlags::UnusedResult | WarningFlags::UnusedValue);
add("unused-function", WarningFlags::UnusedFunctions);
add("unused-variable", WarningFlags::UnusedLocals);
add("unused-parameter", WarningFlags::UnusedParams);
add("unused-result", WarningFlags::UnusedResult);
add("unused-value", WarningFlags::UnusedValue);
add("uninitialized", WarningFlags::UninitializedVars);
}
return flags;
}
QStringList GccToolChain::includedFiles(const QStringList &flags, const QString &directoryPath) const
{
return ToolChain::includedFiles("-include", flags, directoryPath);
}
QStringList GccToolChain::gccPrepareArguments(const QStringList &flags,
const QString &sysRoot,
const QStringList &platformCodeGenFlags,
Utils::Id languageId,
OptionsReinterpreter reinterpretOptions)
{
QStringList arguments;
const bool hasKitSysroot = !sysRoot.isEmpty();
if (hasKitSysroot)
arguments.append(QString::fromLatin1("--sysroot=%1").arg(sysRoot));
QStringList allFlags;
allFlags << platformCodeGenFlags << flags;
arguments += filteredFlags(allFlags, !hasKitSysroot);
arguments << languageOption(languageId) << "-E" << "-v" << "-";
arguments = reinterpretOptions(arguments);
return arguments;
}
// NOTE: extraHeaderPathsFunction must NOT capture this or it's members!!!
void GccToolChain::initExtraHeaderPathsFunction(ExtraHeaderPathsFunction &&extraHeaderPathsFunction) const
{
m_extraHeaderPathsFunction = std::move(extraHeaderPathsFunction);
}
HeaderPaths GccToolChain::builtInHeaderPaths(const Utils::Environment &env,
const Utils::FilePath &compilerCommand,
const QStringList &platformCodeGenFlags,
OptionsReinterpreter reinterpretOptions,
HeaderPathsCache headerCache,
Utils::Id languageId,
ExtraHeaderPathsFunction extraHeaderPathsFunction,
const QStringList &flags,
const QString &sysRoot,
const QString &originalTargetTriple)
{
QStringList arguments = gccPrepareArguments(flags,
sysRoot,
platformCodeGenFlags,
languageId,
reinterpretOptions);
// Must be clang case only.
if (!originalTargetTriple.isEmpty())
arguments << "-target" << originalTargetTriple;
const Utils::optional<HeaderPaths> cachedPaths = headerCache->check(qMakePair(env, arguments));
if (cachedPaths)
return cachedPaths.value();
HeaderPaths paths = gccHeaderPaths(findLocalCompiler(compilerCommand, env),
arguments,
env.toStringList());
extraHeaderPathsFunction(paths);
headerCache->insert(qMakePair(env, arguments), paths);
qCDebug(gccLog) << "Reporting header paths to code model:";
for (const HeaderPath &hp : qAsConst(paths)) {
qCDebug(gccLog) << compilerCommand.toUserOutput()
<< (languageId == Constants::CXX_LANGUAGE_ID ? ": C++ [" : ": C [")
<< arguments.join(", ") << "]" << hp.path;
}
return paths;
}
ToolChain::BuiltInHeaderPathsRunner GccToolChain::createBuiltInHeaderPathsRunner(
const Environment &env) const
{
// Using a clean environment breaks ccache/distcc/etc.
Environment fullEnv = env;
addToEnvironment(fullEnv);
// This runner must be thread-safe!
return [fullEnv,
compilerCommand = compilerCommand(),
platformCodeGenFlags = m_platformCodeGenFlags,
reinterpretOptions = m_optionsReinterpreter,
headerCache = headerPathsCache(),
languageId = language(),
extraHeaderPathsFunction = m_extraHeaderPathsFunction](const QStringList &flags,
const QString &sysRoot,
const QString &) {
return builtInHeaderPaths(fullEnv,
compilerCommand,
platformCodeGenFlags,
reinterpretOptions,
headerCache,
languageId,
extraHeaderPathsFunction,
flags,
sysRoot,
/*originalTargetTriple=*/""); // Must be empty for gcc.
};
}
void GccToolChain::addCommandPathToEnvironment(const FilePath &command, Environment &env)
{
const Utils::FilePath compilerDir = command.parentDir();
if (!compilerDir.isEmpty())
env.prependOrSetPath(compilerDir.toString());
}
void GccToolChain::addToEnvironment(Environment &env) const
{
// On Windows gcc invokes cc1plus which is in libexec directory.
// cc1plus depends on libwinpthread-1.dll which is in bin, so bin must be in the PATH.
if (HostOsInfo::isWindowsHost())
addCommandPathToEnvironment(compilerCommand(), env);
}
QStringList GccToolChain::suggestedMkspecList() const
{
const Abi abi = targetAbi();
const Abi host = Abi::hostAbi();
// Cross compile: Leave the mkspec alone!
if (abi.architecture() != host.architecture()
|| abi.os() != host.os()
|| abi.osFlavor() != host.osFlavor()) // Note: This can fail:-(
return {};
if (abi.os() == Abi::DarwinOS) {
QString v = version();
// prefer versioned g++ on macOS. This is required to enable building for older macOS versions
if (v.startsWith("4.0") && compilerCommand().endsWith("-4.0"))
return {"macx-g++40"};
if (v.startsWith("4.2") && compilerCommand().endsWith("-4.2"))
return {"macx-g++42"};
return {"macx-g++"};
}
if (abi.os() == Abi::LinuxOS) {
if (abi.osFlavor() != Abi::GenericFlavor)
return {}; // most likely not a desktop, so leave the mkspec alone.
if (abi.wordWidth() == host.wordWidth()) {
// no need to explicitly set the word width, but provide that mkspec anyway to make sure
// that the correct compiler is picked if a mkspec with a wordwidth is given.
return {"linux-g++", "linux-g++-" + QString::number(targetAbi().wordWidth())};
}
return {"linux-g++-" + QString::number(targetAbi().wordWidth())};
}
if (abi.os() == Abi::BsdOS && abi.osFlavor() == Abi::FreeBsdFlavor)
return {"freebsd-g++"};
return {};
}
FilePath GccToolChain::makeCommand(const Environment &environment) const
{
const FilePath tmp = environment.searchInPath("make");
return tmp.isEmpty() ? FilePath::fromString("make") : tmp;
}
QList<OutputLineParser *> GccToolChain::createOutputParsers() const
{
return GccParser::gccParserSuite();
}
void GccToolChain::resetToolChain(const FilePath &path)
{
bool resetDisplayName = (displayName() == defaultDisplayName());
setCompilerCommand(path);
const Abi currentAbi = targetAbi();
const DetectedAbisResult detectedAbis = detectSupportedAbis();
m_supportedAbis = detectedAbis.supportedAbis;
m_originalTargetTriple = detectedAbis.originalTargetTriple;
m_installDir = installDir();
if (m_supportedAbis.isEmpty())
setTargetAbiNoSignal(Abi());
else if (!m_supportedAbis.contains(currentAbi))
setTargetAbiNoSignal(m_supportedAbis.at(0));
if (resetDisplayName)
setDisplayName(defaultDisplayName()); // calls toolChainUpdated()!
else
toolChainUpdated();
}
void GccToolChain::setPlatformCodeGenFlags(const QStringList &flags)
{
if (flags != m_platformCodeGenFlags) {
m_platformCodeGenFlags = flags;
toolChainUpdated();
}
}
QStringList GccToolChain::extraCodeModelFlags() const
{
return platformCodeGenFlags();
}
/*!
Code gen flags that have to be passed to the compiler.
*/
QStringList GccToolChain::platformCodeGenFlags() const
{
return m_platformCodeGenFlags;
}
void GccToolChain::setPlatformLinkerFlags(const QStringList &flags)
{
if (flags != m_platformLinkerFlags) {
m_platformLinkerFlags = flags;
toolChainUpdated();
}
}
/*!
Flags that have to be passed to the linker.
For example: \c{-arch armv7}
*/
QStringList GccToolChain::platformLinkerFlags() const
{
return m_platformLinkerFlags;
}
QVariantMap GccToolChain::toMap() const
{
QVariantMap data = ToolChain::toMap();
data.insert(compilerPlatformCodeGenFlagsKeyC, m_platformCodeGenFlags);
data.insert(compilerPlatformLinkerFlagsKeyC, m_platformLinkerFlags);
data.insert(originalTargetTripleKeyC, m_originalTargetTriple);
data.insert(supportedAbisKeyC, Utils::transform<QStringList>(m_supportedAbis, &Abi::toString));
return data;
}
bool GccToolChain::fromMap(const QVariantMap &data)
{
if (!ToolChain::fromMap(data))
return false;
m_platformCodeGenFlags = data.value(compilerPlatformCodeGenFlagsKeyC).toStringList();
m_platformLinkerFlags = data.value(compilerPlatformLinkerFlagsKeyC).toStringList();
m_originalTargetTriple = data.value(originalTargetTripleKeyC).toString();
const QStringList abiList = data.value(supportedAbisKeyC).toStringList();
m_supportedAbis.clear();
for (const QString &a : abiList)
m_supportedAbis.append(Abi::fromString(a));
const QString targetAbiString = data.value(targetAbiKeyC).toString();
if (targetAbiString.isEmpty())
resetToolChain(compilerCommand());
return true;
}
bool GccToolChain::operator ==(const ToolChain &other) const
{
if (!ToolChain::operator ==(other))
return false;
auto gccTc = static_cast<const GccToolChain *>(&other);
return compilerCommand() == gccTc->compilerCommand() && targetAbi() == gccTc->targetAbi()
&& m_platformCodeGenFlags == gccTc->m_platformCodeGenFlags
&& m_platformLinkerFlags == gccTc->m_platformLinkerFlags;
}
std::unique_ptr<ToolChainConfigWidget> GccToolChain::createConfigurationWidget()
{
return std::make_unique<GccToolChainConfigWidget>(this);
}
void GccToolChain::updateSupportedAbis() const
{
if (m_supportedAbis.isEmpty()) {
const DetectedAbisResult detected = detectSupportedAbis();
m_supportedAbis = detected.supportedAbis;
m_originalTargetTriple = detected.originalTargetTriple;
}
}
void GccToolChain::setOptionsReinterpreter(const OptionsReinterpreter &optionsReinterpreter)
{
m_optionsReinterpreter = optionsReinterpreter;
}
GccToolChain::DetectedAbisResult GccToolChain::detectSupportedAbis() const
{
Environment env = Environment::systemEnvironment();
addToEnvironment(env);
ProjectExplorer::Macros macros = createMacroInspectionRunner()({}).macros;
return guessGccAbi(findLocalCompiler(compilerCommand(), env),
env.toStringList(),
macros,
platformCodeGenFlags());
}
QString GccToolChain::detectVersion() const
{
Environment env = Environment::systemEnvironment();
addToEnvironment(env);
return gccVersion(findLocalCompiler(compilerCommand(), env), env.toStringList(),
filteredFlags(platformCodeGenFlags(), true));
}
Utils::FilePath GccToolChain::detectInstallDir() const
{
Environment env = Environment::systemEnvironment();
addToEnvironment(env);
return gccInstallDir(findLocalCompiler(compilerCommand(), env), env.toStringList(),
filteredFlags(platformCodeGenFlags(), true));
}
// --------------------------------------------------------------------------
// GccToolChainFactory
// --------------------------------------------------------------------------
static Utils::FilePaths gnuSearchPathsFromRegistry()
{
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\\";
Utils::FilePaths searchPaths;
QSettings registry(kRegistryToken, QSettings::NativeFormat);
const auto productGroups = registry.childGroups();
for (const QString &productKey : productGroups) {
if (!productKey.startsWith("GNU Tools for ARM Embedded Processors"))
continue;
registry.beginGroup(productKey);
QString uninstallFilePath = registry.value("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(FilePath::fromString(toolchainPath));
}
return searchPaths;
}
static Utils::FilePaths atmelSearchPathsFromRegistry()
{
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\\";
Utils::FilePaths searchPaths;
QSettings registry(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("GCC"))
continue;
registry.beginGroup(toolchainKey);
const auto entries = registry.childGroups();
for (const auto &entryKey : entries) {
registry.beginGroup(entryKey);
const QString installDir = registry.value("Native/InstallDir").toString();
const QString version = registry.value("Native/Version").toString();
registry.endGroup();
QString toolchainPath = installDir
+ QLatin1String("/Atmel Toolchain/")
+ toolchainKey + QLatin1String("/Native/")
+ version;
if (toolchainKey.startsWith("ARM"))
toolchainPath += QLatin1String("/arm-gnu-toolchain");
else if (toolchainKey.startsWith("AVR32"))
toolchainPath += QLatin1String("/avr32-gnu-toolchain");
else if (toolchainKey.startsWith("AVR8"))
toolchainPath += QLatin1String("/avr8-gnu-toolchain");
else
break;
toolchainPath += QLatin1String("/bin");
const FilePath path = FilePath::fromString(toolchainPath);
if (path.exists()) {
searchPaths.push_back(FilePath::fromString(toolchainPath));
break;
}
}
registry.endGroup();
}
// This code enumerate the installed toolchains provided
// by the Atmel Studio v7.
registry.beginGroup("AtmelStudio");
const auto productVersions = registry.childGroups();
for (const auto &productVersionKey : productVersions) {
registry.beginGroup(productVersionKey);
const QString installDir = registry.value("InstallDir").toString();
registry.endGroup();
const QStringList knownToolchainSubdirs = {
"/toolchain/arm/arm-gnu-toolchain/bin/",
"/toolchain/avr8/avr8-gnu-toolchain/bin/",
"/toolchain/avr32/avr32-gnu-toolchain/bin/",
};
for (const auto &subdir : knownToolchainSubdirs) {
const QString toolchainPath = installDir + subdir;
const FilePath path = FilePath::fromString(toolchainPath);
if (!path.exists())
continue;
searchPaths.push_back(path);
}
}
registry.endGroup();
return searchPaths;
}
static Utils::FilePaths renesasRl78SearchPathsFromRegistry()
{
if (!HostOsInfo::isWindowsHost())
return {};
// Registry token for the "Renesas RL78" toolchain.
static const char kRegistryToken[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\" \
"Windows\\CurrentVersion\\Uninstall";
Utils::FilePaths searchPaths;
QSettings registry(QLatin1String(kRegistryToken), QSettings::NativeFormat);
const auto productGroups = registry.childGroups();
for (const QString &productKey : productGroups) {
if (!productKey.startsWith("GCC for Renesas RL78"))
continue;
registry.beginGroup(productKey);
const QString installLocation = registry.value("InstallLocation").toString();
registry.endGroup();
if (installLocation.isEmpty())
continue;
const FilePath toolchainPath = FilePath::fromUserInput(installLocation)
.pathAppended("rl78-elf/rl78-elf/bin");
if (!toolchainPath.exists())
continue;
searchPaths.push_back(toolchainPath);
}
return searchPaths;
}
GccToolChainFactory::GccToolChainFactory()
{
setDisplayName(GccToolChain::tr("GCC"));
setSupportedToolChainType(Constants::GCC_TOOLCHAIN_TYPEID);
setSupportedLanguages({Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID});
setToolchainConstructor([] { return new GccToolChain(Constants::GCC_TOOLCHAIN_TYPEID); });
setUserCreatable(true);
}
QList<ToolChain *> GccToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown,
const IDevice::Ptr &device)
{
// GCC is almost never what you want on macOS, but it is by default found in /usr/bin
if (HostOsInfo::isMacHost())
return {};
QList<ToolChain *> tcs;
static const auto tcChecker = [](const ToolChain *tc) {
return tc->targetAbi().osFlavor() != Abi::WindowsMSysFlavor
&& tc->compilerCommand().fileName() != "c89-gcc"
&& tc->compilerCommand().fileName() != "c99-gcc";
};
tcs.append(autoDetectToolchains("g++", DetectVariants::Yes, Constants::CXX_LANGUAGE_ID,
Constants::GCC_TOOLCHAIN_TYPEID, alreadyKnown,
device, tcChecker));
tcs.append(autoDetectToolchains("gcc", DetectVariants::Yes, Constants::C_LANGUAGE_ID,
Constants::GCC_TOOLCHAIN_TYPEID, alreadyKnown,
device, tcChecker));
return tcs;
}
QList<ToolChain *> GccToolChainFactory::detectForImport(const ToolChainDescription &tcd)
{
const QString fileName = tcd.compilerPath.toFileInfo().completeBaseName();
if ((tcd.language == Constants::C_LANGUAGE_ID && (fileName.startsWith("gcc")
|| fileName.endsWith("gcc")
|| fileName == "cc"))
|| (tcd.language == Constants::CXX_LANGUAGE_ID && (fileName.startsWith("g++")
|| fileName.endsWith("g++")
|| fileName == "c++")))
return autoDetectToolChain(tcd, [](const ToolChain *tc) {
return tc->targetAbi().osFlavor() != Abi::WindowsMSysFlavor;
});
return QList<ToolChain *>();
}
QList<ToolChain *> GccToolChainFactory::autoDetectToolchains(
const QString &compilerName,
DetectVariants detectVariants,
const Id language,
const Id requiredTypeId,
const QList<ToolChain *> &alreadyKnown,
const IDevice::Ptr &device,
const ToolchainChecker &checker)
{
Q_UNUSED(device)
FilePaths compilerPaths;
QFileInfo fi(compilerName);
if (fi.isAbsolute()) {
if (fi.isFile())
compilerPaths << FilePath::fromString(compilerName);
} else {
FilePaths searchPaths = Environment::systemEnvironment().path();
searchPaths << gnuSearchPathsFromRegistry();
searchPaths << atmelSearchPathsFromRegistry();
searchPaths << renesasRl78SearchPathsFromRegistry();
if (HostOsInfo::isAnyUnixHost()) {
FilePath ccachePath = FilePath::fromString("/usr/lib/ccache/bin");
if (!ccachePath.exists())
ccachePath = FilePath::fromString("/usr/lib/ccache");
if (ccachePath.exists() && !searchPaths.contains(ccachePath))
searchPaths << ccachePath;
}
for (const FilePath &dir : qAsConst(searchPaths)) {
static const QRegularExpression regexp(binaryRegexp);
QDir binDir(dir.toString());
QStringList nameFilters(compilerName);
if (detectVariants == DetectVariants::Yes) {
nameFilters
<< compilerName + "-[1-9]*" // "clang-8", "gcc-5"
<< ("*-" + compilerName) // "avr-gcc", "avr32-gcc"
<< ("*-" + compilerName + "-[1-9]*")// "avr-gcc-4.8.1", "avr32-gcc-4.4.7"
<< ("*-*-*-" + compilerName) // "arm-none-eabi-gcc"
<< ("*-*-*-" + compilerName + "-[1-9]*") // "arm-none-eabi-gcc-9.1.0"
<< ("*-*-*-*-" + compilerName) // "x86_64-pc-linux-gnu-gcc"
<< ("*-*-*-*-" + compilerName
+ "-[1-9]*"); // "x86_64-pc-linux-gnu-gcc-7.4.1"
}
nameFilters = transform(nameFilters, [](const QString &baseName) {
return HostOsInfo::withExecutableSuffix(baseName);
});
const QStringList fileNames = binDir.entryList(nameFilters,
QDir::Files | QDir::Executable);
for (const QString &fileName : fileNames) {
if (fileName != compilerName &&
!regexp.match(QFileInfo(fileName).completeBaseName()).hasMatch()) {
continue;
}
compilerPaths << FilePath::fromString(binDir.filePath(fileName));
}
}
}
QList<ToolChain *> existingCandidates
= filtered(alreadyKnown, [requiredTypeId, language, &checker](const ToolChain *tc) {
if (tc->typeId() != requiredTypeId)
return false;
if (tc->language() != language)
return false;
if (checker && !checker(tc))
return false;
return true;
});
QList<ToolChain *> result;
for (const FilePath &compilerPath : qAsConst(compilerPaths)) {
bool alreadyExists = false;
for (ToolChain * const existingTc : existingCandidates) {
// We have a match if the existing toolchain ultimately refers to the same file
// as the candidate path, either directly or via a hard or soft link.
// Exceptions:
// - clang++ is often a soft link to clang, but behaves differently.
// - ccache and icecc also create soft links that must not be followed here.
bool existingTcMatches = false;
const FilePath existingCommand = existingTc->compilerCommand();
if ((requiredTypeId == Constants::CLANG_TOOLCHAIN_TYPEID
&& language == Constants::CXX_LANGUAGE_ID
&& !existingCommand.fileName().contains("clang++"))
|| compilerPath.toString().contains("icecc")
|| compilerPath.toString().contains("ccache")) {
existingTcMatches = existingCommand == compilerPath;
} else {
existingTcMatches = Environment::systemEnvironment().isSameExecutable(
existingCommand.toString(), compilerPath.toString())
|| (HostOsInfo::isWindowsHost() && existingCommand.toFileInfo().size()
== compilerPath.toFileInfo().size());
}
if (existingTcMatches) {
if (!result.contains(existingTc))
result << existingTc;
alreadyExists = true;
}
}
if (!alreadyExists) {
const QList<ToolChain *> newToolchains
= autoDetectToolChain({compilerPath, language}, checker);
result << newToolchains;
existingCandidates << newToolchains;
}
}
return result;
}
QList<ToolChain *> GccToolChainFactory::autoDetectToolChain(const ToolChainDescription &tcd,
const ToolchainChecker &checker)
{
QList<ToolChain *> result;
Environment systemEnvironment = Environment::systemEnvironment();
GccToolChain::addCommandPathToEnvironment(tcd.compilerPath, systemEnvironment);
const FilePath localCompilerPath = findLocalCompiler(tcd.compilerPath, systemEnvironment);
Macros macros
= gccPredefinedMacros(localCompilerPath, gccPredefinedMacrosOptions(tcd.language),
systemEnvironment.toStringList());
if (macros.isEmpty())
return result;
const GccToolChain::DetectedAbisResult detectedAbis = guessGccAbi(localCompilerPath,
systemEnvironment.toStringList(),
macros);
const Utils::FilePath installDir = gccInstallDir(localCompilerPath,
systemEnvironment.toStringList());
for (const Abi &abi : detectedAbis.supportedAbis) {
std::unique_ptr<GccToolChain> tc(dynamic_cast<GccToolChain *>(create()));
if (!tc)
return result;
tc->setLanguage(tcd.language);
tc->setDetection(ToolChain::AutoDetection);
tc->predefinedMacrosCache()
->insert(QStringList(),
ToolChain::MacroInspectionReport{macros,
ToolChain::languageVersion(tcd.language, macros)});
tc->setCompilerCommand(tcd.compilerPath);
tc->setSupportedAbis(detectedAbis.supportedAbis);
tc->setTargetAbi(abi);
tc->setOriginalTargetTriple(detectedAbis.originalTargetTriple);
tc->setInstallDir(installDir);
tc->setDisplayName(tc->defaultDisplayName()); // reset displayname
if (!checker || checker(tc.get()))
result.append(tc.release());
}
return result;
}
// --------------------------------------------------------------------------
// GccToolChainConfigWidget
// --------------------------------------------------------------------------
GccToolChainConfigWidget::GccToolChainConfigWidget(GccToolChain *tc) :
ToolChainConfigWidget(tc),
m_abiWidget(new AbiWidget),
m_compilerCommand(new PathChooser)
{
Q_ASSERT(tc);
const QStringList gnuVersionArgs = QStringList("--version");
m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand);
m_compilerCommand->setCommandVersionArguments(gnuVersionArgs);
m_compilerCommand->setHistoryCompleter("PE.Gcc.Command.History");
m_mainLayout->addRow(tr("&Compiler path:"), m_compilerCommand);
m_platformCodeGenFlagsLineEdit = new QLineEdit(this);
m_platformCodeGenFlagsLineEdit->setText(QtcProcess::joinArgs(tc->platformCodeGenFlags()));
m_mainLayout->addRow(tr("Platform codegen flags:"), m_platformCodeGenFlagsLineEdit);
m_platformLinkerFlagsLineEdit = new QLineEdit(this);
m_platformLinkerFlagsLineEdit->setText(QtcProcess::joinArgs(tc->platformLinkerFlags()));
m_mainLayout->addRow(tr("Platform linker flags:"), m_platformLinkerFlagsLineEdit);
m_mainLayout->addRow(tr("&ABI:"), m_abiWidget);
m_abiWidget->setEnabled(false);
addErrorLabel();
setFromToolchain();
connect(m_compilerCommand, &PathChooser::rawPathChanged,
this, &GccToolChainConfigWidget::handleCompilerCommandChange);
connect(m_platformCodeGenFlagsLineEdit, &QLineEdit::editingFinished,
this, &GccToolChainConfigWidget::handlePlatformCodeGenFlagsChange);
connect(m_platformLinkerFlagsLineEdit, &QLineEdit::editingFinished,
this, &GccToolChainConfigWidget::handlePlatformLinkerFlagsChange);
connect(m_abiWidget, &AbiWidget::abiChanged, this, &ToolChainConfigWidget::dirty);
}
void GccToolChainConfigWidget::applyImpl()
{
if (toolChain()->isAutoDetected())
return;
auto tc = static_cast<GccToolChain *>(toolChain());
Q_ASSERT(tc);
QString displayName = tc->displayName();
tc->setCompilerCommand(m_compilerCommand->filePath());
if (m_abiWidget) {
tc->setSupportedAbis(m_abiWidget->supportedAbis());
tc->setTargetAbi(m_abiWidget->currentAbi());
}
tc->setInstallDir(tc->detectInstallDir());
tc->setOriginalTargetTriple(tc->detectSupportedAbis().originalTargetTriple);
tc->setDisplayName(displayName); // reset display name
tc->setPlatformCodeGenFlags(splitString(m_platformCodeGenFlagsLineEdit->text()));
tc->setPlatformLinkerFlags(splitString(m_platformLinkerFlagsLineEdit->text()));
if (m_macros.isEmpty())
return;
tc->predefinedMacrosCache()
->insert(tc->platformCodeGenFlags(),
ToolChain::MacroInspectionReport{m_macros,
ToolChain::languageVersion(tc->language(),
m_macros)});
}
void GccToolChainConfigWidget::setFromToolchain()
{
// subwidgets are not yet connected!
QSignalBlocker blocker(this);
auto tc = static_cast<GccToolChain *>(toolChain());
m_compilerCommand->setFilePath(tc->compilerCommand());
m_platformCodeGenFlagsLineEdit->setText(QtcProcess::joinArgs(tc->platformCodeGenFlags()));
m_platformLinkerFlagsLineEdit->setText(QtcProcess::joinArgs(tc->platformLinkerFlags()));
if (m_abiWidget) {
m_abiWidget->setAbis(tc->supportedAbis(), tc->targetAbi());
if (!m_isReadOnly && !m_compilerCommand->filePath().toString().isEmpty())
m_abiWidget->setEnabled(true);
}
}
bool GccToolChainConfigWidget::isDirtyImpl() const
{
auto tc = static_cast<GccToolChain *>(toolChain());
Q_ASSERT(tc);
return m_compilerCommand->filePath() != tc->compilerCommand()
|| m_platformCodeGenFlagsLineEdit->text()
!= QtcProcess::joinArgs(tc->platformCodeGenFlags())
|| m_platformLinkerFlagsLineEdit->text()
!= QtcProcess::joinArgs(tc->platformLinkerFlags())
|| (m_abiWidget && m_abiWidget->currentAbi() != tc->targetAbi());
}
void GccToolChainConfigWidget::makeReadOnlyImpl()
{
m_compilerCommand->setReadOnly(true);
if (m_abiWidget)
m_abiWidget->setEnabled(false);
m_platformCodeGenFlagsLineEdit->setEnabled(false);
m_platformLinkerFlagsLineEdit->setEnabled(false);
m_isReadOnly = true;
}
void GccToolChainConfigWidget::handleCompilerCommandChange()
{
if (!m_abiWidget)
return;
bool haveCompiler = false;
Abi currentAbi = m_abiWidget->currentAbi();
bool customAbi = m_abiWidget->isCustomAbi() && m_abiWidget->isEnabled();
FilePath path = m_compilerCommand->filePath();
Abis abiList;
if (!path.isEmpty()) {
QFileInfo fi(path.toFileInfo());
haveCompiler = fi.isExecutable() && fi.isFile();
}
if (haveCompiler) {
Environment env = Environment::systemEnvironment();
GccToolChain::addCommandPathToEnvironment(path, env);
QStringList args = gccPredefinedMacrosOptions(Constants::CXX_LANGUAGE_ID)
+ splitString(m_platformCodeGenFlagsLineEdit->text());
const FilePath localCompilerPath = findLocalCompiler(path, env);
m_macros = gccPredefinedMacros(localCompilerPath, args, env.toStringList());
abiList = guessGccAbi(localCompilerPath, env.toStringList(), m_macros,
splitString(m_platformCodeGenFlagsLineEdit->text())).supportedAbis;
}
m_abiWidget->setEnabled(haveCompiler);
// Find a good ABI for the new compiler:
Abi newAbi;
if (customAbi || abiList.contains(currentAbi))
newAbi = currentAbi;
m_abiWidget->setAbis(abiList, newAbi);
emit dirty();
}
void GccToolChainConfigWidget::handlePlatformCodeGenFlagsChange()
{
QString str1 = m_platformCodeGenFlagsLineEdit->text();
QString str2 = QtcProcess::joinArgs(splitString(str1));
if (str1 != str2)
m_platformCodeGenFlagsLineEdit->setText(str2);
else
handleCompilerCommandChange();
}
void GccToolChainConfigWidget::handlePlatformLinkerFlagsChange()
{
QString str1 = m_platformLinkerFlagsLineEdit->text();
QString str2 = QtcProcess::joinArgs(splitString(str1));
if (str1 != str2)
m_platformLinkerFlagsLineEdit->setText(str2);
else
emit dirty();
}
// --------------------------------------------------------------------------
// ClangToolChain
// --------------------------------------------------------------------------
static QList<ToolChain *> mingwToolChains()
{
return ToolChainManager::toolChains([](const ToolChain *tc) -> bool {
return tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID;
});
}
static const MingwToolChain *mingwToolChainFromId(const QByteArray &id)
{
if (id.isEmpty())
return nullptr;
for (const ToolChain *tc : mingwToolChains()) {
if (tc->id() == id)
return static_cast<const MingwToolChain *>(tc);
}
return nullptr;
}
void ClangToolChain::syncAutodetectedWithParentToolchains()
{
if (!HostOsInfo::isWindowsHost() || typeId() != Constants::CLANG_TOOLCHAIN_TYPEID
|| !isAutoDetected()) {
return;
}
QObject::disconnect(m_thisToolchainRemovedConnection);
QObject::disconnect(m_mingwToolchainAddedConnection);
if (!ToolChainManager::isLoaded()) {
QObject::connect(ToolChainManager::instance(), &ToolChainManager::toolChainsLoaded,
[id = id()] {
if (ToolChain * const tc = ToolChainManager::findToolChain(id)) {
if (tc->typeId() == Constants::CLANG_TOOLCHAIN_TYPEID)
static_cast<ClangToolChain *>(tc)->syncAutodetectedWithParentToolchains();
}
});
return;
}
if (!mingwToolChainFromId(m_parentToolChainId)) {
const QList<ToolChain *> mingwTCs = mingwToolChains();
m_parentToolChainId = mingwTCs.isEmpty() ? QByteArray() : mingwTCs.front()->id();
}
// Subscribe only autodetected toolchains.
ToolChainManager *tcManager = ToolChainManager::instance();
m_mingwToolchainAddedConnection
= QObject::connect(tcManager, &ToolChainManager::toolChainAdded, [this](ToolChain *tc) {
if (tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID
&& !mingwToolChainFromId(m_parentToolChainId)) {
m_parentToolChainId = tc->id();
}
});
m_thisToolchainRemovedConnection
= QObject::connect(tcManager, &ToolChainManager::toolChainRemoved, [this](ToolChain *tc) {
if (tc == this) {
QObject::disconnect(m_thisToolchainRemovedConnection);
QObject::disconnect(m_mingwToolchainAddedConnection);
} else if (m_parentToolChainId == tc->id()) {
const QList<ToolChain *> mingwTCs = mingwToolChains();
m_parentToolChainId = mingwTCs.isEmpty() ? QByteArray() : mingwTCs.front()->id();
}
});
}
ClangToolChain::ClangToolChain() :
ClangToolChain(Constants::CLANG_TOOLCHAIN_TYPEID)
{
}
ClangToolChain::ClangToolChain(Utils::Id typeId) :
GccToolChain(typeId)
{
setTypeDisplayName(tr("Clang"));
syncAutodetectedWithParentToolchains();
}
ClangToolChain::~ClangToolChain()
{
QObject::disconnect(m_thisToolchainRemovedConnection);
QObject::disconnect(m_mingwToolchainAddedConnection);
}
static FilePath mingwAwareMakeCommand(const Environment &environment)
{
const QStringList makes
= HostOsInfo::isWindowsHost() ? QStringList({"mingw32-make.exe", "make.exe"}) : QStringList({"make"});
FilePath tmp;
for (const QString &make : makes) {
tmp = environment.searchInPath(make);
if (!tmp.isEmpty())
return tmp;
}
return FilePath::fromString(makes.first());
}
FilePath ClangToolChain::makeCommand(const Environment &environment) const
{
return mingwAwareMakeCommand(environment);
}
/**
* @brief Similar to \a GccToolchain::languageExtensions, but recognizes
* "-fborland-extensions".
*/
LanguageExtensions ClangToolChain::languageExtensions(const QStringList &cxxflags) const
{
LanguageExtensions extensions = GccToolChain::languageExtensions(cxxflags);
if (cxxflags.contains("-fborland-extensions"))
extensions |= LanguageExtension::Borland;
return extensions;
}
WarningFlags ClangToolChain::warningFlags(const QStringList &cflags) const
{
WarningFlags flags = GccToolChain::warningFlags(cflags);
foreach (const QString &flag, cflags) {
if (flag == "-Wdocumentation")
flags |= WarningFlags::Documentation;
if (flag == "-Wno-documentation")
flags &= ~WarningFlags::Documentation;
}
return flags;
}
QStringList ClangToolChain::suggestedMkspecList() const
{
if (const ToolChain * const parentTc = ToolChainManager::findToolChain(m_parentToolChainId))
return parentTc->suggestedMkspecList();
const Abi abi = targetAbi();
if (abi.os() == Abi::DarwinOS)
return {"macx-clang", "macx-clang-32", "unsupported/macx-clang", "macx-ios-clang"};
if (abi.os() == Abi::LinuxOS)
return {"linux-clang", "unsupported/linux-clang"};
if (abi.os() == Abi::WindowsOS)
return {"win32-clang-g++"};
if (abi.architecture() == Abi::AsmJsArchitecture && abi.binaryFormat() == Abi::EmscriptenFormat)
return {"wasm-emscripten"};
return {}; // Note: Not supported by Qt yet, so default to the mkspec the Qt was build with
}
void ClangToolChain::addToEnvironment(Environment &env) const
{
GccToolChain::addToEnvironment(env);
const QString sysroot = sysRoot();
if (!sysroot.isEmpty())
env.prependOrSetPath(sysroot + "/bin");
// Clang takes PWD as basis for debug info, if set.
// When running Qt Creator from a shell, PWD is initially set to an "arbitrary" value.
// Since the tools are not called through a shell, PWD is never changed to the actual cwd,
// so we better make sure PWD is empty to begin with
env.unset("PWD");
}
QString ClangToolChain::originalTargetTriple() const
{
const MingwToolChain *parentTC = mingwToolChainFromId(m_parentToolChainId);
if (parentTC)
return parentTC->originalTargetTriple();
return GccToolChain::originalTargetTriple();
}
QString ClangToolChain::sysRoot() const
{
const MingwToolChain *parentTC = mingwToolChainFromId(m_parentToolChainId);
if (!parentTC)
return QString();
const FilePath mingwCompiler = parentTC->compilerCommand();
return mingwCompiler.parentDir().parentDir().toString();
}
ToolChain::BuiltInHeaderPathsRunner ClangToolChain::createBuiltInHeaderPathsRunner(
const Environment &env) const
{
// Using a clean environment breaks ccache/distcc/etc.
Environment fullEnv = env;
addToEnvironment(fullEnv);
// This runner must be thread-safe!
return [fullEnv,
compilerCommand = compilerCommand(),
platformCodeGenFlags = m_platformCodeGenFlags,
reinterpretOptions = m_optionsReinterpreter,
headerCache = headerPathsCache(),
languageId = language(),
extraHeaderPathsFunction = m_extraHeaderPathsFunction](const QStringList &flags,
const QString &sysRoot,
const QString &target) {
return builtInHeaderPaths(fullEnv,
compilerCommand,
platformCodeGenFlags,
reinterpretOptions,
headerCache,
languageId,
extraHeaderPathsFunction,
flags,
sysRoot,
target);
};
}
std::unique_ptr<ToolChainConfigWidget> ClangToolChain::createConfigurationWidget()
{
return std::make_unique<ClangToolChainConfigWidget>(this);
}
QVariantMap ClangToolChain::toMap() const
{
QVariantMap data = GccToolChain::toMap();
data.insert(parentToolChainIdKeyC, m_parentToolChainId);
return data;
}
bool ClangToolChain::fromMap(const QVariantMap &data)
{
if (!GccToolChain::fromMap(data))
return false;
m_parentToolChainId = data.value(parentToolChainIdKeyC).toByteArray();
syncAutodetectedWithParentToolchains();
return true;
}
LanguageExtensions ClangToolChain::defaultLanguageExtensions() const
{
return LanguageExtension::Gnu;
}
QList<OutputLineParser *> ClangToolChain::createOutputParsers() const
{
return ClangParser::clangParserSuite();
}
// --------------------------------------------------------------------------
// ClangToolChainFactory
// --------------------------------------------------------------------------
ClangToolChainFactory::ClangToolChainFactory()
{
setDisplayName(ClangToolChain::tr("Clang"));
setSupportedToolChainType(Constants::CLANG_TOOLCHAIN_TYPEID);
setSupportedLanguages({Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID});
setToolchainConstructor([] { return new ClangToolChain; });
}
QList<ToolChain *> ClangToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown,
const IDevice::Ptr &device)
{
QList<ToolChain *> tcs;
QList<ToolChain *> known = alreadyKnown;
tcs.append(autoDetectToolchains("clang++", DetectVariants::Yes, Constants::CXX_LANGUAGE_ID,
Constants::CLANG_TOOLCHAIN_TYPEID, alreadyKnown, device));
tcs.append(autoDetectToolchains("clang", DetectVariants::Yes, Constants::C_LANGUAGE_ID,
Constants::CLANG_TOOLCHAIN_TYPEID, alreadyKnown, device));
known.append(tcs);
const FilePath compilerPath = FilePath::fromString(Core::ICore::clangExecutable(CLANG_BINDIR));
if (!compilerPath.isEmpty()) {
const FilePath clang = compilerPath.parentDir().pathAppended(
HostOsInfo::withExecutableSuffix("clang"));
tcs.append(autoDetectToolchains(clang.toString(), DetectVariants::No,
Constants::C_LANGUAGE_ID, Constants::CLANG_TOOLCHAIN_TYPEID,
tcs, device));
}
return tcs;
}
QList<ToolChain *> ClangToolChainFactory::detectForImport(const ToolChainDescription &tcd)
{
const QString fileName = tcd.compilerPath.toString();
if ((tcd.language == Constants::C_LANGUAGE_ID && fileName.startsWith("clang") && !fileName.startsWith("clang++"))
|| (tcd.language == Constants::CXX_LANGUAGE_ID && fileName.startsWith("clang++")))
return autoDetectToolChain(tcd);
return {};
}
ClangToolChainConfigWidget::ClangToolChainConfigWidget(ClangToolChain *tc) :
GccToolChainConfigWidget(tc)
{
if (!HostOsInfo::isWindowsHost() || tc->typeId() != Constants::CLANG_TOOLCHAIN_TYPEID)
return;
// Remove m_abiWidget row because the parent toolchain abi is going to be used.
m_mainLayout->removeRow(m_mainLayout->rowCount() - 2);
m_abiWidget = nullptr;
m_parentToolchainCombo = new QComboBox(this);
m_mainLayout->insertRow(m_mainLayout->rowCount() - 1,
tr("Parent toolchain:"),
m_parentToolchainCombo);
ToolChainManager *tcManager = ToolChainManager::instance();
m_parentToolChainConnections.append(
connect(tcManager, &ToolChainManager::toolChainUpdated, this, [this](ToolChain *tc) {
if (tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID)
updateParentToolChainComboBox();
}));
m_parentToolChainConnections.append(
connect(tcManager, &ToolChainManager::toolChainAdded, this, [this](ToolChain *tc) {
if (tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID)
updateParentToolChainComboBox();
}));
m_parentToolChainConnections.append(
connect(tcManager, &ToolChainManager::toolChainRemoved, this, [this](ToolChain *tc) {
if (tc->id() == toolChain()->id()) {
for (QMetaObject::Connection &connection : m_parentToolChainConnections)
QObject::disconnect(connection);
return;
}
if (tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID)
updateParentToolChainComboBox();
}));
setFromClangToolchain();
}
void ClangToolChainConfigWidget::updateParentToolChainComboBox()
{
auto *tc = static_cast<ClangToolChain *>(toolChain());
QByteArray parentId = m_parentToolchainCombo->currentData().toByteArray();
if (tc->isAutoDetected() || m_parentToolchainCombo->count() == 0)
parentId = tc->m_parentToolChainId;
const MingwToolChain *parentTC = mingwToolChainFromId(parentId);
m_parentToolchainCombo->clear();
m_parentToolchainCombo->addItem(parentTC ? parentTC->displayName() : QString(),
parentTC ? parentId : QByteArray());
if (tc->isAutoDetected())
return;
for (const ToolChain *mingwTC : mingwToolChains()) {
if (mingwTC->id() == parentId)
continue;
if (mingwTC->language() != tc->language())
continue;
m_parentToolchainCombo->addItem(mingwTC->displayName(), mingwTC->id());
}
}
void ClangToolChainConfigWidget::setFromClangToolchain()
{
GccToolChainConfigWidget::setFromToolchain();
if (m_parentToolchainCombo)
updateParentToolChainComboBox();
}
void ClangToolChainConfigWidget::applyImpl()
{
GccToolChainConfigWidget::applyImpl();
if (!m_parentToolchainCombo)
return;
auto *tc = static_cast<ClangToolChain *>(toolChain());
tc->m_parentToolChainId.clear();
const QByteArray parentId = m_parentToolchainCombo->currentData().toByteArray();
if (!parentId.isEmpty()) {
for (const ToolChain *mingwTC : mingwToolChains()) {
if (parentId == mingwTC->id()) {
tc->m_parentToolChainId = mingwTC->id();
tc->setTargetAbi(mingwTC->targetAbi());
tc->setSupportedAbis(mingwTC->supportedAbis());
break;
}
}
}
}
bool ClangToolChainConfigWidget::isDirtyImpl() const
{
if (GccToolChainConfigWidget::isDirtyImpl())
return true;
if (!m_parentToolchainCombo)
return false;
auto tc = static_cast<ClangToolChain *>(toolChain());
Q_ASSERT(tc);
const MingwToolChain *parentTC = mingwToolChainFromId(tc->m_parentToolChainId);
const QByteArray parentId = parentTC ? parentTC->id() : QByteArray();
return parentId != m_parentToolchainCombo->currentData();
}
void ClangToolChainConfigWidget::makeReadOnlyImpl()
{
GccToolChainConfigWidget::makeReadOnlyImpl();
if (m_parentToolchainCombo)
m_parentToolchainCombo->setEnabled(false);
}
// --------------------------------------------------------------------------
// MingwToolChain
// --------------------------------------------------------------------------
MingwToolChain::MingwToolChain() :
GccToolChain(Constants::MINGW_TOOLCHAIN_TYPEID)
{
setTypeDisplayName(MingwToolChain::tr("MinGW"));
}
QStringList MingwToolChain::suggestedMkspecList() const
{
if (HostOsInfo::isWindowsHost())
return {"win32-g++"};
if (HostOsInfo::isLinuxHost()) {
if (version().startsWith("4.6."))
return {"win32-g++-4.6-cross", "unsupported/win32-g++-4.6-cross"};
return {"win32-g++-cross", "unsupported/win32-g++-cross"};
}
return {};
}
FilePath MingwToolChain::makeCommand(const Environment &environment) const
{
return mingwAwareMakeCommand(environment);
}
// --------------------------------------------------------------------------
// MingwToolChainFactory
// --------------------------------------------------------------------------
MingwToolChainFactory::MingwToolChainFactory()
{
setDisplayName(MingwToolChain::tr("MinGW"));
setSupportedToolChainType(Constants::MINGW_TOOLCHAIN_TYPEID);
setSupportedLanguages({Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID});
setToolchainConstructor([] { return new MingwToolChain; });
}
QList<ToolChain *> MingwToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown,
const IDevice::Ptr &device)
{
static const auto tcChecker = [](const ToolChain *tc) {
return tc->targetAbi().osFlavor() == Abi::WindowsMSysFlavor;
};
QList<ToolChain *> result = autoDetectToolchains(
"g++", DetectVariants::Yes, Constants::CXX_LANGUAGE_ID,
Constants::MINGW_TOOLCHAIN_TYPEID, alreadyKnown, device, tcChecker);
result += autoDetectToolchains("gcc", DetectVariants::Yes, Constants::C_LANGUAGE_ID,
Constants::MINGW_TOOLCHAIN_TYPEID, alreadyKnown, device, tcChecker);
return result;
}
QList<ToolChain *> MingwToolChainFactory::detectForImport(const ToolChainDescription &tcd)
{
const QString fileName = tcd.compilerPath.toFileInfo().completeBaseName();
if ((tcd.language == Constants::C_LANGUAGE_ID && (fileName.startsWith("gcc")
|| fileName.endsWith("gcc")))
|| (tcd.language == Constants::CXX_LANGUAGE_ID && (fileName.startsWith("g++")
|| fileName.endsWith("g++"))))
return autoDetectToolChain(tcd, [](const ToolChain *tc) {
return tc->targetAbi().osFlavor() == Abi::WindowsMSysFlavor;
});
return {};
}
// --------------------------------------------------------------------------
// LinuxIccToolChain
// --------------------------------------------------------------------------
LinuxIccToolChain::LinuxIccToolChain() :
GccToolChain(Constants::LINUXICC_TOOLCHAIN_TYPEID)
{
setTypeDisplayName(LinuxIccToolChain::tr("ICC"));
}
/**
* Similar to \a GccToolchain::languageExtensions, but uses "-openmp" instead of
* "-fopenmp" and "-fms-dialect[=ver]" instead of "-fms-extensions".
* @see UNIX manual for "icc"
*/
LanguageExtensions LinuxIccToolChain::languageExtensions(const QStringList &cxxflags) const
{
QStringList copy = cxxflags;
copy.removeAll("-fopenmp");
copy.removeAll("-fms-extensions");
LanguageExtensions extensions = GccToolChain::languageExtensions(cxxflags);
if (cxxflags.contains("-openmp"))
extensions |= LanguageExtension::OpenMP;
if (cxxflags.contains("-fms-dialect")
|| cxxflags.contains("-fms-dialect=8")
|| cxxflags.contains("-fms-dialect=9")
|| cxxflags.contains("-fms-dialect=10"))
extensions |= LanguageExtension::Microsoft;
return extensions;
}
QList<OutputLineParser *> LinuxIccToolChain::createOutputParsers() const
{
return LinuxIccParser::iccParserSuite();
}
QStringList LinuxIccToolChain::suggestedMkspecList() const
{
return {QString("linux-icc-%1").arg(targetAbi().wordWidth())};
}
// --------------------------------------------------------------------------
// LinuxIccToolChainFactory
// --------------------------------------------------------------------------
LinuxIccToolChainFactory::LinuxIccToolChainFactory()
{
setDisplayName(LinuxIccToolChain::tr("ICC"));
setSupportedToolChainType(Constants::LINUXICC_TOOLCHAIN_TYPEID);
setSupportedLanguages({Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID});
setToolchainConstructor([] { return new LinuxIccToolChain; });
}
QList<ToolChain *> LinuxIccToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown,
const IDevice::Ptr &device)
{
QList<ToolChain *> result
= autoDetectToolchains("icpc", DetectVariants::No, Constants::CXX_LANGUAGE_ID,
Constants::LINUXICC_TOOLCHAIN_TYPEID, alreadyKnown, device);
result += autoDetectToolchains("icc", DetectVariants::Yes, Constants::C_LANGUAGE_ID,
Constants::LINUXICC_TOOLCHAIN_TYPEID, alreadyKnown, device);
return result;
}
QList<ToolChain *> LinuxIccToolChainFactory::detectForImport(const ToolChainDescription &tcd)
{
const QString fileName = tcd.compilerPath.toString();
if ((tcd.language == Constants::CXX_LANGUAGE_ID && fileName.startsWith("icpc")) ||
(tcd.language == Constants::C_LANGUAGE_ID && fileName.startsWith("icc")))
return autoDetectToolChain(tcd);
return {};
}
GccToolChain::WarningFlagAdder::WarningFlagAdder(const QString &flag, WarningFlags &flags) :
m_flags(flags)
{
if (!flag.startsWith("-W")) {
m_triggered = true;
return;
}
m_doesEnable = !flag.startsWith("-Wno-");
if (m_doesEnable)
m_flagUtf8 = flag.mid(2).toUtf8();
else
m_flagUtf8 = flag.mid(5).toUtf8();
}
void GccToolChain::WarningFlagAdder::operator ()(const char name[], WarningFlags flagsSet)
{
if (m_triggered)
return;
if (0 == strcmp(m_flagUtf8.data(), name))
{
m_triggered = true;
if (m_doesEnable)
m_flags |= flagsSet;
else
m_flags &= ~flagsSet;
}
}
bool GccToolChain::WarningFlagAdder::triggered() const
{
return m_triggered;
}
} // namespace ProjectExplorer
// Unit tests:
#ifdef WITH_TESTS
# include "projectexplorer.h"
# include <QTest>
# include <QUrl>
namespace ProjectExplorer {
void ProjectExplorerPlugin::testGccAbiGuessing_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QByteArray>("macros");
QTest::addColumn<QStringList>("abiList");
QTest::newRow("invalid input")
<< QString::fromLatin1("Some text")
<< QByteArray("")
<< (QStringList());
QTest::newRow("empty input")
<< QString::fromLatin1("")
<< QByteArray("")
<< (QStringList());
QTest::newRow("empty input (with macros)")
<< QString::fromLatin1("")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8\n#define __Something\n")
<< (QStringList());
QTest::newRow("broken input -- 64bit")
<< QString::fromLatin1("arm-none-foo-gnueabi")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8\n#define __Something\n")
<< QStringList({"arm-baremetal-generic-elf-64bit"});
QTest::newRow("broken input -- 32bit")
<< QString::fromLatin1("arm-none-foo-gnueabi")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\n#define __Something\n")
<< QStringList({"arm-baremetal-generic-elf-32bit"});
QTest::newRow("totally broken input -- 32bit")
<< QString::fromLatin1("foo-bar-foo")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\n#define __Something\n")
<< QStringList();
QTest::newRow("Linux 1 (32bit intel)")
<< QString::fromLatin1("i686-linux-gnu")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\n")
<< QStringList({"x86-linux-generic-elf-32bit"});
QTest::newRow("Linux 2 (32bit intel)")
<< QString::fromLatin1("i486-linux-gnu")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\n")
<< QStringList({"x86-linux-generic-elf-32bit"});
QTest::newRow("Linux 3 (64bit intel)")
<< QString::fromLatin1("x86_64-linux-gnu")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8\n")
<< QStringList("x86-linux-generic-elf-64bit");
QTest::newRow("Linux 3 (64bit intel -- non 64bit)")
<< QString::fromLatin1("x86_64-linux-gnu")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\n")
<< QStringList({"x86-linux-generic-elf-32bit"});
QTest::newRow("Linux 4 (32bit mips)")
<< QString::fromLatin1("mipsel-linux-uclibc")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4")
<< QStringList({"mips-linux-generic-elf-32bit"});
QTest::newRow("Linux 5 (QTCREATORBUG-4690)") // from QTCREATORBUG-4690
<< QString::fromLatin1("x86_64-redhat-linux6E")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8\n")
<< QStringList("x86-linux-generic-elf-64bit");
QTest::newRow("Linux 6 (QTCREATORBUG-4690)") // from QTCREATORBUG-4690
<< QString::fromLatin1("x86_64-redhat-linux")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8\n")
<< QStringList("x86-linux-generic-elf-64bit");
QTest::newRow("Linux 7 (arm)")
<< QString::fromLatin1("armv5tl-montavista-linux-gnueabi")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\n")
<< QStringList({"arm-linux-generic-elf-32bit"});
QTest::newRow("Linux 8 (arm)")
<< QString::fromLatin1("arm-angstrom-linux-gnueabi")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\n")
<< QStringList({"arm-linux-generic-elf-32bit"});
QTest::newRow("Linux 9 (ppc)")
<< QString::fromLatin1("powerpc-nsg-linux")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\n")
<< QStringList({"ppc-linux-generic-elf-32bit"});
QTest::newRow("Linux 10 (ppc 64bit)")
<< QString::fromLatin1("powerpc64-suse-linux")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8\n")
<< QStringList({"ppc-linux-generic-elf-64bit"});
QTest::newRow("Linux 11 (64bit mips)")
<< QString::fromLatin1("mips64el-linux-uclibc")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8")
<< QStringList({"mips-linux-generic-elf-64bit"});
QTest::newRow("Mingw 1 (32bit)")
<< QString::fromLatin1("i686-w64-mingw32")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\r\n")
<< QStringList({"x86-windows-msys-pe-32bit"});
QTest::newRow("Mingw 2 (64bit)")
<< QString::fromLatin1("i686-w64-mingw32")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8\r\n")
<< QStringList({"x86-windows-msys-pe-64bit"});
QTest::newRow("Mingw 3 (32 bit)")
<< QString::fromLatin1("mingw32")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\r\n")
<< QStringList({"x86-windows-msys-pe-32bit"});
QTest::newRow("Cross Mingw 1 (64bit)")
<< QString::fromLatin1("amd64-mingw32msvc")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8\r\n")
<< QStringList({"x86-windows-msys-pe-64bit"});
QTest::newRow("Cross Mingw 2 (32bit)")
<< QString::fromLatin1("i586-mingw32msvc")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\r\n")
<< QStringList({"x86-windows-msys-pe-32bit"});
QTest::newRow("Clang 1: windows")
<< QString::fromLatin1("x86_64-pc-win32")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8\r\n")
<< QStringList("x86-windows-msys-pe-64bit");
QTest::newRow("Clang 1: linux")
<< QString::fromLatin1("x86_64-unknown-linux-gnu")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8\n")
<< QStringList("x86-linux-generic-elf-64bit");
QTest::newRow("Mac 1")
<< QString::fromLatin1("i686-apple-darwin10")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8\n")
<< QStringList({"x86-darwin-generic-mach_o-64bit", "x86-darwin-generic-mach_o-32bit"});
QTest::newRow("Mac 2")
<< QString::fromLatin1("powerpc-apple-darwin10")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8\n")
<< QStringList({"ppc-darwin-generic-mach_o-64bit", "ppc-darwin-generic-mach_o-32bit"});
QTest::newRow("Mac 3")
<< QString::fromLatin1("i686-apple-darwin9")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\n")
<< QStringList({"x86-darwin-generic-mach_o-32bit", "x86-darwin-generic-mach_o-64bit"});
QTest::newRow("Mac IOS")
<< QString::fromLatin1("arm-apple-darwin9")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\n")
<< QStringList({"arm-darwin-generic-mach_o-32bit", "arm-darwin-generic-mach_o-64bit"});
QTest::newRow("Intel 1")
<< QString::fromLatin1("86_64 x86_64 GNU/Linux")
<< QByteArray("#define __SIZEOF_SIZE_T__ 8\n")
<< QStringList("x86-linux-generic-elf-64bit");
QTest::newRow("FreeBSD 1")
<< QString::fromLatin1("i386-portbld-freebsd9.0")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\n")
<< QStringList({"x86-bsd-freebsd-elf-32bit"});
QTest::newRow("FreeBSD 2")
<< QString::fromLatin1("i386-undermydesk-freebsd")
<< QByteArray("#define __SIZEOF_SIZE_T__ 4\n")
<< QStringList({"x86-bsd-freebsd-elf-32bit"});
}
void ProjectExplorerPlugin::testGccAbiGuessing()
{
QFETCH(QString, input);
QFETCH(QByteArray, macros);
QFETCH(QStringList, abiList);
const Abis al = guessGccAbi(input, ProjectExplorer::Macro::toMacros(macros));
QCOMPARE(al.count(), abiList.count());
for (int i = 0; i < al.count(); ++i)
QCOMPARE(al.at(i).toString(), abiList.at(i));
}
} // namespace ProjectExplorer
#endif