1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "baremetalconstants.h"
27 
28 #include "iarewparser.h"
29 #include "iarewtoolchain.h"
30 
31 #include <projectexplorer/abiwidget.h>
32 #include <projectexplorer/projectexplorerconstants.h>
33 #include <projectexplorer/projectmacro.h>
34 #include <projectexplorer/toolchainmanager.h>
35 
36 #include <utils/algorithm.h>
37 #include <utils/environment.h>
38 #include <utils/pathchooser.h>
39 #include <utils/qtcassert.h>
40 #include <utils/qtcprocess.h>
41 
42 #include <QDebug>
43 #include <QDir>
44 #include <QFile>
45 #include <QFileInfo>
46 #include <QFormLayout>
47 #include <QLineEdit>
48 #include <QPlainTextEdit>
49 #include <QSettings>
50 #include <QTemporaryFile>
51 
52 using namespace ProjectExplorer;
53 using namespace Utils;
54 
55 namespace BareMetal {
56 namespace Internal {
57 
58 // Helpers:
59 
60 static const char compilerPlatformCodeGenFlagsKeyC[] = "PlatformCodeGenFlags";
61 
compilerExists(const FilePath & compilerPath)62 static bool compilerExists(const FilePath &compilerPath)
63 {
64     const QFileInfo fi = compilerPath.toFileInfo();
65     return fi.exists() && fi.isExecutable() && fi.isFile();
66 }
67 
cppLanguageOption(const FilePath & compiler)68 static QString cppLanguageOption(const FilePath &compiler)
69 {
70     const QString baseName = compiler.baseName();
71     if (baseName == "iccarm" || baseName == "iccrl78"
72             || baseName == "iccrh850" || baseName == "iccrx"
73             || baseName == "iccriscv") {
74         return QString("--c++");
75     }
76     if (baseName == "icc8051" || baseName == "iccavr"
77             || baseName == "iccstm8" || baseName == "icc430"
78             || baseName == "iccv850" || baseName == "icc78k"
79             || baseName == "iccavr32" || baseName == "iccsh"
80             || baseName == "icccf" || baseName == "iccm32c"
81             || baseName == "iccm16c" || baseName == "iccr32c"
82             || baseName == "icccr16c") {
83         return QString("--ec++");
84     }
85     return {};
86 }
87 
dumpPredefinedMacros(const FilePath & compiler,const QStringList & extraArgs,const Id languageId,const Environment & env)88 static Macros dumpPredefinedMacros(const FilePath &compiler, const QStringList &extraArgs,
89                                    const Id languageId, const Environment &env)
90 {
91     if (compiler.isEmpty() || !compiler.toFileInfo().isExecutable())
92         return {};
93 
94     // IAR compiler requires an input and output files.
95 
96     QTemporaryFile fakeIn;
97     if (!fakeIn.open())
98         return {};
99     fakeIn.close();
100 
101     const QString outpath = fakeIn.fileName() + ".tmp";
102 
103     QtcProcess cpp;
104     cpp.setEnvironment(env);
105     cpp.setTimeoutS(10);
106 
107     CommandLine cmd(compiler, {fakeIn.fileName()});
108     if (languageId == ProjectExplorer::Constants::CXX_LANGUAGE_ID)
109         cmd.addArg(cppLanguageOption(compiler));
110     cmd.addArgs(extraArgs);
111     cmd.addArg("--predef_macros");
112     cmd.addArg(outpath);
113 
114     cpp.setCommand(cmd);
115     cpp.runBlocking();
116     if (cpp.result() != QtcProcess::FinishedWithSuccess) {
117         qWarning() << cpp.exitMessage();
118         return {};
119     }
120 
121     QByteArray output;
122     QFile fakeOut(outpath);
123     if (fakeOut.open(QIODevice::ReadOnly))
124         output = fakeOut.readAll();
125     fakeOut.remove();
126 
127     return Macro::toMacros(output);
128 }
129 
dumpHeaderPaths(const FilePath & compiler,const Id languageId,const Environment & env)130 static HeaderPaths dumpHeaderPaths(const FilePath &compiler, const Id languageId,
131                                    const Environment &env)
132 {
133     if (!compiler.exists())
134         return {};
135 
136     // Seems, that IAR compiler has not options to show a list of system
137     // include directories. But, we can use the following trick to enumerate
138     // this directories. We need to specify the '--preinclude' option with
139     // the wrong value (e.g. a dot). In this case the compiler fails and its
140     // error output will contains a mention about the using search directories
141     // in a form of tokens, like: ' searched: "<path/to/include>" '. Where are
142     // the resulting paths are escaped with a quotes.
143 
144     QTemporaryFile fakeIn;
145     if (!fakeIn.open())
146         return {};
147     fakeIn.close();
148 
149     CommandLine cmd(compiler, {fakeIn.fileName()});
150     if (languageId == ProjectExplorer::Constants::CXX_LANGUAGE_ID)
151         cmd.addArg(cppLanguageOption(compiler));
152     cmd.addArg("--preinclude");
153     cmd.addArg(".");
154 
155     QtcProcess cpp;
156     cpp.setEnvironment(env);
157     cpp.setTimeoutS(10);
158     cpp.setCommand(cmd);
159     cpp.runBlocking();
160 
161     HeaderPaths headerPaths;
162 
163     const QByteArray output = cpp.allOutput().toUtf8();
164     for (auto pos = 0; pos < output.size(); ++pos) {
165         const int searchIndex = output.indexOf("searched:", pos);
166         if (searchIndex == -1)
167             break;
168         const int startQuoteIndex = output.indexOf('"', searchIndex + 1);
169         if (startQuoteIndex == -1)
170             break;
171         const int endQuoteIndex = output.indexOf('"', startQuoteIndex + 1);
172         if (endQuoteIndex == -1)
173             break;
174 
175         const QByteArray candidate = output.mid(startQuoteIndex + 1,
176                                                 endQuoteIndex - startQuoteIndex - 1)
177                 .simplified();
178 
179         const QString headerPath = QFileInfo(QFile::decodeName(candidate))
180                 .canonicalFilePath();
181 
182         // Ignore the QtC binary directory path.
183         if (headerPath != QCoreApplication::applicationDirPath())
184             headerPaths.append({headerPath, HeaderPathType::BuiltIn});
185 
186         pos = endQuoteIndex + 1;
187     }
188 
189     return headerPaths;
190 }
191 
guessArchitecture(const Macros & macros)192 static Abi::Architecture guessArchitecture(const Macros &macros)
193 {
194     for (const Macro &macro : macros) {
195         if (macro.key == "__ICCARM__")
196             return Abi::Architecture::ArmArchitecture;
197         if (macro.key == "__ICC8051__")
198             return Abi::Architecture::Mcs51Architecture;
199         if (macro.key == "__ICCAVR__")
200             return Abi::Architecture::AvrArchitecture;
201         if (macro.key == "__ICCAVR32__")
202             return Abi::Architecture::Avr32Architecture;
203         if (macro.key == "__ICCSTM8__")
204             return Abi::Architecture::Stm8Architecture;
205         if (macro.key == "__ICC430__")
206             return Abi::Architecture::Msp430Architecture;
207         if (macro.key == "__ICCRL78__")
208             return Abi::Architecture::Rl78Architecture;
209         if (macro.key == "__ICCV850__")
210             return Abi::Architecture::V850Architecture;
211         if (macro.key == "__ICCRH850__")
212             return Abi::Architecture::Rh850Architecture;
213         if (macro.key == "__ICCRX__")
214             return Abi::Architecture::RxArchitecture;
215         if (macro.key == "__ICC78K__")
216             return Abi::Architecture::K78Architecture;
217         if (macro.key == "__ICCSH__")
218             return Abi::Architecture::ShArchitecture;
219         if (macro.key == "__ICCRISCV__")
220             return Abi::Architecture::RiscVArchitecture;
221         if (macro.key == "__ICCCF__")
222             return Abi::Architecture::M68KArchitecture;
223         if (macro.key == "__ICCM32C__")
224             return Abi::Architecture::M32CArchitecture;
225         if (macro.key == "__ICCM16C__")
226             return Abi::Architecture::M16CArchitecture;
227         if (macro.key == "__ICCR32C__")
228             return Abi::Architecture::R32CArchitecture;
229         if (macro.key == "__ICCCR16C__")
230             return Abi::Architecture::CR16Architecture;
231     }
232     return Abi::Architecture::UnknownArchitecture;
233 }
234 
guessWordWidth(const Macros & macros)235 static unsigned char guessWordWidth(const Macros &macros)
236 {
237     const Macro sizeMacro = Utils::findOrDefault(macros, [](const Macro &m) {
238         return m.key == "__INT_SIZE__";
239     });
240     if (sizeMacro.isValid() && sizeMacro.type == MacroType::Define)
241         return sizeMacro.value.toInt() * 8;
242     return 0;
243 }
244 
guessFormat(Abi::Architecture arch)245 static Abi::BinaryFormat guessFormat(Abi::Architecture arch)
246 {
247     if (arch == Abi::Architecture::ArmArchitecture
248             || arch == Abi::Architecture::Stm8Architecture
249             || arch == Abi::Architecture::Rl78Architecture
250             || arch == Abi::Architecture::Rh850Architecture
251             || arch == Abi::Architecture::RxArchitecture
252             || arch == Abi::Architecture::ShArchitecture
253             || arch == Abi::Architecture::RiscVArchitecture) {
254         return Abi::BinaryFormat::ElfFormat;
255     }
256     if (arch == Abi::Architecture::Mcs51Architecture
257             || arch == Abi::Architecture::AvrArchitecture
258             || arch == Abi::Architecture::Avr32Architecture
259             || arch == Abi::Architecture::Msp430Architecture
260             || arch == Abi::Architecture::V850Architecture
261             || arch == Abi::Architecture::K78Architecture
262             || arch == Abi::Architecture::M68KArchitecture
263             || arch == Abi::Architecture::M32CArchitecture
264             || arch == Abi::Architecture::M16CArchitecture
265             || arch == Abi::Architecture::R32CArchitecture
266             || arch == Abi::Architecture::CR16Architecture) {
267         return Abi::BinaryFormat::UbrofFormat;
268     }
269     return Abi::BinaryFormat::UnknownFormat;
270 }
271 
guessAbi(const Macros & macros)272 static Abi guessAbi(const Macros &macros)
273 {
274     const auto arch = guessArchitecture(macros);
275     return {arch, Abi::OS::BareMetalOS, Abi::OSFlavor::GenericFlavor,
276             guessFormat(arch), guessWordWidth(macros)};
277 }
278 
buildDisplayName(Abi::Architecture arch,Utils::Id language,const QString & version)279 static QString buildDisplayName(Abi::Architecture arch, Utils::Id language,
280                                 const QString &version)
281 {
282     const auto archName = Abi::toString(arch);
283     const auto langName = ToolChainManager::displayNameOfLanguageId(language);
284     return IarToolChain::tr("IAREW %1 (%2, %3)").arg(version, langName, archName);
285 }
286 
287 // IarToolChain
288 
IarToolChain()289 IarToolChain::IarToolChain() :
290     ToolChain(Constants::IAREW_TOOLCHAIN_TYPEID)
291 {
292     setTypeDisplayName(Internal::IarToolChain::tr("IAREW"));
293     setTargetAbiKey("TargetAbi");
294     setCompilerCommandKey("CompilerPath");
295 }
296 
createMacroInspectionRunner() const297 ToolChain::MacroInspectionRunner IarToolChain::createMacroInspectionRunner() const
298 {
299     Environment env = Environment::systemEnvironment();
300     addToEnvironment(env);
301 
302     const FilePath compiler = compilerCommand();
303     const Id languageId = language();
304     const QStringList extraArgs = m_extraCodeModelFlags;
305     MacrosCache macrosCache = predefinedMacrosCache();
306 
307     return [env, compiler, extraArgs, macrosCache, languageId]
308             (const QStringList &flags) {
309         Q_UNUSED(flags)
310 
311         Macros macros = dumpPredefinedMacros(compiler, extraArgs, languageId, env);
312         macros.append({"__intrinsic", "", MacroType::Define});
313         macros.append({"__nounwind", "", MacroType::Define});
314         macros.append({"__noreturn", "", MacroType::Define});
315         macros.append({"__packed", "", MacroType::Define});
316         macros.append({"__spec_string", "", MacroType::Define});
317         macros.append({"__constrange(__a,__b)", "", MacroType::Define});
318 
319         const auto languageVersion = ToolChain::languageVersion(languageId, macros);
320         const auto report = MacroInspectionReport{macros, languageVersion};
321         macrosCache->insert({}, report);
322 
323         return report;
324     };
325 }
326 
languageExtensions(const QStringList &) const327 Utils::LanguageExtensions IarToolChain::languageExtensions(const QStringList &) const
328 {
329     return LanguageExtension::None;
330 }
331 
warningFlags(const QStringList & cxxflags) const332 WarningFlags IarToolChain::warningFlags(const QStringList &cxxflags) const
333 {
334     Q_UNUSED(cxxflags)
335     return WarningFlags::Default;
336 }
337 
createBuiltInHeaderPathsRunner(const Environment &) const338 ToolChain::BuiltInHeaderPathsRunner IarToolChain::createBuiltInHeaderPathsRunner(
339         const Environment &) const
340 {
341     Environment env = Environment::systemEnvironment();
342     addToEnvironment(env);
343 
344     const FilePath compiler = compilerCommand();
345     const Id languageId = language();
346 
347     HeaderPathsCache headerPaths = headerPathsCache();
348 
349     return [env, compiler, headerPaths, languageId](const QStringList &flags,
350                                                     const QString &fileName,
351                                                     const QString &) {
352         Q_UNUSED(flags)
353         Q_UNUSED(fileName)
354 
355         const HeaderPaths paths = dumpHeaderPaths(compiler, languageId, env);
356         headerPaths->insert({}, paths);
357 
358         return paths;
359     };
360 }
361 
addToEnvironment(Environment & env) const362 void IarToolChain::addToEnvironment(Environment &env) const
363 {
364     if (!compilerCommand().isEmpty()) {
365         const FilePath path = compilerCommand().parentDir();
366         env.prependOrSetPath(path.toString());
367     }
368 }
369 
createOutputParsers() const370 QList<Utils::OutputLineParser *> IarToolChain::createOutputParsers() const
371 {
372     return {new IarParser()};
373 }
374 
toMap() const375 QVariantMap IarToolChain::toMap() const
376 {
377     QVariantMap data = ToolChain::toMap();
378     data.insert(compilerPlatformCodeGenFlagsKeyC, m_extraCodeModelFlags);
379     return data;
380 }
381 
fromMap(const QVariantMap & data)382 bool IarToolChain::fromMap(const QVariantMap &data)
383 {
384     if (!ToolChain::fromMap(data))
385         return false;
386     m_extraCodeModelFlags = data.value(compilerPlatformCodeGenFlagsKeyC).toStringList();
387     return true;
388 }
389 
createConfigurationWidget()390 std::unique_ptr<ToolChainConfigWidget> IarToolChain::createConfigurationWidget()
391 {
392     return std::make_unique<IarToolChainConfigWidget>(this);
393 }
394 
operator ==(const ToolChain & other) const395 bool IarToolChain::operator==(const ToolChain &other) const
396 {
397     if (!ToolChain::operator==(other))
398         return false;
399 
400     const auto customTc = static_cast<const IarToolChain *>(&other);
401     return compilerCommand() == customTc->compilerCommand()
402             && m_extraCodeModelFlags == customTc->m_extraCodeModelFlags;
403 }
404 
setExtraCodeModelFlags(const QStringList & flags)405 void IarToolChain::setExtraCodeModelFlags(const QStringList &flags)
406 {
407     if (flags == m_extraCodeModelFlags)
408         return;
409     m_extraCodeModelFlags = flags;
410     toolChainUpdated();
411 }
412 
extraCodeModelFlags() const413 QStringList IarToolChain::extraCodeModelFlags() const
414 {
415     return m_extraCodeModelFlags;
416 }
417 
makeCommand(const Environment & env) const418 FilePath IarToolChain::makeCommand(const Environment &env) const
419 {
420     Q_UNUSED(env)
421     return {};
422 }
423 
424 // IarToolChainFactory
425 
IarToolChainFactory()426 IarToolChainFactory::IarToolChainFactory()
427 {
428     setDisplayName(IarToolChain::tr("IAREW"));
429     setSupportedToolChainType(Constants::IAREW_TOOLCHAIN_TYPEID);
430     setSupportedLanguages({ProjectExplorer::Constants::C_LANGUAGE_ID,
431                            ProjectExplorer::Constants::CXX_LANGUAGE_ID});
432     setToolchainConstructor([] { return new IarToolChain; });
433     setUserCreatable(true);
434 }
435 
autoDetect(const QList<ToolChain * > & alreadyKnown,const IDevice::Ptr & device)436 QList<ToolChain *> IarToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown,
437                                                    const IDevice::Ptr &device)
438 {
439     Q_UNUSED(device);
440     Candidates candidates;
441 
442 #ifdef Q_OS_WIN
443 
444 #ifdef Q_OS_WIN64
445     static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\IAR Systems\\Embedded Workbench";
446 #else
447     static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\IAR Systems\\Embedded Workbench";
448 #endif
449 
450     // Dictionary for know toolchains.
451     static const struct Entry {
452         QString registryKey;
453         QString subExePath;
454     } knowToolchains[] = {
455         {{"EWARM"}, {"/arm/bin/iccarm.exe"}},
456         {{"EWAVR"}, {"/avr/bin/iccavr.exe"}},
457         {{"EWAVR32"}, {"/avr32/bin/iccavr32.exe"}},
458         {{"EW8051"}, {"/8051/bin/icc8051.exe"}},
459         {{"EWSTM8"}, {"/stm8/bin/iccstm8.exe"}},
460         {{"EW430"}, {"/430/bin/icc430.exe"}},
461         {{"EWRL78"}, {"/rl78/bin/iccrl78.exe"}},
462         {{"EWV850"}, {"/v850/bin/iccv850.exe"}},
463         {{"EWRH850"}, {"/rh850/bin/iccrh850.exe"}},
464         {{"EWRX"}, {"/rx/bin/iccrx.exe"}},
465         {{"EW78K"}, {"/78k/bin/icc78k.exe"}},
466         {{"EWSH"}, {"/sh/bin/iccsh.exe"}},
467         {{"EWRISCV"}, {"/riscv/bin/iccriscv.exe"}},
468         {{"EWCF"}, {"/cf/bin/icccf.exe"}},
469         {{"EWM32C"}, {"/m32c/bin/iccm32c.exe"}},
470         {{"EWM16C"}, {"/m16c/bin/iccm16c.exe"}},
471         {{"EWR32C"}, {"/r32c/bin/iccr32c.exe"}},
472         {{"EWCR16C"}, {"/cr16c/bin/icccr16c.exe"}},
473     };
474 
475     QSettings registry(kRegistryNode, QSettings::NativeFormat);
476     const auto oneLevelGroups = registry.childGroups();
477     for (const QString &oneLevelKey : oneLevelGroups) {
478         registry.beginGroup(oneLevelKey);
479         const auto twoLevelGroups = registry.childGroups();
480         for (const Entry &entry : knowToolchains) {
481             if (twoLevelGroups.contains(entry.registryKey)) {
482                 registry.beginGroup(entry.registryKey);
483                 const auto threeLevelGroups = registry.childGroups();
484                 for (const QString &threeLevelKey : threeLevelGroups) {
485                     registry.beginGroup(threeLevelKey);
486                     QString compilerPath = registry.value("InstallPath").toString();
487                     if (!compilerPath.isEmpty()) {
488                         // Build full compiler path.
489                         compilerPath += entry.subExePath;
490                         const FilePath fn = FilePath::fromString(compilerPath);
491                         if (compilerExists(fn)) {
492                             // Note: threeLevelKey is a guessed toolchain version.
493                             candidates.push_back({fn, threeLevelKey});
494                         }
495                     }
496                     registry.endGroup();
497                 }
498                 registry.endGroup();
499             }
500         }
501         registry.endGroup();
502     }
503 
504 #endif // Q_OS_WIN
505 
506     return autoDetectToolchains(candidates, alreadyKnown);
507 }
508 
autoDetectToolchains(const Candidates & candidates,const QList<ToolChain * > & alreadyKnown) const509 QList<ToolChain *> IarToolChainFactory::autoDetectToolchains(
510         const Candidates &candidates, const QList<ToolChain *> &alreadyKnown) const
511 {
512     QList<ToolChain *> result;
513 
514     for (const Candidate &candidate : qAsConst(candidates)) {
515         const QList<ToolChain *> filtered = Utils::filtered(
516                     alreadyKnown, [candidate](ToolChain *tc) {
517             return tc->typeId() == Constants::IAREW_TOOLCHAIN_TYPEID
518                 && tc->compilerCommand() == candidate.compilerPath
519                 && (tc->language() == ProjectExplorer::Constants::C_LANGUAGE_ID
520                     || tc->language() == ProjectExplorer::Constants::CXX_LANGUAGE_ID);
521         });
522 
523         if (!filtered.isEmpty()) {
524             result << filtered;
525             continue;
526         }
527 
528         // Create toolchains for both C and C++ languages.
529         result << autoDetectToolchain(candidate, ProjectExplorer::Constants::C_LANGUAGE_ID);
530         result << autoDetectToolchain(candidate, ProjectExplorer::Constants::CXX_LANGUAGE_ID);
531     }
532 
533     return result;
534 }
535 
autoDetectToolchain(const Candidate & candidate,Utils::Id languageId) const536 QList<ToolChain *> IarToolChainFactory::autoDetectToolchain(
537         const Candidate &candidate, Utils::Id languageId) const
538 {
539     const auto env = Environment::systemEnvironment();
540     const Macros macros = dumpPredefinedMacros(candidate.compilerPath, {}, languageId, env);
541     if (macros.isEmpty())
542         return {};
543     const Abi abi = guessAbi(macros);
544 
545     const auto tc = new IarToolChain;
546     tc->setDetection(ToolChain::AutoDetection);
547     tc->setLanguage(languageId);
548     tc->setCompilerCommand(candidate.compilerPath);
549     tc->setTargetAbi(abi);
550     tc->setDisplayName(buildDisplayName(abi.architecture(), languageId,
551                                         candidate.compilerVersion));
552 
553     const auto languageVersion = ToolChain::languageVersion(languageId, macros);
554     tc->predefinedMacrosCache()->insert({}, {macros, languageVersion});
555     return {tc};
556 }
557 
558 // IarToolChainConfigWidget
559 
IarToolChainConfigWidget(IarToolChain * tc)560 IarToolChainConfigWidget::IarToolChainConfigWidget(IarToolChain *tc) :
561     ToolChainConfigWidget(tc),
562     m_compilerCommand(new PathChooser),
563     m_abiWidget(new AbiWidget)
564 {
565     m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand);
566     m_compilerCommand->setHistoryCompleter("PE.IAREW.Command.History");
567     m_mainLayout->addRow(tr("&Compiler path:"), m_compilerCommand);
568     m_platformCodeGenFlagsLineEdit = new QLineEdit(this);
569     m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->extraCodeModelFlags()));
570     m_mainLayout->addRow(tr("Platform codegen flags:"), m_platformCodeGenFlagsLineEdit);
571     m_mainLayout->addRow(tr("&ABI:"), m_abiWidget);
572 
573     m_abiWidget->setEnabled(false);
574 
575     addErrorLabel();
576     setFromToolchain();
577 
578     connect(m_compilerCommand, &PathChooser::rawPathChanged,
579             this, &IarToolChainConfigWidget::handleCompilerCommandChange);
580     connect(m_platformCodeGenFlagsLineEdit, &QLineEdit::editingFinished,
581             this, &IarToolChainConfigWidget::handlePlatformCodeGenFlagsChange);
582     connect(m_abiWidget, &AbiWidget::abiChanged,
583             this, &ToolChainConfigWidget::dirty);
584 }
585 
applyImpl()586 void IarToolChainConfigWidget::applyImpl()
587 {
588     if (toolChain()->isAutoDetected())
589         return;
590 
591     const auto tc = static_cast<IarToolChain *>(toolChain());
592     const QString displayName = tc->displayName();
593     tc->setCompilerCommand(m_compilerCommand->filePath());
594     tc->setExtraCodeModelFlags(splitString(m_platformCodeGenFlagsLineEdit->text()));
595     tc->setTargetAbi(m_abiWidget->currentAbi());
596     tc->setDisplayName(displayName);
597 
598     if (m_macros.isEmpty())
599         return;
600 
601     const auto languageVersion = ToolChain::languageVersion(tc->language(), m_macros);
602     tc->predefinedMacrosCache()->insert({}, {m_macros, languageVersion});
603 
604     setFromToolchain();
605 }
606 
isDirtyImpl() const607 bool IarToolChainConfigWidget::isDirtyImpl() const
608 {
609     const auto tc = static_cast<IarToolChain *>(toolChain());
610     return m_compilerCommand->filePath() != tc->compilerCommand()
611             || m_platformCodeGenFlagsLineEdit->text() != ProcessArgs::joinArgs(tc->extraCodeModelFlags())
612             || m_abiWidget->currentAbi() != tc->targetAbi()
613             ;
614 }
615 
makeReadOnlyImpl()616 void IarToolChainConfigWidget::makeReadOnlyImpl()
617 {
618     m_compilerCommand->setReadOnly(true);
619     m_platformCodeGenFlagsLineEdit->setEnabled(false);
620     m_abiWidget->setEnabled(false);
621 }
622 
setFromToolchain()623 void IarToolChainConfigWidget::setFromToolchain()
624 {
625     const QSignalBlocker blocker(this);
626     const auto tc = static_cast<IarToolChain *>(toolChain());
627     m_compilerCommand->setFilePath(tc->compilerCommand());
628     m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->extraCodeModelFlags()));
629     m_abiWidget->setAbis({}, tc->targetAbi());
630     const bool haveCompiler = compilerExists(m_compilerCommand->filePath());
631     m_abiWidget->setEnabled(haveCompiler && !tc->isAutoDetected());
632 }
633 
handleCompilerCommandChange()634 void IarToolChainConfigWidget::handleCompilerCommandChange()
635 {
636     const FilePath compilerPath = m_compilerCommand->filePath();
637     const bool haveCompiler = compilerExists(compilerPath);
638     if (haveCompiler) {
639         const auto env = Environment::systemEnvironment();
640         const QStringList extraArgs = splitString(m_platformCodeGenFlagsLineEdit->text());
641         const Id languageId = toolChain()->language();
642         m_macros = dumpPredefinedMacros(compilerPath, extraArgs, languageId, env);
643         const Abi guessed = guessAbi(m_macros);
644         m_abiWidget->setAbis({}, guessed);
645     }
646 
647     m_abiWidget->setEnabled(haveCompiler);
648     emit dirty();
649 }
650 
handlePlatformCodeGenFlagsChange()651 void IarToolChainConfigWidget::handlePlatformCodeGenFlagsChange()
652 {
653     const QString str1 = m_platformCodeGenFlagsLineEdit->text();
654     const QString str2 = ProcessArgs::joinArgs(splitString(str1));
655     if (str1 != str2)
656         m_platformCodeGenFlagsLineEdit->setText(str2);
657     else
658         handleCompilerCommandChange();
659 }
660 
661 } // namespace Internal
662 } // namespace BareMetal
663