summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@digia.com>2013-08-26 16:09:06 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-30 16:06:39 +0200
commitc5262bb93a040bd8519c70a8f9adc08549bb49d4 (patch)
tree536f1487f4e9c6b122a10ea6c1c6bf326d22ba53
parent33d9822459a654bf72078b88f6b4466cfcdd609c (diff)
downloadqttools-c5262bb93a040bd8519c70a8f9adc08549bb49d4.tar.gz
Long live windeployqt!
Add windeployqt for deploying Windows/Windows Runtime applications. Change-Id: Ia6ca4af13a93fdc75ef6fe4f794cbe228533e85f Reviewed-by: Oliver Wolff <oliver.wolff@digia.com> Reviewed-by: Maurice Kalinowski <maurice.kalinowski@digia.com>
-rw-r--r--src/src.pro2
-rw-r--r--src/windeployqt/elfreader.cpp450
-rw-r--r--src/windeployqt/elfreader.h189
-rw-r--r--src/windeployqt/main.cpp945
-rw-r--r--src/windeployqt/utils.cpp805
-rw-r--r--src/windeployqt/utils.h252
-rw-r--r--src/windeployqt/windeployqt.pro14
7 files changed, 2657 insertions, 0 deletions
diff --git a/src/src.pro b/src/src.pro
index 0a88949b8..6c78146bb 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -27,6 +27,8 @@ android {
qtHaveModule(dbus): SUBDIRS += qdbus
+win32|winrt:SUBDIRS += windeployqt
+
qtNomakeTools( \
pixeltool \
qtconfig \
diff --git a/src/windeployqt/elfreader.cpp b/src/windeployqt/elfreader.cpp
new file mode 100644
index 000000000..bfd144179
--- /dev/null
+++ b/src/windeployqt/elfreader.cpp
@@ -0,0 +1,450 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.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 = 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 = file.size();
+ ustart = file.map(0, fdlen);
+ if (ustart == 0) {
+ // Try reading the data into memory instead.
+ raw = file.readAll();
+ start = raw.constData();
+ fdlen = 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,
+ 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, 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;
+
+ foreach (const ElfSectionHeader &eh, readHeaders().sectionHeaders) {
+ 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/windeployqt/elfreader.h b/src/windeployqt/elfreader.h
new file mode 100644
index 000000000..0b071a321
--- /dev/null
+++ b/src/windeployqt/elfreader.h
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.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/windeployqt/main.cpp b/src/windeployqt/main.cpp
new file mode 100644
index 000000000..e49603697
--- /dev/null
+++ b/src/windeployqt/main.cpp
@@ -0,0 +1,945 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "utils.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonArray>
+#include <QtCore/QCommandLineParser>
+#include <QtCore/QCommandLineOption>
+#include <QtCore/QVector>
+
+#include <cstdio>
+
+QT_BEGIN_NAMESPACE
+
+enum QtModule {
+ QtCoreModule = 0x1,
+ QtGuiModule = 0x2,
+ QtSqlModule = 0x4,
+ QtNetworkModule = 0x8,
+ QtMultimediaModule = 0x10,
+ QtMultimediaWidgetsModule = 0x20,
+ QtOpenGlModule = 0x40,
+ QtPrintSupportModule = 0x80,
+ QtDeclarativeModule = 0x100,
+ QtQmlModule = 0x200,
+ QtQuickModule = 0x400,
+ QtQuickParticlesModule = 0x800,
+ QtScriptModule = 0x1000,
+ QtSvgModule = 0x2000,
+ QtXmlModule = 0x4000,
+ QtXmlPatternsModule = 0x8000,
+ QtHelpModule = 0x10000,
+ QtSensorsModule = 0x20000,
+ QtWidgetsModule = 0x40000,
+ QtWebKitModule = 0x80000,
+ QtWebKitWidgetsModule = 0x100000
+};
+
+struct QtModuleEntry {
+ unsigned module;
+ const char *option;
+ const char *libraryName;
+ const char *translation;
+};
+
+QtModuleEntry qtModuleEntries[] = {
+ { QtCoreModule, "core", "Qt5Core", "qtbase" },
+ { QtGuiModule, "gui", "Qt5Gui", "qtbase" },
+ { QtQmlModule, "qml", "Qt5Qml", "qtdeclarative" },
+ { QtQuickModule, "quick", "Qt5Quick", "qtdeclarative" },
+ { QtWidgetsModule, "widgets", "Qt5Widgets", "qtbase" },
+ { QtOpenGlModule, "opengl", "Qt5OpenGL", 0 },
+ { QtPrintSupportModule, "printsupport", "Qt5PrintSupport", 0 },
+ { QtNetworkModule, "network", "Qt5Network", "qtbase" },
+ { QtSqlModule, "sql", "Qt5Sql", "qtbase" },
+ { QtMultimediaModule, "multimedia", "Qt5Multimedia", "qtmultimedia" },
+ { QtMultimediaWidgetsModule, "multimediawidgets", "Qt5MultimediaWidgets", "qtmultimedia" },
+ { QtQuickParticlesModule, "quickparticles", "Qt5QuickParticles", 0 },
+ { QtXmlPatternsModule, "xmlpatterns", "Qt5XmlPatterns", "qtxmlpatterns" },
+ { QtHelpModule, "help", "Qt5Help", "qt_help" },
+ { QtSensorsModule, "sensors", "Qt5Sensors", 0 },
+ { QtSvgModule, "svg", "Qt5Svg", 0 },
+ { QtWebKitModule, "webkit", "Qt5WebKit", 0 },
+ { QtWebKitWidgetsModule, "webkitwidgets", "Qt5WebKitWidgets", 0 },
+ { QtScriptModule, "script", "Qt5Script", "qtscript" },
+ { QtXmlModule, "xml", "Qt5Xml", 0 },
+ { QtDeclarativeModule, "declarative", "Qt5Declarative", "qtquick1" },
+};
+
+static const char webProcessC[] = "QtWebProcess";
+
+static inline QString webProcessBinary(Platform p)
+{
+ const QString webProcess = QLatin1String(webProcessC);
+ return (p & WindowsBased) ? webProcess + QStringLiteral(".exe") : webProcess;
+}
+
+static QByteArray formatQtModules(unsigned mask, bool option = false)
+{
+ QByteArray result;
+ const size_t qtModulesCount = sizeof(qtModuleEntries)/sizeof(QtModuleEntry);
+ for (size_t i = 0; i < qtModulesCount; ++i) {
+ if (mask & qtModuleEntries[i].module) {
+ if (!result.isEmpty())
+ result.append(' ');
+ result.append(option ? qtModuleEntries[i].option : qtModuleEntries[i].libraryName);
+ }
+ }
+ return result;
+}
+
+static Platform platformFromMkSpec(const QString &xSpec)
+{
+ if (xSpec == QLatin1String("linux-g++"))
+ return Unix;
+ if (xSpec.startsWith(QLatin1String("win32-")))
+ return Windows;
+ if (xSpec.startsWith(QLatin1String("winrt-x")))
+ return WinRtIntel;
+ if (xSpec.startsWith(QLatin1String("winrt-arm")))
+ return WinRtArm;
+ if (xSpec.startsWith(QLatin1String("winphone-x")))
+ return WinPhoneIntel;
+ if (xSpec.startsWith(QLatin1String("winphone-arm")))
+ return WinPhoneArm;
+ return UnknownPlatform;
+}
+
+bool optHelp = false;
+int optWebKit2 = 0;
+
+// Container class for JSON output
+class JsonOutput {
+public:
+ void addFile(const QString &source, const QString &target)
+ {
+ QJsonObject object;
+ object.insert(QStringLiteral("source"), QDir::toNativeSeparators(source));
+ object.insert(QStringLiteral("target"), QDir::toNativeSeparators(target));
+ m_files.append(object);
+ }
+ QByteArray toJson() const
+ {
+ QJsonObject document;
+ document.insert(QStringLiteral("files"), m_files);
+ return QJsonDocument(document).toJson();
+ }
+private:
+ QJsonArray m_files;
+};
+
+struct Options {
+ Options() : plugins(true), libraries(true), quickImports(true), translations(true)
+ , platform(Windows), additionalLibraries(0), disabledLibraries(0)
+ , updateFileFlags(0), json(0) {}
+
+ bool plugins;
+ bool libraries;
+ bool quickImports;
+ bool translations;
+ Platform platform;
+ unsigned additionalLibraries;
+ unsigned disabledLibraries;
+ unsigned updateFileFlags;
+ QString directory;
+ QString libraryDirectory;
+ QString binary;
+ JsonOutput *json;
+};
+
+// Return binary from folder
+static inline QString findBinary(const QString &directory, Platform platform)
+{
+ QDir dir(QDir::cleanPath(directory));
+
+ const QStringList nameFilters = (platform & WindowsBased) ?
+ QStringList(QStringLiteral("*.exe")) : QStringList();
+ foreach (const QString &binary, dir.entryList(nameFilters, QDir::Files | QDir::Executable))
+ if (!binary.contains(QLatin1String(webProcessC), Qt::CaseInsensitive))
+ return dir.filePath(binary);
+ return QString();
+}
+
+enum CommandLineParseFlag {
+ CommandLineParseError = 0x1,
+ CommandLineParseHelpRequested = 0x2
+};
+
+static inline int parseArguments(const QStringList &arguments, QCommandLineParser *parser,
+ Options *options, QString *errorMessage)
+{
+ typedef QSharedPointer<QCommandLineOption> CommandLineOptionPtr;
+ typedef QPair<CommandLineOptionPtr, unsigned> OptionMaskPair;
+ typedef QVector<OptionMaskPair> OptionMaskVector;
+
+ parser->setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
+ parser->setApplicationDescription(QStringLiteral("Qt Deploy Tool ") + QLatin1String(QT_VERSION_STR));
+ const QCommandLineOption helpOption = parser->addHelpOption();
+ parser->addVersionOption();
+
+ QCommandLineOption dirOption(QStringLiteral("dir"),
+ QStringLiteral("Use directory instead of binary directory."),
+ QStringLiteral("directory"));
+ parser->addOption(dirOption);
+
+ QCommandLineOption libDirOption(QStringLiteral("libdir"),
+ QStringLiteral("Copy libraries to path."),
+ QStringLiteral("path"));
+ parser->addOption(libDirOption);
+
+ QCommandLineOption forceOption(QStringLiteral("force"),
+ QStringLiteral("Force updating files."));
+ parser->addOption(forceOption);
+
+ QCommandLineOption noPluginsOption(QStringLiteral("no-plugins"),
+ QStringLiteral("Skip plugin deployment."));
+ parser->addOption(noPluginsOption);
+
+ QCommandLineOption noLibraryOption(QStringLiteral("no-libraries"),
+ QStringLiteral("Skip library deployment."));
+ parser->addOption(noLibraryOption);
+
+ QCommandLineOption noQuickImportOption(QStringLiteral("no-quick-import"),
+ QStringLiteral("Skip deployment of Qt Quick imports."));
+ parser->addOption(noQuickImportOption);
+
+ QCommandLineOption noTranslationOption(QStringLiteral("no-translations"),
+ QStringLiteral("Skip deployment of translations."));
+ parser->addOption(noTranslationOption);
+
+ QCommandLineOption webKitOption(QStringLiteral("webkit2"),
+ QStringLiteral("Deployment of WebKit2 (web process)."));
+ parser->addOption(webKitOption);
+
+ QCommandLineOption noWebKitOption(QStringLiteral("no-webkit2"),
+ QStringLiteral("Skip deployment of WebKit2."));
+ parser->addOption(noWebKitOption);
+
+ QCommandLineOption jsonOption(QStringLiteral("json"),
+ QStringLiteral("Print to stdout in JSON format."));
+ parser->addOption(jsonOption);
+
+ QCommandLineOption verboseOption(QStringLiteral("verbose"),
+ QStringLiteral("Verbose level."),
+ QStringLiteral("level"));
+ parser->addOption(verboseOption);
+
+ parser->addPositionalArgument(QStringLiteral("[file]"),
+ QStringLiteral("Binary or directory containing the binary."));
+
+ OptionMaskVector enabledModules;
+ OptionMaskVector disabledModules;
+ const size_t qtModulesCount = sizeof(qtModuleEntries)/sizeof(QtModuleEntry);
+ for (size_t i = 0; i < qtModulesCount; ++i) {
+ const QString option = QLatin1String(qtModuleEntries[i].option);
+ const QString name = QLatin1String(qtModuleEntries[i].libraryName);
+ const QString enabledDescription = QStringLiteral("Add ") + name + QStringLiteral(" module.");
+ CommandLineOptionPtr enabledOption(new QCommandLineOption(option, enabledDescription));
+ parser->addOption(*enabledOption.data());
+ enabledModules.push_back(OptionMaskPair(enabledOption, qtModuleEntries[i].module));
+
+ const QString disabledDescription = QStringLiteral("Remove ") + name + QStringLiteral(" module.");
+ CommandLineOptionPtr disabledOption(new QCommandLineOption(QStringLiteral("no-") + option,
+ disabledDescription));
+ disabledModules.push_back(OptionMaskPair(disabledOption, qtModuleEntries[i].module));
+ parser->addOption(*disabledOption.data());
+ }
+
+ const bool success = parser->parse(arguments);
+ if (parser->isSet(helpOption))
+ return CommandLineParseHelpRequested;
+ if (!success) {
+ *errorMessage = parser->errorText();
+ return CommandLineParseError;
+ }
+
+ options->libraryDirectory = parser->value(libDirOption);
+ options->plugins = !parser->isSet(noPluginsOption);
+ options->libraries = !parser->isSet(noLibraryOption);
+ options->translations = !parser->isSet(noTranslationOption);
+ options->quickImports = !parser->isSet(noQuickImportOption);
+ if (parser->isSet(forceOption))
+ options->updateFileFlags |= ForceUpdateFile;
+
+ for (size_t i = 0; i < qtModulesCount; ++i) {
+ if (parser->isSet(*enabledModules.at(int(i)).first.data()))
+ options->additionalLibraries |= enabledModules.at(int(i)).second;
+ if (parser->isSet(*disabledModules.at(int(i)).first.data()))
+ options->disabledLibraries |= disabledModules.at(int(i)).second;
+ }
+
+ if (parser->isSet(jsonOption)) {
+ optVerboseLevel = 0;
+ options->json = new JsonOutput;
+ } else {
+ if (parser->isSet(verboseOption)) {
+ bool ok;
+ const QString value = parser->value(verboseOption);
+ optVerboseLevel = value.toInt(&ok);
+ if (!ok || optVerboseLevel < 0) {
+ *errorMessage = QStringLiteral("Invalid value \"%1\" passed for verbose level.").arg(value);
+ return CommandLineParseError;
+ }
+ }
+ }
+
+ const QStringList posArgs = parser->positionalArguments();
+ if (posArgs.isEmpty()) {
+ *errorMessage = QStringLiteral("Please specify the binary or folder.");
+ return CommandLineParseError | CommandLineParseHelpRequested;
+ }
+ if (posArgs.size() > 1) {
+ QStringList superfluousArguments = posArgs;
+ superfluousArguments.pop_front();
+ *errorMessage = QStringLiteral("Superfluous arguments specified: ") + superfluousArguments.join(QLatin1Char(','));
+ return CommandLineParseError;
+ }
+
+ if (parser->isSet(dirOption))
+ options->directory = parser->value(dirOption);
+
+ const QString &file = posArgs.front();
+ const QFileInfo fi(QDir::cleanPath(file));
+ if (!fi.exists()) {
+ *errorMessage = QLatin1Char('"') + file + QStringLiteral("\" does not exist.");
+ return CommandLineParseError;
+ }
+
+ if (!options->directory.isEmpty() && !fi.isFile()) { // -dir was specified - expecting file.
+ *errorMessage = QLatin1Char('"') + file + QStringLiteral("\" is not an executable file.");
+ return CommandLineParseError;
+ }
+
+ if (fi.isFile()) {
+ options->binary = fi.absoluteFilePath();
+ if (options->directory.isEmpty())
+ options->directory = fi.absolutePath();
+ } else {
+ options->binary = findBinary(fi.absoluteFilePath(), options->platform);
+ if (options->binary.isEmpty()) {
+ *errorMessage = QStringLiteral("Unable to find binary in \"") + file + QLatin1Char('"');
+ return CommandLineParseError;
+ }
+ options->directory = fi.absoluteFilePath();
+ } // directory.
+ return 0;
+}
+
+// Simple line wrapping at 80 character boundaries.
+static inline QString lineBreak(QString s)
+{
+ for (int i = 80; i < s.size(); i += 80) {
+ const int lastBlank = s.lastIndexOf(QLatin1Char(' '), i);
+ if (lastBlank >= 0) {
+ s[lastBlank] = QLatin1Char('\n');
+ i = lastBlank + 1;
+ }
+ }
+ return s;
+}
+
+static inline QString helpText(const QCommandLineParser &p)
+{
+ QString result = p.helpText();
+ // Replace the default-generated text which is too long by a short summary
+ // explaining how to enable single libraries.
+ const int moduleStart = result.indexOf(QLatin1String("\n --core"));
+ const int argumentsStart = result.lastIndexOf(QLatin1String("\nArguments:"));
+ if (moduleStart >= argumentsStart)
+ return result;
+ QString moduleHelp = QLatin1String(
+ "\n\nQt libraries can be added by passing their name (-xml) or removed by passing\n"
+ "the name prepended by --no- (--no-xml). Available libraries:\n");
+ moduleHelp += lineBreak(QString::fromLatin1(formatQtModules(0xFFFFFFFF, true)));
+ moduleHelp += QLatin1Char('\n');
+ result.replace(moduleStart, argumentsStart - moduleStart, moduleHelp);
+ return result;
+}
+
+// Helper for recursively finding all dependent Qt libraries.
+static bool findDependentQtLibraries(const QString &qtBinDir, const QString &binary, Platform platform,
+ QString *errorMessage, QStringList *result,
+ unsigned *wordSize = 0, bool *isDebug = 0,
+ int *directDependencyCount = 0, int recursionDepth = 0)
+{
+ QStringList dependentLibs;
+ if (directDependencyCount)
+ *directDependencyCount = 0;
+ if (!readExecutable(binary, platform, errorMessage, &dependentLibs, wordSize, isDebug)) {
+ errorMessage->prepend(QLatin1String("Unable to find dependent libraries of ") +
+ QDir::toNativeSeparators(binary) + QLatin1String(" :"));
+ return false;
+ }
+ // Filter out the Qt libraries. Note that depends.exe finds libs from optDirectory if we
+ // are run the 2nd time (updating). We want to check against the Qt bin dir libraries
+ const int start = result->size();
+ const QRegExp filterRegExp(QStringLiteral("Qt5"), Qt::CaseInsensitive, QRegExp::FixedString);
+ foreach (const QString &qtLib, dependentLibs.filter(filterRegExp)) {
+ const QString path = normalizeFileName(qtBinDir + QLatin1Char('/') + QFileInfo(qtLib).fileName());
+ if (!result->contains(path))
+ result->append(path);
+ }
+ const int end = result->size();
+ if (directDependencyCount)
+ *directDependencyCount = end - start;
+ // Recurse
+ for (int i = start; i < end; ++i)
+ if (!findDependentQtLibraries(qtBinDir, result->at(i), platform, errorMessage, result, 0, 0, 0, recursionDepth + 1))
+ return false;
+ return true;
+}
+
+// Base class to filter debug/release Windows DLLs for functions to be passed to updateFile().
+// Tries to pre-filter by namefilter and does check via PE.
+class DllDirectoryFileEntryFunction {
+public:
+ explicit DllDirectoryFileEntryFunction(bool debug, const QString &prefix = QLatin1String("*")) :
+ m_nameFilter(QStringList(prefix + (debug ? QStringLiteral("d.dll") : QStringLiteral(".dll")))),
+ m_dllDebug(debug) {}
+
+ QStringList operator()(const QDir &dir) const
+ {
+ QStringList result;
+ QString errorMessage;
+ foreach (const QString &dll, m_nameFilter(dir)) {
+ const QString dllPath = dir.absoluteFilePath(dll);
+ bool debugDll;
+ if (readPeExecutable(dllPath, &errorMessage, 0, 0, &debugDll)) {
+ if (debugDll == m_dllDebug) {
+ result.push_back(dll);
+ }
+ } else {
+ std::fprintf(stderr, "Warning: Unable to read %s: %s",
+ qPrintable(QDir::toNativeSeparators(dllPath)), qPrintable(errorMessage));
+ }
+ }
+ return result;
+ }
+
+private:
+ const NameFilterFileEntryFunction m_nameFilter;
+ const bool m_dllDebug;
+};
+
+// File entry filter function for updateFile() that returns a list of files for
+// QML import trees: DLLs (matching debgug) and .qml/,js, etc.
+class QmlDirectoryFileEntryFunction {
+public:
+ explicit QmlDirectoryFileEntryFunction(bool debug)
+ : m_qmlNameFilter(QStringList() << QStringLiteral("*.js") << QStringLiteral("qmldir") << QStringLiteral("*.qmltypes") << QStringLiteral("*.png"))
+ , m_dllFilter(debug)
+ {}
+
+ QStringList operator()(const QDir &dir) const { return m_dllFilter(dir) + m_qmlNameFilter(dir); }
+
+private:
+ NameFilterFileEntryFunction m_qmlNameFilter;
+ DllDirectoryFileEntryFunction m_dllFilter;
+};
+
+static inline unsigned qtModuleForPlugin(const QString &subDirName)
+{
+ if (subDirName == QLatin1String("accessible") || subDirName == QLatin1String("iconengines")
+ || subDirName == QLatin1String("imageformats") || subDirName == QLatin1String("platforms")) {
+ return QtGuiModule;
+ }
+ if (subDirName == QLatin1String("bearer"))
+ return QtNetworkModule;
+ if (subDirName == QLatin1String("sqldrivers"))
+ return QtSqlModule;
+ if (subDirName == QLatin1String("mediaservice") || subDirName == QLatin1String("playlistformats"))
+ return QtMultimediaModule;
+ if (subDirName == QLatin1String("printsupport"))
+ return QtPrintSupportModule;
+ if (subDirName == QLatin1String("qmltooling"))
+ return QtDeclarativeModule | QtQuickModule;
+ return 0; // "designer"
+}
+
+QStringList findQtPlugins(unsigned usedQtModules,
+ const QString qtPluginsDirName,
+ bool debug, Platform platform,
+ QString *platformPlugin)
+{
+ if (qtPluginsDirName.isEmpty())
+ return QStringList();
+ QDir pluginsDir(qtPluginsDirName);
+ QStringList result;
+ foreach (const QString &subDirName, pluginsDir.entryList(QStringList(QLatin1String("*")), QDir::Dirs | QDir::NoDotAndDotDot)) {
+ const unsigned module = qtModuleForPlugin(subDirName);
+ if (module & usedQtModules) {
+ const QString subDirPath = qtPluginsDirName + QLatin1Char('/') + subDirName;
+ QDir subDir(subDirPath);
+ // Filter for platform or any.
+ QString filter;
+ const bool isPlatformPlugin = subDirName == QLatin1String("platforms");
+ if (isPlatformPlugin) {
+ switch (platform) {
+ case Windows:
+ filter = QStringLiteral("qwindows");
+ break;
+ case WinRtIntel:
+ case WinRtArm:
+ case WinPhoneIntel:
+ case WinPhoneArm:
+ filter = QStringLiteral("qwinrt");
+ break;
+ case Unix:
+ filter = QStringLiteral("libqxcb");
+ break;
+ case UnknownPlatform:
+ break;
+ }
+ } else {
+ filter = QLatin1String("*");
+ }
+ const QStringList plugins = platform == Unix ?
+ NameFilterFileEntryFunction(QStringList(filter + QStringLiteral(".so")))(subDir) :
+ DllDirectoryFileEntryFunction(debug, filter)(subDir);
+ foreach (const QString &plugin, plugins) {
+ const QString pluginPath = subDir.absoluteFilePath(plugin);
+ if (isPlatformPlugin)
+ *platformPlugin = pluginPath;
+ result.push_back(pluginPath);
+ } // for filter
+ } // type matches
+ } // for plugin folder
+ return result;
+}
+
+static unsigned qtModule(const QString &module)
+{
+ const size_t qtModulesCount = sizeof(qtModuleEntries)/sizeof(QtModuleEntry);
+ for (size_t i = 0; i < qtModulesCount; ++i)
+ if (module.contains(QLatin1String(qtModuleEntries[i].libraryName), Qt::CaseInsensitive))
+ return qtModuleEntries[i].module;
+ return 0;
+}
+
+static QStringList translationNameFilters(unsigned modules, const QString &prefix)
+{
+ QStringList result;
+ const size_t qtModulesCount = sizeof(qtModuleEntries)/sizeof(QtModuleEntry);
+ for (size_t i = 0; i < qtModulesCount; ++i) {
+ if ((qtModuleEntries[i].module & modules) && qtModuleEntries[i].translation) {
+ const QString name = QLatin1String(qtModuleEntries[i].translation) +
+ QLatin1Char('_') + prefix + QStringLiteral(".qm");
+ if (!result.contains(name))
+ result.push_back(name);
+ }
+ }
+ return result;
+}
+
+static bool deployTranslations(const QString &sourcePath, unsigned usedQtModules,
+ const QString &target, QString *errorMessage)
+{
+ // Find available languages prefixes by checking on qtbase.
+ QStringList prefixes;
+ QDir sourceDir(sourcePath);
+ const QStringList qmFilter = QStringList(QStringLiteral("qtbase_*.qm"));
+ foreach (QString qmFile, sourceDir.entryList(qmFilter)) {
+ qmFile.chop(3);
+ qmFile.remove(0, 7);
+ prefixes.push_back(qmFile);
+ }
+ if (prefixes.isEmpty()) {
+ fprintf(stderr, "Warning: Could not find any translations in %s (developer build?)\n.",
+ qPrintable(QDir::toNativeSeparators(sourcePath)));
+ return true;
+ }
+ // Run lconvert to concatenate all files into a single named "qt_<prefix>.qm" in the application folder
+ // Use QT_INSTALL_TRANSLATIONS as working directory to keep the command line short.
+ const QString absoluteTarget = QFileInfo(target).absoluteFilePath();
+ const QString binary = QStringLiteral("lconvert");
+ QStringList arguments;
+ foreach (const QString &prefix, prefixes) {
+ const QString targetFile = QStringLiteral("qt_") + prefix + QStringLiteral(".qm");
+ arguments.append(QStringLiteral("-o"));
+ arguments.append(QDir::toNativeSeparators(absoluteTarget + QLatin1Char('/') + targetFile));
+ foreach (const QString &qmFile, sourceDir.entryList(translationNameFilters(usedQtModules, prefix)))
+ arguments.append(qmFile);
+ if (optVerboseLevel)
+ std::printf("Creating %s...\n", qPrintable(targetFile));
+ unsigned long exitCode;
+ if (!runProcess(binary, arguments, sourcePath, &exitCode, 0, 0, errorMessage) || exitCode)
+ return false;
+ } // for prefixes.
+ return true;
+}
+
+struct DeployResult
+{
+ DeployResult() : success(false), directlyUsedQtLibraries(0), usedQtLibraries(0), deployedQtLibraries(0) {}
+ operator bool() const { return success; }
+
+ bool success;
+ unsigned directlyUsedQtLibraries;
+ unsigned usedQtLibraries;
+ unsigned deployedQtLibraries;
+};
+
+static QString libraryPath(const QString &libraryLocation, const char *name, Platform platform, bool debug)
+{
+ QString result = libraryLocation + QLatin1Char('/');
+ if (platform & WindowsBased) {
+ result += QLatin1String(name);
+ if (debug)
+ result += QLatin1Char('d');
+ result += QStringLiteral(".dll");
+ } else if (platform & UnixBased) {
+ result += QStringLiteral("lib");
+ result += QLatin1String(name);
+ result += QStringLiteral(".so");
+ }
+ return result;
+}
+
+static DeployResult deploy(const Options &options,
+ const QMap<QString, QString> &qmakeVariables,
+ QString *errorMessage)
+{
+ DeployResult result;
+
+ const QChar slash = QLatin1Char('/');
+
+ const QString qtBinDir = qmakeVariables.value(QStringLiteral("QT_INSTALL_BINS"));
+ const QString libraryLocation = options.platform == Unix ? qmakeVariables.value(QStringLiteral("QT_INSTALL_LIBS")) : qtBinDir;
+
+ if (optVerboseLevel > 1)
+ std::printf("Qt binaries in %s\n", qPrintable(QDir::toNativeSeparators(qtBinDir)));
+
+ QStringList dependentQtLibs;
+ bool isDebug;
+ unsigned wordSize;
+ int directDependencyCount;
+ if (!findDependentQtLibraries(libraryLocation, options.binary, options.platform, errorMessage, &dependentQtLibs, &wordSize, &isDebug, &directDependencyCount))
+ return result;
+
+ if (optVerboseLevel) {
+ std::printf("%s: %ubit, %s executable.\n", qPrintable(QDir::toNativeSeparators(options.binary)),
+ wordSize, isDebug ? "debug" : "release");
+ }
+
+ if (dependentQtLibs.isEmpty()) {
+ *errorMessage = QDir::toNativeSeparators(options.binary) + QStringLiteral(" does not seem to be a Qt executable.");
+ return result;
+ }
+
+ // Some Windows-specific checks in QtCore: ICU
+ if (options.platform & WindowsBased) {
+ const QStringList qt5Core = dependentQtLibs.filter(QStringLiteral("Qt5Core"), Qt::CaseInsensitive);
+ if (!qt5Core.isEmpty()) {
+ QStringList icuLibs = findDependentLibraries(qt5Core.front(), options.platform, errorMessage).filter(QStringLiteral("ICU"), Qt::CaseInsensitive);
+ if (!icuLibs.isEmpty()) {
+ // Find out the ICU version to add the data library icudtXX.dll, which does not show
+ // as a dependency.
+ QRegExp numberExpression(QStringLiteral("\\d+"));
+ Q_ASSERT(numberExpression.isValid());
+ const int index = numberExpression.indexIn(icuLibs.front());
+ if (index >= 0) {
+ const QString icuVersion = icuLibs.front().mid(index, numberExpression.matchedLength());
+ if (optVerboseLevel > 1)
+ std::printf("Adding ICU version %s\n", qPrintable(icuVersion));
+ icuLibs.push_back(QStringLiteral("icudt") + icuVersion + QStringLiteral(".dll"));
+ }
+ foreach (const QString &icuLib, icuLibs) {
+ const QString icuPath = findInPath(icuLib);
+ if (icuPath.isEmpty()) {
+ *errorMessage = QStringLiteral("Unable to locate ICU library ") + icuLib;
+ return result;
+ }
+ dependentQtLibs.push_back(icuPath);
+ } // foreach icuLib
+ } // !icuLibs.isEmpty()
+ } // Qt5Core
+ } // Windows
+
+ // Find the plugins and check whether ANGLE, D3D are required on the platform plugin.
+ QString platformPlugin;
+ // Sort apart Qt 5 libraries in the ones that are represented by the
+ // QtModule enumeration (and thus controlled by flags) and others.
+ QStringList deployedQtLibraries;
+ for (int i = 0 ; i < dependentQtLibs.size(); ++i) {
+ if (const unsigned qtm = qtModule(dependentQtLibs.at(i))) {
+ result.usedQtLibraries |= qtm;
+ if (i < directDependencyCount)
+ result.directlyUsedQtLibraries |= qtm;
+ } else {
+ deployedQtLibraries.push_back(dependentQtLibs.at(i)); // Not represented by flag.
+ }
+ }
+ result.deployedQtLibraries = (result.usedQtLibraries | options.additionalLibraries) & ~options.disabledLibraries;
+ // Apply options flags and re-add library names.
+ const size_t qtModulesCount = sizeof(qtModuleEntries)/sizeof(QtModuleEntry);
+ for (size_t i = 0; i < qtModulesCount; ++i)
+ if (result.deployedQtLibraries & qtModuleEntries[i].module)
+ deployedQtLibraries.push_back(libraryPath(libraryLocation, qtModuleEntries[i].libraryName, options.platform, isDebug));
+
+ if (optVerboseLevel >= 1) {
+ std::printf("Direct dependencies: %s\nAll dependencies : %s\nTo be deployed : %s\n",
+ formatQtModules(result.directlyUsedQtLibraries).constData(),
+ formatQtModules(result.usedQtLibraries).constData(),
+ formatQtModules(result.deployedQtLibraries).constData());
+ }
+
+ const QStringList plugins = findQtPlugins(result.deployedQtLibraries, qmakeVariables.value(QStringLiteral("QT_INSTALL_PLUGINS")),
+ isDebug, options.platform, &platformPlugin);
+ if (optVerboseLevel > 1)
+ std::printf("Plugins: %s\n", qPrintable(plugins.join(QLatin1Char(','))));
+
+ if (plugins.isEmpty())
+ return result;
+
+ if (platformPlugin.isEmpty()) {
+ *errorMessage =QStringLiteral("Unable to find the platform plugin.");
+ return result;
+ }
+
+ // Check for ANGLE on the platform plugin.
+ if (options.platform & WindowsBased) {
+ const QStringList platformPluginLibraries = findDependentLibraries(platformPlugin, options.platform, errorMessage);
+ const QStringList libEgl = platformPluginLibraries.filter(QStringLiteral("libegl"), Qt::CaseInsensitive);
+ if (!libEgl.isEmpty()) {
+ const QString libEglFullPath = qtBinDir + slash + QFileInfo(libEgl.front()).fileName();
+ deployedQtLibraries.push_back(libEglFullPath);
+ const QStringList libGLESv2 = findDependentLibraries(libEglFullPath, options.platform, errorMessage).filter(QStringLiteral("libGLESv2"), Qt::CaseInsensitive);
+ if (!libGLESv2.isEmpty()) {
+ const QString libGLESv2FullPath = qtBinDir + slash + QFileInfo(libGLESv2.front()).fileName();
+ deployedQtLibraries.push_back(libGLESv2FullPath);
+ }
+ // Find the D3d Compiler matching the D3D library.
+ const QString d3dCompiler = findD3dCompiler(options.platform, wordSize);
+ if (d3dCompiler.isEmpty()) {
+ std::fprintf(stderr, "Warning: Cannot find any version of the d3dcompiler DLL.\n");
+ } else {
+ deployedQtLibraries.push_back(d3dCompiler);
+ }
+ } // !libEgl.isEmpty()
+ } // Windows
+
+ // Update libraries
+ if (options.libraries) {
+ const QString targetPath = options.libraryDirectory.isEmpty() ?
+ options.directory : options.libraryDirectory;
+ foreach (const QString &qtLib, deployedQtLibraries) {
+ if (!updateFile(qtLib, targetPath, options.updateFileFlags, errorMessage))
+ return result;
+ if (options.json)
+ options.json->addFile(qtLib, targetPath);
+ }
+ } // optLibraries
+
+ // Update plugins
+ if (options.plugins) {
+ QDir dir(options.directory);
+ foreach (const QString &plugin, plugins) {
+ const QString targetDirName = plugin.section(slash, -2, -2);
+ if (!dir.exists(targetDirName)) {
+ if (optVerboseLevel)
+ std::printf("Creating directory %s.\n", qPrintable(targetDirName));
+ if (!dir.mkdir(targetDirName)) {
+ std::fprintf(stderr, "Cannot create %s.\n", qPrintable(targetDirName));
+ *errorMessage = QStringLiteral("Cannot create ") + targetDirName + QLatin1Char('.');
+ return result;
+ }
+ }
+ const QString targetPath = options.directory + slash + targetDirName;
+ if (!updateFile(plugin, targetPath, options.updateFileFlags, errorMessage))
+ return result;
+ if (options.json)
+ options.json->addFile(plugin, targetPath);
+ }
+ } // optPlugins
+
+ // Update Quick imports
+ const bool usesQuick1 = result.deployedQtLibraries & QtDeclarativeModule;
+ // Do not be fooled by QtWebKit.dll depending on Quick into always installing Quick imports
+ // for WebKit1-applications. Check direct dependency only.
+ const bool usesQuick2 = (result.directlyUsedQtLibraries & QtQuickModule)
+ || (options.additionalLibraries & QtQuickModule);
+ if (options.quickImports && (usesQuick1 || usesQuick2)) {
+ const QmlDirectoryFileEntryFunction qmlFileEntryFunction(isDebug);
+ if (usesQuick2) {
+ const QString quick2ImportPath = qmakeVariables.value(QStringLiteral("QT_INSTALL_QML"));
+ QStringList quick2Imports;
+ quick2Imports << QStringLiteral("QtQml") << QStringLiteral("QtQuick") << QStringLiteral("QtQuick.2");
+ if (result.deployedQtLibraries & QtMultimediaModule)
+ quick2Imports << QStringLiteral("QtMultimedia");
+ if (result.deployedQtLibraries & QtSensorsModule)
+ quick2Imports << QStringLiteral("QtSensors");
+ if (result.deployedQtLibraries & QtWebKitModule)
+ quick2Imports << QStringLiteral("QtWebKit");
+ foreach (const QString &quick2Import, quick2Imports) {
+ const QString sourceFile = quick2ImportPath + slash + quick2Import;
+ if (!updateFile(sourceFile, qmlFileEntryFunction, options.directory, options.updateFileFlags, errorMessage))
+ return result;
+ if (options.json)
+ options.json->addFile(sourceFile, options.directory);
+ }
+ } // Quick 2
+ if (usesQuick1) {
+ const QString quick1ImportPath = qmakeVariables.value(QStringLiteral("QT_INSTALL_IMPORTS"));
+ QStringList quick1Imports(QStringLiteral("Qt"));
+ if (result.deployedQtLibraries & QtWebKitModule)
+ quick1Imports << QStringLiteral("QtWebKit");
+ foreach (const QString &quick1Import, quick1Imports) {
+ const QString sourceFile = quick1ImportPath + slash + quick1Import;
+ if (!updateFile(sourceFile, qmlFileEntryFunction, options.directory, options.updateFileFlags, errorMessage))
+ return result;
+ if (options.json)
+ options.json->addFile(sourceFile, options.directory);
+ }
+ } // Quick 1
+ } // optQuickImports
+
+ if (options.translations
+ && !deployTranslations(qmakeVariables.value(QStringLiteral("QT_INSTALL_TRANSLATIONS")),
+ result.deployedQtLibraries, options.directory, errorMessage)) {
+ return result;
+ }
+
+ result.success = true;
+ return result;
+}
+
+static bool deployWebKit2(const QMap<QString, QString> &qmakeVariables,
+ const Options &sourceOptions, QString *errorMessage)
+{
+ // Copy the web process and its dependencies
+ const QString webProcess = webProcessBinary(sourceOptions.platform);
+ const QString webProcessSource = qmakeVariables.value(QStringLiteral("QT_INSTALL_LIBEXECS")) +
+ QLatin1Char('/') + webProcess;
+ if (!updateFile(webProcessSource, sourceOptions.directory, sourceOptions.updateFileFlags, errorMessage))
+ return false;
+ if (sourceOptions.json)
+ sourceOptions.json->addFile(webProcessSource, sourceOptions.directory);
+ Options options(sourceOptions);
+ options.binary = options.directory + QLatin1Char('/') + webProcess;
+ options.quickImports = false;
+ options.translations = false;
+ return deploy(options, qmakeVariables, errorMessage);
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication a(argc, argv);
+ QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
+
+ Options options;
+ QString errorMessage;
+ const QMap<QString, QString> qmakeVariables = queryQMakeAll(&errorMessage);
+ const QString xSpec = qmakeVariables.value(QStringLiteral("QMAKE_XSPEC"));
+ options.platform = platformFromMkSpec(xSpec);
+
+ { // Command line
+ QCommandLineParser parser;
+ QString errorMessage;
+ const int result = parseArguments(QCoreApplication::arguments(), &parser, &options, &errorMessage);
+ if (result & CommandLineParseError)
+ std::fprintf(stderr, "%s\n\n", qPrintable(errorMessage));
+ if (result & CommandLineParseHelpRequested)
+ std::fputs(qPrintable(helpText(parser)), stdout);
+ if (result & CommandLineParseError)
+ return 1;
+ if (result & CommandLineParseHelpRequested)
+ return 0;
+ }
+
+ if (qmakeVariables.isEmpty() || xSpec.isEmpty() || !qmakeVariables.contains(QStringLiteral("QT_INSTALL_BINS"))) {
+ std::fprintf(stderr, "Unable to query qmake: %s\n", qPrintable(errorMessage));
+ return 1;
+ }
+
+ if (options.platform == UnknownPlatform) {
+ std::fprintf(stderr, "Unsupported platform %s\n", qPrintable(xSpec));
+ return 1;
+ }
+
+ // Create directories
+ if (!createDirectory(options.directory, &errorMessage)) {
+ std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
+ return 1;
+ }
+ if (!options.libraryDirectory.isEmpty() && options.libraryDirectory != options.directory
+ && !createDirectory(options.libraryDirectory, &errorMessage)) {
+ std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
+ return 1;
+ }
+
+ if (optWebKit2)
+ options.additionalLibraries |= QtWebKitModule;
+
+
+ const DeployResult result = deploy(options, qmakeVariables, &errorMessage);
+ if (!result) {
+ std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
+ return 1;
+ }
+
+ if ((optWebKit2 != -1)
+ && (optWebKit2 == 1
+ || ((result.deployedQtLibraries & QtWebKitModule)
+ && (result.directlyUsedQtLibraries & QtQuickModule)))) {
+ if (optVerboseLevel)
+ std::printf("Deploying: %s...\n", webProcessC);
+ if (!deployWebKit2(qmakeVariables, options, &errorMessage)) {
+ std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
+ return 1;
+ }
+ }
+
+ if (options.json) {
+ std::fputs(options.json->toJson().constData(), stdout);
+ delete options.json;
+ options.json = 0;
+ }
+
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/windeployqt/utils.cpp b/src/windeployqt/utils.cpp
new file mode 100644
index 000000000..2b045ad45
--- /dev/null
+++ b/src/windeployqt/utils.cpp
@@ -0,0 +1,805 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "utils.h"
+#include "elfreader.h"
+
+#include <QtCore/QString>
+#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>
+#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
+
+#include <cstdio>
+
+QT_BEGIN_NAMESPACE
+
+int optVerboseLevel = 1;
+
+// 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::printf("Creating %s...\n", qPrintable(QDir::toNativeSeparators(directory)));
+ QDir dir;
+ if (!dir.mkpath(directory)) {
+ *errorMessage = QString::fromLatin1("Could not create directory %1.").
+ arg(QDir::toNativeSeparators(directory));
+ return false;
+ }
+ return true;
+}
+
+#ifdef Q_OS_WIN
+QString winErrorMessage(unsigned long error)
+{
+ QString rc = QString::fromLatin1("#%1: ").arg(error);
+ ushort *lpMsgBuf;
+
+ const int len = FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
+ if (len) {
+ rc = QString::fromUtf16(lpMsgBuf, 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((wchar_t *)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, 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, 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);
+ foreach (const QString &a, args)
+ appendToCommandLine(a, &commandLine);
+ if (optVerboseLevel > 1)
+ std::printf("Running: %s\n", qPrintable(commandLine));
+
+ 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,
+ (wchar_t *)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::fprintf(stderr, "Failed to change working directory to %s.\n", qPrintable(workingDirectory));
+ ::_exit(-1);
+ }
+
+ char **argv = new char *[args.size() + 2]; // Create argv.
+ char **ap = argv;
+ *ap++ = encodeFileName(binary);
+ foreach (const QString &a, 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
+}
+
+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;
+ int pos = 0;
+ while (true) {
+ const int colonPos = output.indexOf(QLatin1Char(':'), pos);
+ if (colonPos < 0)
+ break;
+ const int endPos = output.indexOf(QLatin1Char('\n'), colonPos + 1);
+ if (endPos < 0)
+ break;
+ 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;
+ }
+ 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, QString *errorMessage)
+{
+ const QFileInfo sourceFileInfo(sourceFileName);
+ const QString targetFileName = targetDirectory + QLatin1Char('/') + sourceFileInfo.fileName();
+ if (optVerboseLevel > 1)
+ std::printf("Checking %s, %s\n", qPrintable(sourceFileName), qPrintable(targetFileName));
+
+ 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::printf("Creating %s.\n", qPrintable( QDir::toNativeSeparators(targetFileName)));
+ if (!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 QStringList allEntries = dir.entryList(nameFilters, QDir::Files) + dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ foreach (const QString &entry, allEntries)
+ if (!updateFile(sourceFileName + QLatin1Char('/') + entry, nameFilters, targetFileName, errorMessage))
+ return false;
+ return true;
+ } // Source is directory.
+
+ if (targetFileInfo.exists()) {
+ if (targetFileInfo.lastModified() >= sourceFileInfo.lastModified()) {
+ if (optVerboseLevel)
+ std::printf("%s is up to date.\n", qPrintable(sourceFileInfo.fileName()));
+ return true;
+ }
+ QFile targetFile(targetFileName);
+ if (!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::printf("Updating %s.\n", qPrintable(sourceFileInfo.fileName()));
+ if (!file.copy(targetFileName)) {
+ *errorMessage = QString::fromLatin1("Cannot copy %1 to %2: %3")
+ .arg(QDir::toNativeSeparators(sourceFileName),
+ QDir::toNativeSeparators(targetFileName),
+ file.errorString());
+ return false;
+ }
+ 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;
+ }
+ foreach (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
+
+// 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 = (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(QString::fromLocal8Bit((const char *)rvaToPtr(importDesc->Name, ntHeaders, base)));
+ return result;
+}
+
+// Read a PE executable and determine dependent libraries, word size
+// and debug flags. Note that the debug flag cannot be relied on for MinGW.
+bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage,
+ QStringList *dependentLibrariesIn, unsigned *wordSizeIn,
+ bool *isDebugIn)
+{
+ 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;
+ bool debug = false;
+ if (wordSize == 32) {
+ const IMAGE_NT_HEADERS32 *ntHeaders32 = reinterpret_cast<const IMAGE_NT_HEADERS32 *>(ntHeaders);
+ debug = ntHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
+ if (dependentLibrariesIn)
+ *dependentLibrariesIn = readImportSections(ntHeaders32, fileMemory, errorMessage);
+ } else {
+ const IMAGE_NT_HEADERS64 *ntHeaders64 = reinterpret_cast<const IMAGE_NT_HEADERS64 *>(ntHeaders);
+ debug = ntHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
+ if (dependentLibrariesIn)
+ *dependentLibrariesIn = readImportSections(ntHeaders64, fileMemory, errorMessage);
+ }
+
+ if (isDebugIn)
+ *isDebugIn = debug;
+ result = true;
+ if (optVerboseLevel > 1)
+ std::printf("%s: %s %u bit, debug: %d\n", __FUNCTION__,
+ qPrintable(peExecutableFileName), wordSize, debug);
+ } 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, unsigned wordSize)
+{
+ const QString prefix = QStringLiteral("D3Dcompiler_");
+ const QString suffix = QStringLiteral(".dll");
+ // 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();
+ }
+ }
+ // Find the latest D3D compiler DLL in path (Windows 8.1 has d3dcompiler_47).
+ if (platform & IntelBased) {
+ for (int i = 47 ; i >= 40 ; --i) {
+ const QString dll = findInPath(prefix + QString::number(i) + suffix);
+ if (!dll.isEmpty())
+ return dll;
+ }
+ }
+ return QString();
+}
+
+#else // Q_OS_WIN
+
+bool readPeExecutable(const QString &, QString *errorMessage,
+ QStringList *, unsigned *, bool *)
+{
+ *errorMessage = QStringLiteral("Not implemented.");
+ return false;
+}
+
+QString findD3dCompiler(Platform, unsigned)
+{
+ return QString();
+}
+
+#endif // !Q_OS_WIN
+
+QT_END_NAMESPACE
diff --git a/src/windeployqt/utils.h b/src/windeployqt/utils.h
new file mode 100644
index 000000000..8ebf7d6ea
--- /dev/null
+++ b/src/windeployqt/utils.h
@@ -0,0 +1,252 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <QStringList>
+#include <QMap>
+#include <QtCore/QFile>
+#include <QtCore/QDir>
+#include <QtCore/QDateTime>
+
+#include <cstdio>
+
+QT_BEGIN_NAMESPACE
+
+enum PlatformFlag {
+ WindowsBased = 0x1000,
+ UnixBased = 0x2000,
+ IntelBased = 0x4000,
+ ArmBased = 0x8000
+};
+
+enum Platform {
+ Windows = WindowsBased + IntelBased,
+ WinRtIntel = WindowsBased + IntelBased + 1,
+ WinRtArm = WindowsBased + ArmBased + 2,
+ WinPhoneIntel = WindowsBased + IntelBased + 3,
+ WinPhoneArm = WindowsBased + ArmBased + 4,
+ Unix = UnixBased,
+ UnknownPlatform
+};
+
+#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
+
+bool createSymbolicLink(const QFileInfo &source, const QString &target, QString *errorMessage);
+bool createDirectory(const QString &directory, QString *errorMessage);
+QString findInPath(const QString &file);
+QMap<QString, QString> queryQMakeAll(QString *errorMessage);
+QString queryQMake(const QString &variable, QString *errorMessage);
+QStringList findDependentLibs(const QString &binary, QString *errorMessage);
+
+bool updateFile(const QString &sourceFileName, const QStringList &nameFilters,
+ const QString &targetDirectory, 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 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);
+}
+
+// Return dependent modules of a PE executable files.
+
+inline QStringList findDependentLibraries(const QString &peExecutableFileName, Platform platform, QString *errorMessage)
+{
+ QStringList result;
+ readExecutable(peExecutableFileName, platform, errorMessage, &result);
+ return result;
+}
+
+QString findD3dCompiler(Platform platform, unsigned wordSize);
+
+extern int optVerboseLevel;
+
+// Recursively update a file or directory, matching DirectoryFileEntryFunction against the QDir
+// to obtain the files.
+enum UpdateFileFlag {
+ ForceUpdateFile = 0x1
+};
+
+template <class DirectoryFileEntryFunction>
+bool updateFile(const QString &sourceFileName,
+ DirectoryFileEntryFunction directoryFileEntryFunction,
+ const QString &targetDirectory,
+ unsigned flags,
+ QString *errorMessage)
+{
+ const QFileInfo sourceFileInfo(sourceFileName);
+ const QString targetFileName = targetDirectory + QLatin1Char('/') + sourceFileInfo.fileName();
+ if (optVerboseLevel > 1)
+ std::printf("Checking %s, %s\n", qPrintable(sourceFileName), qPrintable(targetFileName));
+
+ 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, 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 (!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 (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::printf("Creating %s.\n", qPrintable(targetFileName));
+ if (!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 QStringList allEntries = directoryFileEntryFunction(dir) + dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ foreach (const QString &entry, allEntries)
+ if (!updateFile(sourceFileName + QLatin1Char('/') + entry, directoryFileEntryFunction, targetFileName, flags, errorMessage))
+ return false;
+ return true;
+ } // Source is directory.
+
+ if (targetFileInfo.exists()) {
+ if (!(flags & ForceUpdateFile)
+ && targetFileInfo.lastModified() >= sourceFileInfo.lastModified()) {
+ if (optVerboseLevel)
+ std::printf("%s is up to date.\n", qPrintable(sourceFileInfo.fileName()));
+ return true;
+ }
+ QFile targetFile(targetFileName);
+ if (!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::printf("Updating %s.\n", qPrintable(sourceFileInfo.fileName()));
+ if (!file.copy(targetFileName)) {
+ *errorMessage = QString::fromLatin1("Cannot copy %1 to %2: %3")
+ .arg(QDir::toNativeSeparators(sourceFileName),
+ QDir::toNativeSeparators(targetFileName),
+ file.errorString());
+ return false;
+ }
+ 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, QString *errorMessage)
+{
+ return updateFile(sourceFileName, NameFilterFileEntryFunction(QStringList()), targetDirectory, flags, errorMessage);
+}
+
+QT_END_NAMESPACE
+
+#endif // UTILS_H
diff --git a/src/windeployqt/windeployqt.pro b/src/windeployqt/windeployqt.pro
new file mode 100644
index 000000000..6bf96a34d
--- /dev/null
+++ b/src/windeployqt/windeployqt.pro
@@ -0,0 +1,14 @@
+option(host_build)
+QT = core-private
+DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
+
+SOURCES += main.cpp utils.cpp elfreader.cpp
+HEADERS += utils.h elfreader.h
+
+CONFIG += force_bootstrap console
+
+INCLUDEPATH += $$QT_BUILD_TREE/src/corelib/global
+
+win32: LIBS += -lShlwapi
+
+load(qt_tool)