1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
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 "toolchain.h"
27 
28 #include "abi.h"
29 #include "headerpath.h"
30 #include "projectexplorerconstants.h"
31 #include "toolchainmanager.h"
32 #include "task.h"
33 
34 #include <utils/fileutils.h>
35 #include <utils/qtcassert.h>
36 
37 #include <QCoreApplication>
38 #include <QDir>
39 #include <QFileInfo>
40 #include <QUuid>
41 
42 using namespace Utils;
43 
44 static const char ID_KEY[] = "ProjectExplorer.ToolChain.Id";
45 static const char DISPLAY_NAME_KEY[] = "ProjectExplorer.ToolChain.DisplayName";
46 static const char AUTODETECT_KEY[] = "ProjectExplorer.ToolChain.Autodetect";
47 static const char DETECTION_SOURCE_KEY[] = "ProjectExplorer.ToolChain.DetectionSource";
48 static const char LANGUAGE_KEY_V1[] = "ProjectExplorer.ToolChain.Language"; // For QtCreator <= 4.2
49 static const char LANGUAGE_KEY_V2[] = "ProjectExplorer.ToolChain.LanguageV2"; // For QtCreator > 4.2
50 
51 namespace ProjectExplorer {
52 namespace Internal {
53 
54 static QList<ToolChainFactory *> g_toolChainFactories;
55 
56 // --------------------------------------------------------------------------
57 // ToolChainPrivate
58 // --------------------------------------------------------------------------
59 
60 class ToolChainPrivate
61 {
62 public:
63     using Detection = ToolChain::Detection;
64 
ToolChainPrivate(Utils::Id typeId)65     explicit ToolChainPrivate(Utils::Id typeId) :
66         m_id(QUuid::createUuid().toByteArray()),
67         m_typeId(typeId),
68         m_predefinedMacrosCache(new ToolChain::MacrosCache::element_type()),
69         m_headerPathsCache(new ToolChain::HeaderPathsCache::element_type())
70     {
71         QTC_ASSERT(m_typeId.isValid(), return);
72         QTC_ASSERT(!m_typeId.toString().contains(QLatin1Char(':')), return);
73     }
74 
75     QByteArray m_id;
76     FilePath m_compilerCommand;
77     QString m_compilerCommandKey;
78     Abi m_targetAbi;
79     QString m_targetAbiKey;
80     QSet<Utils::Id> m_supportedLanguages;
81     mutable QString m_displayName;
82     QString m_typeDisplayName;
83     Utils::Id m_typeId;
84     Utils::Id m_language;
85     Detection m_detection = ToolChain::UninitializedDetection;
86     QString m_detectionSource;
87 
88     ToolChain::MacrosCache m_predefinedMacrosCache;
89     ToolChain::HeaderPathsCache m_headerPathsCache;
90 };
91 
92 
93 // Deprecated used from QtCreator <= 4.2
94 
fromLanguageV1(int language)95 Utils::Id fromLanguageV1(int language)
96 {
97     switch (language)
98     {
99     case Deprecated::Toolchain::C :
100         return Utils::Id(Constants::C_LANGUAGE_ID);
101     case Deprecated::Toolchain::Cxx:
102         return Utils::Id(Constants::CXX_LANGUAGE_ID);
103     case Deprecated::Toolchain::None:
104     default:
105         return Utils::Id();
106     }
107 }
108 
109 } // namespace Internal
110 
111 namespace Deprecated {
112 namespace Toolchain {
languageId(Language l)113 QString languageId(Language l)
114 {
115     switch (l) {
116     case Language::None:
117         return QStringLiteral("None");
118     case Language::C:
119         return QStringLiteral("C");
120     case Language::Cxx:
121         return QStringLiteral("Cxx");
122     };
123     return QString();
124 }
125 } // namespace Toolchain
126 } // namespace Deprecated
127 
128 /*!
129     \class ProjectExplorer::ToolChain
130     \brief The ToolChain class represents a tool chain.
131     \sa ProjectExplorer::ToolChainManager
132 */
133 
134 // --------------------------------------------------------------------------
135 
ToolChain(Utils::Id typeId)136 ToolChain::ToolChain(Utils::Id typeId) :
137     d(std::make_unique<Internal::ToolChainPrivate>(typeId))
138 {
139 }
140 
setLanguage(Utils::Id language)141 void ToolChain::setLanguage(Utils::Id language)
142 {
143     QTC_ASSERT(!d->m_language.isValid() || isAutoDetected(), return);
144     QTC_ASSERT(language.isValid(), return);
145     QTC_ASSERT(ToolChainManager::isLanguageSupported(language), return);
146 
147     d->m_language = language;
148 }
149 
150 ToolChain::~ToolChain() = default;
151 
displayName() const152 QString ToolChain::displayName() const
153 {
154     if (d->m_displayName.isEmpty())
155         return typeDisplayName();
156     return d->m_displayName;
157 }
158 
setDisplayName(const QString & name)159 void ToolChain::setDisplayName(const QString &name)
160 {
161     if (d->m_displayName == name)
162         return;
163 
164     d->m_displayName = name;
165     toolChainUpdated();
166 }
167 
isAutoDetected() const168 bool ToolChain::isAutoDetected() const
169 {
170     return detection() == AutoDetection || detection() == AutoDetectionFromSdk;
171 }
172 
detection() const173 ToolChain::Detection ToolChain::detection() const
174 {
175     return d->m_detection;
176 }
177 
detectionSource() const178 QString ToolChain::detectionSource() const
179 {
180     return d->m_detectionSource;
181 }
182 
id() const183 QByteArray ToolChain::id() const
184 {
185     return d->m_id;
186 }
187 
suggestedMkspecList() const188 QStringList ToolChain::suggestedMkspecList() const
189 {
190     return {};
191 }
192 
typeId() const193 Utils::Id ToolChain::typeId() const
194 {
195     return d->m_typeId;
196 }
197 
supportedAbis() const198 Abis ToolChain::supportedAbis() const
199 {
200     return {targetAbi()};
201 }
202 
isValid() const203 bool ToolChain::isValid() const
204 {
205     if (compilerCommand().isEmpty())
206         return false;
207     return compilerCommand().isExecutableFile();
208 }
209 
includedFiles(const QStringList & flags,const QString & directory) const210 QStringList ToolChain::includedFiles(const QStringList &flags, const QString &directory) const
211 {
212     Q_UNUSED(flags)
213     Q_UNUSED(directory)
214     return {};
215 }
216 
language() const217 Utils::Id ToolChain::language() const
218 {
219     return d->m_language;
220 }
221 
operator ==(const ToolChain & tc) const222 bool ToolChain::operator == (const ToolChain &tc) const
223 {
224     if (this == &tc)
225         return true;
226 
227     // We ignore displayname
228     return typeId() == tc.typeId()
229             && isAutoDetected() == tc.isAutoDetected()
230             && language() == tc.language();
231 }
232 
clone() const233 ToolChain *ToolChain::clone() const
234 {
235     for (ToolChainFactory *f : qAsConst(Internal::g_toolChainFactories)) {
236         if (f->supportedToolChainType() == d->m_typeId) {
237             ToolChain *tc = f->create();
238             QTC_ASSERT(tc, return nullptr);
239             tc->fromMap(toMap());
240             // New ID for the clone. It's different.
241             tc->d->m_id = QUuid::createUuid().toByteArray();
242             return tc;
243         }
244     }
245     QTC_CHECK(false);
246     return nullptr;
247 }
248 
249 /*!
250     Used by the tool chain manager to save user-generated tool chains.
251 
252     Make sure to call this function when deriving.
253 */
254 
toMap() const255 QVariantMap ToolChain::toMap() const
256 {
257     QVariantMap result;
258     QString idToSave = d->m_typeId.toString() + QLatin1Char(':') + QString::fromUtf8(id());
259     result.insert(QLatin1String(ID_KEY), idToSave);
260     result.insert(QLatin1String(DISPLAY_NAME_KEY), displayName());
261     result.insert(QLatin1String(AUTODETECT_KEY), isAutoDetected());
262     result.insert(QLatin1String(DETECTION_SOURCE_KEY), d->m_detectionSource);
263     // <Compatibility with QtC 4.2>
264     int oldLanguageId = -1;
265     if (language() == ProjectExplorer::Constants::C_LANGUAGE_ID)
266         oldLanguageId = 1;
267     else if (language() == ProjectExplorer::Constants::CXX_LANGUAGE_ID)
268         oldLanguageId = 2;
269     if (oldLanguageId >= 0)
270         result.insert(LANGUAGE_KEY_V1, oldLanguageId);
271     // </Compatibility>
272     result.insert(QLatin1String(LANGUAGE_KEY_V2), language().toSetting());
273     if (!d->m_targetAbiKey.isEmpty())
274         result.insert(d->m_targetAbiKey, d->m_targetAbi.toString());
275     if (!d->m_compilerCommandKey.isEmpty())
276         result.insert(d->m_compilerCommandKey, d->m_compilerCommand.toVariant());
277     return result;
278 }
279 
toolChainUpdated()280 void ToolChain::toolChainUpdated()
281 {
282     d->m_predefinedMacrosCache->invalidate();
283     d->m_headerPathsCache->invalidate();
284 
285     ToolChainManager::notifyAboutUpdate(this);
286 }
287 
setDetection(ToolChain::Detection de)288 void ToolChain::setDetection(ToolChain::Detection de)
289 {
290     d->m_detection = de;
291 }
292 
setDetectionSource(const QString & source)293 void ToolChain::setDetectionSource(const QString &source)
294 {
295     d->m_detectionSource = source;
296 }
297 
typeDisplayName() const298 QString ToolChain::typeDisplayName() const
299 {
300     return d->m_typeDisplayName;
301 }
302 
targetAbi() const303 Abi ToolChain::targetAbi() const
304 {
305     return d->m_targetAbi;
306 }
307 
setTargetAbi(const Abi & abi)308 void ToolChain::setTargetAbi(const Abi &abi)
309 {
310     if (abi == d->m_targetAbi)
311         return;
312 
313     d->m_targetAbi = abi;
314     toolChainUpdated();
315 }
316 
setTargetAbiNoSignal(const Abi & abi)317 void ToolChain::setTargetAbiNoSignal(const Abi &abi)
318 {
319     d->m_targetAbi = abi;
320 }
321 
setTargetAbiKey(const QString & abiKey)322 void ToolChain::setTargetAbiKey(const QString &abiKey)
323 {
324     d->m_targetAbiKey = abiKey;
325 }
326 
compilerCommand() const327 FilePath ToolChain::compilerCommand() const
328 {
329     return d->m_compilerCommand;
330 }
331 
setCompilerCommand(const FilePath & command)332 void ToolChain::setCompilerCommand(const FilePath &command)
333 {
334     if (command == d->m_compilerCommand)
335         return;
336     d->m_compilerCommand = command;
337     toolChainUpdated();
338 }
339 
setCompilerCommandKey(const QString & commandKey)340 void ToolChain::setCompilerCommandKey(const QString &commandKey)
341 {
342     d->m_compilerCommandKey = commandKey;
343 }
344 
setTypeDisplayName(const QString & typeName)345 void ToolChain::setTypeDisplayName(const QString &typeName)
346 {
347     d->m_typeDisplayName = typeName;
348 }
349 
350 /*!
351     Used by the tool chain manager to load user-generated tool chains.
352 
353     Make sure to call this function when deriving.
354 */
355 
fromMap(const QVariantMap & data)356 bool ToolChain::fromMap(const QVariantMap &data)
357 {
358     d->m_displayName = data.value(QLatin1String(DISPLAY_NAME_KEY)).toString();
359 
360     // make sure we have new style ids:
361     const QString id = data.value(QLatin1String(ID_KEY)).toString();
362     int pos = id.indexOf(QLatin1Char(':'));
363     QTC_ASSERT(pos > 0, return false);
364     d->m_typeId = Utils::Id::fromString(id.left(pos));
365     d->m_id = id.mid(pos + 1).toUtf8();
366 
367     const bool autoDetect = data.value(QLatin1String(AUTODETECT_KEY), false).toBool();
368     d->m_detection = autoDetect ? AutoDetection : ManualDetection;
369     d->m_detectionSource = data.value(DETECTION_SOURCE_KEY).toString();
370 
371     if (data.contains(LANGUAGE_KEY_V2)) {
372         // remove hack to trim language id in 4.4: This is to fix up broken language
373         // ids that happened in 4.3 master branch
374         const QString langId = data.value(QLatin1String(LANGUAGE_KEY_V2)).toString();
375         const int pos = langId.lastIndexOf('.');
376         if (pos >= 0)
377             d->m_language = Utils::Id::fromString(langId.mid(pos + 1));
378         else
379             d->m_language = Utils::Id::fromString(langId);
380     } else if (data.contains(LANGUAGE_KEY_V1)) { // Import from old settings
381         d->m_language = Internal::fromLanguageV1(data.value(QLatin1String(LANGUAGE_KEY_V1)).toInt());
382     }
383 
384     if (!d->m_language.isValid())
385         d->m_language = Utils::Id(Constants::CXX_LANGUAGE_ID);
386 
387     if (!d->m_targetAbiKey.isEmpty())
388         d->m_targetAbi = Abi::fromString(data.value(d->m_targetAbiKey).toString());
389 
390     d->m_compilerCommand = FilePath::fromVariant(data.value(d->m_compilerCommandKey));
391 
392     return true;
393 }
394 
headerPathsCache() const395 const ToolChain::HeaderPathsCache &ToolChain::headerPathsCache() const
396 {
397     return d->m_headerPathsCache;
398 }
399 
predefinedMacrosCache() const400 const ToolChain::MacrosCache &ToolChain::predefinedMacrosCache() const
401 {
402     return d->m_predefinedMacrosCache;
403 }
404 
toLanguageVersionAsLong(QByteArray dateAsByteArray)405 static long toLanguageVersionAsLong(QByteArray dateAsByteArray)
406 {
407     dateAsByteArray.chop(1); // Strip 'L'.
408 
409     bool success = false;
410     const int result = dateAsByteArray.toLong(&success);
411     QTC_CHECK(success);
412 
413     return result;
414 }
415 
cxxLanguageVersion(const QByteArray & cplusplusMacroValue)416 Utils::LanguageVersion ToolChain::cxxLanguageVersion(const QByteArray &cplusplusMacroValue)
417 {
418     using Utils::LanguageVersion;
419     const long version = toLanguageVersionAsLong(cplusplusMacroValue);
420 
421     if (version > 201703L)
422         return LanguageVersion::LatestCxx;
423     if (version > 201402L)
424         return LanguageVersion::CXX17;
425     if (version > 201103L)
426         return LanguageVersion::CXX14;
427     if (version == 201103L)
428         return LanguageVersion::CXX11;
429 
430     return LanguageVersion::CXX03;
431 }
432 
languageVersion(const Utils::Id & language,const Macros & macros)433 Utils::LanguageVersion ToolChain::languageVersion(const Utils::Id &language, const Macros &macros)
434 {
435     using Utils::LanguageVersion;
436 
437     if (language == Constants::CXX_LANGUAGE_ID) {
438         for (const ProjectExplorer::Macro &macro : macros) {
439             if (macro.key == "__cplusplus") // Check for the C++ identifying macro
440                 return cxxLanguageVersion(macro.value);
441         }
442 
443         QTC_CHECK(false && "__cplusplus is not predefined, assuming latest C++ we support.");
444         return LanguageVersion::LatestCxx;
445     } else if (language == Constants::C_LANGUAGE_ID) {
446         for (const ProjectExplorer::Macro &macro : macros) {
447             if (macro.key == "__STDC_VERSION__") {
448                 const long version = toLanguageVersionAsLong(macro.value);
449 
450                 if (version > 201710L)
451                     return LanguageVersion::LatestC;
452                 if (version > 201112L)
453                     return LanguageVersion::C18;
454                 if (version > 199901L)
455                     return LanguageVersion::C11;
456                 if (version > 199409L)
457                     return LanguageVersion::C99;
458 
459                 return LanguageVersion::C89;
460             }
461         }
462 
463         // The __STDC_VERSION__ macro was introduced after C89.
464         // We haven't seen it, so it must be C89.
465         return LanguageVersion::C89;
466     } else {
467         QTC_CHECK(false && "Unexpected toolchain language, assuming latest C++ we support.");
468         return LanguageVersion::LatestCxx;
469     }
470 }
471 
includedFiles(const QString & option,const QStringList & flags,const QString & directoryPath)472 QStringList ToolChain::includedFiles(const QString &option,
473                                      const QStringList &flags,
474                                      const QString &directoryPath)
475 {
476     QStringList result;
477 
478     for (int i = 0; i < flags.size(); ++i) {
479         if (flags[i] == option && i + 1 < flags.size()) {
480             QString includeFile = flags[++i];
481             if (!QFileInfo(includeFile).isAbsolute())
482                 includeFile = directoryPath + "/" + includeFile;
483             result.append(QDir::cleanPath(includeFile));
484         }
485     }
486 
487     return result;
488 }
489 
490 /*!
491     Used by the tool chain kit information to validate the kit.
492 */
493 
validateKit(const Kit *) const494 Tasks ToolChain::validateKit(const Kit *) const
495 {
496     return {};
497 }
498 
sysRoot() const499 QString ToolChain::sysRoot() const
500 {
501     return QString();
502 }
503 
504 /*!
505     \class ProjectExplorer::ToolChainFactory
506     \brief The ToolChainFactory class creates tool chains from settings or
507     autodetects them.
508 */
509 
510 /*!
511     \fn QString ProjectExplorer::ToolChainFactory::displayName() const = 0
512     Contains the name used to display the name of the tool chain that will be
513     created.
514 */
515 
516 /*!
517     \fn QStringList ProjectExplorer::ToolChain::clangParserFlags(const QStringList &cxxflags) const = 0
518     Converts tool chain specific flags to list flags that tune the libclang
519     parser.
520 */
521 
522 /*!
523     \fn bool ProjectExplorer::ToolChainFactory::canRestore(const QVariantMap &data)
524     Used by the tool chain manager to restore user-generated tool chains.
525 */
526 
ToolChainFactory()527 ToolChainFactory::ToolChainFactory()
528 {
529     Internal::g_toolChainFactories.append(this);
530 }
531 
~ToolChainFactory()532 ToolChainFactory::~ToolChainFactory()
533 {
534     Internal::g_toolChainFactories.removeOne(this);
535 }
536 
allToolChainFactories()537 const QList<ToolChainFactory *> ToolChainFactory::allToolChainFactories()
538 {
539     return Internal::g_toolChainFactories;
540 }
541 
autoDetect(const QList<ToolChain * > & alreadyKnown,const IDevice::Ptr & device)542 QList<ToolChain *> ToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown,
543                                                 const IDevice::Ptr &device)
544 {
545     Q_UNUSED(alreadyKnown)
546     Q_UNUSED(device)
547     return {};
548 }
549 
detectForImport(const ToolChainDescription & tcd)550 QList<ToolChain *> ToolChainFactory::detectForImport(const ToolChainDescription &tcd)
551 {
552     Q_UNUSED(tcd)
553     return {};
554 }
555 
canCreate() const556 bool ToolChainFactory::canCreate() const
557 {
558     return m_userCreatable;
559 }
560 
create()561 ToolChain *ToolChainFactory::create()
562 {
563     return m_toolchainConstructor ? m_toolchainConstructor() : nullptr;
564 }
565 
restore(const QVariantMap & data)566 ToolChain *ToolChainFactory::restore(const QVariantMap &data)
567 {
568     if (!m_toolchainConstructor)
569         return nullptr;
570 
571     ToolChain *tc = m_toolchainConstructor();
572     QTC_ASSERT(tc, return nullptr);
573 
574     if (tc->fromMap(data))
575         return tc;
576 
577     delete tc;
578     return nullptr;
579 }
580 
rawIdData(const QVariantMap & data)581 static QPair<QString, QString> rawIdData(const QVariantMap &data)
582 {
583     const QString raw = data.value(QLatin1String(ID_KEY)).toString();
584     const int pos = raw.indexOf(QLatin1Char(':'));
585     QTC_ASSERT(pos > 0, return qMakePair(QString::fromLatin1("unknown"), QString::fromLatin1("unknown")));
586     return qMakePair(raw.mid(0, pos), raw.mid(pos + 1));
587 }
588 
idFromMap(const QVariantMap & data)589 QByteArray ToolChainFactory::idFromMap(const QVariantMap &data)
590 {
591     return rawIdData(data).second.toUtf8();
592 }
593 
typeIdFromMap(const QVariantMap & data)594 Utils::Id ToolChainFactory::typeIdFromMap(const QVariantMap &data)
595 {
596     return Utils::Id::fromString(rawIdData(data).first);
597 }
598 
autoDetectionToMap(QVariantMap & data,bool detected)599 void ToolChainFactory::autoDetectionToMap(QVariantMap &data, bool detected)
600 {
601     data.insert(QLatin1String(AUTODETECT_KEY), detected);
602 }
603 
createToolChain(Utils::Id toolChainType)604 ToolChain *ToolChainFactory::createToolChain(Utils::Id toolChainType)
605 {
606     for (ToolChainFactory *factory : qAsConst(Internal::g_toolChainFactories)) {
607         if (factory->m_supportedToolChainType == toolChainType) {
608             if (ToolChain *tc = factory->create()) {
609                 tc->d->m_typeId = toolChainType;
610                 return tc;
611             }
612         }
613     }
614     return nullptr;
615 }
616 
supportedLanguages() const617 QList<Utils::Id> ToolChainFactory::supportedLanguages() const
618 {
619     return m_supportsAllLanguages ? ToolChainManager::allLanguages() : m_supportedLanguages;
620 }
621 
supportedToolChainType() const622 Utils::Id ToolChainFactory::supportedToolChainType() const
623 {
624     return m_supportedToolChainType;
625 }
626 
setSupportedToolChainType(const Utils::Id & supportedToolChain)627 void ToolChainFactory::setSupportedToolChainType(const Utils::Id &supportedToolChain)
628 {
629     m_supportedToolChainType = supportedToolChain;
630 }
631 
setSupportedLanguages(const QList<Utils::Id> & supportedLanguages)632 void ToolChainFactory::setSupportedLanguages(const QList<Utils::Id> &supportedLanguages)
633 {
634     m_supportedLanguages = supportedLanguages;
635 }
636 
setSupportsAllLanguages(bool supportsAllLanguages)637 void ToolChainFactory::setSupportsAllLanguages(bool supportsAllLanguages)
638 {
639     m_supportsAllLanguages = supportsAllLanguages;
640 }
641 
setToolchainConstructor(const std::function<ToolChain * ()> & toolchainContructor)642 void ToolChainFactory::setToolchainConstructor
643     (const std::function<ToolChain *()> &toolchainContructor)
644 {
645     m_toolchainConstructor = toolchainContructor;
646 }
647 
setUserCreatable(bool userCreatable)648 void ToolChainFactory::setUserCreatable(bool userCreatable)
649 {
650     m_userCreatable = userCreatable;
651 }
652 
653 } // namespace ProjectExplorer
654