summaryrefslogtreecommitdiffstats
path: root/src/shared
diff options
context:
space:
mode:
authorOliver Wolff <oliver.wolff@qt.io>2018-03-21 10:34:42 +0100
committerSami Nurmenniemi <sami.nurmenniemi@qt.io>2018-04-06 13:21:15 +0000
commite02d2e9061a2fd884590c0ebfba6406dbf17cabe (patch)
tree6381a3fe6b2df2c3371396306081e70769070d16 /src/shared
parenta7f65a15e2f0ada1b5bf56865b72451fe2857e73 (diff)
Move windows utilities into shared folder
An upcoming patch for winrtrunner will need to run an external executable. The utilities are moved to the shared folder to avoid code duplication Change-Id: I2be575432626ef796a6ebc95e55ac30ab3631b61 Reviewed-by: Andre de la Rocha <andre.rocha@qt.io> Reviewed-by: Maurice Kalinowski <maurice.kalinowski@qt.io> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/winutils/elfreader.cpp438
-rw-r--r--src/shared/winutils/elfreader.h176
-rw-r--r--src/shared/winutils/qmlutils.cpp173
-rw-r--r--src/shared/winutils/qmlutils.h66
-rw-r--r--src/shared/winutils/utils.cpp996
-rw-r--r--src/shared/winutils/utils.h387
-rw-r--r--src/shared/winutils/winutils.pri8
7 files changed, 2244 insertions, 0 deletions
diff --git a/src/shared/winutils/elfreader.cpp b/src/shared/winutils/elfreader.cpp
new file mode 100644
index 000000000..b175e87d6
--- /dev/null
+++ b/src/shared/winutils/elfreader.cpp
@@ -0,0 +1,438 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "elfreader.h"
+
+#include <QDir>
+
+QT_BEGIN_NAMESPACE
+
+/* This is a copy of the ELF reader contained in Qt Creator (src/libs/utils),
+ * extended by the dependencies() function to read out the dependencies of a dynamic executable. */
+
+quint16 getHalfWord(const unsigned char *&s, const ElfData &context)
+{
+ quint16 res;
+ if (context.endian == Elf_ELFDATA2MSB)
+ res = qFromBigEndian<quint16>(s);
+ else
+ res = qFromLittleEndian<quint16>(s);
+ s += 2;
+ return res;
+}
+
+quint32 getWord(const unsigned char *&s, const ElfData &context)
+{
+ quint32 res;
+ if (context.endian == Elf_ELFDATA2MSB)
+ res = qFromBigEndian<quint32>(s);
+ else
+ res = qFromLittleEndian<quint32>(s);
+ s += 4;
+ return res;
+}
+
+quint64 getAddress(const unsigned char *&s, const ElfData &context)
+{
+ quint64 res;
+ if (context.elfclass == Elf_ELFCLASS32) {
+ if (context.endian == Elf_ELFDATA2MSB)
+ res = qFromBigEndian<quint32>(s);
+ else
+ res = qFromLittleEndian<quint32>(s);
+ s += 4;
+ } else {
+ if (context.endian == Elf_ELFDATA2MSB)
+ res = qFromBigEndian<quint64>(s);
+ else
+ res = qFromLittleEndian<quint64>(s);
+ s += 8;
+ }
+ return res;
+}
+
+quint64 getOffset(const unsigned char *&s, const ElfData &context)
+{
+ return getAddress(s, context);
+}
+
+static void parseSectionHeader(const uchar *s, ElfSectionHeader *sh, const ElfData &context)
+{
+ sh->index = getWord(s, context);
+ sh->type = getWord(s, context);
+ sh->flags = quint32(getOffset(s, context));
+ sh->addr = getAddress(s, context);
+ sh->offset = getOffset(s, context);
+ sh->size = getOffset(s, context);
+}
+
+static void parseProgramHeader(const uchar *s, ElfProgramHeader *sh, const ElfData &context)
+{
+ sh->type = getWord(s, context);
+ sh->offset = getOffset(s, context);
+ /* p_vaddr = */ getAddress(s, context);
+ /* p_paddr = */ getAddress(s, context);
+ sh->filesz = getWord(s, context);
+ sh->memsz = getWord(s, context);
+}
+
+class ElfMapper
+{
+public:
+ ElfMapper(const ElfReader *reader) : file(reader->m_binary) {}
+
+ bool map()
+ {
+ if (!file.open(QIODevice::ReadOnly))
+ return false;
+
+ fdlen = quint64(file.size());
+ ustart = file.map(0, qint64(fdlen));
+ if (ustart == 0) {
+ // Try reading the data into memory instead.
+ raw = file.readAll();
+ start = raw.constData();
+ fdlen = quint64(raw.size());
+ }
+ return true;
+ }
+
+public:
+ QFile file;
+ QByteArray raw;
+ union { const char *start; const uchar *ustart; };
+ quint64 fdlen;
+};
+
+ElfReader::ElfReader(const QString &binary)
+ : m_binary(binary)
+{
+}
+
+ElfData ElfReader::readHeaders()
+{
+ readIt();
+ return m_elfData;
+}
+
+static inline QString msgInvalidElfObject(const QString &binary, const QString &why)
+{
+ return QStringLiteral("'%1' is an invalid ELF object (%2)")
+ .arg(QDir::toNativeSeparators(binary), why);
+}
+
+ElfReader::Result ElfReader::readIt()
+{
+ if (!m_elfData.sectionHeaders.isEmpty())
+ return Ok;
+ if (!m_elfData.programHeaders.isEmpty())
+ return Ok;
+
+ ElfMapper mapper(this);
+ if (!mapper.map())
+ return Corrupt;
+
+ const quint64 fdlen = mapper.fdlen;
+
+ if (fdlen < 64) {
+ m_errorString = QStringLiteral("'%1' is not an ELF object (file too small)").arg(QDir::toNativeSeparators(m_binary));
+ return NotElf;
+ }
+
+ if (strncmp(mapper.start, "\177ELF", 4) != 0) {
+ m_errorString = QStringLiteral("'%1' is not an ELF object").arg(QDir::toNativeSeparators(m_binary));
+ return NotElf;
+ }
+
+ // 32 or 64 bit
+ m_elfData.elfclass = ElfClass(mapper.start[4]);
+ const bool is64Bit = m_elfData.elfclass == Elf_ELFCLASS64;
+ if (m_elfData.elfclass != Elf_ELFCLASS32 && m_elfData.elfclass != Elf_ELFCLASS64) {
+ m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("odd cpu architecture"));
+ return Corrupt;
+ }
+
+ // int bits = (data[4] << 5);
+ // If you remove this check to read ELF objects of a different arch,
+ // please make sure you modify the typedefs
+ // to match the _plugin_ architecture.
+ // if ((sizeof(void*) == 4 && bits != 32)
+ // || (sizeof(void*) == 8 && bits != 64)) {
+ // if (errorString)
+ // *errorString = QLibrary::QStringLiteral("'%1' is an invalid ELF object (%2)")
+ // .arg(m_binary).arg(QLatin1String("wrong cpu architecture"));
+ // return Corrupt;
+ // }
+
+ // Read Endianhness.
+ m_elfData.endian = ElfEndian(mapper.ustart[5]);
+ if (m_elfData.endian != Elf_ELFDATA2LSB && m_elfData.endian != Elf_ELFDATA2MSB) {
+ m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("odd endianness"));
+ return Corrupt;
+ }
+
+ const uchar *data = mapper.ustart + 16; // e_ident
+ m_elfData.elftype = ElfType(getHalfWord(data, m_elfData));
+ m_elfData.elfmachine = ElfMachine(getHalfWord(data, m_elfData));
+ /* e_version = */ getWord(data, m_elfData);
+ m_elfData.entryPoint = getAddress(data, m_elfData);
+
+ quint64 e_phoff = getOffset(data, m_elfData);
+ quint64 e_shoff = getOffset(data, m_elfData);
+ /* e_flags = */ getWord(data, m_elfData);
+
+ quint32 e_shsize = getHalfWord(data, m_elfData);
+
+ if (e_shsize > fdlen) {
+ m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_shsize"));
+ return Corrupt;
+ }
+
+ quint32 e_phentsize = getHalfWord(data, m_elfData);
+ if (e_phentsize != (is64Bit ? 56 : 32)) {
+ m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("invalid structure"));
+ return ElfReader::Corrupt;
+ }
+ quint32 e_phnum = getHalfWord(data, m_elfData);
+
+ quint32 e_shentsize = getHalfWord(data, m_elfData);
+
+ if (e_shentsize % 4) {
+ m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_shentsize"));
+ return Corrupt;
+ }
+
+ quint32 e_shnum = getHalfWord(data, m_elfData);
+ quint32 e_shtrndx = getHalfWord(data, m_elfData);
+ if (data != mapper.ustart + (is64Bit ? 64 : 52)) {
+ m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_phentsize"));
+ return ElfReader::Corrupt;
+ }
+
+ if (quint64(e_shnum) * e_shentsize > fdlen) {
+ const QString reason = QStringLiteral("announced %1 sections, each %2 bytes, exceed file size").arg(e_shnum).arg(e_shentsize);
+ m_errorString = msgInvalidElfObject(m_binary, reason);
+ return Corrupt;
+ }
+
+ quint64 soff = e_shoff + e_shentsize * e_shtrndx;
+
+// if ((soff + e_shentsize) > fdlen || soff % 4 || soff == 0) {
+// m_errorString = QLibrary::QStringLiteral("'%1' is an invalid ELF object (%2)")
+// .arg(m_binary)
+// .arg(QLatin1String("shstrtab section header seems to be at %1"))
+// .arg(QString::number(soff, 16));
+// return Corrupt;
+// }
+
+ if (e_shoff) {
+ ElfSectionHeader strtab;
+ parseSectionHeader(mapper.ustart + soff, &strtab, m_elfData);
+ const quint64 stringTableFileOffset = strtab.offset;
+ if (quint32(stringTableFileOffset + e_shentsize) >= fdlen
+ || stringTableFileOffset == 0) {
+ const QString reason = QStringLiteral("string table seems to be at 0x%1").arg(soff, 0, 16);
+ m_errorString = msgInvalidElfObject(m_binary, reason);
+ return Corrupt;
+ }
+
+ for (quint32 i = 0; i < e_shnum; ++i) {
+ const uchar *s = mapper.ustart + e_shoff + i * e_shentsize;
+ ElfSectionHeader sh;
+ parseSectionHeader(s, &sh, m_elfData);
+
+ if (stringTableFileOffset + sh.index > fdlen) {
+ const QString reason = QStringLiteral("section name %1 of %2 behind end of file")
+ .arg(i).arg(e_shnum);
+ m_errorString = msgInvalidElfObject(m_binary, reason);
+ return Corrupt;
+ }
+
+ sh.name = mapper.start + stringTableFileOffset + sh.index;
+ if (sh.name == ".gdb_index") {
+ m_elfData.symbolsType = FastSymbols;
+ } else if (sh.name == ".debug_info") {
+ m_elfData.symbolsType = PlainSymbols;
+ } else if (sh.name == ".gnu_debuglink") {
+ m_elfData.debugLink = QByteArray(mapper.start + sh.offset);
+ m_elfData.symbolsType = LinkedSymbols;
+ } else if (sh.name == ".note.gnu.build-id") {
+ m_elfData.symbolsType = BuildIdSymbols;
+ if (sh.size > 16)
+ m_elfData.buildId = QByteArray(mapper.start + sh.offset + 16,
+ int(sh.size) - 16).toHex();
+ }
+ m_elfData.sectionHeaders.append(sh);
+ }
+ }
+
+ if (e_phoff) {
+ for (quint32 i = 0; i < e_phnum; ++i) {
+ const uchar *s = mapper.ustart + e_phoff + i * e_phentsize;
+ ElfProgramHeader ph;
+ parseProgramHeader(s, &ph, m_elfData);
+ m_elfData.programHeaders.append(ph);
+ }
+ }
+ return Ok;
+}
+
+QByteArray ElfReader::readSection(const QByteArray &name)
+{
+ readIt();
+ int i = m_elfData.indexOf(name);
+ if (i == -1)
+ return QByteArray();
+
+ ElfMapper mapper(this);
+ if (!mapper.map())
+ return QByteArray();
+
+ const ElfSectionHeader &section = m_elfData.sectionHeaders.at(i);
+ return QByteArray(mapper.start + section.offset, int(section.size));
+}
+
+static QByteArray cutout(const char *s)
+{
+ QByteArray res(s, 80);
+ const int pos = res.indexOf('\0');
+ if (pos != -1)
+ res.resize(pos - 1);
+ return res;
+}
+
+QByteArray ElfReader::readCoreName(bool *isCore)
+{
+ *isCore = false;
+
+ readIt();
+
+ ElfMapper mapper(this);
+ if (!mapper.map())
+ return QByteArray();
+
+ if (m_elfData.elftype != Elf_ET_CORE)
+ return QByteArray();
+
+ *isCore = true;
+
+ for (int i = 0, n = m_elfData.sectionHeaders.size(); i != n; ++i)
+ if (m_elfData.sectionHeaders.at(i).type == Elf_SHT_NOTE) {
+ const ElfSectionHeader &header = m_elfData.sectionHeaders.at(i);
+ return cutout(mapper.start + header.offset + 0x40);
+ }
+
+ for (int i = 0, n = m_elfData.programHeaders.size(); i != n; ++i)
+ if (m_elfData.programHeaders.at(i).type == Elf_PT_NOTE) {
+ const ElfProgramHeader &header = m_elfData.programHeaders.at(i);
+ return cutout(mapper.start + header.offset + 0xec);
+ }
+
+ return QByteArray();
+}
+
+int ElfData::indexOf(const QByteArray &name) const
+{
+ for (int i = 0, n = sectionHeaders.size(); i != n; ++i)
+ if (sectionHeaders.at(i).name == name)
+ return i;
+ return -1;
+}
+
+/* Helpers for reading out the .dynamic section containing the dependencies.
+ * The ".dynamic" section is an array of
+ * typedef struct {
+ * Elf32_Sword d_tag;
+ * union {
+ * Elf32_Word d_val;
+ * dElf32_Addr d_ptr;
+ * } d_un;
+ * } Elf32_Dyn
+ * with entries where a tag DT_NEEDED indicates that m_val is an offset into
+ * the string table ".dynstr". The documentation states that entries with the
+ * tag DT_STRTAB contain an offset for the string table to be used, but that
+ * has been found not to contain valid entries. */
+
+enum DynamicSectionTags {
+ DT_NULL = 0,
+ DT_NEEDED = 1,
+ DT_STRTAB = 5,
+ DT_SONAME = 14,
+ DT_RPATH = 15
+};
+
+QList<QByteArray> ElfReader::dependencies()
+{
+ QList<QByteArray> result;
+
+ ElfMapper mapper(this);
+ if (!mapper.map()) {
+ m_errorString = QStringLiteral("Mapper failure");
+ return result;
+ }
+ quint64 dynStrOffset = 0;
+ quint64 dynamicOffset = 0;
+ quint64 dynamicSize = 0;
+
+ const QVector<ElfSectionHeader> &headers = readHeaders().sectionHeaders;
+ for (const ElfSectionHeader &eh : headers) {
+ if (eh.name == QByteArrayLiteral(".dynstr")) {
+ dynStrOffset = eh.offset;
+ } else if (eh.name == QByteArrayLiteral(".dynamic")) {
+ dynamicOffset = eh.offset;
+ dynamicSize = eh.size;
+ }
+ if (dynStrOffset && dynamicOffset)
+ break;
+ }
+
+ if (!dynStrOffset || !dynamicOffset) {
+ m_errorString = QStringLiteral("Not a dynamically linked executable.");
+ return result;
+ }
+
+ const unsigned char *dynamicData = mapper.ustart + dynamicOffset;
+ const unsigned char *dynamicDataEnd = dynamicData + dynamicSize;
+ while (dynamicData < dynamicDataEnd) {
+ const quint32 tag = getWord(dynamicData, m_elfData);
+ if (tag == DT_NULL)
+ break;
+ if (m_elfData.elfclass == Elf_ELFCLASS64)
+ dynamicData += sizeof(quint32); // padding to d_val/d_ptr.
+ if (tag == DT_NEEDED) {
+ const quint32 offset = getWord(dynamicData, m_elfData);
+ if (m_elfData.elfclass == Elf_ELFCLASS64)
+ dynamicData += sizeof(quint32); // past d_ptr.
+ const char *name = mapper.start + dynStrOffset + offset;
+ result.push_back(name);
+ }
+ }
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/shared/winutils/elfreader.h b/src/shared/winutils/elfreader.h
new file mode 100644
index 000000000..65de80ec4
--- /dev/null
+++ b/src/shared/winutils/elfreader.h
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ELFREADER_H
+#define ELFREADER_H
+
+#include <QtCore/QtEndian>
+#include <QtCore/QString>
+#include <QtCore/QVector>
+
+QT_BEGIN_NAMESPACE
+
+enum ElfProgramHeaderType
+{
+ Elf_PT_NULL = 0,
+ Elf_PT_LOAD = 1,
+ Elf_PT_DYNAMIC = 2,
+ Elf_PT_INTERP = 3,
+ Elf_PT_NOTE = 4,
+ Elf_PT_SHLIB = 5,
+ Elf_PT_PHDR = 6,
+ Elf_PT_TLS = 7,
+ Elf_PT_NUM = 8
+};
+
+enum ElfSectionHeaderType
+{
+ Elf_SHT_NULL = 0,
+ Elf_SHT_PROGBITS = 1,
+ Elf_SHT_SYMTAB = 2,
+ Elf_SHT_STRTAB = 3,
+ Elf_SHT_RELA = 4,
+ Elf_SHT_HASH = 5,
+ Elf_SHT_DYNAMIC = 6,
+ Elf_SHT_NOTE = 7,
+ Elf_SHT_NOBITS = 8,
+ Elf_SHT_REL = 9,
+ Elf_SHT_SHLIB = 10,
+ Elf_SHT_DYNSYM = 11,
+ Elf_SHT_INIT_ARRAY = 14,
+ Elf_SHT_FINI_ARRAY = 15,
+ Elf_SHT_PREINIT_ARRAY = 16,
+ Elf_SHT_GROUP = 17,
+ Elf_SHT_SYMTAB_SHNDX = 18
+};
+
+enum ElfEndian
+{
+ Elf_ELFDATANONE = 0,
+ Elf_ELFDATA2LSB = 1,
+ Elf_ELFDATA2MSB = 2,
+ Elf_ELFDATANUM = 3
+};
+
+enum ElfClass
+{
+ Elf_ELFCLASS32 = 1,
+ Elf_ELFCLASS64 = 2
+};
+
+enum ElfType
+{
+ Elf_ET_NONE = 0,
+ Elf_ET_REL = 1,
+ Elf_ET_EXEC = 2,
+ Elf_ET_DYN = 3,
+ Elf_ET_CORE = 4
+};
+
+enum ElfMachine
+{
+ Elf_EM_386 = 3,
+ Elf_EM_ARM = 40,
+ Elf_EM_X86_64 = 62
+};
+
+enum DebugSymbolsType
+{
+ UnknownSymbols = 0, // Unknown.
+ NoSymbols = 1, // No usable symbols.
+ LinkedSymbols = 2, // Link to symols available.
+ BuildIdSymbols = 4, // BuildId available.
+ PlainSymbols = 8, // Ordinary symbols available.
+ FastSymbols = 16 // Dwarf index available.
+};
+
+class ElfSectionHeader
+{
+public:
+ QByteArray name;
+ quint32 index;
+ quint32 type;
+ quint32 flags;
+ quint64 offset;
+ quint64 size;
+ quint64 addr;
+};
+
+class ElfProgramHeader
+{
+public:
+ quint32 name;
+ quint32 type;
+ quint64 offset;
+ quint64 filesz;
+ quint64 memsz;
+};
+
+class ElfData
+{
+public:
+ ElfData() : symbolsType(UnknownSymbols) {}
+ int indexOf(const QByteArray &name) const;
+
+public:
+ ElfEndian endian;
+ ElfType elftype;
+ ElfMachine elfmachine;
+ ElfClass elfclass;
+ quint64 entryPoint;
+ QByteArray debugLink;
+ QByteArray buildId;
+ DebugSymbolsType symbolsType;
+ QVector<ElfSectionHeader> sectionHeaders;
+ QVector<ElfProgramHeader> programHeaders;
+};
+
+class ElfReader
+{
+public:
+ explicit ElfReader(const QString &binary);
+ enum Result { Ok, NotElf, Corrupt };
+
+ ElfData readHeaders();
+ QByteArray readSection(const QByteArray &sectionName);
+ QString errorString() const { return m_errorString; }
+ QByteArray readCoreName(bool *isCore);
+ QList<QByteArray> dependencies();
+
+private:
+ friend class ElfMapper;
+ Result readIt();
+
+ QString m_binary;
+ QString m_errorString;
+ ElfData m_elfData;
+};
+
+QT_END_NAMESPACE
+
+#endif // ELFREADER_H
diff --git a/src/shared/winutils/qmlutils.cpp b/src/shared/winutils/qmlutils.cpp
new file mode 100644
index 000000000..52f044068
--- /dev/null
+++ b/src/shared/winutils/qmlutils.cpp
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmlutils.h"
+#include "utils.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonParseError>
+
+QT_BEGIN_NAMESPACE
+
+bool operator==(const QmlImportScanResult::Module &m1, const QmlImportScanResult::Module &m2)
+{
+ return m1.className.isEmpty() ? m1.name == m2.name : m1.className == m2.className;
+}
+
+// Return install path (cp -r semantics)
+QString QmlImportScanResult::Module::installPath(const QString &root) const
+{
+ QString result = root;
+ const int lastSlashPos = relativePath.lastIndexOf(QLatin1Char('/'));
+ if (lastSlashPos != -1) {
+ result += QLatin1Char('/');
+ result += relativePath.left(lastSlashPos);
+ }
+ return result;
+}
+
+static QString qmlDirectoryRecursion(Platform platform, const QString &path)
+{
+ QDir dir(path);
+ if (!dir.entryList(QStringList(QStringLiteral("*.qml")), QDir::Files, QDir::NoSort).isEmpty())
+ return dir.path();
+ const QFileInfoList &subDirs = dir.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot, QDir::NoSort);
+ for (const QFileInfo &subDirFi : subDirs) {
+ if (!isBuildDirectory(platform, subDirFi.fileName())) {
+ const QString subPath = qmlDirectoryRecursion(platform, subDirFi.absoluteFilePath());
+ if (!subPath.isEmpty())
+ return subPath;
+ }
+ }
+ return QString();
+}
+
+// Find a directory containing QML files in the project
+QString findQmlDirectory(int platform, const QString &startDirectoryName)
+{
+ QDir startDirectory(startDirectoryName);
+ if (isBuildDirectory(Platform(platform), startDirectory.dirName()))
+ startDirectory.cdUp();
+ return qmlDirectoryRecursion(Platform(platform), startDirectory.path());
+}
+
+static void findFileRecursion(const QDir &directory, Platform platform,
+ DebugMatchMode debugMatchMode, QStringList *matches)
+{
+ const QStringList &dlls = findSharedLibraries(directory, platform, debugMatchMode);
+ for (const QString &dll : dlls)
+ matches->append(directory.filePath(dll));
+ const QFileInfoList &subDirs = directory.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
+ for (const QFileInfo &subDirFi : subDirs) {
+ QDir subDirectory(subDirFi.absoluteFilePath());
+ if (subDirectory.isReadable())
+ findFileRecursion(subDirectory, platform, debugMatchMode, matches);
+ }
+}
+
+QmlImportScanResult runQmlImportScanner(const QString &directory, const QString &qmlImportPath,
+ bool usesWidgets, int platform, DebugMatchMode debugMatchMode,
+ QString *errorMessage)
+{
+ bool quickControlsHandled = false;
+ QmlImportScanResult result;
+ QStringList arguments;
+ arguments << QStringLiteral("-importPath") << qmlImportPath << QStringLiteral("-rootPath") << directory;
+ unsigned long exitCode;
+ QByteArray stdOut;
+ QByteArray stdErr;
+ const QString binary = QStringLiteral("qmlimportscanner");
+ if (!runProcess(binary, arguments, QDir::currentPath(), &exitCode, &stdOut, &stdErr, errorMessage))
+ return result;
+ if (exitCode) {
+ *errorMessage = binary + QStringLiteral(" returned ") + QString::number(exitCode)
+ + QStringLiteral(": ") + QString::fromLocal8Bit(stdErr);
+ return result;
+ }
+ QJsonParseError jsonParseError;
+ const QJsonDocument data = QJsonDocument::fromJson(stdOut, &jsonParseError);
+ if (data.isNull() ) {
+ *errorMessage = binary + QStringLiteral(" returned invalid JSON output: ")
+ + jsonParseError.errorString() + QStringLiteral(" :\"")
+ + QString::fromLocal8Bit(stdOut) + QLatin1Char('"');
+ return result;
+ }
+ const QJsonArray array = data.array();
+ const int childCount = array.count();
+ for (int c = 0; c < childCount; ++c) {
+ const QJsonObject object = array.at(c).toObject();
+ if (object.value(QStringLiteral("type")).toString() == QLatin1String("module")) {
+ const QString path = object.value(QStringLiteral("path")).toString();
+ if (!path.isEmpty()) {
+ QmlImportScanResult::Module module;
+ module.name = object.value(QStringLiteral("name")).toString();
+ module.className = object.value(QStringLiteral("classname")).toString();
+ module.sourcePath = path;
+ module.relativePath = object.value(QStringLiteral("relativePath")).toString();
+ result.modules.append(module);
+ findFileRecursion(QDir(path), Platform(platform), debugMatchMode, &result.plugins);
+ // QTBUG-48424, QTBUG-45977: In release mode, qmlimportscanner does not report
+ // the dependency of QtQuick.Controls on QtQuick.PrivateWidgets due to missing files.
+ // Recreate the run-time logic here as best as we can - deploy it if
+ // 1) QtWidgets is used
+ // 2) QtQuick.Controls is used
+ if (!quickControlsHandled && usesWidgets && module.name == QLatin1String("QtQuick.Controls")) {
+ quickControlsHandled = true;
+ QmlImportScanResult::Module privateWidgetsModule;
+ privateWidgetsModule.name = QStringLiteral("QtQuick.PrivateWidgets");
+ privateWidgetsModule.className = QStringLiteral("QtQuick2PrivateWidgetsPlugin");
+ privateWidgetsModule.sourcePath = QFileInfo(path).absolutePath() + QStringLiteral("/PrivateWidgets");
+ privateWidgetsModule.relativePath = QStringLiteral("QtQuick/PrivateWidgets");
+ result.modules.append(privateWidgetsModule);
+ findFileRecursion(QDir(privateWidgetsModule.sourcePath), Platform(platform), debugMatchMode, &result.plugins);
+ }
+ }
+ }
+ }
+ result.ok = true;
+ return result;
+}
+
+void QmlImportScanResult::append(const QmlImportScanResult &other)
+{
+ for (const QmlImportScanResult::Module &module : other.modules) {
+ if (std::find(modules.cbegin(), modules.cend(), module) == modules.cend())
+ modules.append(module);
+ }
+ for (const QString &plugin : other.plugins) {
+ if (!plugins.contains(plugin))
+ plugins.append(plugin);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/shared/winutils/qmlutils.h b/src/shared/winutils/qmlutils.h
new file mode 100644
index 000000000..895c7f1de
--- /dev/null
+++ b/src/shared/winutils/qmlutils.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMLUTILS_H
+#define QMLUTILS_H
+
+#include "utils.h"
+
+#include <QStringList>
+
+QT_BEGIN_NAMESPACE
+
+QString findQmlDirectory(int platform, const QString &startDirectoryName);
+
+struct QmlImportScanResult {
+ struct Module {
+ QString installPath(const QString &root) const;
+
+ QString name;
+ QString className;
+ QString sourcePath;
+ QString relativePath;
+ };
+
+ QmlImportScanResult() : ok(false) {}
+ void append(const QmlImportScanResult &other);
+
+ bool ok;
+ QList<Module> modules;
+ QStringList plugins;
+};
+
+bool operator==(const QmlImportScanResult::Module &m1, const QmlImportScanResult::Module &m2);
+
+QmlImportScanResult runQmlImportScanner(const QString &directory, const QString &qmlImportPath,
+ bool usesWidgets, int platform, DebugMatchMode debugMatchMode,
+ QString *errorMessage);
+
+QT_END_NAMESPACE
+
+#endif // QMLUTILS_H
diff --git a/src/shared/winutils/utils.cpp b/src/shared/winutils/utils.cpp
new file mode 100644
index 000000000..4824a588a
--- /dev/null
+++ b/src/shared/winutils/utils.cpp
@@ -0,0 +1,996 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "utils.h"
+#include "elfreader.h"
+
+#include <QtCore/QString>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTemporaryFile>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QScopedArrayPointer>
+#include <QtCore/QStandardPaths>
+#if defined(Q_OS_WIN)
+# include <QtCore/qt_windows.h>
+# include <shlwapi.h>
+# include <delayimp.h>
+#else // Q_OS_WIN
+# include <sys/wait.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <unistd.h>
+# include <stdlib.h>
+# include <string.h>
+# include <errno.h>
+# include <fcntl.h>
+#endif // !Q_OS_WIN
+
+QT_BEGIN_NAMESPACE
+
+int optVerboseLevel = 1;
+
+bool isBuildDirectory(Platform platform, const QString &dirName)
+{
+ return (platform & WindowsBased) && (dirName == QLatin1String("debug") || dirName == QLatin1String("release"));
+}
+
+// Create a symbolic link by changing to the source directory to make sure the
+// link uses relative paths only (QFile::link() otherwise uses the absolute path).
+bool createSymbolicLink(const QFileInfo &source, const QString &target, QString *errorMessage)
+{
+ const QString oldDirectory = QDir::currentPath();
+ if (!QDir::setCurrent(source.absolutePath())) {
+ *errorMessage = QStringLiteral("Unable to change to directory %1.").arg(QDir::toNativeSeparators(source.absolutePath()));
+ return false;
+ }
+ QFile file(source.fileName());
+ const bool success = file.link(target);
+ QDir::setCurrent(oldDirectory);
+ if (!success) {
+ *errorMessage = QString::fromLatin1("Failed to create symbolic link %1 -> %2: %3")
+ .arg(QDir::toNativeSeparators(source.absoluteFilePath()),
+ QDir::toNativeSeparators(target), file.errorString());
+ return false;
+ }
+ return true;
+}
+
+bool createDirectory(const QString &directory, QString *errorMessage)
+{
+ const QFileInfo fi(directory);
+ if (fi.isDir())
+ return true;
+ if (fi.exists()) {
+ *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.").
+ arg(QDir::toNativeSeparators(directory));
+ return false;
+ }
+ if (optVerboseLevel)
+ std::wcout << "Creating " << QDir::toNativeSeparators(directory) << "...\n";
+ QDir dir;
+ if (!dir.mkpath(directory)) {
+ *errorMessage = QString::fromLatin1("Could not create directory %1.").
+ arg(QDir::toNativeSeparators(directory));
+ return false;
+ }
+ return true;
+}
+
+// Find shared libraries matching debug/Platform in a directory, return relative names.
+QStringList findSharedLibraries(const QDir &directory, Platform platform,
+ DebugMatchMode debugMatchMode,
+ const QString &prefix)
+{
+ QString nameFilter = prefix;
+ if (nameFilter.isEmpty())
+ nameFilter += QLatin1Char('*');
+ if (debugMatchMode == MatchDebug && (platform & WindowsBased))
+ nameFilter += QLatin1Char('d');
+ nameFilter += sharedLibrarySuffix(platform);
+ QStringList result;
+ QString errorMessage;
+ const QFileInfoList &dlls = directory.entryInfoList(QStringList(nameFilter), QDir::Files);
+ for (const QFileInfo &dllFi : dlls) {
+ const QString dllPath = dllFi.absoluteFilePath();
+ bool matches = true;
+ if (debugMatchMode != MatchDebugOrRelease && (platform & WindowsBased)) {
+ bool debugDll;
+ if (readPeExecutable(dllPath, &errorMessage, 0, 0, &debugDll,
+ (platform == WindowsMinGW))) {
+ matches = debugDll == (debugMatchMode == MatchDebug);
+ } else {
+ std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(dllPath)
+ << ": " << errorMessage;
+ }
+ } // Windows
+ if (matches)
+ result += dllFi.fileName();
+ } // for
+ return result;
+}
+
+#ifdef Q_OS_WIN
+QString winErrorMessage(unsigned long error)
+{
+ QString rc = QString::fromLatin1("#%1: ").arg(error);
+ ushort *lpMsgBuf;
+
+ const DWORD len = FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, error, 0, reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, NULL);
+ if (len) {
+ rc = QString::fromUtf16(lpMsgBuf, int(len));
+ LocalFree(lpMsgBuf);
+ } else {
+ rc += QString::fromLatin1("<unknown error>");
+ }
+ return rc;
+}
+
+// Case-Normalize file name via GetShortPathNameW()/GetLongPathNameW()
+QString normalizeFileName(const QString &name)
+{
+ wchar_t shortBuffer[MAX_PATH];
+ const QString nativeFileName = QDir::toNativeSeparators(name);
+ if (!GetShortPathNameW(reinterpret_cast<LPCWSTR>(nativeFileName.utf16()), shortBuffer, MAX_PATH))
+ return name;
+ wchar_t result[MAX_PATH];
+ if (!GetLongPathNameW(shortBuffer, result, MAX_PATH))
+ return name;
+ return QDir::fromNativeSeparators(QString::fromWCharArray(result));
+}
+
+// Find a tool binary in the Windows SDK 8
+QString findSdkTool(const QString &tool)
+{
+ QStringList paths = QString::fromLocal8Bit(qgetenv("PATH")).split(QLatin1Char(';'));
+ const QByteArray sdkDir = qgetenv("WindowsSdkDir");
+ if (!sdkDir.isEmpty())
+ paths.prepend(QDir::cleanPath(QString::fromLocal8Bit(sdkDir)) + QLatin1String("/Tools/x64"));
+ return QStandardPaths::findExecutable(tool, paths);
+}
+
+// runProcess helper: Create a temporary file for stdout/stderr redirection.
+static HANDLE createInheritableTemporaryFile()
+{
+ wchar_t path[MAX_PATH];
+ if (!GetTempPath(MAX_PATH, path))
+ return INVALID_HANDLE_VALUE;
+ wchar_t name[MAX_PATH];
+ if (!GetTempFileName(path, L"temp", 0, name)) // Creates file.
+ return INVALID_HANDLE_VALUE;
+ SECURITY_ATTRIBUTES securityAttributes;
+ ZeroMemory(&securityAttributes, sizeof(securityAttributes));
+ securityAttributes.nLength = sizeof(securityAttributes);
+ securityAttributes.bInheritHandle = TRUE;
+ return CreateFile(name, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
+ TRUNCATE_EXISTING,
+ FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
+}
+
+// runProcess helper: Rewind and read out a temporary file for stdout/stderr.
+static inline void readTemporaryProcessFile(HANDLE handle, QByteArray *result)
+{
+ if (SetFilePointer(handle, 0, 0, FILE_BEGIN) == 0xFFFFFFFF)
+ return;
+ char buf[1024];
+ DWORD bytesRead;
+ while (ReadFile(handle, buf, sizeof(buf), &bytesRead, NULL) && bytesRead)
+ result->append(buf, int(bytesRead));
+ CloseHandle(handle);
+}
+
+static inline void appendToCommandLine(const QString &argument, QString *commandLine)
+{
+ const bool needsQuote = argument.contains(QLatin1Char(' '));
+ if (!commandLine->isEmpty())
+ commandLine->append(QLatin1Char(' '));
+ if (needsQuote)
+ commandLine->append(QLatin1Char('"'));
+ commandLine->append(argument);
+ if (needsQuote)
+ commandLine->append(QLatin1Char('"'));
+}
+
+// runProcess: Run a command line process (replacement for QProcess which
+// does not exist in the bootstrap library).
+bool runProcess(const QString &binary, const QStringList &args,
+ const QString &workingDirectory,
+ unsigned long *exitCode, QByteArray *stdOut, QByteArray *stdErr,
+ QString *errorMessage)
+{
+ if (exitCode)
+ *exitCode = 0;
+
+ STARTUPINFO si;
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+
+ STARTUPINFO myInfo;
+ GetStartupInfo(&myInfo);
+ si.hStdInput = myInfo.hStdInput;
+ si.hStdOutput = myInfo.hStdOutput;
+ si.hStdError = myInfo.hStdError;
+
+ PROCESS_INFORMATION pi;
+ ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
+ const QChar backSlash = QLatin1Char('\\');
+ QString nativeWorkingDir = QDir::toNativeSeparators(workingDirectory.isEmpty() ? QDir::currentPath() : workingDirectory);
+ if (!nativeWorkingDir.endsWith(backSlash))
+ nativeWorkingDir += backSlash;
+
+ if (stdOut) {
+ si.hStdOutput = createInheritableTemporaryFile();
+ if (si.hStdOutput == INVALID_HANDLE_VALUE) {
+ if (errorMessage)
+ *errorMessage = QStringLiteral("Error creating stdout temporary file");
+ return false;
+ }
+ si.dwFlags |= STARTF_USESTDHANDLES;
+ }
+
+ if (stdErr) {
+ si.hStdError = createInheritableTemporaryFile();
+ if (si.hStdError == INVALID_HANDLE_VALUE) {
+ if (errorMessage)
+ *errorMessage = QStringLiteral("Error creating stderr temporary file");
+ return false;
+ }
+ si.dwFlags |= STARTF_USESTDHANDLES;
+ }
+
+ // Create a copy of the command line which CreateProcessW can modify.
+ QString commandLine;
+ appendToCommandLine(binary, &commandLine);
+ for (const QString &a : args)
+ appendToCommandLine(a, &commandLine);
+ if (optVerboseLevel > 1)
+ std::wcout << "Running: " << commandLine << '\n';
+
+ QScopedArrayPointer<wchar_t> commandLineW(new wchar_t[commandLine.size() + 1]);
+ commandLine.toWCharArray(commandLineW.data());
+ commandLineW[commandLine.size()] = 0;
+ if (!CreateProcessW(0, commandLineW.data(), 0, 0, /* InheritHandles */ TRUE, 0, 0,
+ reinterpret_cast<LPCWSTR>(nativeWorkingDir.utf16()), &si, &pi)) {
+ if (stdOut)
+ CloseHandle(si.hStdOutput);
+ if (stdErr)
+ CloseHandle(si.hStdError);
+ if (errorMessage)
+ *errorMessage = QStringLiteral("CreateProcessW failed: ") + winErrorMessage(GetLastError());
+ return false;
+ }
+
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ CloseHandle(pi.hThread);
+ if (exitCode)
+ GetExitCodeProcess(pi.hProcess, exitCode);
+ CloseHandle(pi.hProcess);
+
+ if (stdOut)
+ readTemporaryProcessFile(si.hStdOutput, stdOut);
+ if (stdErr)
+ readTemporaryProcessFile(si.hStdError, stdErr);
+ return true;
+}
+
+#else // Q_OS_WIN
+
+static inline char *encodeFileName(const QString &f)
+{
+ const QByteArray encoded = QFile::encodeName(f);
+ char *result = new char[encoded.size() + 1];
+ strcpy(result, encoded.constData());
+ return result;
+}
+
+static inline char *tempFilePattern()
+{
+ QString path = QDir::tempPath();
+ if (!path.endsWith(QLatin1Char('/')))
+ path += QLatin1Char('/');
+ path += QStringLiteral("tmpXXXXXX");
+ return encodeFileName(path);
+}
+
+static inline QByteArray readOutRedirectFile(int fd)
+{
+ enum { bufSize = 256 };
+
+ QByteArray result;
+ if (!lseek(fd, 0, 0)) {
+ char buf[bufSize];
+ while (true) {
+ const ssize_t rs = read(fd, buf, bufSize);
+ if (rs <= 0)
+ break;
+ result.append(buf, int(rs));
+ }
+ }
+ close(fd);
+ return result;
+}
+
+// runProcess: Run a command line process (replacement for QProcess which
+// does not exist in the bootstrap library).
+bool runProcess(const QString &binary, const QStringList &args,
+ const QString &workingDirectory,
+ unsigned long *exitCode, QByteArray *stdOut, QByteArray *stdErr,
+ QString *errorMessage)
+{
+ QScopedArrayPointer<char> stdOutFileName;
+ QScopedArrayPointer<char> stdErrFileName;
+
+ int stdOutFile = 0;
+ if (stdOut) {
+ stdOutFileName.reset(tempFilePattern());
+ stdOutFile = mkstemp(stdOutFileName.data());
+ if (stdOutFile < 0) {
+ *errorMessage = QStringLiteral("mkstemp() failed: ") + QString::fromLocal8Bit(strerror(errno));
+ return false;
+ }
+ }
+
+ int stdErrFile = 0;
+ if (stdErr) {
+ stdErrFileName.reset(tempFilePattern());
+ stdErrFile = mkstemp(stdErrFileName.data());
+ if (stdErrFile < 0) {
+ *errorMessage = QStringLiteral("mkstemp() failed: ") + QString::fromLocal8Bit(strerror(errno));
+ return false;
+ }
+ }
+
+ const pid_t pID = fork();
+
+ if (pID < 0) {
+ *errorMessage = QStringLiteral("Fork failed: ") + QString::fromLocal8Bit(strerror(errno));
+ return false;
+ }
+
+ if (!pID) { // Child
+ if (stdOut) {
+ dup2(stdOutFile, STDOUT_FILENO);
+ close(stdOutFile);
+ }
+ if (stdErr) {
+ dup2(stdErrFile, STDERR_FILENO);
+ close(stdErrFile);
+ }
+
+ if (!workingDirectory.isEmpty() && !QDir::setCurrent(workingDirectory)) {
+ std::wcerr << "Failed to change working directory to " << workingDirectory << ".\n";
+ ::_exit(-1);
+ }
+
+ char **argv = new char *[args.size() + 2]; // Create argv.
+ char **ap = argv;
+ *ap++ = encodeFileName(binary);
+ for (const QString &a : qAsConst(args))
+ *ap++ = encodeFileName(a);
+ *ap = 0;
+
+ execvp(argv[0], argv);
+ ::_exit(-1);
+ }
+
+ int status;
+ pid_t waitResult;
+
+ do {
+ waitResult = waitpid(pID, &status, 0);
+ } while (waitResult == -1 && errno == EINTR);
+
+ if (stdOut) {
+ *stdOut = readOutRedirectFile(stdOutFile);
+ unlink(stdOutFileName.data());
+ }
+ if (stdErr) {
+ *stdErr = readOutRedirectFile(stdErrFile);
+ unlink(stdErrFileName.data());
+ }
+
+ if (waitResult < 0) {
+ *errorMessage = QStringLiteral("Wait failed: ") + QString::fromLocal8Bit(strerror(errno));
+ return false;
+ }
+ if (!WIFEXITED(status)) {
+ *errorMessage = binary + QStringLiteral(" did not exit cleanly.");
+ return false;
+ }
+ if (exitCode)
+ *exitCode = WEXITSTATUS(status);
+ return true;
+}
+
+#endif // !Q_OS_WIN
+
+// Find a file in the path using ShellAPI. This can be used to locate DLLs which
+// QStandardPaths cannot do.
+QString findInPath(const QString &file)
+{
+#if defined(Q_OS_WIN)
+ if (file.size() < MAX_PATH - 1) {
+ wchar_t buffer[MAX_PATH];
+ file.toWCharArray(buffer);
+ buffer[file.size()] = 0;
+ if (PathFindOnPath(buffer, NULL))
+ return QDir::cleanPath(QString::fromWCharArray(buffer));
+ }
+ return QString();
+#else // Q_OS_WIN
+ return QStandardPaths::findExecutable(file);
+#endif // !Q_OS_WIN
+}
+
+const char *qmakeInfixKey = "QT_INFIX";
+
+QMap<QString, QString> queryQMakeAll(QString *errorMessage);
+
+QMap<QString, QString> queryQMakeAll(QString *errorMessage)
+{
+ QByteArray stdOut;
+ QByteArray stdErr;
+ unsigned long exitCode = 0;
+ const QString binary = QStringLiteral("qmake");
+ if (!runProcess(binary, QStringList(QStringLiteral("-query")), QString(), &exitCode, &stdOut, &stdErr, errorMessage))
+ return QMap<QString, QString>();
+ if (exitCode) {
+ *errorMessage = binary + QStringLiteral(" returns ") + QString::number(exitCode)
+ + QStringLiteral(": ") + QString::fromLocal8Bit(stdErr);
+ return QMap<QString, QString>();
+ }
+ const QString output = QString::fromLocal8Bit(stdOut).trimmed().remove(QLatin1Char('\r'));
+ QMap<QString, QString> result;
+ const int size = output.size();
+ for (int pos = 0; pos < size; ) {
+ const int colonPos = output.indexOf(QLatin1Char(':'), pos);
+ if (colonPos < 0)
+ break;
+ int endPos = output.indexOf(QLatin1Char('\n'), colonPos + 1);
+ if (endPos < 0)
+ endPos = size;
+ const QString key = output.mid(pos, colonPos - pos);
+ const QString value = output.mid(colonPos + 1, endPos - colonPos - 1);
+ result.insert(key, value);
+ pos = endPos + 1;
+ }
+ QFile qconfigPriFile(result.value(QStringLiteral("QT_HOST_DATA")) + QStringLiteral("/mkspecs/qconfig.pri"));
+ if (qconfigPriFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ while (true) {
+ const QByteArray line = qconfigPriFile.readLine();
+ if (line.isEmpty())
+ break;
+ if (line.startsWith("QT_LIBINFIX")) {
+ const int pos = line.indexOf('=');
+ if (pos >= 0) {
+ const QString infix = QString::fromUtf8(line.right(line.size() - pos - 1).trimmed());
+ if (!infix.isEmpty())
+ result.insert(QLatin1String(qmakeInfixKey), infix);
+ }
+ break;
+ }
+ }
+ } else {
+ std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(qconfigPriFile.fileName())
+ << ": " << qconfigPriFile.errorString()<< '\n';
+ }
+ return result;
+}
+
+QString queryQMake(const QString &variable, QString *errorMessage)
+{
+ QByteArray stdOut;
+ QByteArray stdErr;
+ unsigned long exitCode;
+ const QString binary = QStringLiteral("qmake");
+ QStringList args;
+ args << QStringLiteral("-query ") << variable;
+ if (!runProcess(binary, args, QString(), &exitCode, &stdOut, &stdErr, errorMessage))
+ return QString();
+ if (exitCode) {
+ *errorMessage = binary + QStringLiteral(" returns ") + QString::number(exitCode)
+ + QStringLiteral(": ") + QString::fromLocal8Bit(stdErr);
+ return QString();
+ }
+ return QString::fromLocal8Bit(stdOut).trimmed();
+}
+
+// Update a file or directory.
+bool updateFile(const QString &sourceFileName, const QStringList &nameFilters,
+ const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage)
+{
+ const QFileInfo sourceFileInfo(sourceFileName);
+ const QString targetFileName = targetDirectory + QLatin1Char('/') + sourceFileInfo.fileName();
+ if (optVerboseLevel > 1)
+ std::wcout << "Checking " << sourceFileName << ", " << targetFileName<< '\n';
+
+ if (!sourceFileInfo.exists()) {
+ *errorMessage = QString::fromLatin1("%1 does not exist.").arg(QDir::toNativeSeparators(sourceFileName));
+ return false;
+ }
+
+ if (sourceFileInfo.isSymLink()) {
+ *errorMessage = QString::fromLatin1("Symbolic links are not supported (%1).")
+ .arg(QDir::toNativeSeparators(sourceFileName));
+ return false;
+ }
+
+ const QFileInfo targetFileInfo(targetFileName);
+
+ if (sourceFileInfo.isDir()) {
+ if (targetFileInfo.exists()) {
+ if (!targetFileInfo.isDir()) {
+ *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.")
+ .arg(QDir::toNativeSeparators(targetFileName));
+ return false;
+ } // Not a directory.
+ } else { // exists.
+ QDir d(targetDirectory);
+ if (optVerboseLevel)
+ std::wcout << "Creating " << QDir::toNativeSeparators(targetFileName) << ".\n";
+ if (!(flags & SkipUpdateFile) && !d.mkdir(sourceFileInfo.fileName())) {
+ *errorMessage = QString::fromLatin1("Cannot create directory %1 under %2.")
+ .arg(sourceFileInfo.fileName(), QDir::toNativeSeparators(targetDirectory));
+ return false;
+ }
+ }
+ // Recurse into directory
+ QDir dir(sourceFileName);
+ const QFileInfoList allEntries = dir.entryInfoList(nameFilters, QDir::Files)
+ + dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+ for (const QFileInfo &entryFi : allEntries) {
+ if (!updateFile(entryFi.absoluteFilePath(), nameFilters, targetFileName, flags, json, errorMessage))
+ return false;
+ }
+ return true;
+ } // Source is directory.
+
+ if (targetFileInfo.exists()) {
+ if (!(flags & ForceUpdateFile)
+ && targetFileInfo.lastModified() >= sourceFileInfo.lastModified()) {
+ if (optVerboseLevel)
+ std::wcout << sourceFileInfo.fileName() << " is up to date.\n";
+ if (json)
+ json->addFile(sourceFileName, targetDirectory);
+ return true;
+ }
+ QFile targetFile(targetFileName);
+ if (!(flags & SkipUpdateFile) && !targetFile.remove()) {
+ *errorMessage = QString::fromLatin1("Cannot remove existing file %1: %2")
+ .arg(QDir::toNativeSeparators(targetFileName), targetFile.errorString());
+ return false;
+ }
+ } // target exists
+ QFile file(sourceFileName);
+ if (optVerboseLevel)
+ std::wcout << "Updating " << sourceFileInfo.fileName() << ".\n";
+ if (!(flags & SkipUpdateFile) && !file.copy(targetFileName)) {
+ *errorMessage = QString::fromLatin1("Cannot copy %1 to %2: %3")
+ .arg(QDir::toNativeSeparators(sourceFileName),
+ QDir::toNativeSeparators(targetFileName),
+ file.errorString());
+ return false;
+ }
+ if (json)
+ json->addFile(sourceFileName, targetDirectory);
+ return true;
+}
+
+bool readElfExecutable(const QString &elfExecutableFileName, QString *errorMessage,
+ QStringList *dependentLibraries, unsigned *wordSize,
+ bool *isDebug)
+{
+ ElfReader elfReader(elfExecutableFileName);
+ const ElfData data = elfReader.readHeaders();
+ if (data.sectionHeaders.isEmpty()) {
+ *errorMessage = QStringLiteral("Unable to read ELF binary \"")
+ + QDir::toNativeSeparators(elfExecutableFileName) + QStringLiteral("\": ")
+ + elfReader.errorString();
+ return false;
+ }
+ if (wordSize)
+ *wordSize = data.elfclass == Elf_ELFCLASS64 ? 64 : 32;
+ if (dependentLibraries) {
+ dependentLibraries->clear();
+ const QList<QByteArray> libs = elfReader.dependencies();
+ if (libs.isEmpty()) {
+ *errorMessage = QStringLiteral("Unable to read dependenices of ELF binary \"")
+ + QDir::toNativeSeparators(elfExecutableFileName) + QStringLiteral("\": ")
+ + elfReader.errorString();
+ return false;
+ }
+ for (const QByteArray &l : libs)
+ dependentLibraries->push_back(QString::fromLocal8Bit(l));
+ }
+ if (isDebug)
+ *isDebug = data.symbolsType != UnknownSymbols && data.symbolsType != NoSymbols;
+ return true;
+}
+
+#ifdef Q_OS_WIN
+
+static inline QString stringFromRvaPtr(const void *rvaPtr)
+{
+ return QString::fromLocal8Bit(static_cast<const char *>(rvaPtr));
+}
+
+// Helper for reading out PE executable files: Find a section header for an RVA
+// (IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32).
+template <class ImageNtHeader>
+const IMAGE_SECTION_HEADER *findSectionHeader(DWORD rva, const ImageNtHeader *nTHeader)
+{
+ const IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(nTHeader);
+ const IMAGE_SECTION_HEADER *sectionEnd = section + nTHeader->FileHeader.NumberOfSections;
+ for ( ; section < sectionEnd; ++section)
+ if (rva >= section->VirtualAddress && rva < (section->VirtualAddress + section->Misc.VirtualSize))
+ return section;
+ return 0;
+}
+
+// Helper for reading out PE executable files: convert RVA to pointer (IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32).
+template <class ImageNtHeader>
+inline const void *rvaToPtr(DWORD rva, const ImageNtHeader *nTHeader, const void *imageBase)
+{
+ const IMAGE_SECTION_HEADER *sectionHdr = findSectionHeader(rva, nTHeader);
+ if (!sectionHdr)
+ return 0;
+ const DWORD delta = sectionHdr->VirtualAddress - sectionHdr->PointerToRawData;
+ return static_cast<const char *>(imageBase) + rva - delta;
+}
+
+// Helper for reading out PE executable files: return word size of a IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32
+template <class ImageNtHeader>
+inline unsigned ntHeaderWordSize(const ImageNtHeader *header)
+{
+ // defines IMAGE_NT_OPTIONAL_HDR32_MAGIC, IMAGE_NT_OPTIONAL_HDR64_MAGIC
+ enum { imageNtOptionlHeader32Magic = 0x10b, imageNtOptionlHeader64Magic = 0x20b };
+ if (header->OptionalHeader.Magic == imageNtOptionlHeader32Magic)
+ return 32;
+ if (header->OptionalHeader.Magic == imageNtOptionlHeader64Magic)
+ return 64;
+ return 0;
+}
+
+// Helper for reading out PE executable files: Retrieve the NT image header of an
+// executable via the legacy DOS header.
+static IMAGE_NT_HEADERS *getNtHeader(void *fileMemory, QString *errorMessage)
+{
+ IMAGE_DOS_HEADER *dosHeader = static_cast<PIMAGE_DOS_HEADER>(fileMemory);
+ // Check DOS header consistency
+ if (IsBadReadPtr(dosHeader, sizeof(IMAGE_DOS_HEADER))
+ || dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
+ *errorMessage = QString::fromLatin1("DOS header check failed.");
+ return 0;
+ }
+ // Retrieve NT header
+ char *ntHeaderC = static_cast<char *>(fileMemory) + dosHeader->e_lfanew;
+ IMAGE_NT_HEADERS *ntHeaders = reinterpret_cast<IMAGE_NT_HEADERS *>(ntHeaderC);
+ // check NT header consistency
+ if (IsBadReadPtr(ntHeaders, sizeof(ntHeaders->Signature))
+ || ntHeaders->Signature != IMAGE_NT_SIGNATURE
+ || IsBadReadPtr(&ntHeaders->FileHeader, sizeof(IMAGE_FILE_HEADER))) {
+ *errorMessage = QString::fromLatin1("NT header check failed.");
+ return 0;
+ }
+ // Check magic
+ if (!ntHeaderWordSize(ntHeaders)) {
+ *errorMessage = QString::fromLatin1("NT header check failed; magic %1 is invalid.").
+ arg(ntHeaders->OptionalHeader.Magic);
+ return 0;
+ }
+ // Check section headers
+ IMAGE_SECTION_HEADER *sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders);
+ if (IsBadReadPtr(sectionHeaders, ntHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER))) {
+ *errorMessage = QString::fromLatin1("NT header section header check failed.");
+ return 0;
+ }
+ return ntHeaders;
+}
+
+// Helper for reading out PE executable files: Read out import sections from
+// IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32.
+template <class ImageNtHeader>
+inline QStringList readImportSections(const ImageNtHeader *ntHeaders, const void *base, QString *errorMessage)
+{
+ // Get import directory entry RVA and read out
+ const DWORD importsStartRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+ if (!importsStartRVA) {
+ *errorMessage = QString::fromLatin1("Failed to find IMAGE_DIRECTORY_ENTRY_IMPORT entry.");
+ return QStringList();
+ }
+ const IMAGE_IMPORT_DESCRIPTOR *importDesc = static_cast<const IMAGE_IMPORT_DESCRIPTOR *>(rvaToPtr(importsStartRVA, ntHeaders, base));
+ if (!importDesc) {
+ *errorMessage = QString::fromLatin1("Failed to find IMAGE_IMPORT_DESCRIPTOR entry.");
+ return QStringList();
+ }
+ QStringList result;
+ for ( ; importDesc->Name; ++importDesc)
+ result.push_back(stringFromRvaPtr(rvaToPtr(importDesc->Name, ntHeaders, base)));
+
+ // Read delay-loaded DLLs, see http://msdn.microsoft.com/en-us/magazine/cc301808.aspx .
+ // Check on grAttr bit 1 whether this is the format using RVA's > VS 6
+ if (const DWORD delayedImportsStartRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress) {
+ const ImgDelayDescr *delayedImportDesc = static_cast<const ImgDelayDescr *>(rvaToPtr(delayedImportsStartRVA, ntHeaders, base));
+ for ( ; delayedImportDesc->rvaDLLName && (delayedImportDesc->grAttrs & 1); ++delayedImportDesc)
+ result.push_back(stringFromRvaPtr(rvaToPtr(delayedImportDesc->rvaDLLName, ntHeaders, base)));
+ }
+
+ return result;
+}
+
+// Check for MSCV runtime (MSVCP90D.dll/MSVCP90.dll, MSVCP120D.dll/MSVCP120.dll,
+// VCRUNTIME140D.DLL/VCRUNTIME140.DLL (VS2015) or msvcp120d_app.dll/msvcp120_app.dll).
+enum MsvcDebugRuntimeResult { MsvcDebugRuntime, MsvcReleaseRuntime, NoMsvcRuntime };
+
+static inline MsvcDebugRuntimeResult checkMsvcDebugRuntime(const QStringList &dependentLibraries)
+{
+ for (const QString &lib : dependentLibraries) {
+ int pos = 0;
+ if (lib.startsWith(QLatin1String("MSVCR"), Qt::CaseInsensitive)
+ || lib.startsWith(QLatin1String("MSVCP"), Qt::CaseInsensitive)) {
+ pos = 5;
+ } else if (lib.startsWith(QLatin1String("VCRUNTIME"), Qt::CaseInsensitive)) {
+ pos = 9;
+ }
+ if (pos && lib.at(pos).isDigit()) {
+ for (++pos; lib.at(pos).isDigit(); ++pos)
+ ;
+ return lib.at(pos).toLower() == QLatin1Char('d')
+ ? MsvcDebugRuntime : MsvcReleaseRuntime;
+ }
+ }
+ return NoMsvcRuntime;
+}
+
+template <class ImageNtHeader>
+inline void determineDebugAndDependentLibs(const ImageNtHeader *nth, const void *fileMemory,
+ bool isMinGW,
+ QStringList *dependentLibrariesIn,
+ bool *isDebugIn, QString *errorMessage)
+{
+ const bool hasDebugEntry = nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
+ QStringList dependentLibraries;
+ if (dependentLibrariesIn || (isDebugIn && hasDebugEntry && !isMinGW))
+ dependentLibraries = readImportSections(nth, fileMemory, errorMessage);
+
+ if (dependentLibrariesIn)
+ *dependentLibrariesIn = dependentLibraries;
+ if (isDebugIn) {
+ if (isMinGW) {
+ // Use logic that's used e.g. in objdump / pfd library
+ *isDebugIn = !(nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED);
+ } else {
+ // When an MSVC debug entry is present, check whether the debug runtime
+ // is actually used to detect -release / -force-debug-info builds.
+ *isDebugIn = hasDebugEntry && checkMsvcDebugRuntime(dependentLibraries) != MsvcReleaseRuntime;
+ }
+ }
+}
+
+// Read a PE executable and determine dependent libraries, word size
+// and debug flags.
+bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage,
+ QStringList *dependentLibrariesIn, unsigned *wordSizeIn,
+ bool *isDebugIn, bool isMinGW)
+{
+ bool result = false;
+ HANDLE hFile = NULL;
+ HANDLE hFileMap = NULL;
+ void *fileMemory = 0;
+
+ if (dependentLibrariesIn)
+ dependentLibrariesIn->clear();
+ if (wordSizeIn)
+ *wordSizeIn = 0;
+ if (isDebugIn)
+ *isDebugIn = false;
+
+ do {
+ // Create a memory mapping of the file
+ hFile = CreateFile(reinterpret_cast<const WCHAR*>(peExecutableFileName.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE || hFile == NULL) {
+ *errorMessage = QString::fromLatin1("Cannot open '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
+ break;
+ }
+
+ hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (hFileMap == NULL) {
+ *errorMessage = QString::fromLatin1("Cannot create file mapping of '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
+ break;
+ }
+
+ fileMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
+ if (!fileMemory) {
+ *errorMessage = QString::fromLatin1("Cannot map '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
+ break;
+ }
+
+ const IMAGE_NT_HEADERS *ntHeaders = getNtHeader(fileMemory, errorMessage);
+ if (!ntHeaders)
+ break;
+
+ const unsigned wordSize = ntHeaderWordSize(ntHeaders);
+ if (wordSizeIn)
+ *wordSizeIn = wordSize;
+ if (wordSize == 32) {
+ determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS32 *>(ntHeaders),
+ fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage);
+ } else {
+ determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS64 *>(ntHeaders),
+ fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage);
+ }
+
+ result = true;
+ if (optVerboseLevel > 1) {
+ std::wcout << __FUNCTION__ << ": " << QDir::toNativeSeparators(peExecutableFileName)
+ << ' ' << wordSize << " bit";
+ if (isMinGW)
+ std::wcout << ", MinGW";
+ if (dependentLibrariesIn) {
+ std::wcout << ", dependent libraries: ";
+ if (optVerboseLevel > 2)
+ std::wcout << dependentLibrariesIn->join(QLatin1Char(' '));
+ else
+ std::wcout << dependentLibrariesIn->size();
+ }
+ if (isDebugIn)
+ std::wcout << (*isDebugIn ? ", debug" : ", release");
+ std::wcout << '\n';
+ }
+ } while (false);
+
+ if (fileMemory)
+ UnmapViewOfFile(fileMemory);
+
+ if (hFileMap != NULL)
+ CloseHandle(hFileMap);
+
+ if (hFile != NULL && hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hFile);
+
+ return result;
+}
+
+QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wordSize)
+{
+ const QString prefix = QStringLiteral("D3Dcompiler_");
+ const QString suffix = QLatin1String(windowsSharedLibrarySuffix);
+ // Get the DLL from Kit 8.0 onwards
+ const QString kitDir = QString::fromLocal8Bit(qgetenv("WindowsSdkDir"));
+ if (!kitDir.isEmpty()) {
+ QString redistDirPath = QDir::cleanPath(kitDir) + QStringLiteral("/Redist/D3D/");
+ if (platform & ArmBased) {
+ redistDirPath += QStringLiteral("arm");
+ } else {
+ redistDirPath += wordSize == 32 ? QStringLiteral("x86") : QStringLiteral("x64");
+ }
+ QDir redistDir(redistDirPath);
+ if (redistDir.exists()) {
+ const QFileInfoList files = redistDir.entryInfoList(QStringList(prefix + QLatin1Char('*') + suffix), QDir::Files);
+ if (!files.isEmpty())
+ return files.front().absoluteFilePath();
+ }
+ }
+ QStringList candidateVersions;
+ for (int i = 47 ; i >= 40 ; --i)
+ candidateVersions.append(prefix + QString::number(i) + suffix);
+ // Check the bin directory of the Qt SDK (in case it is shadowed by the
+ // Windows system directory in PATH).
+ for (const QString &candidate : qAsConst(candidateVersions)) {
+ const QFileInfo fi(qtBinDir + QLatin1Char('/') + candidate);
+ if (fi.isFile())
+ return fi.absoluteFilePath();
+ }
+ // Find the latest D3D compiler DLL in path (Windows 8.1 has d3dcompiler_47).
+ if (platform & IntelBased) {
+ QString errorMessage;
+ unsigned detectedWordSize;
+ for (const QString &candidate : qAsConst(candidateVersions)) {
+ const QString dll = findInPath(candidate);
+ if (!dll.isEmpty()
+ && readPeExecutable(dll, &errorMessage, 0, &detectedWordSize, 0)
+ && detectedWordSize == wordSize) {
+ return dll;
+ }
+ }
+ }
+ return QString();
+}
+
+#else // Q_OS_WIN
+
+bool readPeExecutable(const QString &, QString *errorMessage,
+ QStringList *, unsigned *, bool *, bool)
+{
+ *errorMessage = QStringLiteral("Not implemented.");
+ return false;
+}
+
+QString findD3dCompiler(Platform, const QString &, unsigned)
+{
+ return QString();
+}
+
+#endif // !Q_OS_WIN
+
+// Search for "qt_prfxpath=xxxx" in \a path, and replace it with "qt_prfxpath=."
+bool patchQtCore(const QString &path, QString *errorMessage)
+{
+ if (optVerboseLevel)
+ std::wcout << "Patching " << QFileInfo(path).fileName() << "...\n";
+
+ QFile file(path);
+ if (!file.open(QIODevice::ReadWrite)) {
+ *errorMessage = QString::fromLatin1("Unable to patch %1: %2").arg(
+ QDir::toNativeSeparators(path), file.errorString());
+ return false;
+ }
+ QByteArray content = file.readAll();
+
+ if (content.isEmpty()) {
+ *errorMessage = QString::fromLatin1("Unable to patch %1: Could not read file content").arg(
+ QDir::toNativeSeparators(path));
+ return false;
+ }
+
+ QByteArray prfxpath("qt_prfxpath=");
+ int startPos = content.indexOf(prfxpath);
+ if (startPos == -1) {
+ *errorMessage = QString::fromLatin1(
+ "Unable to patch %1: Could not locate pattern \"qt_prfxpath=\"").arg(
+ QDir::toNativeSeparators(path));
+ return false;
+ }
+ startPos += prfxpath.length();
+ int endPos = content.indexOf(char(0), startPos);
+ if (endPos == -1) {
+ *errorMessage = QString::fromLatin1("Unable to patch %1: Internal error").arg(
+ QDir::toNativeSeparators(path));
+ return false;
+ }
+
+ QByteArray replacement = QByteArray(endPos - startPos, char(0));
+ replacement[0] = '.';
+ content.replace(startPos, endPos - startPos, replacement);
+
+ if (!file.seek(0)
+ || (file.write(content) != content.size())) {
+ *errorMessage = QString::fromLatin1("Unable to patch %1: Could not write to file").arg(
+ QDir::toNativeSeparators(path));
+ return false;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/shared/winutils/utils.h b/src/shared/winutils/utils.h
new file mode 100644
index 000000000..cadcb93b7
--- /dev/null
+++ b/src/shared/winutils/utils.h
@@ -0,0 +1,387 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <QStringList>
+#include <QMap>
+#include <QtCore/QFile>
+#include <QtCore/QDir>
+#include <QtCore/QDateTime>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonDocument>
+
+#include <iostream>
+
+QT_BEGIN_NAMESPACE
+
+enum PlatformFlag {
+ WindowsBased = 0x1000,
+ UnixBased = 0x2000,
+ IntelBased = 0x4000,
+ ArmBased = 0x8000,
+ MinGW = 0x10000
+};
+
+enum Platform {
+ Windows = WindowsBased + IntelBased,
+ WindowsMinGW = WindowsBased + IntelBased + MinGW,
+ WinRtIntel = WindowsBased + IntelBased + 1,
+ WinRtArm = WindowsBased + ArmBased + 2,
+ WinCEIntel = WindowsBased + IntelBased + 5,
+ WinCEArm = WindowsBased + ArmBased + 6,
+ Unix = UnixBased,
+ UnknownPlatform
+};
+
+enum ListOption {
+ ListNone = 0,
+ ListSource,
+ ListTarget,
+ ListRelative,
+ ListMapping
+};
+
+inline std::wostream &operator<<(std::wostream &str, const QString &s)
+{
+#ifdef Q_OS_WIN
+ str << reinterpret_cast<const wchar_t *>(s.utf16());
+#else
+ str << s.toStdWString();
+#endif
+ return str;
+}
+
+// Container class for JSON output
+class JsonOutput
+{
+ typedef QPair<QString, QString> SourceTargetMapping;
+ typedef QList<SourceTargetMapping> SourceTargetMappings;
+
+public:
+ void addFile(const QString &source, const QString &target)
+ {
+ m_files.append(SourceTargetMapping(source, target));
+ }
+
+ void removeTargetDirectory(const QString &targetDirectory)
+ {
+ for (int i = m_files.size() - 1; i >= 0; --i) {
+ if (m_files.at(i).second == targetDirectory)
+ m_files.removeAt(i);
+ }
+ }
+
+ QByteArray toJson() const
+ {
+ QJsonObject document;
+ QJsonArray files;
+ for (const SourceTargetMapping &mapping : m_files) {
+ QJsonObject object;
+ object.insert(QStringLiteral("source"), QDir::toNativeSeparators(mapping.first));
+ object.insert(QStringLiteral("target"), QDir::toNativeSeparators(mapping.second));
+ files.append(object);
+ }
+ document.insert(QStringLiteral("files"), files);
+ return QJsonDocument(document).toJson();
+ }
+ QByteArray toList(ListOption option, const QDir &base) const
+ {
+ QByteArray list;
+ for (const SourceTargetMapping &mapping : m_files) {
+ const QString source = QDir::toNativeSeparators(mapping.first);
+ const QString fileName = QFileInfo(mapping.first).fileName();
+ const QString target = QDir::toNativeSeparators(mapping.second) + QDir::separator() + fileName;
+ switch (option) {
+ case ListNone:
+ break;
+ case ListSource:
+ list += source.toUtf8() + '\n';
+ break;
+ case ListTarget:
+ list += target.toUtf8() + '\n';
+ break;
+ case ListRelative:
+ list += QDir::toNativeSeparators(base.relativeFilePath(target)).toUtf8() + '\n';
+ break;
+ case ListMapping:
+ list += '"' + source.toUtf8() + "\" \"" + QDir::toNativeSeparators(base.relativeFilePath(target)).toUtf8() + "\"\n";
+ break;
+ }
+ }
+ return list;
+ }
+private:
+ SourceTargetMappings m_files;
+};
+
+#ifdef Q_OS_WIN
+QString normalizeFileName(const QString &name);
+QString winErrorMessage(unsigned long error);
+QString findSdkTool(const QString &tool);
+#else // !Q_OS_WIN
+inline QString normalizeFileName(const QString &name) { return name; }
+#endif // !Q_OS_WIN
+
+static const char windowsSharedLibrarySuffix[] = ".dll";
+static const char unixSharedLibrarySuffix[] = ".so";
+
+inline QString sharedLibrarySuffix(Platform platform) { return QLatin1String((platform & WindowsBased) ? windowsSharedLibrarySuffix : unixSharedLibrarySuffix); }
+bool isBuildDirectory(Platform platform, const QString &dirName);
+
+bool createSymbolicLink(const QFileInfo &source, const QString &target, QString *errorMessage);
+bool createDirectory(const QString &directory, QString *errorMessage);
+QString findInPath(const QString &file);
+
+extern const char *qmakeInfixKey; // Fake key containing the libinfix
+
+QMap<QString, QString> queryQMakeAll(QString *errorMessage);
+QString queryQMake(const QString &variable, QString *errorMessage);
+
+enum DebugMatchMode {
+ MatchDebug,
+ MatchRelease,
+ MatchDebugOrRelease
+};
+
+QStringList findSharedLibraries(const QDir &directory, Platform platform,
+ DebugMatchMode debugMatchMode,
+ const QString &prefix = QString());
+
+bool updateFile(const QString &sourceFileName, const QStringList &nameFilters,
+ const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage);
+bool runProcess(const QString &binary, const QStringList &args,
+ const QString &workingDirectory = QString(),
+ unsigned long *exitCode = 0, QByteArray *stdOut = 0, QByteArray *stdErr = 0,
+ QString *errorMessage = 0);
+
+bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage,
+ QStringList *dependentLibraries = 0, unsigned *wordSize = 0,
+ bool *isDebug = 0, bool isMinGW = false);
+bool readElfExecutable(const QString &elfExecutableFileName, QString *errorMessage,
+ QStringList *dependentLibraries = 0, unsigned *wordSize = 0,
+ bool *isDebug = 0);
+
+inline bool readExecutable(const QString &executableFileName, Platform platform,
+ QString *errorMessage, QStringList *dependentLibraries = 0,
+ unsigned *wordSize = 0, bool *isDebug = 0)
+{
+ return platform == Unix ?
+ readElfExecutable(executableFileName, errorMessage, dependentLibraries, wordSize, isDebug) :
+ readPeExecutable(executableFileName, errorMessage, dependentLibraries, wordSize, isDebug,
+ (platform == WindowsMinGW));
+}
+
+// Return dependent modules of executable files.
+
+inline QStringList findDependentLibraries(const QString &executableFileName, Platform platform, QString *errorMessage)
+{
+ QStringList result;
+ readExecutable(executableFileName, platform, errorMessage, &result);
+ return result;
+}
+
+QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wordSize);
+
+bool patchQtCore(const QString &path, QString *errorMessage);
+
+extern int optVerboseLevel;
+
+// Recursively update a file or directory, matching DirectoryFileEntryFunction against the QDir
+// to obtain the files.
+enum UpdateFileFlag {
+ ForceUpdateFile = 0x1,
+ SkipUpdateFile = 0x2,
+ RemoveEmptyQmlDirectories = 0x4,
+ SkipQmlDesignerSpecificsDirectories = 0x8
+};
+
+template <class DirectoryFileEntryFunction>
+bool updateFile(const QString &sourceFileName,
+ DirectoryFileEntryFunction directoryFileEntryFunction,
+ const QString &targetDirectory,
+ unsigned flags,
+ JsonOutput *json,
+ QString *errorMessage)
+{
+ const QFileInfo sourceFileInfo(sourceFileName);
+ const QString targetFileName = targetDirectory + QLatin1Char('/') + sourceFileInfo.fileName();
+ if (optVerboseLevel > 1)
+ std::wcout << "Checking " << sourceFileName << ", " << targetFileName << '\n';
+
+ if (!sourceFileInfo.exists()) {
+ *errorMessage = QString::fromLatin1("%1 does not exist.").arg(QDir::toNativeSeparators(sourceFileName));
+ return false;
+ }
+
+ const QFileInfo targetFileInfo(targetFileName);
+
+ if (sourceFileInfo.isSymLink()) {
+ const QString sourcePath = sourceFileInfo.symLinkTarget();
+ const QString relativeSource = QDir(sourceFileInfo.absolutePath()).relativeFilePath(sourcePath);
+ if (relativeSource.contains(QLatin1Char('/'))) {
+ *errorMessage = QString::fromLatin1("Symbolic links across directories are not supported (%1).")
+ .arg(QDir::toNativeSeparators(sourceFileName));
+ return false;
+ }
+
+ // Update the linked-to file
+ if (!updateFile(sourcePath, directoryFileEntryFunction, targetDirectory, flags, json, errorMessage))
+ return false;
+
+ if (targetFileInfo.exists()) {
+ if (!targetFileInfo.isSymLink()) {
+ *errorMessage = QString::fromLatin1("%1 already exists and is not a symbolic link.")
+ .arg(QDir::toNativeSeparators(targetFileName));
+ return false;
+ } // Not a symlink
+ const QString relativeTarget = QDir(targetFileInfo.absolutePath()).relativeFilePath(targetFileInfo.symLinkTarget());
+ if (relativeSource == relativeTarget) // Exists and points to same entry: happy.
+ return true;
+ QFile existingTargetFile(targetFileName);
+ if (!(flags & SkipUpdateFile) && !existingTargetFile.remove()) {
+ *errorMessage = QString::fromLatin1("Cannot remove existing symbolic link %1: %2")
+ .arg(QDir::toNativeSeparators(targetFileName), existingTargetFile.errorString());
+ return false;
+ }
+ } // target symbolic link exists
+ return createSymbolicLink(QFileInfo(targetDirectory + QLatin1Char('/') + relativeSource), sourceFileInfo.fileName(), errorMessage);
+ } // Source is symbolic link
+
+ if (sourceFileInfo.isDir()) {
+ if ((flags & SkipQmlDesignerSpecificsDirectories) && sourceFileInfo.fileName() == QLatin1String("designer")) {
+ if (optVerboseLevel)
+ std::wcout << "Skipping " << QDir::toNativeSeparators(sourceFileName) << ".\n";
+ return true;
+ }
+ bool created = false;
+ if (targetFileInfo.exists()) {
+ if (!targetFileInfo.isDir()) {
+ *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.")
+ .arg(QDir::toNativeSeparators(targetFileName));
+ return false;
+ } // Not a directory.
+ } else { // exists.
+ QDir d(targetDirectory);
+ if (optVerboseLevel)
+ std::wcout << "Creating " << targetFileName << ".\n";
+ if (!(flags & SkipUpdateFile)) {
+ created = d.mkdir(sourceFileInfo.fileName());
+ if (!created) {
+ *errorMessage = QString::fromLatin1("Cannot create directory %1 under %2.")
+ .arg(sourceFileInfo.fileName(), QDir::toNativeSeparators(targetDirectory));
+ return false;
+ }
+ }
+ }
+ // Recurse into directory
+ QDir dir(sourceFileName);
+
+ const QStringList allEntries = directoryFileEntryFunction(dir) + dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ for (const QString &entry : allEntries)
+ if (!updateFile(sourceFileName + QLatin1Char('/') + entry, directoryFileEntryFunction, targetFileName, flags, json, errorMessage))
+ return false;
+ // Remove empty directories, for example QML import folders for which the filter did not match.
+ if (created && (flags & RemoveEmptyQmlDirectories)) {
+ QDir d(targetFileName);
+ const QStringList entries = d.entryList(QStringList(), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
+ if (entries.isEmpty() || (entries.size() == 1 && entries.first() == QLatin1String("qmldir"))) {
+ if (!d.removeRecursively()) {
+ *errorMessage = QString::fromLatin1("Cannot remove empty directory %1.")
+ .arg(QDir::toNativeSeparators(targetFileName));
+ return false;
+ }
+ if (json)
+ json->removeTargetDirectory(targetFileName);
+ }
+ }
+ return true;
+ } // Source is directory.
+
+ if (targetFileInfo.exists()) {
+ if (!(flags & ForceUpdateFile)
+ && targetFileInfo.lastModified() >= sourceFileInfo.lastModified()) {
+ if (optVerboseLevel)
+ std::wcout << sourceFileInfo.fileName() << " is up to date.\n";
+ if (json)
+ json->addFile(sourceFileName, targetDirectory);
+ return true;
+ }
+ QFile targetFile(targetFileName);
+ if (!(flags & SkipUpdateFile) && !targetFile.remove()) {
+ *errorMessage = QString::fromLatin1("Cannot remove existing file %1: %2")
+ .arg(QDir::toNativeSeparators(targetFileName), targetFile.errorString());
+ return false;
+ }
+ } // target exists
+ QFile file(sourceFileName);
+ if (optVerboseLevel)
+ std::wcout << "Updating " << sourceFileInfo.fileName() << ".\n";
+ if (!(flags & SkipUpdateFile)) {
+ if (!file.copy(targetFileName)) {
+ *errorMessage = QString::fromLatin1("Cannot copy %1 to %2: %3")
+ .arg(QDir::toNativeSeparators(sourceFileName),
+ QDir::toNativeSeparators(targetFileName),
+ file.errorString());
+ return false;
+ }
+ if (!(file.permissions() & QFile::WriteUser)) { // QTBUG-40152, clear inherited read-only attribute
+ QFile targetFile(targetFileName);
+ if (!targetFile.setPermissions(targetFile.permissions() | QFile::WriteUser)) {
+ *errorMessage = QString::fromLatin1("Cannot set write permission on %1: %2")
+ .arg(QDir::toNativeSeparators(targetFileName), file.errorString());
+ return false;
+ }
+ } // Check permissions
+ } // !SkipUpdateFile
+ if (json)
+ json->addFile(sourceFileName, targetDirectory);
+ return true;
+}
+
+// Base class to filter files by name filters functions to be passed to updateFile().
+class NameFilterFileEntryFunction {
+public:
+ explicit NameFilterFileEntryFunction(const QStringList &nameFilters) : m_nameFilters(nameFilters) {}
+ QStringList operator()(const QDir &dir) const { return dir.entryList(m_nameFilters, QDir::Files); }
+
+private:
+ const QStringList m_nameFilters;
+};
+
+// Convenience for all files.
+inline bool updateFile(const QString &sourceFileName, const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage)
+{
+ return updateFile(sourceFileName, NameFilterFileEntryFunction(QStringList()), targetDirectory, flags, json, errorMessage);
+}
+
+QT_END_NAMESPACE
+
+#endif // UTILS_H
diff --git a/src/shared/winutils/winutils.pri b/src/shared/winutils/winutils.pri
new file mode 100644
index 000000000..df22781eb
--- /dev/null
+++ b/src/shared/winutils/winutils.pri
@@ -0,0 +1,8 @@
+INCLUDEPATH += $$PWD
+HEADERS += $$PWD/elfreader.h \
+ $$PWD/qmlutils.h \
+ $$PWD/utils.h
+
+SOURCES += $$PWD/elfreader.cpp \
+ $$PWD/utils.cpp \
+ $$PWD/qmlutils.cpp