// Copyright (C) 2019 Denis Shienkov // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "iarewtoolchain.h" #include "baremetalconstants.h" #include "baremetaltr.h" #include "iarewparser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ProjectExplorer; using namespace Utils; namespace BareMetal::Internal { static QString cppLanguageOption(const FilePath &compiler) { const QString baseName = compiler.baseName(); if (baseName == "iccarm" || baseName == "iccrl78" || baseName == "iccrh850" || baseName == "iccrx" || baseName == "iccriscv") { return QString("--c++"); } if (baseName == "icc8051" || baseName == "iccavr" || baseName == "iccstm8" || baseName == "icc430" || baseName == "iccv850" || baseName == "icc78k" || baseName == "iccavr32" || baseName == "iccsh" || baseName == "icccf" || baseName == "iccm32c" || baseName == "iccm16c" || baseName == "iccr32c" || baseName == "icccr16c") { return QString("--ec++"); } return {}; } static Macros dumpPredefinedMacros(const FilePath &compiler, const QStringList &extraArgs, const Id languageId, const Environment &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"; Process cpp; cpp.setEnvironment(env); CommandLine cmd(compiler, {fakeIn.fileName()}); if (languageId == ProjectExplorer::Constants::CXX_LANGUAGE_ID) cmd.addArg(cppLanguageOption(compiler)); cmd.addArgs(extraArgs); cmd.addArg("--predef_macros"); cmd.addArg(outpath); cpp.setCommand(cmd); cpp.runBlocking(); if (cpp.result() != ProcessResult::FinishedWithSuccess) { qWarning() << cpp.exitMessage(Process::FailureMessageFormat::WithStdErr); return {}; } QByteArray output; QFile fakeOut(outpath); if (fakeOut.open(QIODevice::ReadOnly)) output = fakeOut.readAll(); fakeOut.remove(); return Macro::toMacros(output); } static HeaderPaths dumpHeaderPaths(const FilePath &compiler, const Id languageId, const Environment &env) { if (!compiler.exists()) return {}; // Seems, that IAR compiler has not options to show a list of system // include directories. But, we can use the following trick to enumerate // this directories. We need to specify the '--preinclude' option with // the wrong value (e.g. a dot). In this case the compiler fails and its // error output will contains a mention about the using search directories // in a form of tokens, like: ' searched: "" '. Where are // the resulting paths are escaped with a quotes. QTemporaryFile fakeIn; if (!fakeIn.open()) return {}; fakeIn.close(); CommandLine cmd(compiler, {fakeIn.fileName()}); if (languageId == ProjectExplorer::Constants::CXX_LANGUAGE_ID) cmd.addArg(cppLanguageOption(compiler)); cmd.addArg("--preinclude"); cmd.addArg("."); Process cpp; cpp.setEnvironment(env); cpp.setCommand(cmd); cpp.runBlocking(); HeaderPaths headerPaths; const QByteArray output = cpp.allOutput().toUtf8(); for (auto pos = 0; pos < output.size(); ++pos) { const int searchIndex = output.indexOf("searched:", pos); if (searchIndex == -1) break; const int startQuoteIndex = output.indexOf('"', searchIndex + 1); if (startQuoteIndex == -1) break; const int endQuoteIndex = output.indexOf('"', startQuoteIndex + 1); if (endQuoteIndex == -1) break; const QByteArray candidate = output.mid(startQuoteIndex + 1, endQuoteIndex - startQuoteIndex - 1) .simplified(); const QString headerPath = QFileInfo(QFile::decodeName(candidate)) .canonicalFilePath(); // Ignore the QtC binary directory path. if (headerPath != QCoreApplication::applicationDirPath()) headerPaths.append(HeaderPath::makeBuiltIn(headerPath)); pos = endQuoteIndex + 1; } return headerPaths; } 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; if (macro.key == "__ICCAVR32__") return Abi::Architecture::Avr32Architecture; if (macro.key == "__ICCSTM8__") return Abi::Architecture::Stm8Architecture; if (macro.key == "__ICC430__") return Abi::Architecture::Msp430Architecture; if (macro.key == "__ICCRL78__") return Abi::Architecture::Rl78Architecture; if (macro.key == "__ICCV850__") return Abi::Architecture::V850Architecture; if (macro.key == "__ICCRH850__") return Abi::Architecture::Rh850Architecture; if (macro.key == "__ICCRX__") return Abi::Architecture::RxArchitecture; if (macro.key == "__ICC78K__") return Abi::Architecture::K78Architecture; if (macro.key == "__ICCSH__") return Abi::Architecture::ShArchitecture; if (macro.key == "__ICCRISCV__") return Abi::Architecture::RiscVArchitecture; if (macro.key == "__ICCCF__") return Abi::Architecture::M68KArchitecture; if (macro.key == "__ICCM32C__") return Abi::Architecture::M32CArchitecture; if (macro.key == "__ICCM16C__") return Abi::Architecture::M16CArchitecture; if (macro.key == "__ICCR32C__") return Abi::Architecture::R32CArchitecture; if (macro.key == "__ICCCR16C__") return Abi::Architecture::CR16Architecture; } 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 || arch == Abi::Architecture::Stm8Architecture || arch == Abi::Architecture::Rl78Architecture || arch == Abi::Architecture::Rh850Architecture || arch == Abi::Architecture::RxArchitecture || arch == Abi::Architecture::ShArchitecture || arch == Abi::Architecture::RiscVArchitecture) { return Abi::BinaryFormat::ElfFormat; } if (arch == Abi::Architecture::Mcs51Architecture || arch == Abi::Architecture::AvrArchitecture || arch == Abi::Architecture::Avr32Architecture || arch == Abi::Architecture::Msp430Architecture || arch == Abi::Architecture::V850Architecture || arch == Abi::Architecture::K78Architecture || arch == Abi::Architecture::M68KArchitecture || arch == Abi::Architecture::M32CArchitecture || arch == Abi::Architecture::M16CArchitecture || arch == Abi::Architecture::R32CArchitecture || arch == Abi::Architecture::CR16Architecture) { 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)}; } static QString buildDisplayName(Abi::Architecture arch, Utils::Id language, const QString &version) { const auto archName = Abi::toString(arch); const auto langName = ToolchainManager::displayNameOfLanguageId(language); return Tr::tr("IAREW %1 (%2, %3)").arg(version, langName, archName); } // IarToolchainConfigWidget class IarToolchainConfigWidget final : public ToolchainConfigWidget { public: explicit IarToolchainConfigWidget(const ProjectExplorer::ToolchainBundle &bundle); private: void applyImpl() final; void discardImpl() final { setFromToolchain(); } bool isDirtyImpl() const final; void makeReadOnlyImpl() final; void setFromToolchain(); void handleCompilerCommandChange(Id language); void handlePlatformCodeGenFlagsChange(); AbiWidget *m_abiWidget = nullptr; QLineEdit *m_platformCodeGenFlagsLineEdit = nullptr; Macros m_cMacros; Macros m_cxxMacros; }; // IarToolchain class IarToolchain final : public Toolchain { public: IarToolchain() : Toolchain(Constants::IAREW_TOOLCHAIN_TYPEID) { setTypeDisplayName(Tr::tr("IAREW")); setTargetAbiKey("TargetAbi"); setCompilerCommandKey("CompilerPath"); m_extraCodeModelFlags.setSettingsKey("PlatformCodeGenFlags"); connect(&m_extraCodeModelFlags, &BaseAspect::changed, this, &IarToolchain::toolChainUpdated); } MacroInspectionRunner createMacroInspectionRunner() const final; LanguageExtensions languageExtensions(const QStringList &cxxflags) const final; WarningFlags warningFlags(const QStringList &cxxflags) const final; BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner(const Environment &) const final; void addToEnvironment(Environment &env) const final; QList createOutputParsers() const final { return {new IarParser()}; } bool operator==(const Toolchain &other) const final; QStringList extraCodeModelFlags() const final { return m_extraCodeModelFlags(); } FilePath makeCommand(const Environment &) const final { return {}; } private: StringListAspect m_extraCodeModelFlags{this}; friend class IarToolchainFactory; friend class IarToolchainConfigWidget; }; Toolchain::MacroInspectionRunner IarToolchain::createMacroInspectionRunner() const { Environment env = Environment::systemEnvironment(); addToEnvironment(env); const FilePath compiler = compilerCommand(); const Id languageId = language(); const QStringList extraArgs = m_extraCodeModelFlags(); MacrosCache macrosCache = predefinedMacrosCache(); return [env, compiler, extraArgs, macrosCache, languageId] (const QStringList &flags) { Macros macros = dumpPredefinedMacros(compiler, extraArgs + flags, languageId, env); macros.append({"__intrinsic", "", MacroType::Define}); macros.append({"__nounwind", "", MacroType::Define}); macros.append({"__noreturn", "", MacroType::Define}); macros.append({"__no_init", "", MacroType::Define}); macros.append({"__packed", "", MacroType::Define}); macros.append({"__spec_string", "", MacroType::Define}); macros.append({"__constrange(__a,__b)", "", MacroType::Define}); const auto languageVersion = Toolchain::languageVersion(languageId, macros); const auto report = MacroInspectionReport{macros, languageVersion}; macrosCache->insert({}, report); return report; }; } 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 Environment &) const { Environment env = Environment::systemEnvironment(); addToEnvironment(env); const FilePath compiler = compilerCommand(); const Id languageId = language(); HeaderPathsCache headerPaths = headerPathsCache(); return [env, compiler, headerPaths, languageId](const QStringList &flags, const FilePath &sysRoot, const QString &) { Q_UNUSED(flags) Q_UNUSED(sysRoot) const HeaderPaths paths = dumpHeaderPaths(compiler, languageId, env); headerPaths->insert({}, paths); return paths; }; } void IarToolchain::addToEnvironment(Environment &env) const { if (!compilerCommand().isEmpty()) env.prependOrSetPath(compilerCommand().parentDir()); } bool IarToolchain::operator==(const Toolchain &other) const { if (!Toolchain::operator==(other)) return false; const auto customTc = static_cast(&other); return compilerCommand() == customTc->compilerCommand() && m_extraCodeModelFlags() == customTc->m_extraCodeModelFlags(); } // IarToolchainFactory class IarToolchainFactory final : public ToolchainFactory { public: IarToolchainFactory() { setDisplayName(Tr::tr("IAREW")); setSupportedToolchainType(Constants::IAREW_TOOLCHAIN_TYPEID); setSupportedLanguages({ProjectExplorer::Constants::C_LANGUAGE_ID, ProjectExplorer::Constants::CXX_LANGUAGE_ID}); setToolchainConstructor([] { return new IarToolchain; }); setUserCreatable(true); } Toolchains autoDetect(const ToolchainDetector &detector) const final; Toolchains detectForImport(const ToolchainDescription &tcd) const final; std::unique_ptr createConfigurationWidget( const ProjectExplorer::ToolchainBundle &bundle) const final; private: Toolchains autoDetectToolchains(const Candidates &candidates, const Toolchains &alreadyKnown) const; Toolchains autoDetectToolchain(const Candidate &candidate, Id languageId) const; }; void setupIarToolchain() { static IarToolchainFactory theIarToolchainFactory; } Toolchains IarToolchainFactory::autoDetect(const ToolchainDetector &detector) const { Candidates candidates; #ifdef Q_OS_WIN QStringList registryNodes; registryNodes << "HKEY_LOCAL_MACHINE\\SOFTWARE\\IAR Systems\\Embedded Workbench"; #ifdef Q_OS_WIN64 registryNodes << "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\IAR Systems\\Embedded Workbench"; #endif // Dictionary for know toolchains. static const struct Entry { QString registryKey; QString subExePath; } knowToolchains[] = { {{"EWARM"}, {"/arm/bin/iccarm.exe"}}, {{"EWAVR"}, {"/avr/bin/iccavr.exe"}}, {{"EWAVR32"}, {"/avr32/bin/iccavr32.exe"}}, {{"EW8051"}, {"/8051/bin/icc8051.exe"}}, {{"EWSTM8"}, {"/stm8/bin/iccstm8.exe"}}, {{"EW430"}, {"/430/bin/icc430.exe"}}, {{"EWRL78"}, {"/rl78/bin/iccrl78.exe"}}, {{"EWV850"}, {"/v850/bin/iccv850.exe"}}, {{"EWRH850"}, {"/rh850/bin/iccrh850.exe"}}, {{"EWRX"}, {"/rx/bin/iccrx.exe"}}, {{"EW78K"}, {"/78k/bin/icc78k.exe"}}, {{"EWSH"}, {"/sh/bin/iccsh.exe"}}, {{"EWRISCV"}, {"/riscv/bin/iccriscv.exe"}}, {{"EWCF"}, {"/cf/bin/icccf.exe"}}, {{"EWM32C"}, {"/m32c/bin/iccm32c.exe"}}, {{"EWM16C"}, {"/m16c/bin/iccm16c.exe"}}, {{"EWR32C"}, {"/r32c/bin/iccr32c.exe"}}, {{"EWCR16C"}, {"/cr16c/bin/icccr16c.exe"}}, }; for (const QString ®istryNode : registryNodes) { QSettings registry(registryNode, QSettings::NativeFormat); const auto oneLevelGroups = registry.childGroups(); for (const QString &oneLevelKey : oneLevelGroups) { registry.beginGroup(oneLevelKey); const auto twoLevelGroups = registry.childGroups(); for (const Entry &entry : knowToolchains) { if (twoLevelGroups.contains(entry.registryKey)) { registry.beginGroup(entry.registryKey); const auto threeLevelGroups = registry.childGroups(); for (const QString &threeLevelKey : threeLevelGroups) { registry.beginGroup(threeLevelKey); QString compilerPath = registry.value("InstallPath").toString(); if (!compilerPath.isEmpty()) { // Build full compiler path. compilerPath += entry.subExePath; const FilePath fn = FilePath::fromUserInput(compilerPath); if (fn.isExecutableFile()) { // Note: threeLevelKey is a guessed toolchain version. candidates.push_back({fn, threeLevelKey}); } } registry.endGroup(); } registry.endGroup(); } } registry.endGroup(); } } #endif // Q_OS_WIN return autoDetectToolchains(candidates, detector.alreadyKnown); } Toolchains IarToolchainFactory::detectForImport(const ToolchainDescription &tcd) const { return { autoDetectToolchain({tcd.compilerPath, {}}, tcd.language) }; } std::unique_ptr IarToolchainFactory::createConfigurationWidget( const ToolchainBundle &bundle) const { return std::make_unique(bundle); } Toolchains IarToolchainFactory::autoDetectToolchains( const Candidates &candidates, const Toolchains &alreadyKnown) const { Toolchains result; for (const Candidate &candidate : std::as_const(candidates)) { const Toolchains filtered = Utils::filtered(alreadyKnown, [candidate](Toolchain *tc) { return tc->typeId() == Constants::IAREW_TOOLCHAIN_TYPEID && tc->compilerCommand() == candidate.compilerPath && (tc->language() == ProjectExplorer::Constants::C_LANGUAGE_ID || tc->language() == ProjectExplorer::Constants::CXX_LANGUAGE_ID); }); if (!filtered.isEmpty()) { result << filtered; continue; } // Create toolchains for both C and C++ languages. result << autoDetectToolchain(candidate, ProjectExplorer::Constants::C_LANGUAGE_ID); result << autoDetectToolchain(candidate, ProjectExplorer::Constants::CXX_LANGUAGE_ID); } return result; } Toolchains IarToolchainFactory::autoDetectToolchain(const Candidate &candidate, Id languageId) const { if (ToolchainManager::isBadToolchain(candidate.compilerPath)) return {}; const auto env = Environment::systemEnvironment(); const Macros macros = dumpPredefinedMacros(candidate.compilerPath, {}, languageId, env); if (macros.isEmpty()) { ToolchainManager::addBadToolchain(candidate.compilerPath); return {}; } const Abi abi = guessAbi(macros); const auto tc = new IarToolchain; tc->setDetection(Toolchain::AutoDetection); tc->setLanguage(languageId); tc->setCompilerCommand(candidate.compilerPath); tc->setTargetAbi(abi); tc->setDisplayName(buildDisplayName(abi.architecture(), languageId, candidate.compilerVersion)); const auto languageVersion = Toolchain::languageVersion(languageId, macros); tc->predefinedMacrosCache()->insert({}, {macros, languageVersion}); return {tc}; } // IarToolchainConfigWidget IarToolchainConfigWidget::IarToolchainConfigWidget(const ToolchainBundle &bundle) : ToolchainConfigWidget(bundle), m_abiWidget(new AbiWidget) { m_platformCodeGenFlagsLineEdit = new QLineEdit(this); m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(bundle.extraCodeModelFlags())); m_mainLayout->addRow(Tr::tr("Platform codegen flags:"), m_platformCodeGenFlagsLineEdit); m_mainLayout->addRow(Tr::tr("&ABI:"), m_abiWidget); m_abiWidget->setEnabled(false); addErrorLabel(); setFromToolchain(); connect(this, &ToolchainConfigWidget::compilerCommandChanged, this, &IarToolchainConfigWidget::handleCompilerCommandChange); connect(m_platformCodeGenFlagsLineEdit, &QLineEdit::editingFinished, this, &IarToolchainConfigWidget::handlePlatformCodeGenFlagsChange); connect(m_abiWidget, &AbiWidget::abiChanged, this, &ToolchainConfigWidget::dirty); } void IarToolchainConfigWidget::applyImpl() { if (bundle().isAutoDetected()) return; bundle().forEach([this](IarToolchain &tc) { tc.m_extraCodeModelFlags.setValue(splitString(m_platformCodeGenFlagsLineEdit->text())); }); bundle().setTargetAbi(m_abiWidget->currentAbi()); if (m_cMacros.isEmpty() && m_cxxMacros.isEmpty()) return; bundle().forEach([this](IarToolchain &tc) { const Macros ¯os = tc.language() == ProjectExplorer::Constants::C_LANGUAGE_ID ? m_cMacros : m_cxxMacros; const auto languageVersion = Toolchain::languageVersion(tc.language(), macros); tc.predefinedMacrosCache()->insert({}, {macros, languageVersion}); }); setFromToolchain(); } bool IarToolchainConfigWidget::isDirtyImpl() const { return m_platformCodeGenFlagsLineEdit->text() != ProcessArgs::joinArgs(bundle().extraCodeModelFlags()) || m_abiWidget->currentAbi() != bundle().targetAbi(); } void IarToolchainConfigWidget::makeReadOnlyImpl() { m_platformCodeGenFlagsLineEdit->setEnabled(false); m_abiWidget->setEnabled(false); } void IarToolchainConfigWidget::setFromToolchain() { const QSignalBlocker blocker(this); m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(bundle().extraCodeModelFlags())); m_abiWidget->setAbis({}, bundle().targetAbi()); m_abiWidget->setEnabled(hasAnyCompiler() && !bundle().isAutoDetected()); } void IarToolchainConfigWidget::handleCompilerCommandChange(Id language) { const bool isC = language == ProjectExplorer::Constants::C_LANGUAGE_ID; const FilePath compilerPath = compilerCommand(language); Macros ¯os = isC ? m_cMacros : m_cxxMacros; const bool haveCompiler = compilerPath.isExecutableFile(); if (haveCompiler) { const auto env = Environment::systemEnvironment(); const QStringList extraArgs = splitString(m_platformCodeGenFlagsLineEdit->text()); macros = dumpPredefinedMacros(compilerPath, extraArgs, language, env); const Abi guessed = guessAbi(macros); m_abiWidget->setAbis({}, guessed); } m_abiWidget->setEnabled(hasAnyCompiler() && !bundle().isAutoDetected()); emit dirty(); } void IarToolchainConfigWidget::handlePlatformCodeGenFlagsChange() { const QString str1 = m_platformCodeGenFlagsLineEdit->text(); const QString str2 = ProcessArgs::joinArgs(splitString(str1)); if (str1 != str2) { m_platformCodeGenFlagsLineEdit->setText(str2); } else { handleCompilerCommandChange(ProjectExplorer::Constants::C_LANGUAGE_ID); handleCompilerCommandChange(ProjectExplorer::Constants::CXX_LANGUAGE_ID); } } } // BareMetal::Internal