diff --git a/src/plugins/baremetal/baremetal.pro b/src/plugins/baremetal/baremetal.pro index 9af653b43ea..bb089aa2b81 100644 --- a/src/plugins/baremetal/baremetal.pro +++ b/src/plugins/baremetal/baremetal.pro @@ -19,7 +19,8 @@ SOURCES += baremetalplugin.cpp \ gdbserverprovidermanager.cpp \ openocdgdbserverprovider.cpp \ defaultgdbserverprovider.cpp \ - stlinkutilgdbserverprovider.cpp + stlinkutilgdbserverprovider.cpp \ + iarewtoolchain.cpp \ HEADERS += baremetalplugin.h \ baremetalconstants.h \ @@ -38,7 +39,8 @@ HEADERS += baremetalplugin.h \ gdbserverprovidermanager.h \ openocdgdbserverprovider.h \ defaultgdbserverprovider.h \ - stlinkutilgdbserverprovider.h + stlinkutilgdbserverprovider.h \ + iarewtoolchain.h \ RESOURCES += \ baremetal.qrc diff --git a/src/plugins/baremetal/baremetal.qbs b/src/plugins/baremetal/baremetal.qbs index 3ac05a12761..40212e65083 100644 --- a/src/plugins/baremetal/baremetal.qbs +++ b/src/plugins/baremetal/baremetal.qbs @@ -31,5 +31,6 @@ QtcPlugin { "openocdgdbserverprovider.cpp", "openocdgdbserverprovider.h", "defaultgdbserverprovider.cpp", "defaultgdbserverprovider.h", "stlinkutilgdbserverprovider.cpp", "stlinkutilgdbserverprovider.h", + "iarewtoolchain.cpp", "iarewtoolchain.h", ] } diff --git a/src/plugins/baremetal/baremetalconstants.h b/src/plugins/baremetal/baremetalconstants.h index def72715773..a5d4c6015f9 100644 --- a/src/plugins/baremetal/baremetalconstants.h +++ b/src/plugins/baremetal/baremetalconstants.h @@ -41,5 +41,8 @@ const char OPENOCD_PROVIDER_ID[] = "BareMetal.GdbServerProvider.OpenOcd"; const char DEFAULT_PROVIDER_ID[] = "BareMetal.GdbServerProvider.Default"; const char STLINK_UTIL_PROVIDER_ID[] = "BareMetal.GdbServerProvider.STLinkUtil"; +// Toolchain types. +const char IAREW_TOOLCHAIN_TYPEID[] = "BareMetal.ToolChain.Iar"; + } // namespace BareMetal } // namespace Constants diff --git a/src/plugins/baremetal/baremetalplugin.cpp b/src/plugins/baremetal/baremetalplugin.cpp index 60afa6f911a..f272063cb2d 100644 --- a/src/plugins/baremetal/baremetalplugin.cpp +++ b/src/plugins/baremetal/baremetalplugin.cpp @@ -34,6 +34,8 @@ #include "gdbserverproviderssettingspage.h" #include "gdbserverprovidermanager.h" +#include "iarewtoolchain.h" + #include #include #include @@ -49,6 +51,7 @@ namespace Internal { class BareMetalPluginPrivate { public: + IarToolChainFactory iarToolChainFactory; BareMetalDeviceFactory deviceFactory; BareMetalRunConfigurationFactory runConfigurationFactory; BareMetalCustomRunConfigurationFactory customRunConfigurationFactory; diff --git a/src/plugins/baremetal/iarewtoolchain.cpp b/src/plugins/baremetal/iarewtoolchain.cpp new file mode 100644 index 00000000000..c1eb28dd07e --- /dev/null +++ b/src/plugins/baremetal/iarewtoolchain.cpp @@ -0,0 +1,431 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov +** 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 "iarewtoolchain.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace ProjectExplorer; +using namespace Utils; + +namespace BareMetal { +namespace Internal { + +// Helpers: + +static const char compilerCommandKeyC[] = "BareMetal.IarToolChain.CompilerPath"; +static const char targetAbiKeyC[] = "BareMetal.IarToolChain.TargetAbi"; + +static bool isCompilerExists(const FileName &compilerPath) +{ + const QFileInfo fi = compilerPath.toFileInfo(); + return fi.exists() && fi.isExecutable() && fi.isFile(); +} + +static Macros dumpPredefinedMacros(const FileName &compiler, const QStringList &env) +{ + if (compiler.isEmpty() || !compiler.toFileInfo().isExecutable()) + return {}; + + // IAR compiler requires an input and output files. + + QTemporaryFile fakeIn; + if (!fakeIn.open()) + return {}; + fakeIn.close(); + + const QString outpath = fakeIn.fileName() + ".tmp"; + + SynchronousProcess cpp; + cpp.setEnvironment(env); + cpp.setTimeoutS(10); + + QStringList arguments; + arguments.push_back(fakeIn.fileName()); + arguments.push_back("--predef_macros"); + arguments.push_back(outpath); + + const SynchronousProcessResponse response = cpp.runBlocking(compiler.toString(), arguments); + if (response.result != SynchronousProcessResponse::Finished + || response.exitCode != 0) { + qWarning() << response.exitMessage(compiler.toString(), 10); + return {}; + } + + QByteArray output; + QFile fakeOut(outpath); + if (fakeOut.open(QIODevice::ReadOnly)) + output = fakeOut.readAll(); + fakeOut.remove(); + + return Macro::toMacros(output); +} + +static Abi::Architecture guessArchitecture(const Macros ¯os) +{ + for (const Macro ¯o : macros) { + if (macro.key == "__ICCARM__") + return Abi::Architecture::ArmArchitecture; + if (macro.key == "__ICC8051__") + return Abi::Architecture::Mcs51Architecture; + if (macro.key == "__ICCAVR__") + return Abi::Architecture::AvrArchitecture; + } + return Abi::Architecture::UnknownArchitecture; +} + +static unsigned char guessWordWidth(const Macros ¯os) +{ + const Macro sizeMacro = Utils::findOrDefault(macros, [](const Macro &m) { + return m.key == "__INT_SIZE__"; + }); + if (sizeMacro.isValid() && sizeMacro.type == MacroType::Define) + return sizeMacro.value.toInt() * 8; + return 0; +} + +static Abi::BinaryFormat guessFormat(Abi::Architecture arch) +{ + if (arch == Abi::Architecture::ArmArchitecture) + return Abi::BinaryFormat::ElfFormat; + if (arch == Abi::Architecture::Mcs51Architecture + || arch == Abi::Architecture::AvrArchitecture) { + return Abi::BinaryFormat::UbrofFormat; + } + 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)}; +} + +// IarToolChain + +IarToolChain::IarToolChain(Detection d) : + ToolChain(Constants::IAREW_TOOLCHAIN_TYPEID, d), + m_predefinedMacrosCache(std::make_shared>()) +{ } + +IarToolChain::IarToolChain(Core::Id language, Detection d) : + IarToolChain(d) +{ + setLanguage(language); +} + +QString IarToolChain::typeDisplayName() const +{ + return Internal::IarToolChainFactory::tr("IAREW"); +} + +void IarToolChain::setTargetAbi(const Abi &abi) +{ + if (abi == m_targetAbi) + return; + m_targetAbi = abi; + toolChainUpdated(); +} + +Abi IarToolChain::targetAbi() const +{ + return m_targetAbi; +} + +bool IarToolChain::isValid() const +{ + return true; +} + +ToolChain::MacroInspectionRunner IarToolChain::createMacroInspectionRunner() const +{ + Environment env = Environment::systemEnvironment(); + addToEnvironment(env); + + const Utils::FileName compilerCommand = m_compilerCommand; + const Core::Id lang = language(); + + MacrosCache macrosCache = m_predefinedMacrosCache; + + return [env, compilerCommand, macrosCache, lang] + (const QStringList &flags) { + Q_UNUSED(flags) + + const Macros macros = dumpPredefinedMacros(compilerCommand, env.toStringList()); + const auto report = MacroInspectionReport{macros, languageVersion(lang, macros)}; + macrosCache->insert({}, report); + + return report; + }; +} + +Macros IarToolChain::predefinedMacros(const QStringList &cxxflags) const +{ + return createMacroInspectionRunner()(cxxflags).macros; +} + +Utils::LanguageExtensions IarToolChain::languageExtensions(const QStringList &) const +{ + return LanguageExtension::None; +} + +WarningFlags IarToolChain::warningFlags(const QStringList &cxxflags) const +{ + Q_UNUSED(cxxflags); + return WarningFlags::Default; +} + +ToolChain::BuiltInHeaderPathsRunner IarToolChain::createBuiltInHeaderPathsRunner() const +{ + return {}; +} + +HeaderPaths IarToolChain::builtInHeaderPaths(const QStringList &cxxFlags, + const FileName &fileName) const +{ + Q_UNUSED(cxxFlags) + Q_UNUSED(fileName) + return {}; +} + +void IarToolChain::addToEnvironment(Environment &env) const +{ + if (!m_compilerCommand.isEmpty()) { + const FileName path = m_compilerCommand.parentDir(); + env.prependOrSetPath(path.toString()); + } +} + +IOutputParser *IarToolChain::outputParser() const +{ + return nullptr; +} + +QVariantMap IarToolChain::toMap() const +{ + QVariantMap data = ToolChain::toMap(); + data.insert(compilerCommandKeyC, m_compilerCommand.toString()); + data.insert(targetAbiKeyC, m_targetAbi.toString()); + return data; +} + +bool IarToolChain::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 IarToolChain::createConfigurationWidget() +{ + return std::make_unique(this); +} + +bool IarToolChain::operator==(const ToolChain &other) const +{ + if (!ToolChain::operator==(other)) + return false; + + const auto customTc = static_cast(&other); + return m_compilerCommand == customTc->m_compilerCommand + && m_targetAbi == customTc->m_targetAbi + ; +} + +void IarToolChain::setCompilerCommand(const FileName &file) +{ + if (file == m_compilerCommand) + return; + m_compilerCommand = file; + toolChainUpdated(); +} + +FileName IarToolChain::compilerCommand() const +{ + return m_compilerCommand; +} + +QString IarToolChain::makeCommand(const Environment &env) const +{ + Q_UNUSED(env) + return {}; +} + +ToolChain *IarToolChain::clone() const +{ + return new IarToolChain(*this); +} + +void IarToolChain::toolChainUpdated() +{ + m_predefinedMacrosCache->invalidate(); + ToolChain::toolChainUpdated(); +} + +// IarToolChainFactory + +IarToolChainFactory::IarToolChainFactory() +{ + setDisplayName(tr("IAREW")); +} + +QSet IarToolChainFactory::supportedLanguages() const +{ + return {ProjectExplorer::Constants::C_LANGUAGE_ID, + ProjectExplorer::Constants::CXX_LANGUAGE_ID}; +} + +bool IarToolChainFactory::canCreate() +{ + return true; +} + +ToolChain *IarToolChainFactory::create(Core::Id language) +{ + return new IarToolChain(language, ToolChain::ManualDetection); +} + +bool IarToolChainFactory::canRestore(const QVariantMap &data) +{ + return typeIdFromMap(data) == Constants::IAREW_TOOLCHAIN_TYPEID; +} + +ToolChain *IarToolChainFactory::restore(const QVariantMap &data) +{ + const auto tc = new IarToolChain(ToolChain::ManualDetection); + if (tc->fromMap(data)) + return tc; + + delete tc; + return nullptr; +} + +// IarToolChainConfigWidget + +IarToolChainConfigWidget::IarToolChainConfigWidget(IarToolChain *tc) : + ToolChainConfigWidget(tc), + m_compilerCommand(new PathChooser), + m_abiWidget(new AbiWidget) +{ + Q_ASSERT(tc); + + m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); + m_compilerCommand->setHistoryCompleter("PE.ToolChainCommand.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, &IarToolChainConfigWidget::handleCompilerCommandChange); + connect(m_abiWidget, &AbiWidget::abiChanged, + this, &ToolChainConfigWidget::dirty); +} + +void IarToolChainConfigWidget::applyImpl() +{ + if (toolChain()->isAutoDetected()) + return; + + const auto tc = static_cast(toolChain()); + Q_ASSERT(tc); + 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 IarToolChainConfigWidget::isDirtyImpl() const +{ + const auto tc = static_cast(toolChain()); + Q_ASSERT(tc); + return m_compilerCommand->fileName() != tc->compilerCommand() + || m_abiWidget->currentAbi() != tc->targetAbi() + ; +} + +void IarToolChainConfigWidget::makeReadOnlyImpl() +{ + m_mainLayout->setEnabled(false); +} + +void IarToolChainConfigWidget::setFromToolchain() +{ + const QSignalBlocker blocker(this); + const auto tc = static_cast(toolChain()); + m_compilerCommand->setFileName(tc->compilerCommand()); + m_abiWidget->setAbis({}, tc->targetAbi()); + const bool haveCompiler = isCompilerExists(m_compilerCommand->fileName()); + m_abiWidget->setEnabled(haveCompiler); +} + +void IarToolChainConfigWidget::handleCompilerCommandChange() +{ + const FileName compilerPath = m_compilerCommand->fileName(); + const bool haveCompiler = isCompilerExists(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 diff --git a/src/plugins/baremetal/iarewtoolchain.h b/src/plugins/baremetal/iarewtoolchain.h new file mode 100644 index 00000000000..4e7e4b6daf1 --- /dev/null +++ b/src/plugins/baremetal/iarewtoolchain.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov +** 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 +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QPlainTextEdit; +class QPushButton; +class QTextEdit; +QT_END_NAMESPACE + +namespace Utils { class PathChooser; } +namespace ProjectExplorer { class AbiWidget; } + +namespace BareMetal { +namespace Internal { + +// IarToolChain + +class IarToolChain final : public ProjectExplorer::ToolChain +{ + Q_DECLARE_TR_FUNCTIONS(IarToolChain) + +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 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: + IarToolChain(const IarToolChain &tc) = default; + +private: + explicit IarToolChain(Detection d); + explicit IarToolChain(Core::Id language, Detection d); + + ProjectExplorer::Abi m_targetAbi; + Utils::FileName m_compilerCommand; + + using MacrosCache = std::shared_ptr>; + mutable MacrosCache m_predefinedMacrosCache; + + friend class IarToolChainFactory; + friend class IarToolChainConfigWidget; +}; + +// IarToolChainFactory + +class IarToolChainFactory final : public ProjectExplorer::ToolChainFactory +{ + Q_OBJECT + +public: + IarToolChainFactory(); + QSet supportedLanguages() const override; + + bool canCreate() override; + ProjectExplorer::ToolChain *create(Core::Id language) override; + + bool canRestore(const QVariantMap &data) override; + ProjectExplorer::ToolChain *restore(const QVariantMap &data) override; +}; + +// IarToolChainConfigWidget + +class IarToolChainConfigWidget final : public ProjectExplorer::ToolChainConfigWidget +{ + Q_OBJECT + +public: + explicit IarToolChainConfigWidget(IarToolChain *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 diff --git a/src/plugins/projectexplorer/abi.cpp b/src/plugins/projectexplorer/abi.cpp index 0de4ea655d2..29a93d35bfd 100644 --- a/src/plugins/projectexplorer/abi.cpp +++ b/src/plugins/projectexplorer/abi.cpp @@ -661,6 +661,8 @@ QString Abi::toString(const Architecture &a) return QLatin1String("xtensa"); case X86Architecture: return QLatin1String("x86"); + case Mcs51Architecture: + return QLatin1String("mcs51"); case MipsArchitecture: return QLatin1String("mips"); case PowerPCArchitecture: @@ -722,6 +724,8 @@ QString Abi::toString(const BinaryFormat &bf) return QLatin1String("mach_o"); case RuntimeQmlFormat: return QLatin1String("qml_rt"); + case UbrofFormat: + return "ubrof"; case UnknownFormat: Q_FALLTHROUGH(); default: diff --git a/src/plugins/projectexplorer/abi.h b/src/plugins/projectexplorer/abi.h index 43243847c87..d8d9a9ee9f9 100644 --- a/src/plugins/projectexplorer/abi.h +++ b/src/plugins/projectexplorer/abi.h @@ -54,6 +54,7 @@ public: ShArchitecture, AvrArchitecture, XtensaArchitecture, + Mcs51Architecture, UnknownArchitecture }; @@ -108,6 +109,7 @@ public: MachOFormat, PEFormat, RuntimeQmlFormat, + UbrofFormat, UnknownFormat }; diff --git a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp index 513a163ed67..92d6da6b0c9 100644 --- a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp +++ b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp @@ -29,6 +29,7 @@ #include "qbsprojectmanagerconstants.h" #include +#include #include #include #include @@ -130,6 +131,8 @@ static QStringList toolchainList(const ProjectExplorer::ToolChain *tc) list << QLatin1String("mingw") << QLatin1String("gcc"); else if (tc->typeId() == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) list << QLatin1String("msvc"); + else if (tc->typeId() == BareMetal::Constants::IAREW_TOOLCHAIN_TYPEID) + list << QLatin1String("iar"); return list; }