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 "abi.h"
27
28 #include <utils/algorithm.h>
29 #include <utils/fileutils.h>
30 #include <utils/qtcassert.h>
31
32 #include <QDebug>
33 #include <QtEndian>
34 #include <QRegularExpression>
35 #include <QString>
36 #include <QStringList>
37 #include <QSysInfo>
38
39 /*!
40 \class ProjectExplorer::Abi
41
42 \brief The Abi class represents the Application Binary Interface (ABI) of
43 a target platform.
44
45 \sa ProjectExplorer::ToolChain
46 */
47
48 namespace ProjectExplorer {
49
50 // --------------------------------------------------------------------------
51 // Helpers
52 // --------------------------------------------------------------------------
53
54 static std::vector<QByteArray> m_registeredOsFlavors;
55 static std::map<int, QList<Abi::OSFlavor>> m_osToOsFlavorMap;
56
moveGenericAndUnknownLast(const QList<Abi::OSFlavor> & in)57 static QList<Abi::OSFlavor> moveGenericAndUnknownLast(const QList<Abi::OSFlavor> &in)
58 {
59 QList<Abi::OSFlavor> result = in;
60 // Move unknown and generic to the back
61 if (result.removeOne(Abi::GenericFlavor))
62 result.append(Abi::GenericFlavor);
63 if (result.removeOne(Abi::UnknownFlavor))
64 result.append(Abi::UnknownFlavor);
65
66 return result;
67 }
68
insertIntoOsFlavorMap(const std::vector<Abi::OS> & oses,const Abi::OSFlavor flavor)69 static void insertIntoOsFlavorMap(const std::vector<Abi::OS> &oses, const Abi::OSFlavor flavor)
70 {
71 QTC_ASSERT(oses.size() > 0, return);
72 for (const Abi::OS &os : oses) {
73 auto it = m_osToOsFlavorMap.find(os);
74 if (it == m_osToOsFlavorMap.end()) {
75 m_osToOsFlavorMap[os] = {flavor};
76 continue;
77 }
78 QList<Abi::OSFlavor> flavors = it->second;
79 if (!flavors.contains(flavor)) {
80 flavors.append(flavor);
81 m_osToOsFlavorMap[os] = moveGenericAndUnknownLast(flavors);
82 }
83 }
84 }
85
registerOsFlavor(const Abi::OSFlavor & flavor,const QByteArray & flavorName,const std::vector<Abi::OS> & oses)86 static void registerOsFlavor(const Abi::OSFlavor &flavor, const QByteArray &flavorName,
87 const std::vector<Abi::OS> &oses)
88 {
89 const auto pos = static_cast<size_t>(flavor);
90 if (m_registeredOsFlavors.size() <= pos)
91 m_registeredOsFlavors.resize(pos + 1);
92
93 m_registeredOsFlavors[pos] = flavorName;
94
95 insertIntoOsFlavorMap(oses, flavor);
96 }
97
setupPreregisteredOsFlavors()98 static void setupPreregisteredOsFlavors() {
99 m_registeredOsFlavors.resize(static_cast<size_t>(Abi::UnknownFlavor));
100
101 registerOsFlavor(Abi::FreeBsdFlavor, "freebsd", {Abi::OS::BsdOS});
102 registerOsFlavor(Abi::NetBsdFlavor, "netbsd", {Abi::OS::BsdOS});
103 registerOsFlavor(Abi::OpenBsdFlavor, "openbsd", {Abi::OS::BsdOS});
104 registerOsFlavor(Abi::AndroidLinuxFlavor, "android", {Abi::OS::LinuxOS});
105 registerOsFlavor(Abi::SolarisUnixFlavor, "solaris", {Abi::OS::UnixOS});
106 registerOsFlavor(Abi::WindowsMsvc2005Flavor, "msvc2005", {Abi::OS::WindowsOS});
107 registerOsFlavor(Abi::WindowsMsvc2008Flavor, "msvc2008", {Abi::OS::WindowsOS});
108 registerOsFlavor(Abi::WindowsMsvc2010Flavor, "msvc2010", {Abi::OS::WindowsOS});
109 registerOsFlavor(Abi::WindowsMsvc2012Flavor, "msvc2012", {Abi::OS::WindowsOS});
110 registerOsFlavor(Abi::WindowsMsvc2013Flavor, "msvc2013", {Abi::OS::WindowsOS});
111 registerOsFlavor(Abi::WindowsMsvc2015Flavor, "msvc2015", {Abi::OS::WindowsOS});
112 registerOsFlavor(Abi::WindowsMsvc2017Flavor, "msvc2017", {Abi::OS::WindowsOS});
113 registerOsFlavor(Abi::WindowsMsvc2019Flavor, "msvc2019", {Abi::OS::WindowsOS});
114 registerOsFlavor(Abi::WindowsMSysFlavor, "msys", {Abi::OS::WindowsOS});
115 registerOsFlavor(Abi::WindowsCEFlavor, "ce", {Abi::OS::WindowsOS});
116 registerOsFlavor(Abi::VxWorksFlavor, "vxworks", {Abi::OS::VxWorks});
117 registerOsFlavor(Abi::RtosFlavor, "rtos", {Abi::OS::WindowsOS});
118 registerOsFlavor(Abi::GenericFlavor, "generic", {Abi::OS::LinuxOS,
119 Abi::OS::DarwinOS,
120 Abi::OS::UnixOS,
121 Abi::OS::QnxOS,
122 Abi::OS::BareMetalOS});
123 registerOsFlavor(Abi::UnknownFlavor, "unknown", {Abi::OS::BsdOS,
124 Abi::OS::LinuxOS,
125 Abi::OS::DarwinOS,
126 Abi::OS::UnixOS,
127 Abi::OS::WindowsOS,
128 Abi::OS::VxWorks,
129 Abi::OS::QnxOS,
130 Abi::OS::BareMetalOS,
131 Abi::OS::UnknownOS});
132 }
133
registeredOsFlavors()134 static std::vector<QByteArray> ®isteredOsFlavors() {
135 if (m_registeredOsFlavors.size() == 0)
136 setupPreregisteredOsFlavors();
137 return m_registeredOsFlavors;
138 }
139
indexOfFlavor(const QByteArray & flavor)140 static int indexOfFlavor(const QByteArray &flavor)
141 {
142 return Utils::indexOf(registeredOsFlavors(), [flavor](const QByteArray &f) { return f == flavor; });
143 }
144
architectureFromQt()145 static Abi::Architecture architectureFromQt()
146 {
147 const QString arch = QSysInfo::buildCpuArchitecture();
148 if (arch.startsWith("arm"))
149 return Abi::ArmArchitecture;
150 if (arch.startsWith("x86") || arch == "i386")
151 return Abi::X86Architecture;
152 if (arch == "ia64")
153 return Abi::ItaniumArchitecture;
154 if (arch.startsWith("mips"))
155 return Abi::MipsArchitecture;
156 if (arch.startsWith("power"))
157 return Abi::PowerPCArchitecture;
158 if (arch.startsWith("sh")) // Not in Qt documentation!
159 return Abi::ShArchitecture;
160 if (arch.startsWith("avr32")) // Not in Qt documentation!
161 return Abi::Avr32Architecture;
162 if (arch.startsWith("avr")) // Not in Qt documentation!
163 return Abi::AvrArchitecture;
164 if (arch.startsWith("asmjs"))
165 return Abi::AsmJsArchitecture;
166
167 return Abi::UnknownArchitecture;
168 }
169
getUint8(const QByteArray & data,int pos)170 static quint8 getUint8(const QByteArray &data, int pos)
171 {
172 return static_cast<quint8>(data.at(pos));
173 }
174
getLEUint32(const QByteArray & ba,int pos)175 static quint32 getLEUint32(const QByteArray &ba, int pos)
176 {
177 Q_ASSERT(ba.size() >= pos + 3);
178 return (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 3))) << 24)
179 + (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 2)) << 16))
180 + (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 1))) << 8)
181 + static_cast<quint8>(ba.at(pos));
182 }
183
getBEUint32(const QByteArray & ba,int pos)184 static quint32 getBEUint32(const QByteArray &ba, int pos)
185 {
186 Q_ASSERT(ba.size() >= pos + 3);
187 return (static_cast<quint32>(static_cast<quint8>(ba.at(pos))) << 24)
188 + (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 1))) << 16)
189 + (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 2))) << 8)
190 + static_cast<quint8>(ba.at(pos + 3));
191 }
192
getLEUint16(const QByteArray & ba,int pos)193 static quint32 getLEUint16(const QByteArray &ba, int pos)
194 {
195 Q_ASSERT(ba.size() >= pos + 1);
196 return (static_cast<quint16>(static_cast<quint8>(ba.at(pos + 1))) << 8) + static_cast<quint8>(ba.at(pos));
197 }
198
getBEUint16(const QByteArray & ba,int pos)199 static quint32 getBEUint16(const QByteArray &ba, int pos)
200 {
201 Q_ASSERT(ba.size() >= pos + 1);
202 return (static_cast<quint16>(static_cast<quint8>(ba.at(pos))) << 8) + static_cast<quint8>(ba.at(pos + 1));
203 }
204
macAbiForCpu(quint32 type)205 static Abi macAbiForCpu(quint32 type) {
206 switch (type) {
207 case 7: // CPU_TYPE_X86, CPU_TYPE_I386
208 return Abi(Abi::X86Architecture, Abi::DarwinOS, Abi::GenericFlavor, Abi::MachOFormat, 32);
209 case 0x01000000 + 7: // CPU_TYPE_X86_64
210 return Abi(Abi::X86Architecture, Abi::DarwinOS, Abi::GenericFlavor, Abi::MachOFormat, 64);
211 case 18: // CPU_TYPE_POWERPC
212 case 0x01000000 + 18: // CPU_TYPE_POWERPC64
213 return Abi(Abi::PowerPCArchitecture, Abi::DarwinOS, Abi::GenericFlavor, Abi::MachOFormat, 32);
214 case 12: // CPU_TYPE_ARM
215 return Abi(Abi::ArmArchitecture, Abi::DarwinOS, Abi::GenericFlavor, Abi::MachOFormat, 32);
216 case 0x01000000 + 12: // CPU_TYPE_ARM64
217 return Abi(Abi::ArmArchitecture, Abi::DarwinOS, Abi::GenericFlavor, Abi::MachOFormat, 64);
218 default:
219 return Abi();
220 }
221 }
222
parseCoffHeader(const QByteArray & data)223 static Abis parseCoffHeader(const QByteArray &data)
224 {
225 Abis result;
226 if (data.size() < 20)
227 return result;
228
229 Abi::Architecture arch = Abi::UnknownArchitecture;
230 Abi::OSFlavor flavor = Abi::UnknownFlavor;
231 int width = 0;
232
233 // Get machine field from COFF file header
234 quint16 machine = getLEUint16(data, 0);
235 switch (machine) {
236 case 0x01c0: // ARM LE
237 case 0x01c2: // ARM or thumb
238 case 0x01c4: // ARMv7 thumb
239 arch = Abi::ArmArchitecture;
240 width = 32;
241 break;
242 case 0xaa64: // ARM64
243 arch = Abi::ArmArchitecture;
244 width = 64;
245 break;
246 case 0x8664: // x86_64
247 arch = Abi::X86Architecture;
248 width = 64;
249 break;
250 case 0x014c: // i386
251 arch = Abi::X86Architecture;
252 width = 32;
253 break;
254 case 0x0166: // MIPS, little endian
255 arch = Abi::MipsArchitecture;
256 width = 32;
257 break;
258 case 0x0200: // ia64
259 arch = Abi::ItaniumArchitecture;
260 width = 64;
261 break;
262 }
263
264 if (data.size() >= 24) {
265 // Get Major and Minor Image Version from optional header fields
266 quint8 minorLinker = data.at(23);
267 switch (data.at(22)) {
268 case 2:
269 case 3: // not yet reached:-)
270 flavor = Abi::WindowsMSysFlavor;
271 break;
272 case 8:
273 flavor = Abi::WindowsMsvc2005Flavor;
274 break;
275 case 9:
276 flavor = Abi::WindowsMsvc2008Flavor;
277 break;
278 case 10:
279 flavor = Abi::WindowsMsvc2010Flavor;
280 break;
281 case 11:
282 flavor = Abi::WindowsMsvc2012Flavor;
283 break;
284 case 12:
285 flavor = Abi::WindowsMsvc2013Flavor;
286 break;
287 case 14:
288 if (minorLinker >= quint8(20))
289 flavor = Abi::WindowsMsvc2019Flavor;
290 else if (minorLinker >= quint8(10))
291 flavor = Abi::WindowsMsvc2017Flavor;
292 else
293 flavor = Abi::WindowsMsvc2015Flavor;
294 break;
295 case 15:
296 flavor = Abi::WindowsMsvc2019Flavor;
297 break;
298 default: // Keep unknown flavor
299 if (minorLinker != 0)
300 flavor = Abi::WindowsMSysFlavor; // MSVC seems to avoid using minor numbers
301 else
302 qWarning("%s: Unknown MSVC flavour encountered.", Q_FUNC_INFO);
303 break;
304 }
305 }
306
307 if (arch != Abi::UnknownArchitecture && width != 0)
308 result.append(Abi(arch, Abi::WindowsOS, flavor, Abi::PEFormat, width));
309
310 return result;
311 }
312
abiOf(const QByteArray & data)313 static Abis abiOf(const QByteArray &data)
314 {
315 Abis result;
316 if (data.size() <= 8)
317 return result;
318
319 if (data.size() >= 20
320 && getUint8(data, 0) == 0x7f && getUint8(data, 1) == 'E' && getUint8(data, 2) == 'L'
321 && getUint8(data, 3) == 'F') {
322 // ELF format:
323 bool isLE = (getUint8(data, 5) == 1);
324 quint16 machine = isLE ? getLEUint16(data, 18) : getBEUint16(data, 18);
325 quint8 osAbi = getUint8(data, 7);
326
327 Abi::OS os = Abi::UnixOS;
328 Abi::OSFlavor flavor = Abi::GenericFlavor;
329 // http://www.sco.com/developers/gabi/latest/ch4.eheader.html#elfid
330 switch (osAbi) {
331 #if defined(Q_OS_NETBSD)
332 case 0: // NetBSD: ELFOSABI_NETBSD 2, however, NetBSD uses 0
333 os = Abi::BsdOS;
334 flavor = Abi::NetBsdFlavor;
335 break;
336 #elif defined(Q_OS_OPENBSD)
337 case 0: // OpenBSD: ELFOSABI_OPENBSD 12, however, OpenBSD uses 0
338 os = Abi::BsdOS;
339 flavor = Abi::OpenBsdFlavor;
340 break;
341 #else
342 case 0: // no extra info available: Default to Linux:
343 #endif
344 case 3: // Linux:
345 case 97: // ARM, also linux most of the time.
346 os = Abi::LinuxOS;
347 flavor = Abi::GenericFlavor;
348 break;
349 case 6: // Solaris:
350 os = Abi::UnixOS;
351 flavor = Abi::SolarisUnixFlavor;
352 break;
353 case 9: // FreeBSD:
354 os = Abi::BsdOS;
355 flavor = Abi::FreeBsdFlavor;
356 break;
357 }
358
359 switch (machine) {
360 case 3: // EM_386
361 result.append(Abi(Abi::X86Architecture, os, flavor, Abi::ElfFormat, 32));
362 break;
363 case 8: // EM_MIPS
364 result.append(Abi(Abi::MipsArchitecture, os, flavor, Abi::ElfFormat, 32));
365 break;
366 case 20: // EM_PPC
367 result.append(Abi(Abi::PowerPCArchitecture, os, flavor, Abi::ElfFormat, 32));
368 break;
369 case 21: // EM_PPC64
370 result.append(Abi(Abi::PowerPCArchitecture, os, flavor, Abi::ElfFormat, 64));
371 break;
372 case 40: // EM_ARM
373 result.append(Abi(Abi::ArmArchitecture, os, flavor, Abi::ElfFormat, 32));
374 break;
375 case 183: // EM_AARCH64
376 result.append(Abi(Abi::ArmArchitecture, os, flavor, Abi::ElfFormat, 64));
377 break;
378 case 62: // EM_X86_64
379 result.append(Abi(Abi::X86Architecture, os, flavor, Abi::ElfFormat, 64));
380 break;
381 case 42: // EM_SH
382 result.append(Abi(Abi::ShArchitecture, os, flavor, Abi::ElfFormat, 32));
383 break;
384 case 50: // EM_IA_64
385 result.append(Abi(Abi::ItaniumArchitecture, os, flavor, Abi::ElfFormat, 64));
386 break;
387 default:
388 ;
389 }
390 } else if (((getUint8(data, 0) == 0xce || getUint8(data, 0) == 0xcf)
391 && getUint8(data, 1) == 0xfa && getUint8(data, 2) == 0xed && getUint8(data, 3) == 0xfe
392 )
393 ||
394 (getUint8(data, 0) == 0xfe && getUint8(data, 1) == 0xed && getUint8(data, 2) == 0xfa
395 && (getUint8(data, 3) == 0xce || getUint8(data, 3) == 0xcf)
396 )
397 ) {
398 // Mach-O format (Mac non-fat binary, 32 and 64bit magic):
399 quint32 type = (getUint8(data, 1) == 0xfa) ? getLEUint32(data, 4) : getBEUint32(data, 4);
400 result.append(macAbiForCpu(type));
401 } else if ((getUint8(data, 0) == 0xbe && getUint8(data, 1) == 0xba
402 && getUint8(data, 2) == 0xfe && getUint8(data, 3) == 0xca)
403 ||
404 (getUint8(data, 0) == 0xca && getUint8(data, 1) == 0xfe
405 && getUint8(data, 2) == 0xba && getUint8(data, 3) == 0xbe)
406 ) {
407 // Mach-0 format Fat binary header:
408 bool isLE = (getUint8(data, 0) == 0xbe);
409 quint32 count = isLE ? getLEUint32(data, 4) : getBEUint32(data, 4);
410 int pos = 8;
411 for (quint32 i = 0; i < count; ++i) {
412 if (data.size() <= pos + 4)
413 break;
414
415 quint32 type = isLE ? getLEUint32(data, pos) : getBEUint32(data, pos);
416 result.append(macAbiForCpu(type));
417 pos += 20;
418 }
419 } else if (// Qt 5.15+ https://webassembly.github.io/spec/core/binary/modules.html#binary-module
420 (getUint8(data, 0) == 0 && getUint8(data, 1) == 'a'
421 && getUint8(data, 2) == 's' && getUint8(data, 3) == 'm')
422
423 // Qt < 5.15 https://llvm.org/docs/BitCodeFormat.html#llvm-ir-magic-number
424 || (getUint8(data, 0) == 'B' && getUint8(data, 1) == 'C'
425 && getUint8(data, 2) == 0xc0 && getUint8(data, 3) == 0xde)) {
426 result.append(Abi(Abi::AsmJsArchitecture, Abi::UnknownOS, Abi::UnknownFlavor,
427 Abi::EmscriptenFormat, 32));
428 } else if (data.size() >= 64){
429 // Windows PE: values are LE (except for a few exceptions which we will not use here).
430
431 // MZ header first (ZM is also allowed, but rarely used)
432 const quint8 firstChar = getUint8(data, 0);
433 const quint8 secondChar = getUint8(data, 1);
434 if ((firstChar != 'M' || secondChar != 'Z') && (firstChar != 'Z' || secondChar != 'M'))
435 return result;
436
437 // Get PE/COFF header position from MZ header:
438 qint32 pePos = getLEUint32(data, 60);
439 if (pePos <= 0 || data.size() < pePos + 4 + 20) // PE magic bytes plus COFF header
440 return result;
441 if (getUint8(data, pePos) == 'P' && getUint8(data, pePos + 1) == 'E'
442 && getUint8(data, pePos + 2) == 0 && getUint8(data, pePos + 3) == 0)
443 result = parseCoffHeader(data.mid(pePos + 4));
444 }
445 return result;
446 }
447
448 // --------------------------------------------------------------------------
449 // Abi
450 // --------------------------------------------------------------------------
451
Abi(const Architecture & a,const OS & o,const OSFlavor & of,const BinaryFormat & f,unsigned char w,const QString & p)452 Abi::Abi(const Architecture &a, const OS &o,
453 const OSFlavor &of, const BinaryFormat &f, unsigned char w, const QString &p) :
454 m_architecture(a), m_os(o), m_osFlavor(of), m_binaryFormat(f), m_wordWidth(w), m_param(p)
455 {
456 QTC_ASSERT(osSupportsFlavor(o, of), m_osFlavor = UnknownFlavor);
457 }
458
abiFromTargetTriplet(const QString & triple)459 Abi Abi::abiFromTargetTriplet(const QString &triple)
460 {
461 const QString machine = triple.toLower();
462 if (machine.isEmpty())
463 return Abi();
464
465 const QStringList parts = machine.split(QRegularExpression("[ /-]"));
466
467 Architecture arch = UnknownArchitecture;
468 OS os = UnknownOS;
469 OSFlavor flavor = UnknownFlavor;
470 BinaryFormat format = UnknownFormat;
471 unsigned char width = 0;
472 int unknownCount = 0;
473
474 for (const QString &p : parts) {
475 if (p == "unknown" || p == "pc"
476 || p == "gnu" || p == "uclibc"
477 || p == "86_64" || p == "redhat"
478 || p == "w64") {
479 continue;
480 } else if (p == "i386" || p == "i486" || p == "i586"
481 || p == "i686" || p == "x86") {
482 arch = X86Architecture;
483 width = 32;
484 } else if (p == "xtensa") {
485 arch = XtensaArchitecture;
486 os = BareMetalOS;
487 flavor = GenericFlavor;
488 format = ElfFormat;
489 width = 32;
490 } else if (p.startsWith("arm")) {
491 arch = ArmArchitecture;
492 width = p.contains("64") ? 64 : 32;
493 } else if (p.startsWith("aarch64")) {
494 arch = ArmArchitecture;
495 width = 64;
496 } else if (p == "avr") {
497 arch = AvrArchitecture;
498 os = BareMetalOS;
499 flavor = GenericFlavor;
500 format = ElfFormat;
501 width = 16;
502 } else if (p == "avr32") {
503 arch = Avr32Architecture;
504 os = BareMetalOS;
505 flavor = GenericFlavor;
506 format = ElfFormat;
507 width = 32;
508 } else if (p == "cr16") {
509 arch = CR16Architecture;
510 os = BareMetalOS;
511 flavor = GenericFlavor;
512 format = ElfFormat;
513 // Note that GCC macro returns 32-bit value for this architecture.
514 width = 32;
515 } else if (p == "msp430") {
516 arch = Msp430Architecture;
517 os = BareMetalOS;
518 flavor = GenericFlavor;
519 format = ElfFormat;
520 width = 16;
521 } else if (p == "rl78") {
522 arch = Rl78Architecture;
523 os = BareMetalOS;
524 flavor = GenericFlavor;
525 format = ElfFormat;
526 width = 16;
527 } else if (p == "rx") {
528 arch = RxArchitecture;
529 os = BareMetalOS;
530 flavor = GenericFlavor;
531 format = ElfFormat;
532 width = 32;
533 } else if (p == "sh") {
534 arch = ShArchitecture;
535 os = BareMetalOS;
536 flavor = GenericFlavor;
537 format = ElfFormat;
538 width = 32;
539 } else if (p == "v850") {
540 arch = V850Architecture;
541 os = BareMetalOS;
542 flavor = GenericFlavor;
543 format = ElfFormat;
544 width = 32;
545 } else if (p == "m68k") {
546 arch = M68KArchitecture;
547 os = BareMetalOS;
548 flavor = GenericFlavor;
549 format = ElfFormat;
550 width = 16;
551 } else if (p == "m32c") {
552 arch = M32CArchitecture;
553 os = BareMetalOS;
554 flavor = GenericFlavor;
555 format = ElfFormat;
556 width = 16;
557 } else if (p == "m32r") {
558 arch = M32RArchitecture;
559 os = BareMetalOS;
560 flavor = GenericFlavor;
561 format = ElfFormat;
562 width = 32;
563 } else if (p.startsWith("riscv")) {
564 arch = RiscVArchitecture;
565 os = BareMetalOS;
566 flavor = GenericFlavor;
567 format = ElfFormat;
568 width = p.contains("64") ? 64 : 32;
569 } else if (p.startsWith("mips")) {
570 arch = MipsArchitecture;
571 width = p.contains("64") ? 64 : 32;
572 } else if (p == "x86_64" || p == "amd64") {
573 arch = X86Architecture;
574 width = 64;
575 } else if (p == "powerpc64") {
576 arch = PowerPCArchitecture;
577 width = 64;
578 } else if (p == "powerpc") {
579 arch = PowerPCArchitecture;
580 width = 32;
581 } else if (p == "linux" || p == "linux6e") {
582 os = LinuxOS;
583 if (flavor == UnknownFlavor)
584 flavor = GenericFlavor;
585 format = ElfFormat;
586 } else if (p == "android" || p == "androideabi") {
587 flavor = AndroidLinuxFlavor;
588 } else if (p.startsWith("freebsd")) {
589 os = BsdOS;
590 if (flavor == UnknownFlavor)
591 flavor = FreeBsdFlavor;
592 format = ElfFormat;
593 } else if (p.startsWith("openbsd")) {
594 os = BsdOS;
595 if (flavor == UnknownFlavor)
596 flavor = OpenBsdFlavor;
597 format = ElfFormat;
598 } else if (p == "mingw32" || p == "win32"
599 || p == "mingw32msvc" || p == "msys"
600 || p == "cygwin" || p == "windows") {
601 arch = X86Architecture;
602 os = WindowsOS;
603 flavor = WindowsMSysFlavor;
604 format = PEFormat;
605 } else if (p == "apple") {
606 os = DarwinOS;
607 flavor = GenericFlavor;
608 format = MachOFormat;
609 } else if (p == "darwin10") {
610 width = 64;
611 } else if (p == "darwin9") {
612 width = 32;
613 } else if (p == "gnueabi" || p == "elf") {
614 format = ElfFormat;
615 } else if (p == "wrs") {
616 continue;
617 } else if (p == "vxworks") {
618 os = VxWorks;
619 flavor = VxWorksFlavor;
620 format = ElfFormat;
621 } else if (p.startsWith("qnx")) {
622 os = QnxOS;
623 flavor = GenericFlavor;
624 format = ElfFormat;
625 } else if (p.startsWith("emscripten")) {
626 format = EmscriptenFormat;
627 width = 32;
628 } else if (p.startsWith("asmjs")) {
629 arch = AsmJsArchitecture;
630 } else if (p == "none") {
631 os = BareMetalOS;
632 flavor = GenericFlavor;
633 format = ElfFormat;
634 } else {
635 ++unknownCount;
636 }
637 }
638
639 return Abi(arch, os, flavor, format, width);
640 }
641
abiOsToOsType(const Abi::OS os)642 Utils::OsType Abi::abiOsToOsType(const Abi::OS os)
643 {
644 switch (os) {
645 case ProjectExplorer::Abi::LinuxOS:
646 return Utils::OsType::OsTypeLinux;
647 case ProjectExplorer::Abi::DarwinOS:
648 return Utils::OsType::OsTypeMac;
649 case ProjectExplorer::Abi::BsdOS:
650 case ProjectExplorer::Abi::UnixOS:
651 return Utils::OsType::OsTypeOtherUnix;
652 case ProjectExplorer::Abi::WindowsOS:
653 return Utils::OsType::OsTypeWindows;
654 case ProjectExplorer::Abi::VxWorks:
655 case ProjectExplorer::Abi::QnxOS:
656 case ProjectExplorer::Abi::BareMetalOS:
657 case ProjectExplorer::Abi::UnknownOS:
658 return Utils::OsType::OsTypeOther;
659 }
660 return Utils::OsType::OsTypeOther;
661 }
662
toString() const663 QString Abi::toString() const
664 {
665 const QStringList dn = {toString(m_architecture), toString(m_os), toString(m_osFlavor),
666 toString(m_binaryFormat), toString(m_wordWidth)};
667 return dn.join('-');
668 }
669
param() const670 QString Abi::param() const
671 {
672 if (m_param.isEmpty())
673 return toString();
674 return m_param;
675 }
676
operator !=(const Abi & other) const677 bool Abi::operator != (const Abi &other) const
678 {
679 return !operator ==(other);
680 }
681
operator ==(const Abi & other) const682 bool Abi::operator == (const Abi &other) const
683 {
684 return m_architecture == other.m_architecture
685 && m_os == other.m_os
686 && m_osFlavor == other.m_osFlavor
687 && m_binaryFormat == other.m_binaryFormat
688 && m_wordWidth == other.m_wordWidth;
689 }
690
compatibleMSVCFlavors(const Abi::OSFlavor & left,const Abi::OSFlavor & right)691 static bool compatibleMSVCFlavors(const Abi::OSFlavor &left, const Abi ::OSFlavor &right)
692 {
693 // MSVC 2019, 2017 and 2015 are compatible
694 return left >= Abi::WindowsMsvc2015Flavor && left <= Abi::WindowsMsvc2019Flavor
695 && right >= Abi::WindowsMsvc2015Flavor && right <= Abi::WindowsMsvc2019Flavor;
696 }
697
isCompatibleWith(const Abi & other) const698 bool Abi::isCompatibleWith(const Abi &other) const
699 {
700 // Generic match: If stuff is identical or the other side is unknown, then this is a match.
701 bool isCompat = (architecture() == other.architecture() || other.architecture() == UnknownArchitecture)
702 && (os() == other.os() || other.os() == UnknownOS)
703 && (osFlavor() == other.osFlavor() || other.osFlavor() == UnknownFlavor)
704 && (binaryFormat() == other.binaryFormat() || other.binaryFormat() == UnknownFormat)
705 && ((wordWidth() == other.wordWidth() && wordWidth() != 0) || other.wordWidth() == 0);
706
707 // *-linux-generic-* is compatible with *-linux-* (both ways): This is for the benefit of
708 // people building Qt themselves using e.g. a meego toolchain.
709 //
710 // We leave it to the specific targets to filter out the tool chains that do not
711 // work for them.
712 if (!isCompat && (architecture() == other.architecture() || other.architecture() == UnknownArchitecture)
713 && ((os() == other.os()) && (os() == LinuxOS))
714 && (osFlavor() == GenericFlavor || other.osFlavor() == GenericFlavor)
715 && (binaryFormat() == other.binaryFormat() || other.binaryFormat() == UnknownFormat)
716 && ((wordWidth() == other.wordWidth() && wordWidth() != 0) || other.wordWidth() == 0)) {
717 isCompat = true;
718 }
719
720 // Make Android matching more strict than the generic Linux matches so far:
721 if (isCompat && (osFlavor() == AndroidLinuxFlavor || other.osFlavor() == AndroidLinuxFlavor))
722 isCompat = (architecture() == other.architecture()) && (osFlavor() == other.osFlavor());
723
724 if (!isCompat && wordWidth() == other.wordWidth()
725 && compatibleMSVCFlavors(osFlavor(), other.osFlavor())) {
726 isCompat = true;
727 }
728
729 return isCompat;
730 }
731
isValid() const732 bool Abi::isValid() const
733 {
734 return m_architecture != UnknownArchitecture
735 && m_os != UnknownOS
736 && m_osFlavor != UnknownFlavor
737 && m_binaryFormat != UnknownFormat
738 && m_wordWidth != 0;
739 }
740
isNull() const741 bool Abi::isNull() const
742 {
743 return m_architecture == UnknownArchitecture
744 && m_os == UnknownOS
745 && m_osFlavor == UnknownFlavor
746 && m_binaryFormat == UnknownFormat
747 && m_wordWidth == 0;
748 }
749
toString(const Architecture & a)750 QString Abi::toString(const Architecture &a)
751 {
752 switch (a) {
753 case ArmArchitecture:
754 return QLatin1String("arm");
755 case AvrArchitecture:
756 return QLatin1String("avr");
757 case Avr32Architecture:
758 return QLatin1String("avr32");
759 case XtensaArchitecture:
760 return QLatin1String("xtensa");
761 case X86Architecture:
762 return QLatin1String("x86");
763 case Mcs51Architecture:
764 return QLatin1String("mcs51");
765 case Mcs251Architecture:
766 return QLatin1String("mcs251");
767 case MipsArchitecture:
768 return QLatin1String("mips");
769 case PowerPCArchitecture:
770 return QLatin1String("ppc");
771 case ItaniumArchitecture:
772 return QLatin1String("itanium");
773 case ShArchitecture:
774 return QLatin1String("sh");
775 case AsmJsArchitecture:
776 return QLatin1String("asmjs");
777 case Stm8Architecture:
778 return QLatin1String("stm8");
779 case Msp430Architecture:
780 return QLatin1String("msp430");
781 case Rl78Architecture:
782 return QLatin1String("rl78");
783 case C166Architecture:
784 return QLatin1String("c166");
785 case V850Architecture:
786 return QLatin1String("v850");
787 case Rh850Architecture:
788 return QLatin1String("rh850");
789 case RxArchitecture:
790 return QLatin1String("rx");
791 case K78Architecture:
792 return QLatin1String("78k");
793 case M68KArchitecture:
794 return QLatin1String("m68k");
795 case M32CArchitecture:
796 return QLatin1String("m32c");
797 case M16CArchitecture:
798 return QLatin1String("m16c");
799 case M32RArchitecture:
800 return QLatin1String("m32r");
801 case R32CArchitecture:
802 return QLatin1String("r32c");
803 case CR16Architecture:
804 return QLatin1String("cr16");
805 case RiscVArchitecture:
806 return QLatin1String("riscv");
807 case UnknownArchitecture:
808 Q_FALLTHROUGH();
809 default:
810 return QLatin1String("unknown");
811 }
812 }
813
toString(const OS & o)814 QString Abi::toString(const OS &o)
815 {
816 switch (o) {
817 case LinuxOS:
818 return QLatin1String("linux");
819 case BsdOS:
820 return QLatin1String("bsd");
821 case DarwinOS:
822 return QLatin1String("darwin");
823 case UnixOS:
824 return QLatin1String("unix");
825 case WindowsOS:
826 return QLatin1String("windows");
827 case VxWorks:
828 return QLatin1String("vxworks");
829 case QnxOS:
830 return QLatin1String("qnx");
831 case BareMetalOS:
832 return QLatin1String("baremetal");
833 case UnknownOS:
834 Q_FALLTHROUGH();
835 default:
836 return QLatin1String("unknown");
837 };
838 }
839
toString(const OSFlavor & of)840 QString Abi::toString(const OSFlavor &of)
841 {
842 const auto index = static_cast<size_t>(of);
843 const std::vector<QByteArray> &flavors = registeredOsFlavors();
844 QTC_ASSERT(index < flavors.size(),
845 return QString::fromUtf8(flavors.at(int(UnknownFlavor))));
846 return QString::fromUtf8(flavors.at(index));
847 }
848
toString(const BinaryFormat & bf)849 QString Abi::toString(const BinaryFormat &bf)
850 {
851 switch (bf) {
852 case ElfFormat:
853 return QLatin1String("elf");
854 case PEFormat:
855 return QLatin1String("pe");
856 case MachOFormat:
857 return QLatin1String("mach_o");
858 case RuntimeQmlFormat:
859 return QLatin1String("qml_rt");
860 case UbrofFormat:
861 return QLatin1String("ubrof");
862 case OmfFormat:
863 return QLatin1String("omf");
864 case EmscriptenFormat:
865 return QLatin1String("emscripten");
866 case UnknownFormat:
867 Q_FALLTHROUGH();
868 default:
869 return QLatin1String("unknown");
870 }
871 }
872
toString(int w)873 QString Abi::toString(int w)
874 {
875 if (w == 0)
876 return QLatin1String("unknown");
877 return QString::fromLatin1("%1bit").arg(w);
878 }
879
fromString(const QString & abiString)880 Abi Abi::fromString(const QString &abiString)
881 {
882 Abi::Architecture architecture = UnknownArchitecture;
883 const QStringList abiParts = abiString.split('-');
884 if (!abiParts.isEmpty()) {
885 architecture = architectureFromString(abiParts.at(0));
886 if (abiParts.at(0) != toString(architecture))
887 return Abi();
888 }
889
890 Abi::OS os = UnknownOS;
891 if (abiParts.count() >= 2) {
892 os = osFromString(abiParts.at(1));
893 if (abiParts.at(1) != toString(os))
894 return Abi(architecture, UnknownOS, UnknownFlavor, UnknownFormat, 0);
895 }
896
897 Abi::OSFlavor flavor = UnknownFlavor;
898 if (abiParts.count() >= 3) {
899 flavor = osFlavorFromString(abiParts.at(2), os);
900 if (abiParts.at(2) != toString(flavor))
901 return Abi(architecture, os, UnknownFlavor, UnknownFormat, 0);
902 }
903
904 Abi::BinaryFormat format = UnknownFormat;
905 if (abiParts.count() >= 4) {
906 format = binaryFormatFromString(abiParts.at(3));
907 if (abiParts.at(3) != toString(format))
908 return Abi(architecture, os, flavor, UnknownFormat, 0);
909 }
910
911 unsigned char wordWidth = 0;
912 if (abiParts.count() >= 5) {
913 wordWidth = wordWidthFromString(abiParts.at(4));
914 if (abiParts.at(4) != toString(wordWidth))
915 return Abi(architecture, os, flavor, format, 0);
916 }
917
918 return Abi(architecture, os, flavor, format, wordWidth);
919 }
920
architectureFromString(const QString & a)921 Abi::Architecture Abi::architectureFromString(const QString &a)
922 {
923 if (a == "unknown")
924 return UnknownArchitecture;
925 if (a == "arm")
926 return ArmArchitecture;
927 if (a == "aarch64")
928 return ArmArchitecture;
929 if (a == "avr")
930 return AvrArchitecture;
931 if (a == "avr32")
932 return Avr32Architecture;
933 if (a == "x86")
934 return X86Architecture;
935 if (a == "mcs51")
936 return Mcs51Architecture;
937 if (a == "mcs251")
938 return Mcs251Architecture;
939 if (a == "mips")
940 return MipsArchitecture;
941 if (a == "ppc")
942 return PowerPCArchitecture;
943 if (a == "itanium")
944 return ItaniumArchitecture;
945 if (a == "sh")
946 return ShArchitecture;
947 if (a == "stm8")
948 return Stm8Architecture;
949 if (a == "msp430")
950 return Msp430Architecture;
951 if (a == "rl78")
952 return Rl78Architecture;
953 if (a == "c166")
954 return C166Architecture;
955 if (a == "v850")
956 return V850Architecture;
957 if (a == "rh850")
958 return Rh850Architecture;
959 if (a == "rx")
960 return RxArchitecture;
961 if (a == "78k")
962 return K78Architecture;
963 if (a == "m68k")
964 return M68KArchitecture;
965 if (a == "m32c")
966 return M32CArchitecture;
967 if (a == "m16c")
968 return M16CArchitecture;
969 if (a == "m32r")
970 return M32RArchitecture;
971 if (a == "r32c")
972 return R32CArchitecture;
973 if (a == "cr16")
974 return CR16Architecture;
975 if (a == "riscv")
976 return RiscVArchitecture;
977 else if (a == "xtensa")
978 return XtensaArchitecture;
979 if (a == "asmjs")
980 return AsmJsArchitecture;
981
982 return UnknownArchitecture;
983 }
984
osFromString(const QString & o)985 Abi::OS Abi::osFromString(const QString &o)
986 {
987 if (o == "unknown")
988 return UnknownOS;
989 if (o == "linux")
990 return LinuxOS;
991 if (o == "bsd")
992 return BsdOS;
993 if (o == "darwin" || o == "macos")
994 return DarwinOS;
995 if (o == "unix")
996 return UnixOS;
997 if (o == "windows")
998 return WindowsOS;
999 if (o == "vxworks")
1000 return VxWorks;
1001 if (o == "qnx")
1002 return QnxOS;
1003 if (o == "baremetal")
1004 return BareMetalOS;
1005 return UnknownOS;
1006 }
1007
osFlavorFromString(const QString & of,const OS os)1008 Abi::OSFlavor Abi::osFlavorFromString(const QString &of, const OS os)
1009 {
1010 const int index = indexOfFlavor(of.toUtf8());
1011 const auto flavor = OSFlavor(index);
1012 if (index >= 0 && osSupportsFlavor(os, flavor))
1013 return flavor;
1014 return UnknownFlavor;
1015 }
1016
binaryFormatFromString(const QString & bf)1017 Abi::BinaryFormat Abi::binaryFormatFromString(const QString &bf)
1018 {
1019 if (bf == "unknown")
1020 return UnknownFormat;
1021 if (bf == "elf")
1022 return ElfFormat;
1023 if (bf == "pe")
1024 return PEFormat;
1025 if (bf == "mach_o")
1026 return MachOFormat;
1027 if (bf == "ubrof")
1028 return UbrofFormat;
1029 if (bf == "omf")
1030 return OmfFormat;
1031 if (bf == "qml_rt")
1032 return RuntimeQmlFormat;
1033 if (bf == "emscripten")
1034 return EmscriptenFormat;
1035 return UnknownFormat;
1036 }
1037
wordWidthFromString(const QString & w)1038 unsigned char Abi::wordWidthFromString(const QString &w)
1039 {
1040 if (!w.endsWith("bit"))
1041 return 0;
1042
1043 bool ok = false;
1044 const QString number = w.left(w.size() - 3);
1045 const int bitCount = number.toInt(&ok);
1046 if (!ok)
1047 return 0;
1048 if (bitCount != 8 && bitCount != 16 && bitCount != 32 && bitCount != 64)
1049 return 0;
1050 return static_cast<unsigned char>(bitCount);
1051 }
1052
registerOsFlavor(const std::vector<OS> & oses,const QString & flavorName)1053 Abi::OSFlavor Abi::registerOsFlavor(const std::vector<OS> &oses, const QString &flavorName)
1054 {
1055 QTC_ASSERT(oses.size() > 0, return UnknownFlavor);
1056 const QByteArray flavorBytes = flavorName.toUtf8();
1057
1058 int index = indexOfFlavor(flavorBytes);
1059 if (index < 0)
1060 index = int(registeredOsFlavors().size());
1061
1062 auto toRegister = OSFlavor(index);
1063 ProjectExplorer::registerOsFlavor(toRegister, flavorBytes, oses);
1064 return toRegister;
1065 }
1066
flavorsForOs(const Abi::OS & o)1067 QList<Abi::OSFlavor> Abi::flavorsForOs(const Abi::OS &o)
1068 {
1069 registeredOsFlavors(); // Make sure m_osToOsFlavorMap is populated!
1070 auto it = m_osToOsFlavorMap.find(o);
1071 if (it == m_osToOsFlavorMap.end())
1072 return {};
1073
1074 return it->second;
1075 }
1076
allOsFlavors()1077 QList<Abi::OSFlavor> Abi::allOsFlavors()
1078 {
1079 QList<OSFlavor> result;
1080 for (size_t i = 0; i < registeredOsFlavors().size(); ++i)
1081 result << OSFlavor(i);
1082 return moveGenericAndUnknownLast(result);
1083 }
1084
osSupportsFlavor(const Abi::OS & os,const Abi::OSFlavor & flavor)1085 bool Abi::osSupportsFlavor(const Abi::OS &os, const Abi::OSFlavor &flavor)
1086 {
1087 return flavorsForOs(os).contains(flavor);
1088 }
1089
flavorForMsvcVersion(int version)1090 Abi::OSFlavor Abi::flavorForMsvcVersion(int version)
1091 {
1092 if (version >= 1920)
1093 return WindowsMsvc2019Flavor;
1094 if (version >= 1910)
1095 return WindowsMsvc2017Flavor;
1096 switch (version) {
1097 case 1900:
1098 return WindowsMsvc2015Flavor;
1099 case 1800:
1100 return WindowsMsvc2013Flavor;
1101 case 1700:
1102 return WindowsMsvc2012Flavor;
1103 case 1600:
1104 return WindowsMsvc2010Flavor;
1105 case 1500:
1106 return WindowsMsvc2008Flavor;
1107 case 1400:
1108 return WindowsMsvc2005Flavor;
1109 default:
1110 return WindowsMSysFlavor;
1111 }
1112 }
1113
hostAbi()1114 Abi Abi::hostAbi()
1115 {
1116 Architecture arch = architectureFromQt();
1117 OS os = UnknownOS;
1118 OSFlavor subos = UnknownFlavor;
1119 BinaryFormat format = UnknownFormat;
1120
1121 #if defined (Q_OS_WIN)
1122 os = WindowsOS;
1123 #ifdef _MSC_VER
1124 subos = flavorForMsvcVersion(_MSC_VER);
1125 #elif defined (Q_CC_MINGW)
1126 subos = WindowsMSysFlavor;
1127 #endif
1128 format = PEFormat;
1129 #elif defined (Q_OS_LINUX)
1130 os = LinuxOS;
1131 subos = GenericFlavor;
1132 format = ElfFormat;
1133 #elif defined (Q_OS_DARWIN)
1134 os = DarwinOS;
1135 subos = GenericFlavor;
1136 format = MachOFormat;
1137 #elif defined (Q_OS_BSD4)
1138 os = BsdOS;
1139 # if defined (Q_OS_FREEBSD)
1140 subos = FreeBsdFlavor;
1141 # elif defined (Q_OS_NETBSD)
1142 subos = NetBsdFlavor;
1143 # elif defined (Q_OS_OPENBSD)
1144 subos = OpenBsdFlavor;
1145 # endif
1146 format = ElfFormat;
1147 #endif
1148
1149 const Abi result(arch, os, subos, format, QSysInfo::WordSize);
1150 if (!result.isValid())
1151 qWarning("Unable to completely determine the host ABI (%s).",
1152 qPrintable(result.toString()));
1153 return result;
1154 }
1155
1156 //! Extract available ABIs from a binary using heuristics.
abisOfBinary(const Utils::FilePath & path)1157 Abis Abi::abisOfBinary(const Utils::FilePath &path)
1158 {
1159 Abis tmp;
1160 if (path.isEmpty())
1161 return tmp;
1162
1163 QByteArray data = path.fileContents(1024);
1164 if (data.size() >= 67
1165 && getUint8(data, 0) == '!' && getUint8(data, 1) == '<' && getUint8(data, 2) == 'a'
1166 && getUint8(data, 3) == 'r' && getUint8(data, 4) == 'c' && getUint8(data, 5) == 'h'
1167 && getUint8(data, 6) == '>' && getUint8(data, 7) == 0x0a) {
1168 // We got an ar file: possibly a static lib for ELF, PE or Mach-O
1169
1170 data = data.mid(8); // Cut of ar file magic
1171 quint64 offset = 8;
1172
1173 while (!data.isEmpty()) {
1174 if ((getUint8(data, 58) != 0x60 || getUint8(data, 59) != 0x0a)) {
1175 qWarning() << path.toString() << ": Thought it was an ar-file, but it is not!";
1176 break;
1177 }
1178
1179 const QString fileName = QString::fromLocal8Bit(data.mid(0, 16));
1180 quint64 fileNameOffset = 0;
1181 if (fileName.startsWith("#1/"))
1182 fileNameOffset = fileName.mid(3).toInt();
1183 const QString fileLength = QString::fromLatin1(data.mid(48, 10));
1184
1185 int toSkip = 60 + fileNameOffset;
1186 offset += fileLength.toInt() + 60 /* header */;
1187
1188 tmp.append(abiOf(data.mid(toSkip)));
1189 if (tmp.isEmpty() && fileName == "/0 ")
1190 tmp = parseCoffHeader(data.mid(toSkip, 20)); // This might be windws...
1191
1192 if (!tmp.isEmpty() && tmp.at(0).binaryFormat() != MachOFormat)
1193 break;
1194
1195 offset += (offset % 2); // ar is 2 byte aligned
1196 data = path.fileContents(1024, offset);
1197 }
1198 } else {
1199 tmp = abiOf(data);
1200 }
1201
1202 // Remove duplicates:
1203 Abis result;
1204 for (const Abi &a : qAsConst(tmp)) {
1205 if (!result.contains(a))
1206 result.append(a);
1207 }
1208
1209 return result;
1210 }
1211
1212 } // namespace ProjectExplorer
1213
1214 // Unit tests:
1215 #ifdef WITH_TESTS
1216 # include <QTest>
1217 # include <QFileInfo>
1218
1219 # include "projectexplorer.h"
1220
isGenericFlavor(ProjectExplorer::Abi::OSFlavor f)1221 static bool isGenericFlavor(ProjectExplorer::Abi::OSFlavor f)
1222 {
1223 return f == ProjectExplorer::Abi::GenericFlavor;
1224 }
1225
testAbiRoundTrips()1226 void ProjectExplorer::ProjectExplorerPlugin::testAbiRoundTrips()
1227 {
1228 for (int i = 0; i <= Abi::UnknownArchitecture; ++i) {
1229 const QString string = Abi::toString(static_cast<Abi::Architecture>(i));
1230 const Abi::Architecture arch = Abi::architectureFromString(string);
1231 QCOMPARE(static_cast<Abi::Architecture>(i), arch);
1232 }
1233 for (int i = 0; i <= Abi::UnknownOS; ++i) {
1234 const QString string = Abi::toString(static_cast<Abi::OS>(i));
1235 const Abi::OS os = Abi::osFromString(string);
1236 QCOMPARE(static_cast<Abi::OS>(i), os);
1237 }
1238 for (const Abi::OSFlavor flavorIt : Abi::allOsFlavors()) {
1239 const QString string = Abi::toString(flavorIt);
1240 for (int os = 0; os <= Abi::UnknownOS; ++os) {
1241 const auto osEnum = static_cast<Abi::OS>(os);
1242 const Abi::OSFlavor flavor = Abi::osFlavorFromString(string, osEnum);
1243 if (isGenericFlavor(flavorIt) && flavor != Abi::UnknownFlavor)
1244 QVERIFY(isGenericFlavor(flavor));
1245 else if (flavor == Abi::UnknownFlavor && flavorIt != Abi::UnknownFlavor)
1246 QVERIFY(!Abi::flavorsForOs(osEnum).contains(flavorIt));
1247 else
1248 QCOMPARE(flavorIt, flavor);
1249 }
1250 }
1251 for (int i = 0; i <= Abi::UnknownFormat; ++i) {
1252 QString string = Abi::toString(static_cast<Abi::BinaryFormat>(i));
1253 Abi::BinaryFormat format = Abi::binaryFormatFromString(string);
1254 QCOMPARE(static_cast<Abi::BinaryFormat>(i), format);
1255 }
1256 for (unsigned char i : {0, 8, 16, 32, 64}) {
1257 QString string = Abi::toString(i);
1258 unsigned char wordwidth = Abi::wordWidthFromString(string);
1259 QCOMPARE(i, wordwidth);
1260 }
1261 }
1262
testAbiOfBinary_data()1263 void ProjectExplorer::ProjectExplorerPlugin::testAbiOfBinary_data()
1264 {
1265 QTest::addColumn<QString>("file");
1266 QTest::addColumn<QStringList>("abis");
1267
1268 QTest::newRow("no file")
1269 << QString()
1270 << (QStringList());
1271 QTest::newRow("non existing file")
1272 << QString::fromLatin1("/does/not/exist")
1273 << (QStringList());
1274
1275 // Clone test data from: https://git.qt.io/chstenge/creator-test-data
1276 // Set up prefix for test data now that we can be sure to have some tests to run:
1277 QString prefix = QString::fromLocal8Bit(qgetenv("QTC_TEST_EXTRADATALOCATION"));
1278 if (prefix.isEmpty())
1279 return;
1280 prefix += "/projectexplorer/abi";
1281
1282 QFileInfo fi(prefix);
1283 if (!fi.exists() || !fi.isDir())
1284 return;
1285 prefix = fi.absoluteFilePath();
1286
1287 QTest::newRow("text file")
1288 << QString::fromLatin1("%1/broken/text.txt").arg(prefix)
1289 << (QStringList());
1290
1291 QTest::newRow("static QtCore: win msvc2008")
1292 << QString::fromLatin1("%1/static/win-msvc2008-release.lib").arg(prefix)
1293 << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
1294 QTest::newRow("static QtCore: win msvc2008 II")
1295 << QString::fromLatin1("%1/static/win-msvc2008-release2.lib").arg(prefix)
1296 << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-64bit"));
1297 QTest::newRow("static QtCore: win msvc2008 (debug)")
1298 << QString::fromLatin1("%1/static/win-msvc2008-debug.lib").arg(prefix)
1299 << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
1300 QTest::newRow("static QtCore: win mingw")
1301 << QString::fromLatin1("%1/static/win-mingw.a").arg(prefix)
1302 << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
1303 QTest::newRow("static QtCore: mac (debug)")
1304 << QString::fromLatin1("%1/static/mac-32bit-debug.a").arg(prefix)
1305 << (QStringList() << QString::fromLatin1("x86-darwin-generic-mach_o-32bit"));
1306 QTest::newRow("static QtCore: linux 32bit")
1307 << QString::fromLatin1("%1/static/linux-32bit-release.a").arg(prefix)
1308 << (QStringList() << QString::fromLatin1("x86-linux-generic-elf-32bit"));
1309 QTest::newRow("static QtCore: linux 64bit")
1310 << QString::fromLatin1("%1/static/linux-64bit-release.a").arg(prefix)
1311 << (QStringList() << QString::fromLatin1("x86-linux-generic-elf-64bit"));
1312 QTest::newRow("static QtCore: asmjs emscripten 32bit (Qt < 5.15)")
1313 << QString::fromLatin1("%1/static/asmjs-emscripten.a").arg(prefix)
1314 << (QStringList() << QString::fromLatin1("asmjs-unknown-unknown-emscripten-32bit"));
1315 QTest::newRow("static QtCore: asmjs emscripten 32bit (Qt >= 5.15)")
1316 << QString::fromLatin1("%1/static/asmjs-emscripten-5.15.a").arg(prefix)
1317 << (QStringList() << QString::fromLatin1("asmjs-unknown-unknown-emscripten-32bit"));
1318
1319 QTest::newRow("static stdc++: mac fat")
1320 << QString::fromLatin1("%1/static/mac-fat.a").arg(prefix)
1321 << (QStringList() << QString::fromLatin1("x86-darwin-generic-mach_o-32bit")
1322 << QString::fromLatin1("ppc-darwin-generic-mach_o-32bit")
1323 << QString::fromLatin1("x86-darwin-generic-mach_o-64bit"));
1324
1325 QTest::newRow("executable: win msvc2013 64bit")
1326 << QString::fromLatin1("%1/executables/x86-windows-mvsc2013-pe-64bit.exe").arg(prefix)
1327 << (QStringList() << QString::fromLatin1("x86-windows-msvc2013-pe-64bit"));
1328 QTest::newRow("executable: win msvc2013 32bit")
1329 << QString::fromLatin1("%1/executables/x86-windows-mvsc2013-pe-32bit.exe").arg(prefix)
1330 << (QStringList() << QString::fromLatin1("x86-windows-msvc2013-pe-32bit"));
1331 QTest::newRow("dynamic: win msvc2013 64bit")
1332 << QString::fromLatin1("%1/dynamic/x86-windows-mvsc2013-pe-64bit.dll").arg(prefix)
1333 << (QStringList() << QString::fromLatin1("x86-windows-msvc2013-pe-64bit"));
1334 QTest::newRow("dynamic: win msvc2013 32bit")
1335 << QString::fromLatin1("%1/dynamic/x86-windows-mvsc2013-pe-32bit.dll").arg(prefix)
1336 << (QStringList() << QString::fromLatin1("x86-windows-msvc2013-pe-32bit"));
1337 QTest::newRow("dynamic QtCore: win msvc2010 64bit")
1338 << QString::fromLatin1("%1/dynamic/win-msvc2010-64bit.dll").arg(prefix)
1339 << (QStringList() << QString::fromLatin1("x86-windows-msvc2010-pe-64bit"));
1340 QTest::newRow("dynamic QtCore: win msvc2008 32bit")
1341 << QString::fromLatin1("%1/dynamic/win-msvc2008-32bit.dll").arg(prefix)
1342 << (QStringList() << QString::fromLatin1("x86-windows-msvc2008-pe-32bit"));
1343 QTest::newRow("dynamic QtCore: win msvc2005 32bit")
1344 << QString::fromLatin1("%1/dynamic/win-msvc2005-32bit.dll").arg(prefix)
1345 << (QStringList() << QString::fromLatin1("x86-windows-msvc2005-pe-32bit"));
1346 QTest::newRow("dynamic QtCore: win msys 32bit")
1347 << QString::fromLatin1("%1/dynamic/win-mingw-32bit.dll").arg(prefix)
1348 << (QStringList() << QString::fromLatin1("x86-windows-msys-pe-32bit"));
1349 QTest::newRow("dynamic QtCore: win mingw 64bit")
1350 << QString::fromLatin1("%1/dynamic/win-mingw-64bit.dll").arg(prefix)
1351 << (QStringList() << QString::fromLatin1("x86-windows-msys-pe-64bit"));
1352 QTest::newRow("dynamic QtCore: wince mips msvc2005 32bit")
1353 << QString::fromLatin1("%1/dynamic/wince-32bit.dll").arg(prefix)
1354 << (QStringList() << QString::fromLatin1("mips-windows-msvc2005-pe-32bit"));
1355 QTest::newRow("dynamic QtCore: wince arm msvc2008 32bit")
1356 << QString::fromLatin1("%1/dynamic/arm-win-ce-pe-32bit").arg(prefix)
1357 << (QStringList() << QString::fromLatin1("arm-windows-msvc2008-pe-32bit"));
1358
1359 QTest::newRow("dynamic stdc++: mac fat")
1360 << QString::fromLatin1("%1/dynamic/mac-fat.dylib").arg(prefix)
1361 << (QStringList() << QString::fromLatin1("x86-darwin-generic-mach_o-32bit")
1362 << QString::fromLatin1("ppc-darwin-generic-mach_o-32bit")
1363 << QString::fromLatin1("x86-darwin-generic-mach_o-64bit"));
1364 QTest::newRow("dynamic QtCore: arm linux 32bit")
1365 << QString::fromLatin1("%1/dynamic/arm-linux.so").arg(prefix)
1366 << (QStringList() << QString::fromLatin1("arm-linux-generic-elf-32bit"));
1367 QTest::newRow("dynamic QtCore: arm linux 32bit, using ARM as OSABI")
1368 << QString::fromLatin1("%1/dynamic/arm-linux2.so").arg(prefix)
1369 << (QStringList() << QString::fromLatin1("arm-linux-generic-elf-32bit"));
1370 QTest::newRow("dynamic QtCore: arm linux 32bit (angstrom)")
1371 << QString::fromLatin1("%1/dynamic/arm-angstrom-linux.so").arg(prefix)
1372 << (QStringList() << QString::fromLatin1("arm-linux-generic-elf-32bit"));
1373 QTest::newRow("dynamic QtCore: sh4 linux 32bit")
1374 << QString::fromLatin1("%1/dynamic/sh4-linux.so").arg(prefix)
1375 << (QStringList() << QString::fromLatin1("sh-linux-generic-elf-32bit"));
1376 QTest::newRow("dynamic QtCore: mips linux 32bit")
1377 << QString::fromLatin1("%1/dynamic/mips-linux.so").arg(prefix)
1378 << (QStringList() << QString::fromLatin1("mips-linux-generic-elf-32bit"));
1379 QTest::newRow("dynamic QtCore: projectexplorer/abi/static/win-msvc2010-32bit.libppc be linux 32bit")
1380 << QString::fromLatin1("%1/dynamic/ppcbe-linux-32bit.so").arg(prefix)
1381 << (QStringList() << QString::fromLatin1("ppc-linux-generic-elf-32bit"));
1382 QTest::newRow("dynamic QtCore: x86 freebsd 64bit")
1383 << QString::fromLatin1("%1/dynamic/freebsd-elf-64bit.so").arg(prefix)
1384 << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-64bit"));
1385 QTest::newRow("dynamic QtCore: x86 freebsd 64bit")
1386 << QString::fromLatin1("%1/dynamic/freebsd-elf-64bit.so").arg(prefix)
1387 << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-64bit"));
1388 QTest::newRow("dynamic QtCore: x86 freebsd 32bit")
1389 << QString::fromLatin1("%1/dynamic/freebsd-elf-32bit.so").arg(prefix)
1390 << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-32bit"));
1391
1392 QTest::newRow("executable: x86 win 32bit cygwin executable")
1393 << QString::fromLatin1("%1/executable/cygwin-32bit.exe").arg(prefix)
1394 << (QStringList() << QString::fromLatin1("x86-windows-msys-pe-32bit"));
1395 QTest::newRow("executable: x86 win 32bit mingw executable")
1396 << QString::fromLatin1("%1/executable/mingw-32bit.exe").arg(prefix)
1397 << (QStringList() << QString::fromLatin1("x86-windows-msys-pe-32bit"));
1398 }
1399
testAbiOfBinary()1400 void ProjectExplorer::ProjectExplorerPlugin::testAbiOfBinary()
1401 {
1402 QFETCH(QString, file);
1403 QFETCH(QStringList, abis);
1404
1405 const Utils::FilePath fp = Utils::FilePath::fromString(file);
1406 const QString dataTag = QString::fromLocal8Bit(QTest::currentDataTag());
1407 if (!fp.exists() && dataTag != "no file" && dataTag != "non existing file")
1408 QSKIP("binary file not present");
1409
1410 const Abis result = Abi::abisOfBinary(fp);
1411 QCOMPARE(result.count(), abis.count());
1412 for (int i = 0; i < abis.count(); ++i)
1413 QCOMPARE(result.at(i).toString(), abis.at(i));
1414 }
1415
testAbiFromTargetTriplet_data()1416 void ProjectExplorer::ProjectExplorerPlugin::testAbiFromTargetTriplet_data()
1417 {
1418 QTest::addColumn<int>("architecture");
1419 QTest::addColumn<int>("os");
1420 QTest::addColumn<int>("osFlavor");
1421 QTest::addColumn<int>("binaryFormat");
1422 QTest::addColumn<int>("wordWidth");
1423
1424 QTest::newRow("x86_64-apple-darwin") << int(Abi::X86Architecture)
1425 << int(Abi::DarwinOS) << int(Abi::GenericFlavor)
1426 << int(Abi::MachOFormat) << 64;
1427
1428 QTest::newRow("x86_64-apple-darwin12.5.0") << int(Abi::X86Architecture)
1429 << int(Abi::DarwinOS) << int(Abi::GenericFlavor)
1430 << int(Abi::MachOFormat) << 64;
1431
1432 QTest::newRow("x86_64-linux-gnu") << int(Abi::X86Architecture)
1433 << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1434 << int(Abi::ElfFormat) << 64;
1435
1436 QTest::newRow("x86_64-pc-mingw32msvc") << int(Abi::X86Architecture)
1437 << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1438 << int(Abi::PEFormat) << 64;
1439
1440 QTest::newRow("i586-pc-mingw32msvc") << int(Abi::X86Architecture)
1441 << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1442 << int(Abi::PEFormat) << 32;
1443
1444 QTest::newRow("i686-linux-gnu") << int(Abi::X86Architecture)
1445 << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1446 << int(Abi::ElfFormat) << 32;
1447
1448 QTest::newRow("i686-linux-android") << int(Abi::X86Architecture)
1449 << int(Abi::LinuxOS) << int(Abi::AndroidLinuxFlavor)
1450 << int(Abi::ElfFormat) << 32;
1451
1452 QTest::newRow("i686-pc-linux-android") << int(Abi::X86Architecture)
1453 << int(Abi::LinuxOS) << int(Abi::AndroidLinuxFlavor)
1454 << int(Abi::ElfFormat) << 32;
1455
1456 QTest::newRow("i686-pc-mingw32") << int(Abi::X86Architecture)
1457 << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1458 << int(Abi::PEFormat) << 32;
1459
1460 QTest::newRow("i686-w64-mingw32") << int(Abi::X86Architecture)
1461 << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1462 << int(Abi::PEFormat) << 32;
1463
1464 QTest::newRow("x86_64-pc-msys") << int(Abi::X86Architecture)
1465 << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1466 << int(Abi::PEFormat) << 64;
1467
1468 QTest::newRow("x86_64-pc-cygwin") << int(Abi::X86Architecture)
1469 << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1470 << int(Abi::PEFormat) << 64;
1471
1472 QTest::newRow("x86-pc-windows-msvc") << int(Abi::X86Architecture)
1473 << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1474 << int(Abi::PEFormat) << 32;
1475
1476 QTest::newRow("mingw32") << int(Abi::X86Architecture)
1477 << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1478 << int(Abi::PEFormat) << 0;
1479
1480 QTest::newRow("arm-linux-android") << int(Abi::ArmArchitecture)
1481 << int(Abi::LinuxOS) << int(Abi::AndroidLinuxFlavor)
1482 << int(Abi::ElfFormat) << 32;
1483
1484 QTest::newRow("arm-linux-androideabi") << int(Abi::ArmArchitecture)
1485 << int(Abi::LinuxOS) << int(Abi::AndroidLinuxFlavor)
1486 << int(Abi::ElfFormat) << 32;
1487
1488 QTest::newRow("arm-none-linux-gnueabi") << int(Abi::ArmArchitecture)
1489 << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1490 << int(Abi::ElfFormat) << 32;
1491
1492 QTest::newRow("mips-linux-gnu") << int(Abi::MipsArchitecture)
1493 << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1494 << int(Abi::ElfFormat) << 32;
1495
1496 QTest::newRow("mips64-linux-octeon-gnu") << int(Abi::MipsArchitecture)
1497 << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1498 << int(Abi::ElfFormat) << 64;
1499
1500 QTest::newRow("mips64el-linux-gnuabi") << int(Abi::MipsArchitecture)
1501 << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1502 << int(Abi::ElfFormat) << 64;
1503
1504 QTest::newRow("arm-wrs-vxworks") << int(Abi::ArmArchitecture)
1505 << int(Abi::VxWorks) << int(Abi::VxWorksFlavor)
1506 << int(Abi::ElfFormat) << 32;
1507
1508 QTest::newRow("x86_64-unknown-openbsd") << int(Abi::X86Architecture)
1509 << int(Abi::BsdOS) << int(Abi::OpenBsdFlavor)
1510 << int(Abi::ElfFormat) << 64;
1511
1512 QTest::newRow("aarch64-unknown-linux-gnu") << int(Abi::ArmArchitecture)
1513 << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1514 << int(Abi::ElfFormat) << 64;
1515 QTest::newRow("xtensa-lx106-elf") << int(Abi::XtensaArchitecture)
1516 << int(Abi::BareMetalOS) << int(Abi::GenericFlavor)
1517 << int(Abi::ElfFormat) << 32;
1518
1519 // Yes, that's the entire triplet
1520 QTest::newRow("avr") << int(Abi::AvrArchitecture)
1521 << int(Abi::BareMetalOS) << int(Abi::GenericFlavor)
1522 << int(Abi::ElfFormat) << 16;
1523
1524 QTest::newRow("avr32") << int(Abi::Avr32Architecture)
1525 << int(Abi::BareMetalOS) << int(Abi::GenericFlavor)
1526 << int(Abi::ElfFormat) << 32;
1527
1528 QTest::newRow("asmjs-unknown-emscripten") << int(Abi::AsmJsArchitecture)
1529 << int(Abi::UnknownOS) << int(Abi::UnknownFlavor)
1530 << int(Abi::EmscriptenFormat) << 32;
1531 }
1532
testAbiFromTargetTriplet()1533 void ProjectExplorer::ProjectExplorerPlugin::testAbiFromTargetTriplet()
1534 {
1535 QFETCH(int, architecture);
1536 QFETCH(int, os);
1537 QFETCH(int, osFlavor);
1538 QFETCH(int, binaryFormat);
1539 QFETCH(int, wordWidth);
1540
1541 const Abi expectedAbi = Abi(Abi::Architecture(architecture),
1542 Abi::OS(os), Abi::OSFlavor(osFlavor),
1543 Abi::BinaryFormat(binaryFormat),
1544 static_cast<unsigned char>(wordWidth));
1545
1546 QCOMPARE(Abi::abiFromTargetTriplet(QLatin1String(QTest::currentDataTag())), expectedAbi);
1547 }
1548
testAbiUserOsFlavor_data()1549 void ProjectExplorer::ProjectExplorerPlugin::testAbiUserOsFlavor_data()
1550 {
1551 QTest::addColumn<int>("os");
1552 QTest::addColumn<QString>("osFlavorName");
1553 QTest::addColumn<int>("expectedFlavor");
1554
1555 QTest::newRow("linux-generic flavor")
1556 << int(Abi::LinuxOS) << "generic" << int(Abi::GenericFlavor);
1557 QTest::newRow("linux-unknown flavor")
1558 << int(Abi::LinuxOS) << "unknown" << int(Abi::UnknownFlavor);
1559 QTest::newRow("windows-msvc2010 flavor")
1560 << int(Abi::WindowsOS) << "msvc2010" << int(Abi::WindowsMsvc2010Flavor);
1561 QTest::newRow("windows-unknown flavor")
1562 << int(Abi::WindowsOS) << "unknown" << int(Abi::UnknownFlavor);
1563
1564 QTest::newRow("windows-msvc2100 flavor")
1565 << int(Abi::WindowsOS) << "msvc2100" << int(Abi::UnknownFlavor) + 1;
1566 QTest::newRow("linux-msvc2100 flavor")
1567 << int(Abi::LinuxOS) << "msvc2100" << int(Abi::UnknownFlavor) + 1;
1568
1569 QTest::newRow("windows-msvc2100 flavor reregister")
1570 << int(Abi::WindowsOS) << "msvc2100" << int(Abi::UnknownFlavor) + 1;
1571 QTest::newRow("linux-msvc2100 flavor reregister")
1572 << int(Abi::LinuxOS) << "msvc2100" << int(Abi::UnknownFlavor) + 1;
1573 QTest::newRow("unix-msvc2100 flavor register")
1574 << int(Abi::UnixOS) << "msvc2100" << int(Abi::UnknownFlavor) + 1;
1575 }
1576
testAbiUserOsFlavor()1577 void ProjectExplorer::ProjectExplorerPlugin::testAbiUserOsFlavor()
1578 {
1579 QFETCH(int, os);
1580 QFETCH(QString, osFlavorName);
1581 QFETCH(int, expectedFlavor);
1582
1583 QMap<int, QList<Abi::OSFlavor>> osFlavorMap;
1584 for (int i = 0; i <= Abi::UnknownOS; ++i)
1585 osFlavorMap.insert(i, Abi::flavorsForOs(static_cast<ProjectExplorer::Abi::OS>(i)));
1586
1587 Abi::OSFlavor osFlavor = Abi::registerOsFlavor({static_cast<Abi::OS>(os)}, osFlavorName);
1588 QCOMPARE(osFlavor, static_cast<Abi::OSFlavor>(expectedFlavor));
1589
1590 for (int i = 0; i <= Abi::UnknownOS; ++i) {
1591 const QList<Abi::OSFlavor> flavors = Abi::flavorsForOs(static_cast<Abi::OS>(i));
1592 const QList<Abi::OSFlavor> previousFlavors = osFlavorMap.value(static_cast<Abi::OS>(i));
1593 const int previousCount = previousFlavors.count();
1594
1595 if (i == os && previousCount != flavors.count()) {
1596 QVERIFY(flavors.count() == previousCount + 1);
1597 QVERIFY(flavors.contains(osFlavor));
1598 for (const Abi::OSFlavor &f : previousFlavors) {
1599 if (f == osFlavor)
1600 continue;
1601 QVERIFY(previousFlavors.contains(f));
1602 }
1603 } else {
1604 QCOMPARE(flavors, previousFlavors);
1605 }
1606 }
1607 }
1608
1609
1610 #endif
1611