forked from qt-creator/qt-creator
bare-metal: Add support for SDCC toolchain
This patch adds support for the SDCC toolchain: * http://sdcc.sourceforge.net/ Now QtC's compiler page have additional 'SDCC' selector which allows to user to choose a desired compiler. Main point is that the SDCC supports only the C-language, so the C++ selector is not available. When the compiler is added, the QtC will tries to detect the compiler ABI. Also it is implemented the compiler auto-detection; each toolchain is displayed with its detected version. Currently is supported the following architecture: * 8051 (aka MCS51) In addition, were added changes to the QBS Project Manager plugin to make it work with QBS . So, now do not need to do an additional 'hacks' into the 'qbs.toolchain' property of QtC SDCC kit. Following features are not implemented yet: * Debugger integration. * Compile output parsers. * Support for other architectures (z80, ds390 and etc) . Change-Id: I92b97a81152d94cabf569e2013fc7260b8d7b953 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
@@ -22,6 +22,7 @@ SOURCES += baremetalplugin.cpp \
|
|||||||
stlinkutilgdbserverprovider.cpp \
|
stlinkutilgdbserverprovider.cpp \
|
||||||
iarewtoolchain.cpp \
|
iarewtoolchain.cpp \
|
||||||
keiltoolchain.cpp \
|
keiltoolchain.cpp \
|
||||||
|
sdcctoolchain.cpp \
|
||||||
iarewparser.cpp \
|
iarewparser.cpp \
|
||||||
keilparser.cpp \
|
keilparser.cpp \
|
||||||
|
|
||||||
@@ -45,6 +46,7 @@ HEADERS += baremetalplugin.h \
|
|||||||
stlinkutilgdbserverprovider.h \
|
stlinkutilgdbserverprovider.h \
|
||||||
iarewtoolchain.h \
|
iarewtoolchain.h \
|
||||||
keiltoolchain.h \
|
keiltoolchain.h \
|
||||||
|
sdcctoolchain.h \
|
||||||
iarewparser.h \
|
iarewparser.h \
|
||||||
keilparser.h \
|
keilparser.h \
|
||||||
|
|
||||||
|
@@ -34,6 +34,7 @@ QtcPlugin {
|
|||||||
"stlinkutilgdbserverprovider.cpp", "stlinkutilgdbserverprovider.h",
|
"stlinkutilgdbserverprovider.cpp", "stlinkutilgdbserverprovider.h",
|
||||||
"iarewtoolchain.cpp", "iarewtoolchain.h",
|
"iarewtoolchain.cpp", "iarewtoolchain.h",
|
||||||
"keiltoolchain.cpp", "keiltoolchain.h",
|
"keiltoolchain.cpp", "keiltoolchain.h",
|
||||||
|
"sdcctoolchain.cpp", "sdcctoolchain.h",
|
||||||
"iarewparser.cpp", "iarewparser.h",
|
"iarewparser.cpp", "iarewparser.h",
|
||||||
"keilparser.cpp", "keilparser.h",
|
"keilparser.cpp", "keilparser.h",
|
||||||
]
|
]
|
||||||
|
@@ -44,6 +44,7 @@ const char STLINK_UTIL_PROVIDER_ID[] = "BareMetal.GdbServerProvider.STLinkUtil";
|
|||||||
// Toolchain types.
|
// Toolchain types.
|
||||||
const char IAREW_TOOLCHAIN_TYPEID[] = "BareMetal.ToolChain.Iar";
|
const char IAREW_TOOLCHAIN_TYPEID[] = "BareMetal.ToolChain.Iar";
|
||||||
const char KEIL_TOOLCHAIN_TYPEID[] = "BareMetal.ToolChain.Keil";
|
const char KEIL_TOOLCHAIN_TYPEID[] = "BareMetal.ToolChain.Keil";
|
||||||
|
const char SDCC_TOOLCHAIN_TYPEID[] = "BareMetal.ToolChain.Sdcc";
|
||||||
|
|
||||||
} // namespace BareMetal
|
} // namespace BareMetal
|
||||||
} // namespace Constants
|
} // namespace Constants
|
||||||
|
@@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
#include "iarewtoolchain.h"
|
#include "iarewtoolchain.h"
|
||||||
#include "keiltoolchain.h"
|
#include "keiltoolchain.h"
|
||||||
|
#include "sdcctoolchain.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/icontext.h>
|
#include <coreplugin/icontext.h>
|
||||||
@@ -54,6 +55,7 @@ class BareMetalPluginPrivate
|
|||||||
public:
|
public:
|
||||||
IarToolChainFactory iarToolChainFactory;
|
IarToolChainFactory iarToolChainFactory;
|
||||||
KeilToolchainFactory keilToolChainFactory;
|
KeilToolchainFactory keilToolChainFactory;
|
||||||
|
SdccToolChainFactory sdccToolChainFactory;
|
||||||
BareMetalDeviceFactory deviceFactory;
|
BareMetalDeviceFactory deviceFactory;
|
||||||
BareMetalRunConfigurationFactory runConfigurationFactory;
|
BareMetalRunConfigurationFactory runConfigurationFactory;
|
||||||
BareMetalCustomRunConfigurationFactory customRunConfigurationFactory;
|
BareMetalCustomRunConfigurationFactory customRunConfigurationFactory;
|
||||||
|
605
src/plugins/baremetal/sdcctoolchain.cpp
Normal file
605
src/plugins/baremetal/sdcctoolchain.cpp
Normal file
@@ -0,0 +1,605 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
|
||||||
|
** 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 "baremetalconstants.h"
|
||||||
|
#include "sdcctoolchain.h"
|
||||||
|
|
||||||
|
#include <projectexplorer/abiwidget.h>
|
||||||
|
#include <projectexplorer/projectexplorerconstants.h>
|
||||||
|
#include <projectexplorer/projectmacro.h>
|
||||||
|
#include <projectexplorer/toolchainmanager.h>
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/environment.h>
|
||||||
|
#include <utils/pathchooser.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/synchronousprocess.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
using namespace ProjectExplorer;
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
|
namespace BareMetal {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
// Helpers:
|
||||||
|
|
||||||
|
static const char compilerCommandKeyC[] = "BareMetal.SdccToolChain.CompilerPath";
|
||||||
|
static const char targetAbiKeyC[] = "BareMetal.SdccToolChain.TargetAbi";
|
||||||
|
|
||||||
|
static bool compilerExists(const FileName &compilerPath)
|
||||||
|
{
|
||||||
|
const QFileInfo fi = compilerPath.toFileInfo();
|
||||||
|
return fi.exists() && fi.isExecutable() && fi.isFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString compilerTargetFlag(const Abi &abi)
|
||||||
|
{
|
||||||
|
switch (abi.architecture()) {
|
||||||
|
case Abi::Architecture::Mcs51Architecture:
|
||||||
|
return "-mmcs51";
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Macros dumpPredefinedMacros(const FileName &compiler, const QStringList &env,
|
||||||
|
const Abi &abi)
|
||||||
|
{
|
||||||
|
if (compiler.isEmpty() || !compiler.toFileInfo().isExecutable())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
QTemporaryFile fakeIn("XXXXXX.c");
|
||||||
|
if (!fakeIn.open())
|
||||||
|
return {};
|
||||||
|
fakeIn.close();
|
||||||
|
|
||||||
|
SynchronousProcess cpp;
|
||||||
|
cpp.setEnvironment(env);
|
||||||
|
cpp.setTimeoutS(10);
|
||||||
|
|
||||||
|
QStringList arguments;
|
||||||
|
arguments.push_back(compilerTargetFlag(abi));
|
||||||
|
arguments.push_back("-dM");
|
||||||
|
arguments.push_back("-E");
|
||||||
|
arguments.push_back(fakeIn.fileName());
|
||||||
|
|
||||||
|
const SynchronousProcessResponse response = cpp.runBlocking(compiler.toString(), arguments);
|
||||||
|
if (response.result != SynchronousProcessResponse::Finished
|
||||||
|
|| response.exitCode != 0) {
|
||||||
|
qWarning() << response.exitMessage(compiler.toString(), 10);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray output = response.allOutput().toUtf8();
|
||||||
|
return Macro::toMacros(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HeaderPaths dumpHeaderPaths(const FileName &compiler, const QStringList &env,
|
||||||
|
const Abi &abi)
|
||||||
|
{
|
||||||
|
if (!compiler.exists())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
SynchronousProcess cpp;
|
||||||
|
cpp.setEnvironment(env);
|
||||||
|
cpp.setTimeoutS(10);
|
||||||
|
|
||||||
|
QStringList arguments;
|
||||||
|
arguments.push_back(compilerTargetFlag(abi));
|
||||||
|
arguments.push_back("--print-search-dirs");
|
||||||
|
|
||||||
|
const SynchronousProcessResponse response = cpp.runBlocking(compiler.toString(), arguments);
|
||||||
|
if (response.result != SynchronousProcessResponse::Finished
|
||||||
|
|| response.exitCode != 0) {
|
||||||
|
qWarning() << response.exitMessage(compiler.toString(), 10);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString output = response.allOutput();
|
||||||
|
HeaderPaths headerPaths;
|
||||||
|
QTextStream in(&output);
|
||||||
|
QString line;
|
||||||
|
bool synchronized = false;
|
||||||
|
while (in.readLineInto(&line)) {
|
||||||
|
if (!synchronized) {
|
||||||
|
if (line.startsWith("includedir:"))
|
||||||
|
synchronized = true;
|
||||||
|
} else {
|
||||||
|
if (line.startsWith("programs:") || line.startsWith("datadir:")
|
||||||
|
|| line.startsWith("libdir:") || line.startsWith("libpath:")) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
const QString headerPath = QFileInfo(line.trimmed())
|
||||||
|
.canonicalFilePath();
|
||||||
|
headerPaths.append({headerPath, HeaderPathType::BuiltIn});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return headerPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString findMacroValue(const Macros ¯os, const QByteArray &key)
|
||||||
|
{
|
||||||
|
for (const Macro ¯o : macros) {
|
||||||
|
if (macro.key == key)
|
||||||
|
return QString::fromLocal8Bit(macro.value);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString guessVersion(const Macros ¯os)
|
||||||
|
{
|
||||||
|
const QString major = findMacroValue(macros, "__SDCC_VERSION_MAJOR");
|
||||||
|
const QString minor = findMacroValue(macros, "__SDCC_VERSION_MINOR");
|
||||||
|
const QString patch = findMacroValue(macros, "__SDCC_VERSION_PATCH");
|
||||||
|
return QString("%1.%2.%3").arg(major, minor, patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Abi::Architecture guessArchitecture(const Macros ¯os)
|
||||||
|
{
|
||||||
|
for (const Macro ¯o : macros) {
|
||||||
|
if (macro.key == "__SDCC_mcs51")
|
||||||
|
return Abi::Architecture::Mcs51Architecture;
|
||||||
|
}
|
||||||
|
return Abi::Architecture::UnknownArchitecture;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char guessWordWidth(const Macros ¯os)
|
||||||
|
{
|
||||||
|
Q_UNUSED(macros)
|
||||||
|
// SDCC always have 16-bit word width.
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Abi::BinaryFormat guessFormat(Abi::Architecture arch)
|
||||||
|
{
|
||||||
|
Q_UNUSED(arch)
|
||||||
|
return Abi::BinaryFormat::UnknownFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Abi guessAbi(const Macros ¯os)
|
||||||
|
{
|
||||||
|
const auto arch = guessArchitecture(macros);
|
||||||
|
return {arch, Abi::OS::BareMetalOS, Abi::OSFlavor::GenericFlavor,
|
||||||
|
guessFormat(arch), guessWordWidth(macros)};
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString buildDisplayName(Abi::Architecture arch, Core::Id language,
|
||||||
|
const QString &version)
|
||||||
|
{
|
||||||
|
return SdccToolChain::tr("SDCC %1 (%2, %3)")
|
||||||
|
.arg(version, language.toString(), Abi::toString(arch));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Utils::FileName compilerPathFromEnvironment(const QString &compilerName)
|
||||||
|
{
|
||||||
|
const Environment systemEnvironment = Environment::systemEnvironment();
|
||||||
|
return systemEnvironment.searchInPath(compilerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SdccToolChain
|
||||||
|
|
||||||
|
SdccToolChain::SdccToolChain(Detection d) :
|
||||||
|
ToolChain(Constants::SDCC_TOOLCHAIN_TYPEID, d),
|
||||||
|
m_predefinedMacrosCache(std::make_shared<Cache<MacroInspectionReport, 64>>()),
|
||||||
|
m_headerPathsCache(std::make_shared<HeaderPathsCache>())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
SdccToolChain::SdccToolChain(Core::Id language, Detection d) :
|
||||||
|
SdccToolChain(d)
|
||||||
|
{
|
||||||
|
setLanguage(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SdccToolChain::typeDisplayName() const
|
||||||
|
{
|
||||||
|
return Internal::SdccToolChainFactory::tr("SDCC");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdccToolChain::setTargetAbi(const Abi &abi)
|
||||||
|
{
|
||||||
|
if (abi == m_targetAbi)
|
||||||
|
return;
|
||||||
|
m_targetAbi = abi;
|
||||||
|
toolChainUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
Abi SdccToolChain::targetAbi() const
|
||||||
|
{
|
||||||
|
return m_targetAbi;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdccToolChain::isValid() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolChain::MacroInspectionRunner SdccToolChain::createMacroInspectionRunner() const
|
||||||
|
{
|
||||||
|
Environment env = Environment::systemEnvironment();
|
||||||
|
addToEnvironment(env);
|
||||||
|
|
||||||
|
const Utils::FileName compilerCommand = m_compilerCommand;
|
||||||
|
const Core::Id lang = language();
|
||||||
|
const Abi abi = m_targetAbi;
|
||||||
|
|
||||||
|
MacrosCache macrosCache = m_predefinedMacrosCache;
|
||||||
|
|
||||||
|
return [env, compilerCommand, macrosCache, lang, abi]
|
||||||
|
(const QStringList &flags) {
|
||||||
|
Q_UNUSED(flags)
|
||||||
|
|
||||||
|
const Macros macros = dumpPredefinedMacros(compilerCommand, env.toStringList(),
|
||||||
|
abi);
|
||||||
|
const auto report = MacroInspectionReport{macros, languageVersion(lang, macros)};
|
||||||
|
macrosCache->insert({}, report);
|
||||||
|
|
||||||
|
return report;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Macros SdccToolChain::predefinedMacros(const QStringList &cxxflags) const
|
||||||
|
{
|
||||||
|
return createMacroInspectionRunner()(cxxflags).macros;
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::LanguageExtensions SdccToolChain::languageExtensions(const QStringList &) const
|
||||||
|
{
|
||||||
|
return LanguageExtension::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
WarningFlags SdccToolChain::warningFlags(const QStringList &cxxflags) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(cxxflags);
|
||||||
|
return WarningFlags::Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolChain::BuiltInHeaderPathsRunner SdccToolChain::createBuiltInHeaderPathsRunner() const
|
||||||
|
{
|
||||||
|
Environment env = Environment::systemEnvironment();
|
||||||
|
addToEnvironment(env);
|
||||||
|
|
||||||
|
const Utils::FileName compilerCommand = m_compilerCommand;
|
||||||
|
const Core::Id languageId = language();
|
||||||
|
const Abi abi = m_targetAbi;
|
||||||
|
|
||||||
|
HeaderPathsCachePtr headerPathsCache = m_headerPathsCache;
|
||||||
|
|
||||||
|
return [env, compilerCommand, headerPathsCache, languageId, abi]
|
||||||
|
(const QStringList &flags, const QString &fileName) {
|
||||||
|
Q_UNUSED(flags)
|
||||||
|
Q_UNUSED(fileName)
|
||||||
|
|
||||||
|
const HeaderPaths paths = dumpHeaderPaths(compilerCommand, env.toStringList(),
|
||||||
|
abi);
|
||||||
|
headerPathsCache->insert({}, paths);
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderPaths SdccToolChain::builtInHeaderPaths(const QStringList &cxxFlags,
|
||||||
|
const FileName &fileName) const
|
||||||
|
{
|
||||||
|
return createBuiltInHeaderPathsRunner()(cxxFlags, fileName.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdccToolChain::addToEnvironment(Environment &env) const
|
||||||
|
{
|
||||||
|
if (!m_compilerCommand.isEmpty()) {
|
||||||
|
const FileName path = m_compilerCommand.parentDir();
|
||||||
|
env.prependOrSetPath(path.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IOutputParser *SdccToolChain::outputParser() const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap SdccToolChain::toMap() const
|
||||||
|
{
|
||||||
|
QVariantMap data = ToolChain::toMap();
|
||||||
|
data.insert(compilerCommandKeyC, m_compilerCommand.toString());
|
||||||
|
data.insert(targetAbiKeyC, m_targetAbi.toString());
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdccToolChain::fromMap(const QVariantMap &data)
|
||||||
|
{
|
||||||
|
if (!ToolChain::fromMap(data))
|
||||||
|
return false;
|
||||||
|
m_compilerCommand = FileName::fromString(data.value(compilerCommandKeyC).toString());
|
||||||
|
m_targetAbi = Abi::fromString(data.value(targetAbiKeyC).toString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ToolChainConfigWidget> SdccToolChain::createConfigurationWidget()
|
||||||
|
{
|
||||||
|
return std::make_unique<SdccToolChainConfigWidget>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdccToolChain::operator==(const ToolChain &other) const
|
||||||
|
{
|
||||||
|
if (!ToolChain::operator==(other))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto customTc = static_cast<const SdccToolChain *>(&other);
|
||||||
|
return m_compilerCommand == customTc->m_compilerCommand
|
||||||
|
&& m_targetAbi == customTc->m_targetAbi
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdccToolChain::setCompilerCommand(const FileName &file)
|
||||||
|
{
|
||||||
|
if (file == m_compilerCommand)
|
||||||
|
return;
|
||||||
|
m_compilerCommand = file;
|
||||||
|
toolChainUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileName SdccToolChain::compilerCommand() const
|
||||||
|
{
|
||||||
|
return m_compilerCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SdccToolChain::makeCommand(const Environment &env) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(env)
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolChain *SdccToolChain::clone() const
|
||||||
|
{
|
||||||
|
return new SdccToolChain(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdccToolChain::toolChainUpdated()
|
||||||
|
{
|
||||||
|
m_predefinedMacrosCache->invalidate();
|
||||||
|
m_headerPathsCache->invalidate();
|
||||||
|
ToolChain::toolChainUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
// SdccToolChainFactory
|
||||||
|
|
||||||
|
SdccToolChainFactory::SdccToolChainFactory()
|
||||||
|
{
|
||||||
|
setDisplayName(tr("SDCC"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<Core::Id> SdccToolChainFactory::supportedLanguages() const
|
||||||
|
{
|
||||||
|
return {ProjectExplorer::Constants::C_LANGUAGE_ID};
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<ToolChain *> SdccToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown)
|
||||||
|
{
|
||||||
|
Candidates candidates;
|
||||||
|
|
||||||
|
if (Utils::HostOsInfo::isWindowsHost()) {
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN64
|
||||||
|
static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\SDCC";
|
||||||
|
#else
|
||||||
|
static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\SDCC";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QSettings registry(kRegistryNode, QSettings::NativeFormat);
|
||||||
|
QString compilerPath = registry.value("Default").toString();
|
||||||
|
if (!compilerPath.isEmpty()) {
|
||||||
|
// Build full compiler path.
|
||||||
|
compilerPath += "\\bin\\sdcc.exe";
|
||||||
|
const FileName fn = FileName::fromString(
|
||||||
|
QFileInfo(compilerPath).absoluteFilePath());
|
||||||
|
if (compilerExists(fn)) {
|
||||||
|
// Build compiler version.
|
||||||
|
const QString version = QString("%1.%2.%3").arg(
|
||||||
|
registry.value("VersionMajor").toString(),
|
||||||
|
registry.value("VersionMinor").toString(),
|
||||||
|
registry.value("VersionRevision").toString());
|
||||||
|
candidates.push_back({fn, version});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FileName fn = compilerPathFromEnvironment("sdcc");
|
||||||
|
if (fn.exists()) {
|
||||||
|
const auto env = Environment::systemEnvironment();
|
||||||
|
const auto macros = dumpPredefinedMacros(fn, env.toStringList(), {});
|
||||||
|
const QString version = guessVersion(macros);
|
||||||
|
const Candidate candidate(fn, version);
|
||||||
|
if (!candidates.contains(candidate))
|
||||||
|
candidates.push_back(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return autoDetectToolchains(candidates, alreadyKnown);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdccToolChainFactory::canCreate()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolChain *SdccToolChainFactory::create(Core::Id language)
|
||||||
|
{
|
||||||
|
return new SdccToolChain(language, ToolChain::ManualDetection);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdccToolChainFactory::canRestore(const QVariantMap &data)
|
||||||
|
{
|
||||||
|
return typeIdFromMap(data) == Constants::SDCC_TOOLCHAIN_TYPEID;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolChain *SdccToolChainFactory::restore(const QVariantMap &data)
|
||||||
|
{
|
||||||
|
const auto tc = new SdccToolChain(ToolChain::ManualDetection);
|
||||||
|
if (tc->fromMap(data))
|
||||||
|
return tc;
|
||||||
|
|
||||||
|
delete tc;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<ToolChain *> SdccToolChainFactory::autoDetectToolchains(
|
||||||
|
const Candidates &candidates, const QList<ToolChain *> &alreadyKnown) const
|
||||||
|
{
|
||||||
|
QList<ToolChain *> result;
|
||||||
|
|
||||||
|
for (const Candidate &candidate : qAsConst(candidates)) {
|
||||||
|
const QList<ToolChain *> filtered = Utils::filtered(
|
||||||
|
alreadyKnown, [candidate](ToolChain *tc) {
|
||||||
|
return tc->typeId() == Constants::SDCC_TOOLCHAIN_TYPEID
|
||||||
|
&& tc->compilerCommand() == candidate.first
|
||||||
|
&& (tc->language() == ProjectExplorer::Constants::C_LANGUAGE_ID);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!filtered.isEmpty()) {
|
||||||
|
result << filtered;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create toolchain only for C language (because SDCC does not support C++).
|
||||||
|
result << autoDetectToolchain(candidate, ProjectExplorer::Constants::C_LANGUAGE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<ToolChain *> SdccToolChainFactory::autoDetectToolchain(
|
||||||
|
const Candidate &candidate, Core::Id language) const
|
||||||
|
{
|
||||||
|
const auto env = Environment::systemEnvironment();
|
||||||
|
const Macros macros = dumpPredefinedMacros(candidate.first, env.toStringList(), {});
|
||||||
|
if (macros.isEmpty())
|
||||||
|
return {};
|
||||||
|
const Abi abi = guessAbi(macros);
|
||||||
|
|
||||||
|
const auto tc = new SdccToolChain(ToolChain::AutoDetection);
|
||||||
|
tc->setLanguage(language);
|
||||||
|
tc->setCompilerCommand(candidate.first);
|
||||||
|
tc->setTargetAbi(abi);
|
||||||
|
tc->setDisplayName(buildDisplayName(abi.architecture(), language, candidate.second));
|
||||||
|
|
||||||
|
const auto languageVersion = ToolChain::languageVersion(language, macros);
|
||||||
|
tc->m_predefinedMacrosCache->insert({}, {macros, languageVersion});
|
||||||
|
return {tc};
|
||||||
|
}
|
||||||
|
|
||||||
|
// SdccToolChainConfigWidget
|
||||||
|
|
||||||
|
SdccToolChainConfigWidget::SdccToolChainConfigWidget(SdccToolChain *tc) :
|
||||||
|
ToolChainConfigWidget(tc),
|
||||||
|
m_compilerCommand(new PathChooser),
|
||||||
|
m_abiWidget(new AbiWidget)
|
||||||
|
{
|
||||||
|
m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand);
|
||||||
|
m_compilerCommand->setHistoryCompleter("PE.SDCC.Command.History");
|
||||||
|
m_mainLayout->addRow(tr("&Compiler path:"), m_compilerCommand);
|
||||||
|
m_mainLayout->addRow(tr("&ABI:"), m_abiWidget);
|
||||||
|
|
||||||
|
m_abiWidget->setEnabled(false);
|
||||||
|
|
||||||
|
addErrorLabel();
|
||||||
|
setFromToolchain();
|
||||||
|
|
||||||
|
connect(m_compilerCommand, &PathChooser::rawPathChanged,
|
||||||
|
this, &SdccToolChainConfigWidget::handleCompilerCommandChange);
|
||||||
|
connect(m_abiWidget, &AbiWidget::abiChanged,
|
||||||
|
this, &ToolChainConfigWidget::dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdccToolChainConfigWidget::applyImpl()
|
||||||
|
{
|
||||||
|
if (toolChain()->isAutoDetected())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto tc = static_cast<SdccToolChain *>(toolChain());
|
||||||
|
const QString displayName = tc->displayName();
|
||||||
|
tc->setCompilerCommand(m_compilerCommand->fileName());
|
||||||
|
tc->setTargetAbi(m_abiWidget->currentAbi());
|
||||||
|
tc->setDisplayName(displayName);
|
||||||
|
|
||||||
|
if (m_macros.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto languageVersion = ToolChain::languageVersion(tc->language(), m_macros);
|
||||||
|
tc->m_predefinedMacrosCache->insert({}, {m_macros, languageVersion});
|
||||||
|
|
||||||
|
setFromToolchain();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdccToolChainConfigWidget::isDirtyImpl() const
|
||||||
|
{
|
||||||
|
const auto tc = static_cast<SdccToolChain *>(toolChain());
|
||||||
|
return m_compilerCommand->fileName() != tc->compilerCommand()
|
||||||
|
|| m_abiWidget->currentAbi() != tc->targetAbi()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdccToolChainConfigWidget::makeReadOnlyImpl()
|
||||||
|
{
|
||||||
|
m_compilerCommand->setReadOnly(true);
|
||||||
|
m_abiWidget->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdccToolChainConfigWidget::setFromToolchain()
|
||||||
|
{
|
||||||
|
const QSignalBlocker blocker(this);
|
||||||
|
const auto tc = static_cast<SdccToolChain *>(toolChain());
|
||||||
|
m_compilerCommand->setFileName(tc->compilerCommand());
|
||||||
|
m_abiWidget->setAbis({}, tc->targetAbi());
|
||||||
|
const bool haveCompiler = compilerExists(m_compilerCommand->fileName());
|
||||||
|
m_abiWidget->setEnabled(haveCompiler && !tc->isAutoDetected());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdccToolChainConfigWidget::handleCompilerCommandChange()
|
||||||
|
{
|
||||||
|
const FileName compilerPath = m_compilerCommand->fileName();
|
||||||
|
const bool haveCompiler = compilerExists(compilerPath);
|
||||||
|
if (haveCompiler) {
|
||||||
|
const auto env = Environment::systemEnvironment();
|
||||||
|
m_macros = dumpPredefinedMacros(compilerPath, env.toStringList(), {});
|
||||||
|
const Abi guessed = guessAbi(m_macros);
|
||||||
|
m_abiWidget->setAbis({}, guessed);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_abiWidget->setEnabled(haveCompiler);
|
||||||
|
emit dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace BareMetal
|
166
src/plugins/baremetal/sdcctoolchain.h
Normal file
166
src/plugins/baremetal/sdcctoolchain.h
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
|
||||||
|
** 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.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <projectexplorer/abi.h>
|
||||||
|
#include <projectexplorer/toolchain.h>
|
||||||
|
#include <projectexplorer/toolchaincache.h>
|
||||||
|
#include <projectexplorer/toolchainconfigwidget.h>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QPlainTextEdit;
|
||||||
|
class QPushButton;
|
||||||
|
class QTextEdit;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
class FileName;
|
||||||
|
class PathChooser;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ProjectExplorer { class AbiWidget; }
|
||||||
|
|
||||||
|
namespace BareMetal {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
// SdccToolChain
|
||||||
|
|
||||||
|
class SdccToolChain final : public ProjectExplorer::ToolChain
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(SdccToolChain)
|
||||||
|
|
||||||
|
public:
|
||||||
|
QString typeDisplayName() const override;
|
||||||
|
|
||||||
|
void setTargetAbi(const ProjectExplorer::Abi &abi);
|
||||||
|
ProjectExplorer::Abi targetAbi() const override;
|
||||||
|
|
||||||
|
bool isValid() const override;
|
||||||
|
|
||||||
|
MacroInspectionRunner createMacroInspectionRunner() const override;
|
||||||
|
ProjectExplorer::Macros predefinedMacros(const QStringList &cxxflags) const override;
|
||||||
|
|
||||||
|
Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override;
|
||||||
|
ProjectExplorer::WarningFlags warningFlags(const QStringList &cxxflags) const override;
|
||||||
|
|
||||||
|
BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner() const override;
|
||||||
|
ProjectExplorer::HeaderPaths builtInHeaderPaths(const QStringList &cxxFlags,
|
||||||
|
const Utils::FileName &) const override;
|
||||||
|
void addToEnvironment(Utils::Environment &env) const override;
|
||||||
|
ProjectExplorer::IOutputParser *outputParser() const override;
|
||||||
|
|
||||||
|
QVariantMap toMap() const override;
|
||||||
|
bool fromMap(const QVariantMap &data) override;
|
||||||
|
|
||||||
|
std::unique_ptr<ProjectExplorer::ToolChainConfigWidget> createConfigurationWidget() override;
|
||||||
|
|
||||||
|
bool operator ==(const ToolChain &other) const override;
|
||||||
|
|
||||||
|
void setCompilerCommand(const Utils::FileName &file);
|
||||||
|
Utils::FileName compilerCommand() const override;
|
||||||
|
|
||||||
|
QString makeCommand(const Utils::Environment &env) const override;
|
||||||
|
|
||||||
|
ToolChain *clone() const override;
|
||||||
|
|
||||||
|
void toolChainUpdated() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SdccToolChain(const SdccToolChain &tc) = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit SdccToolChain(Detection d);
|
||||||
|
explicit SdccToolChain(Core::Id language, Detection d);
|
||||||
|
|
||||||
|
ProjectExplorer::Abi m_targetAbi;
|
||||||
|
Utils::FileName m_compilerCommand;
|
||||||
|
|
||||||
|
using MacrosCache = std::shared_ptr<ProjectExplorer::Cache<MacroInspectionReport, 64>>;
|
||||||
|
mutable MacrosCache m_predefinedMacrosCache;
|
||||||
|
|
||||||
|
using HeaderPathsCache = ProjectExplorer::Cache<ProjectExplorer::HeaderPaths>;
|
||||||
|
using HeaderPathsCachePtr = std::shared_ptr<HeaderPathsCache>;
|
||||||
|
mutable HeaderPathsCachePtr m_headerPathsCache;
|
||||||
|
|
||||||
|
friend class SdccToolChainFactory;
|
||||||
|
friend class SdccToolChainConfigWidget;
|
||||||
|
};
|
||||||
|
|
||||||
|
// SdccToolChainFactory
|
||||||
|
|
||||||
|
class SdccToolChainFactory final : public ProjectExplorer::ToolChainFactory
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SdccToolChainFactory();
|
||||||
|
QSet<Core::Id> supportedLanguages() const override;
|
||||||
|
|
||||||
|
QList<ProjectExplorer::ToolChain *> autoDetect(
|
||||||
|
const QList<ProjectExplorer::ToolChain *> &alreadyKnown) override;
|
||||||
|
|
||||||
|
bool canCreate() override;
|
||||||
|
ProjectExplorer::ToolChain *create(Core::Id language) override;
|
||||||
|
|
||||||
|
bool canRestore(const QVariantMap &data) override;
|
||||||
|
ProjectExplorer::ToolChain *restore(const QVariantMap &data) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// File path + version.
|
||||||
|
using Candidate = QPair<Utils::FileName, QString>;
|
||||||
|
using Candidates = QVector<Candidate>;
|
||||||
|
|
||||||
|
QList<ProjectExplorer::ToolChain *> autoDetectToolchains(const Candidates &candidates,
|
||||||
|
const QList<ProjectExplorer::ToolChain *> &alreadyKnown) const;
|
||||||
|
QList<ProjectExplorer::ToolChain *> autoDetectToolchain(
|
||||||
|
const Candidate &candidate, Core::Id language) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// SdccToolChainConfigWidget
|
||||||
|
|
||||||
|
class SdccToolChainConfigWidget final : public ProjectExplorer::ToolChainConfigWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SdccToolChainConfigWidget(SdccToolChain *tc);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void applyImpl() override;
|
||||||
|
void discardImpl() override { setFromToolchain(); }
|
||||||
|
bool isDirtyImpl() const override;
|
||||||
|
void makeReadOnlyImpl() override;
|
||||||
|
|
||||||
|
void setFromToolchain();
|
||||||
|
void handleCompilerCommandChange();
|
||||||
|
|
||||||
|
Utils::PathChooser *m_compilerCommand = nullptr;
|
||||||
|
ProjectExplorer::AbiWidget *m_abiWidget = nullptr;
|
||||||
|
ProjectExplorer::Macros m_macros;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace BareMetal
|
@@ -135,6 +135,8 @@ static QStringList toolchainList(const ProjectExplorer::ToolChain *tc)
|
|||||||
list << QLatin1String("iar");
|
list << QLatin1String("iar");
|
||||||
else if (tc->typeId() == BareMetal::Constants::KEIL_TOOLCHAIN_TYPEID)
|
else if (tc->typeId() == BareMetal::Constants::KEIL_TOOLCHAIN_TYPEID)
|
||||||
list << QLatin1String("keil");
|
list << QLatin1String("keil");
|
||||||
|
else if (tc->typeId() == BareMetal::Constants::SDCC_TOOLCHAIN_TYPEID)
|
||||||
|
list << QLatin1String("sdcc");
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user