diff options
author | Christian Kandeler <christian.kandeler@qt.io> | 2019-01-18 16:02:43 +0100 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@qt.io> | 2019-02-12 09:12:39 +0000 |
commit | c4e60ed8283aa7a86e13c09113e7fec6bf41cc42 (patch) | |
tree | 6cefbf3ece17e255c3962e69573abc88bef060ef /src/lib/corelib/jsextensions | |
parent | 17058d1fc537e40e7dda9d6e48ccfb24ea1220f7 (diff) |
Detect Qt via a module provider
Creation of qbs modules for Qt is now done on demand during project
resolving. The qmake executable(s) are looked up via PATH or taken from
the Qt.qmakeFilePaths provider property. As a result, Qt projects can
now be built without a profile.
The qtprofilesetup library is gone; its code is now in the module
provider. I kept the C++ -> JavaScript conversion as straightforward as
possible and mostly resisted the temptation to "optimize".
The setup-qt tool still exists and mainly sets Qt.qmakeFilePaths.
[ChangeLog] It is no longer required to call setup-qt before building Qt
projects.
Change-Id: I5b7e4711ec47b996911c499f29d8129d90e4731e
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
Diffstat (limited to 'src/lib/corelib/jsextensions')
-rw-r--r-- | src/lib/corelib/jsextensions/utilitiesextension.cpp | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/src/lib/corelib/jsextensions/utilitiesextension.cpp b/src/lib/corelib/jsextensions/utilitiesextension.cpp index a10251c54..4cbe6ed2f 100644 --- a/src/lib/corelib/jsextensions/utilitiesextension.cpp +++ b/src/lib/corelib/jsextensions/utilitiesextension.cpp @@ -51,6 +51,26 @@ #include <tools/applecodesignutils.h> #endif +#ifdef __APPLE__ +#include <ar.h> +#include <mach/machine.h> +#include <mach-o/fat.h> +#include <mach-o/loader.h> +#ifndef FAT_MAGIC_64 +#define FAT_MAGIC_64 0xcafebabf +#define FAT_CIGAM_64 0xbfbafeca +struct fat_arch_64 { + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint64_t offset; + uint64_t size; + uint32_t align; + uint32_t reserved; +}; +#endif +#endif + + #ifdef Q_OS_WIN #include <tools/msvcinfo.h> #include <tools/vsenvironmentdetector.h> @@ -58,6 +78,9 @@ #include <QtCore/qcryptographichash.h> #include <QtCore/qdir.h> +#include <QtCore/qendian.h> +#include <QtCore/qfile.h> +#include <QtCore/qlibrary.h> #include <QtScript/qscriptable.h> #include <QtScript/qscriptengine.h> @@ -91,6 +114,10 @@ public: static QScriptValue js_qmlTypeInfo(QScriptContext *context, QScriptEngine *engine); static QScriptValue js_builtinExtensionNames(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_isSharedLibrary(QScriptContext *context, QScriptEngine *engine); + + static QScriptValue js_getArchitecturesFromBinary(QScriptContext *context, + QScriptEngine *engine); }; QScriptValue UtilitiesExtension::js_ctor(QScriptContext *context, QScriptEngine *engine) @@ -504,6 +531,237 @@ QScriptValue UtilitiesExtension::js_builtinExtensionNames(QScriptContext *contex return engine->toScriptValue(JsExtensions::extensionNames()); } +QScriptValue UtilitiesExtension::js_isSharedLibrary(QScriptContext *context, QScriptEngine *engine) +{ + if (context->argumentCount() == 1) { + const QScriptValue value = context->argument(0); + if (value.isString()) + return engine->toScriptValue(QLibrary::isLibrary(value.toString())); + } + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("isSharedLibrary expects one argument of type string")); +} + +#ifdef __APPLE__ +template <typename T = uint32_t> T readInt(QIODevice *ioDevice, bool *ok, + bool swap, bool peek = false) { + const auto bytes = peek + ? ioDevice->peek(sizeof(T)) + : ioDevice->read(sizeof(T)); + if (bytes.size() != sizeof(T)) { + if (ok) + *ok = false; + return T(); + } + if (ok) + *ok = true; + T n = *reinterpret_cast<const T *>(bytes.constData()); + return swap ? qbswap(n) : n; +} + +static QString archName(cpu_type_t cputype, cpu_subtype_t cpusubtype) +{ + switch (cputype) { + case CPU_TYPE_X86: + switch (cpusubtype) { + case CPU_SUBTYPE_X86_ALL: + return QStringLiteral("i386"); + default: + return QString(); + } + case CPU_TYPE_X86_64: + switch (cpusubtype) { + case CPU_SUBTYPE_X86_64_ALL: + return QStringLiteral("x86_64"); + case CPU_SUBTYPE_X86_64_H: + return QStringLiteral("x86_64h"); + default: + return QString(); + } + case CPU_TYPE_ARM: + switch (cpusubtype) { + case CPU_SUBTYPE_ARM_V7: + return QStringLiteral("armv7a"); + case CPU_SUBTYPE_ARM_V7S: + return QStringLiteral("armv7s"); + case CPU_SUBTYPE_ARM_V7K: + return QStringLiteral("armv7k"); + default: + return QString(); + } + case CPU_TYPE_ARM64: + switch (cpusubtype) { + case CPU_SUBTYPE_ARM64_ALL: + return QStringLiteral("arm64"); + default: + return QString(); + } + default: + return QString(); + } +} + +static QStringList detectMachOArchs(QIODevice *device) +{ + bool ok; + bool foundMachO = false; + qint64 pos = device->pos(); + + char ar_header[SARMAG]; + if (device->read(ar_header, SARMAG) == SARMAG) { + if (strncmp(ar_header, ARMAG, SARMAG) == 0) { + while (!device->atEnd()) { + static_assert(sizeof(ar_hdr) == 60, "sizeof(ar_hdr) != 60"); + ar_hdr header; + if (device->read(reinterpret_cast<char *>(&header), + sizeof(ar_hdr)) != sizeof(ar_hdr)) + return { }; + + // If the file name is stored in the "extended format" manner, + // the real filename is prepended to the data section, so skip that many bytes + int filenameLength = 0; + if (strncmp(header.ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0) { + char arName[sizeof(header.ar_name)] = { 0 }; + memcpy(arName, header.ar_name + sizeof(AR_EFMT1) - 1, + sizeof(header.ar_name) - (sizeof(AR_EFMT1) - 1) - 1); + filenameLength = strtoul(arName, nullptr, 10); + if (device->read(filenameLength).size() != filenameLength) + return { }; + } + + switch (readInt(device, nullptr, false, true)) { + case MH_CIGAM: + case MH_CIGAM_64: + case MH_MAGIC: + case MH_MAGIC_64: + foundMachO = true; + break; + default: { + // Skip the data and go to the next archive member... + char szBuf[sizeof(header.ar_size) + 1] = { 0 }; + memcpy(szBuf, header.ar_size, sizeof(header.ar_size)); + int sz = static_cast<int>(strtoul(szBuf, nullptr, 10)); + if (sz % 2 != 0) + ++sz; + sz -= filenameLength; + const auto data = device->read(sz); + if (data.size() != sz) + return { }; + } + } + + if (foundMachO) + break; + } + } + } + + // Wasn't an archive file, so try a fat file + if (!foundMachO && !device->seek(pos)) + return QStringList(); + + pos = device->pos(); + + fat_header fatheader; + fatheader.magic = readInt(device, nullptr, false); + if (fatheader.magic == FAT_MAGIC || fatheader.magic == FAT_CIGAM || + fatheader.magic == FAT_MAGIC_64 || fatheader.magic == FAT_CIGAM_64) { + const bool swap = fatheader.magic == FAT_CIGAM || fatheader.magic == FAT_CIGAM_64; + const bool is64bit = fatheader.magic == FAT_MAGIC_64 || fatheader.magic == FAT_CIGAM_64; + fatheader.nfat_arch = readInt(device, &ok, swap); + if (!ok) + return QStringList(); + + QStringList archs; + + for (uint32_t n = 0; n < fatheader.nfat_arch; ++n) { + fat_arch_64 fatarch; + static_assert(sizeof(fat_arch_64) == 32, "sizeof(fat_arch_64) != 32"); + static_assert(sizeof(fat_arch) == 20, "sizeof(fat_arch) != 20"); + const qint64 expectedBytes = is64bit ? sizeof(fat_arch_64) : sizeof(fat_arch); + if (device->read(reinterpret_cast<char *>(&fatarch), expectedBytes) != expectedBytes) + return QStringList(); + + if (swap) { + fatarch.cputype = qbswap(fatarch.cputype); + fatarch.cpusubtype = qbswap(fatarch.cpusubtype); + } + + const QString name = archName(fatarch.cputype, fatarch.cpusubtype); + if (name.isEmpty()) { + qWarning("Unknown cputype %d and cpusubtype %d", + fatarch.cputype, fatarch.cpusubtype); + return QStringList(); + } + archs.push_back(name); + } + + std::sort(archs.begin(), archs.end()); + return archs; + } + + // Wasn't a fat file, so we just read a thin Mach-O from the original offset + if (!device->seek(pos)) + return QStringList(); + + bool swap = false; + mach_header header; + header.magic = readInt(device, nullptr, swap); + switch (header.magic) { + case MH_CIGAM: + case MH_CIGAM_64: + swap = true; + break; + case MH_MAGIC: + case MH_MAGIC_64: + break; + default: + return QStringList(); + } + + header.cputype = static_cast<cpu_type_t>(readInt(device, &ok, swap)); + if (!ok) + return QStringList(); + + header.cpusubtype = static_cast<cpu_subtype_t>(readInt(device, &ok, swap)); + if (!ok) + return QStringList(); + + const QString name = archName(header.cputype, header.cpusubtype); + if (name.isEmpty()) { + qWarning("Unknown cputype %d and cpusubtype %d", + header.cputype, header.cpusubtype); + return { }; + } + return { name }; +} +#endif + +QScriptValue UtilitiesExtension::js_getArchitecturesFromBinary(QScriptContext *context, + QScriptEngine *engine) +{ + if (context->argumentCount() != 1) { + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("getArchitecturesFromBinary expects exactly one argument")); + } + const QScriptValue arg = context->argument(0); + if (!arg.isString()) { + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("getArchitecturesFromBinary expects a string argument")); + } + QStringList archs; +#ifdef __APPLE__ + QFile file(arg.toString()); + if (!file.open(QIODevice::ReadOnly)) { + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("Failed to open file '%1': %2") + .arg(file.fileName(), file.errorString())); + } + archs = detectMachOArchs(&file); +#endif // __APPLE__ + return engine->toScriptValue(archs); +} + } // namespace Internal } // namespace qbs @@ -548,6 +806,10 @@ void initializeJsExtensionUtilities(QScriptValue extensionObject) engine->newFunction(UtilitiesExtension::js_qmlTypeInfo, 0)); environmentObj.setProperty(QStringLiteral("builtinExtensionNames"), engine->newFunction(UtilitiesExtension::js_builtinExtensionNames, 0)); + environmentObj.setProperty(QStringLiteral("isSharedLibrary"), + engine->newFunction(UtilitiesExtension::js_isSharedLibrary, 1)); + environmentObj.setProperty(QStringLiteral("getArchitecturesFromBinary"), + engine->newFunction(UtilitiesExtension::js_getArchitecturesFromBinary, 1)); extensionObject.setProperty(QStringLiteral("Utilities"), environmentObj); } |