summaryrefslogtreecommitdiffstats
path: root/src/tools/windeployqt
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/windeployqt')
-rw-r--r--src/tools/windeployqt/CMakeLists.txt13
-rw-r--r--src/tools/windeployqt/elfreader.cpp417
-rw-r--r--src/tools/windeployqt/elfreader.h151
-rw-r--r--src/tools/windeployqt/main.cpp1310
-rw-r--r--src/tools/windeployqt/qmlutils.cpp3
-rw-r--r--src/tools/windeployqt/qtmoduleinfo.cpp183
-rw-r--r--src/tools/windeployqt/qtmoduleinfo.h51
-rw-r--r--src/tools/windeployqt/qtplugininfo.cpp100
-rw-r--r--src/tools/windeployqt/qtplugininfo.h48
-rw-r--r--src/tools/windeployqt/utils.cpp217
-rw-r--r--src/tools/windeployqt/utils.h31
11 files changed, 1348 insertions, 1176 deletions
diff --git a/src/tools/windeployqt/CMakeLists.txt b/src/tools/windeployqt/CMakeLists.txt
index a63af56efb..2e50116484 100644
--- a/src/tools/windeployqt/CMakeLists.txt
+++ b/src/tools/windeployqt/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
#####################################################################
## windeployqt Tool:
#####################################################################
@@ -6,16 +9,19 @@ qt_get_tool_target_name(target_name windeployqt)
qt_internal_add_tool(${target_name}
TOOLS_TARGET Core
USER_FACING
+ INSTALL_VERSIONED_LINK
TARGET_DESCRIPTION "Qt Windows Deployment Tool"
SOURCES
- elfreader.cpp elfreader.h
qmlutils.cpp qmlutils.h
+ qtmoduleinfo.cpp qtmoduleinfo.h
+ qtplugininfo.cpp qtplugininfo.h
utils.cpp utils.h
main.cpp
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
QT_NO_FOREACH
+ QT_NO_QPAIR
LIBRARIES
Qt::CorePrivate
)
@@ -25,8 +31,3 @@ qt_internal_extend_target(${target_name} CONDITION WIN32
PUBLIC_LIBRARIES
shlwapi
)
-
-qt_internal_extend_target(${target_name} CONDITION QT_FEATURE_relocatable
- DEFINES
- QT_RELOCATABLE
-)
diff --git a/src/tools/windeployqt/elfreader.cpp b/src/tools/windeployqt/elfreader.cpp
deleted file mode 100644
index 9ef3b6bfa4..0000000000
--- a/src/tools/windeployqt/elfreader.cpp
+++ /dev/null
@@ -1,417 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "elfreader.h"
-
-#include <QDir>
-
-QT_BEGIN_NAMESPACE
-
-using namespace Qt::StringLiterals;
-
-/* This is a copy of the ELF reader contained in Qt Creator (src/libs/utils),
- * extended by the dependencies() function to read out the dependencies of a dynamic executable. */
-
-quint16 getHalfWord(const unsigned char *&s, const ElfData &context)
-{
- quint16 res;
- if (context.endian == Elf_ELFDATA2MSB)
- res = qFromBigEndian<quint16>(s);
- else
- res = qFromLittleEndian<quint16>(s);
- s += 2;
- return res;
-}
-
-quint32 getWord(const unsigned char *&s, const ElfData &context)
-{
- quint32 res;
- if (context.endian == Elf_ELFDATA2MSB)
- res = qFromBigEndian<quint32>(s);
- else
- res = qFromLittleEndian<quint32>(s);
- s += 4;
- return res;
-}
-
-quint64 getAddress(const unsigned char *&s, const ElfData &context)
-{
- quint64 res;
- if (context.elfclass == Elf_ELFCLASS32) {
- if (context.endian == Elf_ELFDATA2MSB)
- res = qFromBigEndian<quint32>(s);
- else
- res = qFromLittleEndian<quint32>(s);
- s += 4;
- } else {
- if (context.endian == Elf_ELFDATA2MSB)
- res = qFromBigEndian<quint64>(s);
- else
- res = qFromLittleEndian<quint64>(s);
- s += 8;
- }
- return res;
-}
-
-quint64 getOffset(const unsigned char *&s, const ElfData &context)
-{
- return getAddress(s, context);
-}
-
-static void parseSectionHeader(const uchar *s, ElfSectionHeader *sh, const ElfData &context)
-{
- sh->index = getWord(s, context);
- sh->type = getWord(s, context);
- sh->flags = quint32(getOffset(s, context));
- sh->addr = getAddress(s, context);
- sh->offset = getOffset(s, context);
- sh->size = getOffset(s, context);
-}
-
-static void parseProgramHeader(const uchar *s, ElfProgramHeader *sh, const ElfData &context)
-{
- sh->type = getWord(s, context);
- sh->offset = getOffset(s, context);
- /* p_vaddr = */ getAddress(s, context);
- /* p_paddr = */ getAddress(s, context);
- sh->filesz = getWord(s, context);
- sh->memsz = getWord(s, context);
-}
-
-class ElfMapper
-{
-public:
- ElfMapper(const ElfReader *reader) : file(reader->m_binary) {}
-
- bool map()
- {
- if (!file.open(QIODevice::ReadOnly))
- return false;
-
- fdlen = quint64(file.size());
- ustart = file.map(0, qint64(fdlen));
- if (ustart == 0) {
- // Try reading the data into memory instead.
- raw = file.readAll();
- start = raw.constData();
- fdlen = quint64(raw.size());
- }
- return true;
- }
-
-public:
- QFile file;
- QByteArray raw;
- union { const char *start; const uchar *ustart; };
- quint64 fdlen;
-};
-
-ElfReader::ElfReader(const QString &binary)
- : m_binary(binary)
-{
-}
-
-ElfData ElfReader::readHeaders()
-{
- readIt();
- return m_elfData;
-}
-
-static inline QString msgInvalidElfObject(const QString &binary, const QString &why)
-{
- return QStringLiteral("'%1' is an invalid ELF object (%2)")
- .arg(QDir::toNativeSeparators(binary), why);
-}
-
-ElfReader::Result ElfReader::readIt()
-{
- if (!m_elfData.sectionHeaders.isEmpty())
- return Ok;
- if (!m_elfData.programHeaders.isEmpty())
- return Ok;
-
- ElfMapper mapper(this);
- if (!mapper.map())
- return Corrupt;
-
- const quint64 fdlen = mapper.fdlen;
-
- if (fdlen < 64) {
- m_errorString = QStringLiteral("'%1' is not an ELF object (file too small)").arg(QDir::toNativeSeparators(m_binary));
- return NotElf;
- }
-
- if (strncmp(mapper.start, "\177ELF", 4) != 0) {
- m_errorString = QStringLiteral("'%1' is not an ELF object").arg(QDir::toNativeSeparators(m_binary));
- return NotElf;
- }
-
- // 32 or 64 bit
- m_elfData.elfclass = ElfClass(mapper.start[4]);
- const bool is64Bit = m_elfData.elfclass == Elf_ELFCLASS64;
- if (m_elfData.elfclass != Elf_ELFCLASS32 && m_elfData.elfclass != Elf_ELFCLASS64) {
- m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("odd cpu architecture"));
- return Corrupt;
- }
-
- // int bits = (data[4] << 5);
- // If you remove this check to read ELF objects of a different arch,
- // please make sure you modify the typedefs
- // to match the _plugin_ architecture.
- // if ((sizeof(void*) == 4 && bits != 32)
- // || (sizeof(void*) == 8 && bits != 64)) {
- // if (errorString)
- // *errorString = QLibrary::QStringLiteral("'%1' is an invalid ELF object (%2)")
- // .arg(m_binary).arg("wrong cpu architecture"_L1);
- // 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("shstrtab section header seems to be at %1"_L1)
-// .arg(QString::number(soff, 16));
-// return Corrupt;
-// }
-
- if (e_shoff) {
- ElfSectionHeader strtab;
- parseSectionHeader(mapper.ustart + soff, &strtab, m_elfData);
- const quint64 stringTableFileOffset = strtab.offset;
- if (quint32(stringTableFileOffset + e_shentsize) >= fdlen
- || stringTableFileOffset == 0) {
- const QString reason = QStringLiteral("string table seems to be at 0x%1").arg(soff, 0, 16);
- m_errorString = msgInvalidElfObject(m_binary, reason);
- return Corrupt;
- }
-
- for (quint32 i = 0; i < e_shnum; ++i) {
- const uchar *s = mapper.ustart + e_shoff + i * e_shentsize;
- ElfSectionHeader sh;
- parseSectionHeader(s, &sh, m_elfData);
-
- if (stringTableFileOffset + sh.index > fdlen) {
- const QString reason = QStringLiteral("section name %1 of %2 behind end of file")
- .arg(i).arg(e_shnum);
- m_errorString = msgInvalidElfObject(m_binary, reason);
- return Corrupt;
- }
-
- sh.name = mapper.start + stringTableFileOffset + sh.index;
- if (sh.name == ".gdb_index") {
- m_elfData.symbolsType = FastSymbols;
- } else if (sh.name == ".debug_info") {
- m_elfData.symbolsType = PlainSymbols;
- } else if (sh.name == ".gnu_debuglink") {
- m_elfData.debugLink = QByteArray(mapper.start + sh.offset);
- m_elfData.symbolsType = LinkedSymbols;
- } else if (sh.name == ".note.gnu.build-id") {
- m_elfData.symbolsType = BuildIdSymbols;
- if (sh.size > 16)
- m_elfData.buildId = QByteArray(mapper.start + sh.offset + 16,
- int(sh.size) - 16).toHex();
- }
- m_elfData.sectionHeaders.append(sh);
- }
- }
-
- if (e_phoff) {
- for (quint32 i = 0; i < e_phnum; ++i) {
- const uchar *s = mapper.ustart + e_phoff + i * e_phentsize;
- ElfProgramHeader ph;
- parseProgramHeader(s, &ph, m_elfData);
- m_elfData.programHeaders.append(ph);
- }
- }
- return Ok;
-}
-
-QByteArray ElfReader::readSection(const QByteArray &name)
-{
- readIt();
- int i = m_elfData.indexOf(name);
- if (i == -1)
- return QByteArray();
-
- ElfMapper mapper(this);
- if (!mapper.map())
- return QByteArray();
-
- const ElfSectionHeader &section = m_elfData.sectionHeaders.at(i);
- return QByteArray(mapper.start + section.offset, int(section.size));
-}
-
-static QByteArray cutout(const char *s)
-{
- QByteArray res(s, 80);
- const int pos = res.indexOf('\0');
- if (pos != -1)
- res.resize(pos - 1);
- return res;
-}
-
-QByteArray ElfReader::readCoreName(bool *isCore)
-{
- *isCore = false;
-
- readIt();
-
- ElfMapper mapper(this);
- if (!mapper.map())
- return QByteArray();
-
- if (m_elfData.elftype != Elf_ET_CORE)
- return QByteArray();
-
- *isCore = true;
-
- for (int i = 0, n = m_elfData.sectionHeaders.size(); i != n; ++i)
- if (m_elfData.sectionHeaders.at(i).type == Elf_SHT_NOTE) {
- const ElfSectionHeader &header = m_elfData.sectionHeaders.at(i);
- return cutout(mapper.start + header.offset + 0x40);
- }
-
- for (int i = 0, n = m_elfData.programHeaders.size(); i != n; ++i)
- if (m_elfData.programHeaders.at(i).type == Elf_PT_NOTE) {
- const ElfProgramHeader &header = m_elfData.programHeaders.at(i);
- return cutout(mapper.start + header.offset + 0xec);
- }
-
- return QByteArray();
-}
-
-int ElfData::indexOf(const QByteArray &name) const
-{
- for (int i = 0, n = sectionHeaders.size(); i != n; ++i)
- if (sectionHeaders.at(i).name == name)
- return i;
- return -1;
-}
-
-/* Helpers for reading out the .dynamic section containing the dependencies.
- * The ".dynamic" section is an array of
- * typedef struct {
- * Elf32_Sword d_tag;
- * union {
- * Elf32_Word d_val;
- * dElf32_Addr d_ptr;
- * } d_un;
- * } Elf32_Dyn
- * with entries where a tag DT_NEEDED indicates that m_val is an offset into
- * the string table ".dynstr". The documentation states that entries with the
- * tag DT_STRTAB contain an offset for the string table to be used, but that
- * has been found not to contain valid entries. */
-
-enum DynamicSectionTags {
- DT_NULL = 0,
- DT_NEEDED = 1,
- DT_STRTAB = 5,
- DT_SONAME = 14,
- DT_RPATH = 15
-};
-
-QList<QByteArray> ElfReader::dependencies()
-{
- QList<QByteArray> result;
-
- ElfMapper mapper(this);
- if (!mapper.map()) {
- m_errorString = QStringLiteral("Mapper failure");
- return result;
- }
- quint64 dynStrOffset = 0;
- quint64 dynamicOffset = 0;
- quint64 dynamicSize = 0;
-
- const QList<ElfSectionHeader> &headers = readHeaders().sectionHeaders;
- for (const ElfSectionHeader &eh : headers) {
- if (eh.name == QByteArrayLiteral(".dynstr")) {
- dynStrOffset = eh.offset;
- } else if (eh.name == QByteArrayLiteral(".dynamic")) {
- dynamicOffset = eh.offset;
- dynamicSize = eh.size;
- }
- if (dynStrOffset && dynamicOffset)
- break;
- }
-
- if (!dynStrOffset || !dynamicOffset) {
- m_errorString = QStringLiteral("Not a dynamically linked executable.");
- return result;
- }
-
- const unsigned char *dynamicData = mapper.ustart + dynamicOffset;
- const unsigned char *dynamicDataEnd = dynamicData + dynamicSize;
- while (dynamicData < dynamicDataEnd) {
- const quint32 tag = getWord(dynamicData, m_elfData);
- if (tag == DT_NULL)
- break;
- if (m_elfData.elfclass == Elf_ELFCLASS64)
- dynamicData += sizeof(quint32); // padding to d_val/d_ptr.
- if (tag == DT_NEEDED) {
- const quint32 offset = getWord(dynamicData, m_elfData);
- if (m_elfData.elfclass == Elf_ELFCLASS64)
- dynamicData += sizeof(quint32); // past d_ptr.
- const char *name = mapper.start + dynStrOffset + offset;
- result.push_back(name);
- } else {
- dynamicData += m_elfData.elfclass == Elf_ELFCLASS64 ? 8 : 4;
- }
- }
- return result;
-}
-
-QT_END_NAMESPACE
diff --git a/src/tools/windeployqt/elfreader.h b/src/tools/windeployqt/elfreader.h
deleted file mode 100644
index bea53c2ee4..0000000000
--- a/src/tools/windeployqt/elfreader.h
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#ifndef ELFREADER_H
-#define ELFREADER_H
-
-#include <QtCore/QList>
-#include <QtCore/QString>
-#include <QtCore/QtEndian>
-
-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 symbols 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;
- QList<ElfSectionHeader> sectionHeaders;
- QList<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/tools/windeployqt/main.cpp b/src/tools/windeployqt/main.cpp
index 9d483e123f..dca9132e15 100644
--- a/src/tools/windeployqt/main.cpp
+++ b/src/tools/windeployqt/main.cpp
@@ -3,6 +3,8 @@
#include "utils.h"
#include "qmlutils.h"
+#include "qtmoduleinfo.h"
+#include "qtplugininfo.h"
#include <QtCore/QCommandLineOption>
#include <QtCore/QCommandLineParser>
@@ -22,137 +24,61 @@
#define IMAGE_FILE_MACHINE_ARM64 0xaa64
#endif
+#include <QtCore/private/qconfig_p.h>
+
#include <algorithm>
+#include <cstdio>
#include <iostream>
#include <iterator>
-#include <cstdio>
+#include <unordered_map>
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-enum QtModule
-#if defined(Q_COMPILER_CLASS_ENUM) || defined(Q_CC_MSVC)
- : quint64
-#endif
-{
- QtBluetoothModule = 0x0000000000000001,
- QtConcurrentModule = 0x0000000000000002,
- QtCoreModule = 0x0000000000000004,
- QtDeclarativeModule = 0x0000000000000008,
- QtDesignerComponents = 0x0000000000000010,
- QtDesignerModule = 0x0000000000000020,
- QtGuiModule = 0x0000000000000040,
- QtHelpModule = 0x0000000000000080,
- QtMultimediaModule = 0x0000000000000100,
- QtMultimediaWidgetsModule = 0x0000000000000200,
- QtMultimediaQuickModule = 0x0000000000000400,
- QtNetworkModule = 0x0000000000000800,
- QtNfcModule = 0x0000000000001000,
- QtOpenGLModule = 0x0000000000002000,
- QtOpenGLWidgetsModule = 0x0000000000004000,
- QtPositioningModule = 0x0000000000008000,
- QtPrintSupportModule = 0x0000000000010000,
- QtQmlModule = 0x0000000000020000,
- QtQuickModule = 0x0000000000040000,
- QtQuickParticlesModule = 0x0000000000080000,
- QtScriptModule = 0x0000000000100000,
- QtScriptToolsModule = 0x0000000000200000,
- QtSensorsModule = 0x0000000000400000,
- QtSerialPortModule = 0x0000000000800000,
- QtSqlModule = 0x0000000001000000,
- QtSvgModule = 0x0000000002000000,
- QtSvgWidgetsModule = 0x0000000004000000,
- QtTestModule = 0x0000000008000000,
- QtWidgetsModule = 0x0000000010000000,
- QtWinExtrasModule = 0x0000000020000000,
- QtXmlModule = 0x0000000040000000,
- QtQuickWidgetsModule = 0x0000000100000000,
- QtWebSocketsModule = 0x0000000200000000,
- QtWebEngineCoreModule = 0x0000000800000000,
- QtWebEngineModule = 0x0000001000000000,
- QtWebEngineWidgetsModule = 0x0000002000000000,
- QtQmlToolingModule = 0x0000004000000000,
- Qt3DCoreModule = 0x0000008000000000,
- Qt3DRendererModule = 0x0000010000000000,
- Qt3DQuickModule = 0x0000020000000000,
- Qt3DQuickRendererModule = 0x0000040000000000,
- Qt3DInputModule = 0x0000080000000000,
- QtLocationModule = 0x0000100000000000,
- QtWebChannelModule = 0x0000200000000000,
- QtTextToSpeechModule = 0x0000400000000000,
- QtSerialBusModule = 0x0000800000000000,
- QtGamePadModule = 0x0001000000000000,
- Qt3DAnimationModule = 0x0002000000000000,
- QtWebViewModule = 0x0004000000000000,
- Qt3DExtrasModule = 0x0008000000000000,
- QtShaderToolsModule = 0x0010000000000000
-};
+static QtModuleInfoStore qtModuleEntries;
-struct QtModuleEntry {
- quint64 module;
- const char *option;
- const char *libraryName;
- const char *translation;
-};
+#define DECLARE_KNOWN_MODULE(name) \
+ static size_t Qt##name ## ModuleId = QtModule::InvalidId
-static QtModuleEntry qtModuleEntries[] = {
- { QtBluetoothModule, "bluetooth", "Qt6Bluetooth", nullptr },
- { QtConcurrentModule, "concurrent", "Qt6Concurrent", "qtbase" },
- { QtCoreModule, "core", "Qt6Core", "qtbase" },
- { QtDeclarativeModule, "declarative", "Qt6Declarative", "qtquick1" },
- { QtDesignerModule, "designer", "Qt6Designer", nullptr },
- { QtDesignerComponents, "designercomponents", "Qt6DesignerComponents", nullptr },
- { QtGamePadModule, "gamepad", "Qt6Gamepad", nullptr },
- { QtGuiModule, "gui", "Qt6Gui", "qtbase" },
- { QtHelpModule, "qthelp", "Qt6Help", "qt_help" },
- { QtMultimediaModule, "multimedia", "Qt6Multimedia", "qtmultimedia" },
- { QtMultimediaWidgetsModule, "multimediawidgets", "Qt6MultimediaWidgets", "qtmultimedia" },
- { QtMultimediaQuickModule, "multimediaquick", "Qt6MultimediaQuick_p", "qtmultimedia" },
- { QtNetworkModule, "network", "Qt6Network", "qtbase" },
- { QtNfcModule, "nfc", "Qt6Nfc", nullptr },
- { QtOpenGLModule, "opengl", "Qt6OpenGL", nullptr },
- { QtOpenGLWidgetsModule, "openglwidgets", "Qt6OpenGLWidgets", nullptr },
- { QtPositioningModule, "positioning", "Qt6Positioning", nullptr },
- { QtPrintSupportModule, "printsupport", "Qt6PrintSupport", nullptr },
- { QtQmlModule, "qml", "Qt6Qml", "qtdeclarative" },
- { QtQmlToolingModule, "qmltooling", "qmltooling", nullptr },
- { QtQuickModule, "quick", "Qt6Quick", "qtdeclarative" },
- { QtQuickParticlesModule, "quickparticles", "Qt6QuickParticles", nullptr },
- { QtQuickWidgetsModule, "quickwidgets", "Qt6QuickWidgets", nullptr },
- { QtScriptModule, "script", "Qt6Script", "qtscript" },
- { QtScriptToolsModule, "scripttools", "Qt6ScriptTools", "qtscript" },
- { QtSensorsModule, "sensors", "Qt6Sensors", nullptr },
- { QtSerialPortModule, "serialport", "Qt6SerialPort", "qtserialport" },
- { QtSqlModule, "sql", "Qt6Sql", "qtbase" },
- { QtSvgModule, "svg", "Qt6Svg", nullptr },
- { QtSvgWidgetsModule, "svgwidgets", "Qt6SvgWidgets", nullptr },
- { QtTestModule, "test", "Qt6Test", "qtbase" },
- { QtWebSocketsModule, "websockets", "Qt6WebSockets", nullptr },
- { QtWidgetsModule, "widgets", "Qt6Widgets", "qtbase" },
- { QtWinExtrasModule, "winextras", "Qt6WinExtras", nullptr },
- { QtXmlModule, "xml", "Qt6Xml", "qtbase" },
- { QtWebEngineCoreModule, "webenginecore", "Qt6WebEngineCore", nullptr },
- { QtWebEngineModule, "webengine", "Qt6WebEngine", "qtwebengine" },
- { QtWebEngineWidgetsModule, "webenginewidgets", "Qt6WebEngineWidgets", nullptr },
- { Qt3DCoreModule, "3dcore", "Qt63DCore", nullptr },
- { Qt3DRendererModule, "3drenderer", "Qt63DRender", nullptr },
- { Qt3DQuickModule, "3dquick", "Qt63DQuick", nullptr },
- { Qt3DQuickRendererModule, "3dquickrenderer", "Qt63DQuickRender", nullptr },
- { Qt3DInputModule, "3dinput", "Qt63DInput", nullptr },
- { Qt3DAnimationModule, "3danimation", "Qt63DAnimation", nullptr },
- { Qt3DExtrasModule, "3dextras", "Qt63DExtras", nullptr },
- { QtLocationModule, "geoservices", "Qt6Location", nullptr },
- { QtWebChannelModule, "webchannel", "Qt6WebChannel", nullptr },
- { QtTextToSpeechModule, "texttospeech", "Qt6TextToSpeech", nullptr },
- { QtSerialBusModule, "serialbus", "Qt6SerialBus", nullptr },
- { QtWebViewModule, "webview", "Qt6WebView", nullptr },
- { QtShaderToolsModule, "shadertools", "Qt6ShaderTools", nullptr }
-};
+DECLARE_KNOWN_MODULE(3DQuick);
+DECLARE_KNOWN_MODULE(Core);
+DECLARE_KNOWN_MODULE(Designer);
+DECLARE_KNOWN_MODULE(DesignerComponents);
+DECLARE_KNOWN_MODULE(Gui);
+DECLARE_KNOWN_MODULE(Qml);
+DECLARE_KNOWN_MODULE(QmlTooling);
+DECLARE_KNOWN_MODULE(Quick);
+DECLARE_KNOWN_MODULE(WebEngineCore);
+DECLARE_KNOWN_MODULE(Widgets);
-enum QtPlugin {
- QtVirtualKeyboardPlugin = 0x1
-};
+#define DEFINE_KNOWN_MODULE(name) \
+ m[QLatin1String("Qt6" #name)] = &Qt##name ## ModuleId
+
+static void assignKnownModuleIds()
+{
+ std::unordered_map<QString, size_t *> m;
+ DEFINE_KNOWN_MODULE(3DQuick);
+ DEFINE_KNOWN_MODULE(Core);
+ DEFINE_KNOWN_MODULE(Designer);
+ DEFINE_KNOWN_MODULE(DesignerComponents);
+ DEFINE_KNOWN_MODULE(Gui);
+ DEFINE_KNOWN_MODULE(Qml);
+ DEFINE_KNOWN_MODULE(QmlTooling);
+ DEFINE_KNOWN_MODULE(Quick);
+ DEFINE_KNOWN_MODULE(WebEngineCore);
+ DEFINE_KNOWN_MODULE(Widgets);
+ for (size_t i = 0; i < qtModuleEntries.size(); ++i) {
+ const QtModule &module = qtModuleEntries.moduleById(i);
+ auto it = m.find(module.name);
+ if (it == m.end())
+ continue;
+ *(it->second) = i;
+ }
+}
+
+#undef DECLARE_KNOWN_MODULE
+#undef DEFINE_KNOWN_MODULE
static const char webEngineProcessC[] = "QtWebEngineProcess";
@@ -162,14 +88,43 @@ static inline QString webProcessBinary(const char *binaryName, Platform p)
return (p & WindowsBased) ? webProcess + QStringLiteral(".exe") : webProcess;
}
-static QByteArray formatQtModules(quint64 mask, bool option = false)
+static QString moduleNameToOptionName(const QString &moduleName)
+{
+ QString result = moduleName
+ .mid(3) // strip the "Qt6" prefix
+ .toLower();
+ if (result == u"help"_s)
+ result.prepend("qt"_L1);
+ return result;
+}
+
+static QByteArray formatQtModules(const ModuleBitset &mask, bool option = false)
{
QByteArray result;
for (const auto &qtModule : qtModuleEntries) {
- if (mask & qtModule.module) {
+ if (mask.test(qtModule.id)) {
if (!result.isEmpty())
result.append(' ');
- result.append(option ? qtModule.option : qtModule.libraryName);
+ result.append(option
+ ? moduleNameToOptionName(qtModule.name).toUtf8()
+ : qtModule.name.toUtf8());
+ if (qtModule.internal)
+ result.append("Internal");
+ }
+ }
+ return result;
+}
+
+static QString formatQtPlugins(const PluginInformation &pluginInfo)
+{
+ QString result(u'\n');
+ for (const auto &pair : pluginInfo.typeMap()) {
+ result += pair.first;
+ result += u": \n";
+ for (const QString &plugin : pair.second) {
+ result += u" ";
+ result += plugin;
+ result += u'\n';
}
}
return result;
@@ -177,14 +132,17 @@ static QByteArray formatQtModules(quint64 mask, bool option = false)
static Platform platformFromMkSpec(const QString &xSpec)
{
- if (xSpec == "linux-g++"_L1)
- return Unix;
if (xSpec.startsWith("win32-"_L1)) {
if (xSpec.contains("clang-g++"_L1))
return WindowsDesktopClangMinGW;
if (xSpec.contains("clang-msvc++"_L1))
return WindowsDesktopClangMsvc;
- return xSpec.contains("g++"_L1) ? WindowsDesktopMinGW : WindowsDesktopMsvc;
+ if (xSpec.contains("arm"_L1))
+ return WindowsDesktopMsvcArm;
+ if (xSpec.contains("G++"_L1))
+ return WindowsDesktopMinGW;
+
+ return WindowsDesktopMsvc;
}
return UnknownPlatform;
}
@@ -225,12 +183,14 @@ struct Options {
bool quickImports = true;
bool translations = true;
bool systemD3dCompiler = true;
+ bool systemDxc = true;
bool compilerRunTime = false;
- unsigned disabledPlugins = 0;
bool softwareRasterizer = true;
- Platform platform = WindowsDesktopMsvc;
- quint64 additionalLibraries = 0;
- quint64 disabledLibraries = 0;
+ bool ffmpeg = true;
+ PluginSelections pluginSelections;
+ Platform platform = WindowsDesktopMsvcIntel;
+ ModuleBitset additionalLibraries;
+ ModuleBitset disabledLibraries;
unsigned updateFileFlags = 0;
QStringList qmlDirectories; // Project's QML files.
QStringList qmlImportPaths; // Custom QML module locations.
@@ -240,6 +200,8 @@ struct Options {
QStringList languages;
QString libraryDirectory;
QString pluginDirectory;
+ QString openSslRootDirectory;
+ QString qmlDirectory;
QStringList binaries;
JsonOutput *json = nullptr;
ListOption list = ListNone;
@@ -248,6 +210,8 @@ struct Options {
bool dryRun = false;
bool patchQt = true;
bool ignoreLibraryErrors = false;
+ bool deployInsightTrackerPlugin = false;
+ bool forceOpenSslPlugin = false;
};
// Return binary to be deployed from folder, ignore pre-existing web engine process.
@@ -268,14 +232,101 @@ static inline QString findBinary(const QString &directory, Platform platform)
static QString msgFileDoesNotExist(const QString & file)
{
- return u'"' + QDir::toNativeSeparators(file) + QStringLiteral("\" does not exist.");
+ return u'"' + QDir::toNativeSeparators(file) + "\" does not exist."_L1;
}
enum CommandLineParseFlag {
CommandLineParseError = 0x1,
- CommandLineParseHelpRequested = 0x2
+ CommandLineParseHelpRequested = 0x2,
+ CommandLineVersionRequested = 0x4
};
+static QCommandLineOption createQMakeOption()
+{
+ return {
+ u"qmake"_s,
+ u"Use specified qmake instead of qmake from PATH. Deprecated, use qtpaths instead."_s,
+ u"path"_s
+ };
+}
+
+static QCommandLineOption createQtPathsOption()
+{
+ return {
+ u"qtpaths"_s,
+ u"Use specified qtpaths.exe instead of qtpaths.exe from PATH."_s,
+ u"path"_s
+ };
+}
+
+static QCommandLineOption createVerboseOption()
+{
+ return {
+ u"verbose"_s,
+ u"Verbose level (0-2)."_s,
+ u"level"_s
+ };
+}
+
+static int parseEarlyArguments(const QStringList &arguments, Options *options,
+ QString *errorMessage)
+{
+ QCommandLineParser parser;
+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
+
+ QCommandLineOption qmakeOption = createQMakeOption();
+ parser.addOption(qmakeOption);
+
+ QCommandLineOption qtpathsOption = createQtPathsOption();
+ parser.addOption(qtpathsOption);
+
+ QCommandLineOption verboseOption = createVerboseOption();
+ parser.addOption(verboseOption);
+
+ // Deliberately don't check for errors. We want to ignore options we don't know about.
+ parser.parse(arguments);
+
+ if (parser.isSet(qmakeOption) && parser.isSet(qtpathsOption)) {
+ *errorMessage = QStringLiteral("-qmake and -qtpaths are mutually exclusive.");
+ return CommandLineParseError;
+ }
+
+ if (parser.isSet(qmakeOption) && optVerboseLevel >= 1)
+ std::wcerr << "Warning: -qmake option is deprecated. Use -qtpaths instead.\n";
+
+ if (parser.isSet(qtpathsOption) || parser.isSet(qmakeOption)) {
+ const QString qtpathsArg = parser.isSet(qtpathsOption) ? parser.value(qtpathsOption)
+ : parser.value(qmakeOption);
+
+ const QString qtpathsBinary = QDir::cleanPath(qtpathsArg);
+ const QFileInfo fi(qtpathsBinary);
+ if (!fi.exists()) {
+ *errorMessage = msgFileDoesNotExist(qtpathsBinary);
+ return CommandLineParseError;
+ }
+
+ if (!fi.isExecutable()) {
+ *errorMessage = u'"' + QDir::toNativeSeparators(qtpathsBinary)
+ + QStringLiteral("\" is not an executable.");
+ return CommandLineParseError;
+ }
+ options->qtpathsBinary = qtpathsBinary;
+ }
+
+ 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;
+ }
+ }
+
+ return 0;
+}
+
static inline int parseArguments(const QStringList &arguments, QCommandLineParser *parser,
Options *options, QString *errorMessage)
{
@@ -283,29 +334,21 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
using OptionPtrVector = QList<CommandLineOptionPtr>;
parser->setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
- parser->setApplicationDescription(QStringLiteral("Qt Deploy Tool ") + QT_VERSION_STR ""_L1
- + "\n\nThe simplest way to use windeployqt is to add the bin directory of your Qt\n"
+ parser->setApplicationDescription(u"Qt Deploy Tool " QT_VERSION_STR
+ "\n\nThe simplest way to use windeployqt is to add the bin directory of your Qt\n"
"installation (e.g. <QT_DIR\\bin>) to the PATH variable and then run:\n windeployqt <path-to-app-binary>\n\n"
- "If your application uses Qt Quick, run:\n windeployqt --qmldir <path-to-app-qml-files> <path-to-app-binary>"_L1);
+ "If your application uses Qt Quick, run:\n windeployqt --qmldir <path-to-app-qml-files> <path-to-app-binary>"_s);
const QCommandLineOption helpOption = parser->addHelpOption();
- parser->addVersionOption();
+ const QCommandLineOption versionOption = parser->addVersionOption();
QCommandLineOption dirOption(QStringLiteral("dir"),
QStringLiteral("Use directory instead of binary directory."),
QStringLiteral("directory"));
parser->addOption(dirOption);
- QCommandLineOption qmakeOption(QStringLiteral("qmake"),
- QStringLiteral("Use specified qmake instead of qmake from PATH. "
- "Deprecated, use qtpaths instead."),
- QStringLiteral("path"));
- parser->addOption(qmakeOption);
-
- QCommandLineOption qtpathsOption(
- QStringLiteral("qtpaths"),
- QStringLiteral("Use specified qtpaths.exe instead of qtpaths.exe from PATH."),
- QStringLiteral("path"));
- parser->addOption(qtpathsOption);
+ // Add early options to have them available in the help text.
+ parser->addOption(createQMakeOption());
+ parser->addOption(createQtPathsOption());
QCommandLineOption libDirOption(QStringLiteral("libdir"),
QStringLiteral("Copy libraries to path."),
@@ -317,6 +360,17 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
QStringLiteral("path"));
parser->addOption(pluginDirOption);
+ const QCommandLineOption translationDirOption(
+ u"translationdir"_s,
+ u"Copy translations to path."_s,
+ u"path"_s);
+ parser->addOption(translationDirOption);
+
+ QCommandLineOption qmlDeployDirOption(QStringLiteral("qml-deploy-dir"),
+ QStringLiteral("Copy qml files to path."),
+ QStringLiteral("path"));
+ parser->addOption(qmlDeployDirOption);
+
QCommandLineOption debugOption(QStringLiteral("debug"),
QStringLiteral("Assume debug binaries."));
parser->addOption(debugOption);
@@ -352,6 +406,30 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
QStringLiteral("Skip plugin deployment."));
parser->addOption(noPluginsOption);
+ QCommandLineOption includeSoftPluginsOption(QStringLiteral("include-soft-plugins"),
+ QStringLiteral("Include in the deployment all relevant plugins by taking into account all soft dependencies."));
+ parser->addOption(includeSoftPluginsOption);
+
+ QCommandLineOption skipPluginTypesOption(QStringLiteral("skip-plugin-types"),
+ QStringLiteral("A comma-separated list of plugin types that are not deployed (qmltooling,generic)."),
+ QStringLiteral("plugin types"));
+ parser->addOption(skipPluginTypesOption);
+
+ QCommandLineOption addPluginTypesOption(QStringLiteral("add-plugin-types"),
+ QStringLiteral("A comma-separated list of plugin types that will be added to deployment (imageformats,iconengines)"),
+ QStringLiteral("plugin types"));
+ parser->addOption(addPluginTypesOption);
+
+ QCommandLineOption includePluginsOption(QStringLiteral("include-plugins"),
+ QStringLiteral("A comma-separated list of individual plugins that will be added to deployment (scene2d,qjpeg)"),
+ QStringLiteral("plugins"));
+ parser->addOption(includePluginsOption);
+
+ QCommandLineOption excludePluginsOption(QStringLiteral("exclude-plugins"),
+ QStringLiteral("A comma-separated list of individual plugins that will not be deployed (qsvg,qpdf)"),
+ QStringLiteral("plugins"));
+ parser->addOption(excludePluginsOption);
+
QCommandLineOption noLibraryOption(QStringLiteral("no-libraries"),
QStringLiteral("Skip library deployment."));
parser->addOption(noLibraryOption);
@@ -384,15 +462,15 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
QStringLiteral("Skip deployment of the system D3D compiler."));
parser->addOption(noSystemD3DCompilerOption);
+ QCommandLineOption noSystemDxcOption(QStringLiteral("no-system-dxc-compiler"),
+ QStringLiteral("Skip deployment of the system DXC (dxcompiler.dll, dxil.dll)."));
+ parser->addOption(noSystemDxcOption);
+
QCommandLineOption compilerRunTimeOption(QStringLiteral("compiler-runtime"),
QStringLiteral("Deploy compiler runtime (Desktop only)."));
parser->addOption(compilerRunTimeOption);
- QCommandLineOption noVirtualKeyboardOption(QStringLiteral("no-virtualkeyboard"),
- QStringLiteral("Disable deployment of the Virtual Keyboard."));
- parser->addOption(noVirtualKeyboardOption);
-
QCommandLineOption noCompilerRunTimeOption(QStringLiteral("no-compiler-runtime"),
QStringLiteral("Do not deploy compiler runtime (Desktop only)."));
parser->addOption(noCompilerRunTimeOption);
@@ -405,6 +483,20 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
QStringLiteral("Do not deploy the software rasterizer library."));
parser->addOption(suppressSoftwareRasterizerOption);
+ QCommandLineOption noFFmpegOption(QStringLiteral("no-ffmpeg"),
+ QStringLiteral("Do not deploy the FFmpeg libraries."));
+ parser->addOption(noFFmpegOption);
+
+ QCommandLineOption forceOpenSslOption(QStringLiteral("force-openssl"),
+ QStringLiteral("Deploy openssl plugin but ignore openssl library dependency"));
+ parser->addOption(forceOpenSslOption);
+
+ QCommandLineOption openSslRootOption(QStringLiteral("openssl-root"),
+ QStringLiteral("Directory containing openSSL libraries."),
+ QStringLiteral("directory"));
+ parser->addOption(openSslRootOption);
+
+
QCommandLineOption listOption(QStringLiteral("list"),
"Print only the names of the files copied.\n"
"Available options:\n"
@@ -418,22 +510,29 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
QStringLiteral("option"));
parser->addOption(listOption);
- QCommandLineOption verboseOption(QStringLiteral("verbose"),
- QStringLiteral("Verbose level (0-2)."),
- QStringLiteral("level"));
- parser->addOption(verboseOption);
+ // Add early option to have it available in the help text.
+ parser->addOption(createVerboseOption());
parser->addPositionalArgument(QStringLiteral("[files]"),
QStringLiteral("Binaries or directory containing the binary."));
+ QCommandLineOption deployInsightTrackerOption(QStringLiteral("deploy-insighttracker"),
+ QStringLiteral("Deploy insight tracker plugin."));
+ // The option will be added to the parser if the module is available (see block below)
+ bool insightTrackerModuleAvailable = false;
+
OptionPtrVector enabledModuleOptions;
OptionPtrVector disabledModuleOptions;
- const int qtModulesCount = int(sizeof(qtModuleEntries) / sizeof(QtModuleEntry));
+ const size_t qtModulesCount = qtModuleEntries.size();
enabledModuleOptions.reserve(qtModulesCount);
disabledModuleOptions.reserve(qtModulesCount);
- for (int i = 0; i < qtModulesCount; ++i) {
- const QString option = QLatin1StringView(qtModuleEntries[i].option);
- const QString name = QLatin1StringView(qtModuleEntries[i].libraryName);
+ for (const QtModule &module : qtModuleEntries) {
+ const QString option = moduleNameToOptionName(module.name);
+ const QString name = module.name;
+ if (name == u"InsightTracker") {
+ parser->addOption(deployInsightTrackerOption);
+ insightTrackerModuleAvailable = true;
+ }
const QString enabledDescription = QStringLiteral("Add ") + name + QStringLiteral(" module.");
CommandLineOptionPtr enabledOption(new QCommandLineOption(option, enabledDescription));
parser->addOption(*enabledOption.data());
@@ -448,6 +547,8 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
const bool success = parser->parse(arguments);
if (parser->isSet(helpOption))
return CommandLineParseHelpRequested;
+ if (parser->isSet(versionOption))
+ return CommandLineVersionRequested;
if (!success) {
*errorMessage = parser->errorText();
return CommandLineParseError;
@@ -455,28 +556,43 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
options->libraryDirectory = parser->value(libDirOption);
options->pluginDirectory = parser->value(pluginDirOption);
+ options->translationsDirectory = parser->value(translationDirOption);
+ options->qmlDirectory = parser->value(qmlDeployDirOption);
options->plugins = !parser->isSet(noPluginsOption);
options->libraries = !parser->isSet(noLibraryOption);
options->translations = !parser->isSet(noTranslationOption);
if (parser->isSet(translationOption))
options->languages = parser->value(translationOption).split(u',');
options->systemD3dCompiler = !parser->isSet(noSystemD3DCompilerOption);
+ options->systemDxc = !parser->isSet(noSystemDxcOption);
options->quickImports = !parser->isSet(noQuickImportOption);
// default to deployment of compiler runtime for windows desktop configurations
- if (options->platform == WindowsDesktopMinGW || options->platform == WindowsDesktopMsvc
+ if (options->platform == WindowsDesktopMinGW || options->platform.testFlags(WindowsDesktopMsvc)
|| parser->isSet(compilerRunTimeOption))
options->compilerRunTime = true;
if (parser->isSet(noCompilerRunTimeOption))
options->compilerRunTime = false;
- if (options->compilerRunTime && options->platform != WindowsDesktopMinGW && options->platform != WindowsDesktopMsvc) {
+ if (options->compilerRunTime && options->platform != WindowsDesktopMinGW
+ && !options->platform.testFlags(WindowsDesktopMsvc)) {
*errorMessage = QStringLiteral("Deployment of the compiler runtime is implemented for Desktop MSVC/g++ only.");
return CommandLineParseError;
}
- if (parser->isSet(noVirtualKeyboardOption))
- options->disabledPlugins |= QtVirtualKeyboardPlugin;
+ options->pluginSelections.includeSoftPlugins = parser->isSet(includeSoftPluginsOption);
+
+ if (parser->isSet(skipPluginTypesOption))
+ options->pluginSelections.disabledPluginTypes = parser->value(skipPluginTypesOption).split(u',');
+
+ if (parser->isSet(addPluginTypesOption))
+ options->pluginSelections.enabledPluginTypes = parser->value(addPluginTypesOption).split(u',');
+
+ if (parser->isSet(includePluginsOption))
+ options->pluginSelections.includedPlugins = parser->value(includePluginsOption).split(u',');
+
+ if (parser->isSet(excludePluginsOption))
+ options->pluginSelections.excludedPlugins = parser->value(excludePluginsOption).split(u',');
if (parser->isSet(releaseWithDebugInfoOption))
std::wcerr << "Warning: " << releaseWithDebugInfoOption.names().first() << " is obsolete.";
@@ -502,6 +618,20 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
if (parser->isSet(suppressSoftwareRasterizerOption))
options->softwareRasterizer = false;
+ if (parser->isSet(noFFmpegOption))
+ options->ffmpeg = false;
+
+ if (parser->isSet(forceOpenSslOption))
+ options->forceOpenSslPlugin = true;
+
+ if (parser->isSet(openSslRootOption))
+ options->openSslRootDirectory = parser->value(openSslRootOption);
+
+ if (options->forceOpenSslPlugin && !options->openSslRootDirectory.isEmpty()) {
+ *errorMessage = QStringLiteral("force-openssl and openssl-root are mutually exclusive");
+ return CommandLineParseError;
+ }
+
if (parser->isSet(forceOption))
options->updateFileFlags |= ForceUpdateFile;
if (parser->isSet(dryRunOption)) {
@@ -511,19 +641,21 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
options->patchQt = !parser->isSet(noPatchQtOption);
options->ignoreLibraryErrors = parser->isSet(ignoreErrorOption);
-
- for (int i = 0; i < qtModulesCount; ++i) {
- if (parser->isSet(*enabledModuleOptions.at(i)))
- options->additionalLibraries |= qtModuleEntries[i].module;
- if (parser->isSet(*disabledModuleOptions.at(i)))
- options->disabledLibraries |= qtModuleEntries[i].module;
+ if (insightTrackerModuleAvailable)
+ options->deployInsightTrackerPlugin = parser->isSet(deployInsightTrackerOption);
+
+ for (const QtModule &module : qtModuleEntries) {
+ if (parser->isSet(*enabledModuleOptions.at(module.id)))
+ options->additionalLibraries[module.id] = 1;
+ if (parser->isSet(*disabledModuleOptions.at(module.id)))
+ options->disabledLibraries[module.id] = 1;
}
// Add some dependencies
- if (options->additionalLibraries & QtQuickModule)
- options->additionalLibraries |= QtQmlModule;
- if (options->additionalLibraries & QtDesignerComponents)
- options->additionalLibraries |= QtDesignerModule;
+ if (options->additionalLibraries.test(QtQuickModuleId))
+ options->additionalLibraries[QtQmlModuleId] = 1;
+ if (options->additionalLibraries.test(QtDesignerComponentsModuleId))
+ options->additionalLibraries[QtDesignerModuleId] = 1;
if (parser->isSet(listOption)) {
const QString value = parser->value(listOption);
@@ -544,16 +676,6 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
if (parser->isSet(jsonOption) || options->list) {
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();
@@ -565,33 +687,6 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
if (parser->isSet(dirOption))
options->directory = parser->value(dirOption);
- if (parser->isSet(qmakeOption) && parser->isSet(qtpathsOption)) {
- *errorMessage = QStringLiteral("-qmake and -qtpaths are mutually exclusive.");
- return CommandLineParseError;
- }
-
- if (parser->isSet(qmakeOption) && optVerboseLevel >= 1)
- std::wcerr << "Warning: -qmake option is deprecated. Use -qpaths instead.\n";
-
- if (parser->isSet(qtpathsOption) || parser->isSet(qmakeOption)) {
- const QString qtpathsArg = parser->isSet(qtpathsOption) ? parser->value(qtpathsOption)
- : parser->value(qmakeOption);
-
- const QString qtpathsBinary = QDir::cleanPath(qtpathsArg);
- const QFileInfo fi(qtpathsBinary);
- if (!fi.exists()) {
- *errorMessage = msgFileDoesNotExist(qtpathsBinary);
- return CommandLineParseError;
- }
-
- if (!fi.isExecutable()) {
- *errorMessage = u'"' + QDir::toNativeSeparators(qtpathsBinary)
- + QStringLiteral("\" is not an executable.");
- return CommandLineParseError;
- }
- options->qtpathsBinary = qtpathsBinary;
- }
-
if (parser->isSet(qmlDirOption))
options->qmlDirectories = parser->values(qmlDirOption);
@@ -625,6 +720,7 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
} // directory.
// Remaining files or plugin directories
+ bool multipleDirs = false;
for (int i = 1; i < posArgs.size(); ++i) {
const QFileInfo fi(QDir::cleanPath(posArgs.at(i)));
const QString path = fi.absoluteFilePath();
@@ -638,10 +734,17 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
for (const QString &library : libraries)
options->binaries.append(path + u'/' + library);
} else {
+ if (!parser->isSet(dirOption) && fi.absolutePath() != options->directory)
+ multipleDirs = true;
options->binaries.append(path);
}
}
- options->translationsDirectory = options->directory + "/translations"_L1;
+ if (multipleDirs) {
+ std::wcerr << "Warning: using binaries from different directories, deploying to following path: "
+ << options->directory << '\n' << "To disable this warning, use the --dir option\n";
+ }
+ if (options->translationsDirectory.isEmpty())
+ options->translationsDirectory = options->directory + "/translations"_L1;
return 0;
}
@@ -658,20 +761,37 @@ static inline QString lineBreak(QString s)
return s;
}
-static inline QString helpText(const QCommandLineParser &p)
+static inline QString helpText(const QCommandLineParser &p, const PluginInformation &pluginInfo)
{
QString result = p.helpText();
// Replace the default-generated text which is too long by a short summary
// explaining how to enable single libraries.
- const qsizetype moduleStart = result.indexOf("\n --bluetooth"_L1);
+ if (qtModuleEntries.size() == 0)
+ return result;
+ const QtModule &firstModule = qtModuleEntries.moduleById(0);
+ const QString firstModuleOption = moduleNameToOptionName(firstModule.name);
+ const qsizetype moduleStart = result.indexOf("\n --"_L1 + firstModuleOption);
const qsizetype argumentsStart = result.lastIndexOf("\nArguments:"_L1);
if (moduleStart >= argumentsStart)
return result;
- QString moduleHelp =
+ QString moduleHelp;
+ moduleHelp +=
"\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"_L1;
- moduleHelp += lineBreak(QString::fromLatin1(formatQtModules(0xFFFFFFFFFFFFFFFFull, true)));
- moduleHelp += u'\n';
+ ModuleBitset mask;
+ moduleHelp += lineBreak(QString::fromLatin1(formatQtModules(mask.set(), true)));
+ moduleHelp += u"\n\n";
+ moduleHelp +=
+ u"Qt plugins can be included or excluded individually or by type.\n"
+ u"To deploy or block plugins individually, use the --include-plugins\n"
+ u"and --exclude-plugins options (--include-plugins qjpeg,qsvgicon)\n"
+ u"You can also use the --skip-plugin-types or --add-plugin-types to\n"
+ u"achieve similar results with entire plugin groups, like imageformats, e.g.\n"
+ u"(--add-plugin-types imageformats,iconengines). Exclusion always takes\n"
+ u"precedence over inclusion, and types take precedence over specific plugins.\n"
+ u"For example, including qjpeg, but skipping imageformats, will NOT deploy qjpeg.\n"
+ u"\nDetected available plugins:\n";
+ moduleHelp += formatQtPlugins(pluginInfo);
result.replace(moduleStart, argumentsStart - moduleStart, moduleHelp);
return result;
}
@@ -695,7 +815,8 @@ static bool findDependentQtLibraries(const QString &qtBinDir, const QString &bin
QStringList dependentLibs;
if (directDependencyCount)
*directDependencyCount = 0;
- if (!readExecutable(binary, platform, errorMessage, &dependentLibs, wordSize, isDebug, machineArch)) {
+ if (!readPeExecutable(binary, errorMessage, &dependentLibs, wordSize, isDebug,
+ platform == WindowsDesktopMinGW, machineArch)) {
errorMessage->prepend("Unable to find dependent libraries of "_L1 +
QDir::toNativeSeparators(binary) + " :"_L1);
return false;
@@ -703,7 +824,7 @@ static bool findDependentQtLibraries(const QString &qtBinDir, const QString &bin
// 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();
- for (const QString &lib : qAsConst(dependentLibs)) {
+ for (const QString &lib : std::as_const(dependentLibs)) {
if (isQtModule(lib)) {
const QString path = normalizeFileName(qtBinDir + u'/' + QFileInfo(lib).fileName());
if (!result->contains(path))
@@ -760,13 +881,19 @@ public:
SkipSources = 0x2
};
- explicit QmlDirectoryFileEntryFunction(Platform platform, DebugMatchMode debugMatchMode, unsigned flags)
+ explicit QmlDirectoryFileEntryFunction(
+ const QString &moduleSourcePath, Platform platform, DebugMatchMode debugMatchMode, unsigned flags)
: m_flags(flags), m_qmlNameFilter(QmlDirectoryFileEntryFunction::qmlNameFilters(flags))
- , m_dllFilter(platform, debugMatchMode)
+ , m_dllFilter(platform, debugMatchMode), m_moduleSourcePath(moduleSourcePath)
{}
QStringList operator()(const QDir &dir) const
{
+ if (moduleSourceDir(dir).canonicalPath() != m_moduleSourcePath) {
+ // If we're in a different module, return nothing.
+ return {};
+ }
+
QStringList result;
const QStringList &libraries = m_dllFilter(dir);
for (const QString &library : libraries) {
@@ -782,6 +909,17 @@ public:
}
private:
+ static QDir moduleSourceDir(const QDir &dir)
+ {
+ QDir moduleSourceDir = dir;
+ while (!moduleSourceDir.exists(QStringLiteral("qmldir"))) {
+ if (!moduleSourceDir.cdUp()) {
+ return {};
+ }
+ }
+ return moduleSourceDir;
+ }
+
static inline QStringList qmlNameFilters(unsigned flags)
{
QStringList result;
@@ -798,63 +936,10 @@ private:
const unsigned m_flags;
NameFilterFileEntryFunction m_qmlNameFilter;
DllDirectoryFileEntryFunction m_dllFilter;
+ QString m_moduleSourcePath;
};
-struct PluginModuleMapping
-{
- const char *directoryName;
- quint64 module;
-};
-
-static const PluginModuleMapping pluginModuleMappings[] =
-{
- {"qml1tooling", QtDeclarativeModule},
-#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
- {"gamepads", QtGamePadModule},
-#endif
- {"accessible", QtGuiModule},
- {"iconengines", QtGuiModule},
- {"imageformats", QtGuiModule},
- {"platforms", QtGuiModule},
- {"platforminputcontexts", QtGuiModule},
- {"virtualkeyboard", QtGuiModule},
- {"geoservices", QtLocationModule},
- {"audio", QtMultimediaModule},
- {"mediaservice", QtMultimediaModule},
- {"playlistformats", QtMultimediaModule},
- {"networkaccess", QtNetworkModule},
- {"networkinformation", QtNetworkModule},
- {"tls", QtNetworkModule},
- {"position", QtPositioningModule},
- {"printsupport", QtPrintSupportModule},
- {"scenegraph", QtQuickModule},
- {"qmltooling", QtQuickModule | QtQmlToolingModule},
- {"sensors", QtSensorsModule},
- {"sensorgestures", QtSensorsModule},
- {"canbus", QtSerialBusModule},
- {"sqldrivers", QtSqlModule},
-#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
- {"texttospeech", QtTextToSpeechModule},
-#endif
- {"qtwebengine", QtWebEngineModule | QtWebEngineCoreModule | QtWebEngineWidgetsModule},
- {"styles", QtWidgetsModule},
- {"sceneparsers", Qt3DRendererModule},
- {"renderers", Qt3DRendererModule | QtShaderToolsModule},
- {"renderplugins", Qt3DRendererModule},
- {"geometryloaders", Qt3DRendererModule},
- {"webview", QtWebViewModule}
-};
-
-static inline quint64 qtModuleForPlugin(const QString &subDirName)
-{
- const auto end = std::end(pluginModuleMappings);
- const auto result =
- std::find_if(std::begin(pluginModuleMappings), end,
- [&subDirName] (const PluginModuleMapping &m) { return subDirName == QLatin1StringView(m.directoryName); });
- return result != end ? result->module : 0; // "designer"
-}
-
-static quint64 qtModule(QString module, const QString &infix)
+static qint64 qtModule(QString module, const QString &infix)
{
// Match needle 'path/Qt6Core<infix><d>.dll' or 'path/libQt6Core<infix>.so.5.0'
const qsizetype lastSlashPos = module.lastIndexOf(u'/');
@@ -869,104 +954,198 @@ static quint64 qtModule(QString module, const QString &infix)
module.truncate(endPos);
// That should leave us with 'Qt6Core<d>'.
for (const auto &qtModule : qtModuleEntries) {
- const QLatin1StringView libraryName(qtModule.libraryName);
+ const QString &libraryName = qtModule.name;
if (module == libraryName
|| (module.size() == libraryName.size() + 1 && module.startsWith(libraryName))) {
- return qtModule.module;
+ return qtModule.id;
}
}
- return 0;
+ std::wcerr << "Warning: module " << qPrintable(module) << " could not be found\n";
+ return -1;
}
-QStringList findQtPlugins(quint64 *usedQtModules, quint64 disabledQtModules,
- unsigned disabledPlugins,
- const QString &qtPluginsDirName, const QString &libraryLocation,
- const QString &infix,
- DebugMatchMode debugMatchModeIn, Platform platform, QString *platformPlugin)
+// Return the path if a plugin is to be deployed
+static QString deployPlugin(const QString &plugin, const QDir &subDir, const bool dueToModule,
+ const DebugMatchMode &debugMatchMode, ModuleBitset *pluginNeededQtModules,
+ const ModuleBitset &disabledQtModules,
+ const PluginSelections &pluginSelections, const QString &libraryLocation,
+ const QString &infix, Platform platform,
+ bool deployInsightTrackerPlugin, bool deployOpenSslPlugin)
{
+ const QString subDirName = subDir.dirName();
+ // Filter out disabled plugins
+ if (optVerboseLevel && pluginSelections.disabledPluginTypes.contains(subDirName)) {
+ std::wcout << "Skipping plugin " << plugin << " due to skipped plugin type " << subDirName << '\n';
+ return {};
+ }
+ if (optVerboseLevel && subDirName == u"generic" && plugin.contains(u"qinsighttracker")
+ && !deployInsightTrackerPlugin) {
+ std::wcout << "Skipping plugin " << plugin
+ << ". Use -deploy-insighttracker if you want to use it.\n";
+ return {};
+ }
+ if (optVerboseLevel && subDirName == u"tls" && plugin.contains(u"qopensslbackend")
+ && !deployOpenSslPlugin) {
+ std::wcout << "Skipping plugin " << plugin
+ << ". Use -force_openssl or specify -openssl-root if you want to use it.\n";
+ return {};
+ }
+
+ const int dotIndex = plugin.lastIndexOf(u'.');
+ // Strip the .dll from the name, and an additional 'd' if it's a debug library with the 'd'
+ // suffix
+ const int stripIndex = debugMatchMode == MatchDebug && platformHasDebugSuffix(platform)
+ ? dotIndex - 1
+ : dotIndex;
+ const QString pluginName = plugin.first(stripIndex);
+
+ if (optVerboseLevel && pluginSelections.excludedPlugins.contains(pluginName)) {
+ std::wcout << "Skipping plugin " << plugin << " due to exclusion option" << '\n';
+ return {};
+ }
+
+ // By default, only deploy qwindows.dll
+ if (subDirName == u"platforms"
+ && !(pluginSelections.includedPlugins.contains(pluginName)
+ || (pluginSelections.enabledPluginTypes.contains(subDirName)))
+ && !pluginName.startsWith(u"qwindows")) {
+ return {};
+ }
+
+ const QString pluginPath = subDir.absoluteFilePath(plugin);
+
+ // If dueToModule is false, check if the user included the plugin or the entire type. In the
+ // former's case, only deploy said plugin and not all plugins of that type.
+ const bool requiresPlugin = pluginSelections.includedPlugins.contains(pluginName)
+ || pluginSelections.enabledPluginTypes.contains(subDirName);
+ if (!dueToModule && !requiresPlugin)
+ return {};
+
+ // Deploy QUiTools plugins as is without further dependency checking.
+ // The user needs to ensure all required libraries are present (would
+ // otherwise pull QtWebEngine for its plugin).
+ if (subDirName == u"designer")
+ return pluginPath;
+
+ QStringList dependentQtLibs;
QString errorMessage;
+ if (findDependentQtLibraries(libraryLocation, pluginPath, platform,
+ &errorMessage, &dependentQtLibs)) {
+ for (int d = 0; d < dependentQtLibs.size(); ++d) {
+ const qint64 module = qtModule(dependentQtLibs.at(d), infix);
+ if (module >= 0)
+ (*pluginNeededQtModules)[module] = 1;
+ }
+ } else {
+ std::wcerr << "Warning: Cannot determine dependencies of "
+ << QDir::toNativeSeparators(pluginPath) << ": " << errorMessage << '\n';
+ }
+
+ ModuleBitset disabledNeededQtModules;
+ disabledNeededQtModules = *pluginNeededQtModules & disabledQtModules;
+ if (disabledNeededQtModules.any()) {
+ if (optVerboseLevel) {
+ std::wcout << "Skipping plugin " << plugin
+ << " due to disabled dependencies ("
+ << formatQtModules(disabledNeededQtModules).constData() << ").\n";
+ }
+ return {};
+ }
+
+ return pluginPath;
+}
+
+static bool needsPluginType(const QString &subDirName, const PluginInformation &pluginInfo,
+ const PluginSelections &pluginSelections)
+{
+ bool needsTypeForPlugin = false;
+ for (const QString &plugin: pluginSelections.includedPlugins) {
+ if (pluginInfo.isTypeForPlugin(subDirName, plugin))
+ needsTypeForPlugin = true;
+ }
+ return (pluginSelections.enabledPluginTypes.contains(subDirName) || needsTypeForPlugin);
+}
+
+QStringList findQtPlugins(ModuleBitset *usedQtModules, const ModuleBitset &disabledQtModules,
+ const PluginInformation &pluginInfo, const PluginSelections &pluginSelections,
+ const QString &qtPluginsDirName, const QString &libraryLocation,
+ const QString &infix, DebugMatchMode debugMatchModeIn, Platform platform,
+ QString *platformPlugin, bool deployInsightTrackerPlugin,
+ bool deployOpenSslPlugin)
+{
if (qtPluginsDirName.isEmpty())
return QStringList();
QDir pluginsDir(qtPluginsDirName);
QStringList result;
- const QFileInfoList &pluginDirs = pluginsDir.entryInfoList(QStringList("*"_L1), QDir::Dirs | QDir::NoDotAndDotDot);
+ bool missingQtModulesAdded = false;
+ const QFileInfoList &pluginDirs = pluginsDir.entryInfoList(QStringList(u"*"_s), QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo &subDirFi : pluginDirs) {
const QString subDirName = subDirFi.fileName();
- const quint64 module = qtModuleForPlugin(subDirName);
- if (module & *usedQtModules) {
- const DebugMatchMode debugMatchMode = (module & QtWebEngineCoreModule)
+ const size_t module = qtModuleEntries.moduleIdForPluginType(subDirName);
+ if (module == QtModule::InvalidId) {
+ if (optVerboseLevel > 1) {
+ std::wcerr << "No Qt module found for plugin type \"" << subDirName << "\".\n";
+ }
+ continue;
+ }
+ const bool dueToModule = usedQtModules->test(module);
+ if (dueToModule || needsPluginType(subDirName, pluginInfo, pluginSelections)) {
+ const DebugMatchMode debugMatchMode = (module == QtWebEngineCoreModuleId)
? MatchDebugOrRelease // QTBUG-44331: Debug detection does not work for webengine, deploy all.
: debugMatchModeIn;
QDir subDir(subDirFi.absoluteFilePath());
- // Filter out disabled plugins
- if ((disabledPlugins & QtVirtualKeyboardPlugin) && subDirName == "virtualkeyboard"_L1)
- continue;
- if (disabledQtModules & QtQmlToolingModule && subDirName == "qmltooling"_L1)
- continue;
- // Filter for platform or any.
- QString filter;
+ if (optVerboseLevel)
+ std::wcout << "Adding in plugin type " << subDirFi.baseName() << " for module: " << qtModuleEntries.moduleById(module).name << '\n';
+
const bool isPlatformPlugin = subDirName == "platforms"_L1;
- if (isPlatformPlugin) {
- switch (platform) {
- case WindowsDesktopMsvc:
- case WindowsDesktopMinGW:
- filter = QStringLiteral("qwindows");
- break;
- case Unix:
- filter = QStringLiteral("libqxcb");
- break;
- case UnknownPlatform:
- break;
- }
- } else {
- filter = "*"_L1;
- }
- const QStringList plugins = findSharedLibraries(subDir, platform, debugMatchMode, filter);
+ const QStringList plugins =
+ findSharedLibraries(subDir, platform, debugMatchMode, QString());
for (const QString &plugin : plugins) {
- // Filter out disabled plugins
- if ((disabledPlugins & QtVirtualKeyboardPlugin)
- && plugin.startsWith("qtvirtualkeyboardplugin"_L1)) {
- continue;
- }
- const QString pluginPath = subDir.absoluteFilePath(plugin);
- if (isPlatformPlugin)
- *platformPlugin = pluginPath;
- QStringList dependentQtLibs;
- quint64 neededModules = 0;
- if (findDependentQtLibraries(libraryLocation, pluginPath, platform, &errorMessage, &dependentQtLibs)) {
- for (int d = 0; d < dependentQtLibs.size(); ++ d)
- neededModules |= qtModule(dependentQtLibs.at(d), infix);
- } else {
- std::wcerr << "Warning: Cannot determine dependencies of "
- << QDir::toNativeSeparators(pluginPath) << ": " << errorMessage << '\n';
- }
- if (const quint64 missingModules = neededModules & disabledQtModules) {
- if (optVerboseLevel) {
- std::wcout << "Skipping plugin " << plugin
- << " due to disabled dependencies ("
- << formatQtModules(missingModules).constData() << ").\n";
- }
- } else {
- if (const quint64 missingModules = (neededModules & ~*usedQtModules)) {
+ ModuleBitset pluginNeededQtModules;
+ const QString pluginPath =
+ deployPlugin(plugin, subDir, dueToModule, debugMatchMode, &pluginNeededQtModules,
+ disabledQtModules, pluginSelections, libraryLocation, infix,
+ platform, deployInsightTrackerPlugin, deployOpenSslPlugin);
+ if (!pluginPath.isEmpty()) {
+ if (isPlatformPlugin && plugin.startsWith(u"qwindows"))
+ *platformPlugin = subDir.absoluteFilePath(plugin);
+ result.append(pluginPath);
+
+ const ModuleBitset missingModules = (pluginNeededQtModules & ~*usedQtModules);
+ if (missingModules.any()) {
*usedQtModules |= missingModules;
- if (optVerboseLevel)
- std::wcout << "Adding " << formatQtModules(missingModules).constData() << " for " << plugin << '\n';
+ missingQtModulesAdded = true;
+ if (optVerboseLevel) {
+ std::wcout << "Adding " << formatQtModules(missingModules).constData()
+ << " for " << plugin << " from plugin type: " << subDirName << '\n';
+ }
}
- result.append(pluginPath);
}
} // for filter
} // type matches
} // for plugin folder
+
+ // If missing Qt modules were added during plugin deployment make additional pass, because we may need
+ // additional plugins.
+ if (pluginSelections.includeSoftPlugins && missingQtModulesAdded) {
+ if (optVerboseLevel) {
+ std::wcout << "Performing additional pass of finding Qt plugins due to updated Qt module list: "
+ << formatQtModules(*usedQtModules).constData() << "\n";
+ }
+ return findQtPlugins(usedQtModules, disabledQtModules, pluginInfo, pluginSelections, qtPluginsDirName,
+ libraryLocation, infix, debugMatchModeIn, platform, platformPlugin,
+ deployInsightTrackerPlugin, deployOpenSslPlugin);
+ }
+
return result;
}
-static QStringList translationNameFilters(quint64 modules, const QString &prefix)
+static QStringList translationNameFilters(const ModuleBitset &modules, const QString &prefix)
{
QStringList result;
for (const auto &qtModule : qtModuleEntries) {
- if ((qtModule.module & modules) && qtModule.translation) {
- const QString name = QLatin1StringView(qtModule.translation) +
- u'_' + prefix + QStringLiteral(".qm");
+ if (modules.test(qtModule.id) && !qtModule.translationCatalog.isEmpty()) {
+ const QString name = qtModule.translationCatalog + u'_' + prefix + ".qm"_L1;
if (!result.contains(name))
result.push_back(name);
}
@@ -974,7 +1153,7 @@ static QStringList translationNameFilters(quint64 modules, const QString &prefix
return result;
}
-static bool deployTranslations(const QString &sourcePath, quint64 usedQtModules,
+static bool deployTranslations(const QString &sourcePath, const ModuleBitset &usedQtModules,
const QString &target, const Options &options,
QString *errorMessage)
{
@@ -998,7 +1177,7 @@ static bool deployTranslations(const QString &sourcePath, quint64 usedQtModules,
const QString absoluteTarget = QFileInfo(target).absoluteFilePath();
const QString binary = QStringLiteral("lconvert");
QStringList arguments;
- for (const QString &prefix : qAsConst(prefixes)) {
+ for (const QString &prefix : std::as_const(prefixes)) {
arguments.clear();
const QString targetFile = QStringLiteral("qt_") + prefix + QStringLiteral(".qm");
arguments.append(QStringLiteral("-o"));
@@ -1006,7 +1185,12 @@ static bool deployTranslations(const QString &sourcePath, quint64 usedQtModules,
if (options.json)
options.json->addFile(sourcePath + u'/' + targetFile, absoluteTarget);
arguments.append(QDir::toNativeSeparators(targetFilePath));
- const QFileInfoList &langQmFiles = sourceDir.entryInfoList(translationNameFilters(usedQtModules, prefix));
+ const QStringList translationFilters = translationNameFilters(usedQtModules, prefix);
+ if (translationFilters.isEmpty()){
+ std::wcerr << "Warning: translation catalogs are all empty, skipping translation deployment\n";
+ return true;
+ }
+ const QFileInfoList &langQmFiles = sourceDir.entryInfoList(translationFilters);
for (const QFileInfo &langQmFileFi : langQmFiles) {
if (options.json) {
options.json->addFile(langQmFileFi.absoluteFilePath(),
@@ -1026,32 +1210,82 @@ static bool deployTranslations(const QString &sourcePath, quint64 usedQtModules,
return true;
}
+static QStringList findFFmpegLibs(const QString &qtBinDir, Platform platform)
+{
+ const std::vector<QLatin1StringView> ffmpegHints = { "avcodec"_L1, "avformat"_L1, "avutil"_L1,
+ "swresample"_L1, "swscale"_L1 };
+ const QStringList bundledLibs =
+ findSharedLibraries(qtBinDir, platform, MatchDebugOrRelease, {});
+
+ QStringList ffmpegLibs;
+ for (const QLatin1StringView &libHint : ffmpegHints) {
+ const QStringList ffmpegLib = bundledLibs.filter(libHint, Qt::CaseInsensitive);
+
+ if (ffmpegLib.empty()) {
+ std::wcerr << "Warning: Cannot find FFmpeg libraries. Multimedia features will not work as expected.\n";
+ return {};
+ } else if (ffmpegLib.size() != 1u) {
+ std::wcerr << "Warning: Multiple versions of FFmpeg libraries found. Multimedia features will not work as expected.\n";
+ return {};
+ }
+
+ const QChar slash(u'/');
+ QFileInfo ffmpegLibPath{ qtBinDir + slash + ffmpegLib.front() };
+ ffmpegLibs.append(ffmpegLibPath.absoluteFilePath());
+ }
+
+ return ffmpegLibs;
+}
+
+// Find the openssl libraries Qt executables depend on.
+static QStringList findOpenSslLibraries(const QString &openSslRootDir, Platform platform)
+{
+ const std::vector<QLatin1StringView> libHints = { "libcrypto"_L1, "libssl"_L1 };
+ const QChar slash(u'/');
+ const QString openSslBinDir = openSslRootDir + slash + "bin"_L1;
+ const QStringList openSslRootLibs =
+ findSharedLibraries(openSslBinDir, platform, MatchDebugOrRelease, {});
+
+ QStringList result;
+ for (const QLatin1StringView &libHint : libHints) {
+ const QStringList lib = openSslRootLibs.filter(libHint, Qt::CaseInsensitive);
+
+ if (lib.empty()) {
+ std::wcerr << "Warning: Cannot find openssl libraries.\n";
+ return {};
+ } else if (lib.size() != 1u) {
+ std::wcerr << "Warning: Multiple versions of openssl libraries found.\n";
+ return {};
+ }
+
+ QFileInfo libPath{ openSslBinDir + slash + lib.front() };
+ result.append(libPath.absoluteFilePath());
+ }
+
+ return result;
+}
+
+
struct DeployResult
{
operator bool() const { return success; }
bool success = false;
bool isDebug = false;
- quint64 directlyUsedQtLibraries = 0;
- quint64 usedQtLibraries = 0;
- quint64 deployedQtLibraries = 0;
+ ModuleBitset directlyUsedQtLibraries;
+ ModuleBitset usedQtLibraries;
+ ModuleBitset deployedQtLibraries;
};
static QString libraryPath(const QString &libraryLocation, const char *name,
- const QString &qtLibInfix, Platform platform, bool debug)
+ const QString &infix, Platform platform, bool debug)
{
QString result = libraryLocation + u'/';
- if (platform & WindowsBased) {
- result += QLatin1StringView(name);
- result += qtLibInfix;
- if (debug && platformHasDebugSuffix(platform))
- result += u'd';
- } else if (platform.testFlag(UnixBased)) {
- result += QStringLiteral("lib");
- result += QLatin1StringView(name);
- result += qtLibInfix;
- }
- result += sharedLibrarySuffix(platform);
+ result += QLatin1StringView(name);
+ result += infix;
+ if (debug && platformHasDebugSuffix(platform))
+ result += u'd';
+ result += sharedLibrarySuffix();
return result;
}
@@ -1092,29 +1326,51 @@ static QString vcRedistDir()
return QString();
}
-static QStringList compilerRunTimeLibs(Platform platform, bool isDebug, unsigned short machineArch)
+static QStringList findMinGWRuntimePaths(const QString &qtBinDir, Platform platform, const QStringList &runtimeFilters)
{
+ //MinGW: Add runtime libraries. Check first for the Qt binary directory, and default to path if nothing is found.
QStringList result;
- switch (platform) {
- case WindowsDesktopMinGW: { // MinGW: Add runtime libraries
- static const char *minGwRuntimes[] = {"*gcc_", "*stdc++", "*winpthread"};
- const QString gcc = findInPath(QStringLiteral("g++.exe"));
- if (gcc.isEmpty()) {
- std::wcerr << "Warning: Cannot find GCC installation directory. g++.exe must be in the path.\n";
- break;
+ const bool isClang = platform == WindowsDesktopClangMinGW;
+ QStringList filters;
+ const QString suffix = u'*' + sharedLibrarySuffix();
+ for (const auto &minGWRuntime : runtimeFilters)
+ filters.append(minGWRuntime + suffix);
+
+ QFileInfoList dlls = QDir(qtBinDir).entryInfoList(filters, QDir::Files);
+ if (dlls.isEmpty()) {
+ std::wcerr << "Warning: Runtime libraries not found in Qt binary folder, defaulting to looking in path\n";
+ const QString binaryPath = isClang ? findInPath("clang++.exe"_L1) : findInPath("g++.exe"_L1);
+ if (binaryPath.isEmpty()) {
+ std::wcerr << "Warning: Cannot find " << (isClang ? "Clang" : "GCC") << " installation directory, " << (isClang ? "clang++" : "g++") << ".exe must be in the path\n";
+ return {};
}
- const QString binPath = QFileInfo(gcc).absolutePath();
- QStringList filters;
- const QString suffix = u'*' + sharedLibrarySuffix(platform);
- for (auto minGwRuntime : minGwRuntimes)
- filters.append(QLatin1StringView(minGwRuntime) + suffix);
- const QFileInfoList &dlls = QDir(binPath).entryInfoList(filters, QDir::Files);
- for (const QFileInfo &dllFi : dlls)
- result.append(dllFi.absoluteFilePath());
+ const QString binaryFolder = QFileInfo(binaryPath).absolutePath();
+ dlls = QDir(binaryFolder).entryInfoList(filters, QDir::Files);
+ }
+
+ for (const QFileInfo &dllFi : dlls)
+ result.append(dllFi.absoluteFilePath());
+
+ return result;
+}
+
+static QStringList compilerRunTimeLibs(const QString &qtBinDir, Platform platform, bool isDebug, unsigned short machineArch)
+{
+ QStringList result;
+ switch (platform) {
+ case WindowsDesktopMinGW: {
+ const QStringList minGWRuntimes = { "*gcc_"_L1, "*stdc++"_L1, "*winpthread"_L1 };
+ result.append(findMinGWRuntimePaths(qtBinDir, platform, minGWRuntimes));
+ break;
}
+ case WindowsDesktopClangMinGW: {
+ const QStringList clangMinGWRuntimes = { "*unwind"_L1, "*c++"_L1 };
+ result.append(findMinGWRuntimePaths(qtBinDir, platform, clangMinGWRuntimes));
break;
+ }
#ifdef Q_OS_WIN
- case WindowsDesktopMsvc: { // MSVC/Desktop: Add redistributable packages.
+ case WindowsDesktopMsvcIntel:
+ case WindowsDesktopMsvcArm: { // MSVC/Desktop: Add redistributable packages.
QString vcRedistDirName = vcRedistDir();
if (vcRedistDirName.isEmpty())
break;
@@ -1136,11 +1392,11 @@ static QStringList compilerRunTimeLibs(Platform platform, bool isDebug, unsigned
const QStringList countryCodes = vcRedistDir.entryList(QStringList(QStringLiteral("[0-9]*")), QDir::Dirs);
if (!countryCodes.isEmpty()) // Pre MSVC2017
releaseRedistDir += u'/' + countryCodes.constFirst();
- QFileInfo fi(releaseRedistDir + u'/' + QStringLiteral("vc_redist.")
- + machineArchString + QStringLiteral(".exe"));
+ QFileInfo fi(releaseRedistDir + "/vc_redist."_L1
+ + machineArchString + ".exe"_L1);
if (!fi.isFile()) { // Pre MSVC2017/15.5
- fi.setFile(releaseRedistDir + u'/' + QStringLiteral("vcredist_")
- + machineArchString + QStringLiteral(".exe"));
+ fi.setFile(releaseRedistDir + "/vcredist_"_L1
+ + machineArchString + ".exe"_L1);
}
if (fi.isFile())
redistFiles.append(fi.absoluteFilePath());
@@ -1170,16 +1426,6 @@ static inline int qtVersion(const QMap<QString, QString> &qtpathsVariables)
return (majorVersion << 16) | (minorVersion << 8) | patchVersion;
}
-// Determine the Qt lib infix from the library path of "Qt6Core<qtblibinfix>[d].dll".
-static inline QString qtlibInfixFromCoreLibName(const QString &path, bool isDebug, Platform platform)
-{
- const qsizetype startPos = path.lastIndexOf(u'/') + 8;
- qsizetype endPos = path.lastIndexOf(u'.');
- if (isDebug && (platform & WindowsBased))
- endPos--;
- return endPos > startPos ? path.mid(startPos, endPos - startPos) : QString();
-}
-
// Deploy a library along with its .pdb debug info file (MSVC) should it exist.
static bool updateLibrary(const QString &sourceFileName, const QString &targetDirectory,
const Options &options, QString *errorMessage)
@@ -1212,16 +1458,14 @@ static QString getIcuVersion(const QString &libName)
}
static DeployResult deploy(const Options &options, const QMap<QString, QString> &qtpathsVariables,
- QString *errorMessage)
+ const PluginInformation &pluginInfo, QString *errorMessage)
{
DeployResult result;
const QChar slash = u'/';
const QString qtBinDir = qtpathsVariables.value(QStringLiteral("QT_INSTALL_BINS"));
- const QString libraryLocation = options.platform == Unix
- ? qtpathsVariables.value(QStringLiteral("QT_INSTALL_LIBS"))
- : qtBinDir;
+ const QString libraryLocation = qtBinDir;
const QString infix = qtpathsVariables.value(QLatin1StringView(qmakeInfixKey));
const int version = qtVersion(qtpathsVariables);
Q_UNUSED(version);
@@ -1268,17 +1512,17 @@ static DeployResult deploy(const Options &options, const QMap<QString, QString>
// Determine application type, check Quick2 is used by looking at the
// direct dependencies (do not be fooled by QtWebKit depending on it).
- QString qtLibInfix;
- for (int m = 0; m < directDependencyCount; ++m) {
- const quint64 module = qtModule(dependentQtLibs.at(m), infix);
- result.directlyUsedQtLibraries |= module;
- if (module == QtCoreModule)
- qtLibInfix = qtlibInfixFromCoreLibName(dependentQtLibs.at(m), detectedDebug, options.platform);
+ for (int m = 0; m < dependentQtLibs.size(); ++m) {
+ const qint64 module = qtModule(dependentQtLibs.at(m), infix);
+ if (module >= 0)
+ result.directlyUsedQtLibraries[module] = 1;
}
- const bool usesQml2 = !(options.disabledLibraries & QtQmlModule)
- && ((result.directlyUsedQtLibraries & (QtQmlModule | QtQuickModule | Qt3DQuickModule))
- || (options.additionalLibraries & QtQmlModule));
+ const bool usesQml = result.directlyUsedQtLibraries.test(QtQmlModuleId);
+ const bool usesQuick = result.directlyUsedQtLibraries.test(QtQuickModuleId);
+ const bool uses3DQuick = result.directlyUsedQtLibraries.test(Qt3DQuickModuleId);
+ const bool usesQml2 = !(options.disabledLibraries.test(QtQmlModuleId))
+ && (usesQml || usesQuick || uses3DQuick || (options.additionalLibraries.test(QtQmlModuleId)));
if (optVerboseLevel) {
std::wcout << QDir::toNativeSeparators(options.binaries.first()) << ' '
@@ -1300,7 +1544,7 @@ static DeployResult deploy(const Options &options, const QMap<QString, QString>
const QStringList qtLibs = dependentQtLibs.filter(QStringLiteral("Qt6Core"), Qt::CaseInsensitive)
+ dependentQtLibs.filter(QStringLiteral("Qt5WebKit"), Qt::CaseInsensitive);
for (const QString &qtLib : qtLibs) {
- QStringList icuLibs = findDependentLibraries(qtLib, options.platform, errorMessage).filter(QStringLiteral("ICU"), Qt::CaseInsensitive);
+ QStringList icuLibs = findDependentLibraries(qtLib, 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.
@@ -1309,7 +1553,7 @@ static DeployResult deploy(const Options &options, const QMap<QString, QString>
if (optVerboseLevel > 1)
std::wcout << "Adding ICU version " << icuVersion << '\n';
QString icuLib = QStringLiteral("icudt") + icuVersion
- + QLatin1StringView(windowsSharedLibrarySuffix);;
+ + QLatin1StringView(windowsSharedLibrarySuffix);
// Some packages contain debug dlls of ICU libraries even though it's a C
// library and the official packages do not differentiate (QTBUG-87677)
if (result.isDebug) {
@@ -1321,7 +1565,7 @@ static DeployResult deploy(const Options &options, const QMap<QString, QString>
}
icuLibs.push_back(icuLib);
}
- for (const QString &icuLib : qAsConst(icuLibs)) {
+ for (const QString &icuLib : std::as_const(icuLibs)) {
const QString icuPath = findInPath(icuLib);
if (icuPath.isEmpty()) {
*errorMessage = QStringLiteral("Unable to locate ICU library ") + icuLib;
@@ -1347,30 +1591,30 @@ static DeployResult deploy(const Options &options, const QMap<QString, QString>
if (!qmlDirectory.isEmpty())
qmlDirectories.append(qmlDirectory);
}
- for (const QString &qmlDirectory : qAsConst(qmlDirectories)) {
+ for (const QString &qmlDirectory : std::as_const(qmlDirectories)) {
if (optVerboseLevel >= 1)
std::wcout << "Scanning " << QDir::toNativeSeparators(qmlDirectory) << ":\n";
const QmlImportScanResult scanResult =
runQmlImportScanner(qmlDirectory, qmlImportPaths,
- result.directlyUsedQtLibraries & QtWidgetsModule,
+ result.directlyUsedQtLibraries.test(QtWidgetsModuleId),
options.platform, debugMatchMode, errorMessage);
if (!scanResult.ok)
return result;
qmlScanResult.append(scanResult);
// Additional dependencies of QML plugins.
- for (const QString &plugin : qAsConst(qmlScanResult.plugins)) {
+ for (const QString &plugin : std::as_const(qmlScanResult.plugins)) {
if (!findDependentQtLibraries(libraryLocation, plugin, options.platform, errorMessage, &dependentQtLibs, &wordSize, &detectedDebug, &machineArch))
return result;
}
if (optVerboseLevel >= 1) {
std::wcout << "QML imports:\n";
- for (const QmlImportScanResult::Module &mod : qAsConst(qmlScanResult.modules)) {
+ for (const QmlImportScanResult::Module &mod : std::as_const(qmlScanResult.modules)) {
std::wcout << " '" << mod.name << "' "
<< QDir::toNativeSeparators(mod.sourcePath) << '\n';
}
if (optVerboseLevel >= 2) {
std::wcout << "QML plugins:\n";
- for (const QString &p : qAsConst(qmlScanResult.plugins))
+ for (const QString &p : std::as_const(qmlScanResult.plugins))
std::wcout << " " << QDir::toNativeSeparators(p) << '\n';
}
}
@@ -1382,28 +1626,50 @@ static DeployResult deploy(const Options &options, const QMap<QString, QString>
// QtModule enumeration (and thus controlled by flags) and others.
QStringList deployedQtLibraries;
for (int i = 0 ; i < dependentQtLibs.size(); ++i) {
- if (const quint64 qtm = qtModule(dependentQtLibs.at(i), infix))
- result.usedQtLibraries |= qtm;
+ const qint64 module = qtModule(dependentQtLibs.at(i), infix);
+ if (module >= 0)
+ result.usedQtLibraries[module] = 1;
else
deployedQtLibraries.push_back(dependentQtLibs.at(i)); // Not represented by flag.
}
result.deployedQtLibraries = (result.usedQtLibraries | options.additionalLibraries) & ~options.disabledLibraries;
+ ModuleBitset disabled = options.disabledLibraries;
+ if (!usesQml2) {
+ disabled[QtQmlModuleId] = 1;
+ disabled[QtQuickModuleId] = 1;
+ }
+
+ QStringList openSslLibs;
+ if (!options.openSslRootDirectory.isEmpty()) {
+ openSslLibs = findOpenSslLibraries(options.openSslRootDirectory, options.platform);
+ if (openSslLibs.isEmpty()) {
+ *errorMessage = QStringLiteral("Unable to find openSSL libraries in ")
+ + options.openSslRootDirectory;
+ return result;
+ }
+
+ deployedQtLibraries.append(openSslLibs);
+ }
+ const bool deployOpenSslPlugin = options.forceOpenSslPlugin || !openSslLibs.isEmpty();
+
const QStringList plugins = findQtPlugins(
&result.deployedQtLibraries,
// For non-QML applications, disable QML to prevent it from being pulled in by the
// qtaccessiblequick plugin.
- options.disabledLibraries | (usesQml2 ? 0 : (QtQmlModule | QtQuickModule)),
- options.disabledPlugins, qtpathsVariables.value(QStringLiteral("QT_INSTALL_PLUGINS")),
- libraryLocation, infix, debugMatchMode, options.platform, &platformPlugin);
+ disabled, pluginInfo,
+ options.pluginSelections, qtpathsVariables.value(QStringLiteral("QT_INSTALL_PLUGINS")),
+ libraryLocation, infix, debugMatchMode, options.platform, &platformPlugin,
+ options.deployInsightTrackerPlugin, deployOpenSslPlugin);
// Apply options flags and re-add library names.
QString qtGuiLibrary;
for (const auto &qtModule : qtModuleEntries) {
- if (result.deployedQtLibraries & qtModule.module) {
- const QString library = libraryPath(libraryLocation, qtModule.libraryName, qtLibInfix, options.platform, result.isDebug);
+ if (result.deployedQtLibraries.test(qtModule.id)) {
+ const QString library = libraryPath(libraryLocation, qtModule.name.toUtf8(), infix,
+ options.platform, result.isDebug);
deployedQtLibraries.append(library);
- if (qtModule.module == QtGuiModule)
+ if (qtModule.id == QtGuiModuleId)
qtGuiLibrary = library;
}
}
@@ -1417,13 +1683,13 @@ static DeployResult deploy(const Options &options, const QMap<QString, QString>
if (optVerboseLevel > 1)
std::wcout << "Plugins: " << plugins.join(u',') << '\n';
- if ((result.deployedQtLibraries & QtGuiModule) && platformPlugin.isEmpty()) {
+ if (result.deployedQtLibraries.test(QtGuiModuleId) && platformPlugin.isEmpty()) {
*errorMessage =QStringLiteral("Unable to find the platform plugin.");
return result;
}
if (options.platform.testFlag(WindowsBased) && !qtGuiLibrary.isEmpty()) {
- const QStringList guiLibraries = findDependentLibraries(qtGuiLibrary, options.platform, errorMessage);
+ const QStringList guiLibraries = findDependentLibraries(qtGuiLibrary, errorMessage);
const bool dependsOnOpenGl = !guiLibraries.filter(QStringLiteral("opengl32"), Qt::CaseInsensitive).isEmpty();
if (options.softwareRasterizer && !dependsOnOpenGl) {
const QFileInfo softwareRasterizer(qtBinDir + slash + QStringLiteral("opengl32sw") + QLatin1StringView(windowsSharedLibrarySuffix));
@@ -1438,30 +1704,43 @@ static DeployResult deploy(const Options &options, const QMap<QString, QString>
deployedQtLibraries.push_back(d3dCompiler);
}
}
+ if (options.systemDxc) {
+ const QStringList dxcLibs = findDxc(options.platform, qtBinDir, wordSize);
+ if (!dxcLibs.isEmpty())
+ deployedQtLibraries.append(dxcLibs);
+ else
+ std::wcerr << "Warning: Cannot find any version of the dxcompiler.dll and dxil.dll.\n";
+ }
} // Windows
+ // Add FFmpeg if we deploy the FFmpeg backend
+ if (options.ffmpeg
+ && !plugins.filter(QStringLiteral("ffmpegmediaplugin"), Qt::CaseInsensitive).empty()) {
+ deployedQtLibraries.append(findFFmpegLibs(qtBinDir, options.platform));
+ }
+
// Update libraries
if (options.libraries) {
const QString targetPath = options.libraryDirectory.isEmpty() ?
options.directory : options.libraryDirectory;
QStringList libraries = deployedQtLibraries;
if (options.compilerRunTime)
- libraries.append(compilerRunTimeLibs(options.platform, result.isDebug, machineArch));
- for (const QString &qtLib : qAsConst(libraries)) {
+ libraries.append(compilerRunTimeLibs(qtBinDir, options.platform, result.isDebug, machineArch));
+ for (const QString &qtLib : std::as_const(libraries)) {
if (!updateLibrary(qtLib, targetPath, options, errorMessage))
return result;
}
+#if !QT_CONFIG(relocatable)
if (options.patchQt && !options.dryRun) {
- const QString qt6CoreName = QFileInfo(libraryPath(libraryLocation, "Qt6Core", qtLibInfix,
+ const QString qt6CoreName = QFileInfo(libraryPath(libraryLocation, "Qt6Core", infix,
options.platform, result.isDebug)).fileName();
-#ifndef QT_RELOCATABLE
if (!patchQtCore(targetPath + u'/' + qt6CoreName, errorMessage)) {
std::wcerr << "Warning: " << *errorMessage << '\n';
errorMessage->clear();
}
-#endif
}
+#endif // QT_CONFIG(relocatable)
} // optLibraries
// Update plugins
@@ -1491,44 +1770,39 @@ static DeployResult deploy(const Options &options, const QMap<QString, QString>
} // 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.
- if (options.quickImports && (usesQuick1 || usesQml2)) {
- if (usesQml2) {
- for (const QmlImportScanResult::Module &module : qAsConst(qmlScanResult.modules)) {
- const QString installPath = module.installPath(options.directory);
- if (optVerboseLevel > 1)
- std::wcout << "Installing: '" << module.name
- << "' from " << module.sourcePath << " to "
- << QDir::toNativeSeparators(installPath) << '\n';
- if (installPath != options.directory && !createDirectory(installPath, errorMessage))
- return result;
- unsigned updateFileFlags = options.updateFileFlags | SkipQmlDesignerSpecificsDirectories;
- unsigned qmlDirectoryFileFlags = 0;
- if (options.deployPdb)
- qmlDirectoryFileFlags |= QmlDirectoryFileEntryFunction::DeployPdb;
- if (!updateFile(module.sourcePath, QmlDirectoryFileEntryFunction(options.platform, debugMatchMode, qmlDirectoryFileFlags),
- installPath, updateFileFlags, options.json, errorMessage)) {
- return result;
- }
- }
- } // Quick 2
- if (usesQuick1) {
- const QString quick1ImportPath =
- qtpathsVariables.value(QStringLiteral("QT_INSTALL_IMPORTS"));
- const QmlDirectoryFileEntryFunction qmlFileEntryFunction(options.platform, debugMatchMode, options.deployPdb ? QmlDirectoryFileEntryFunction::DeployPdb : 0);
- QStringList quick1Imports(QStringLiteral("Qt"));
- for (const QString &quick1Import : qAsConst(quick1Imports)) {
- const QString sourceFile = quick1ImportPath + slash + quick1Import;
- if (!updateFile(sourceFile, qmlFileEntryFunction, options.directory, options.updateFileFlags, options.json, errorMessage))
- return result;
+ if (options.quickImports && usesQml2) {
+ const QString targetPath = options.qmlDirectory.isEmpty()
+ ? options.directory + QStringLiteral("/qml")
+ : options.qmlDirectory;
+ if (!createDirectory(targetPath, errorMessage, options.dryRun))
+ return result;
+ for (const QmlImportScanResult::Module &module : std::as_const(qmlScanResult.modules)) {
+ const QString installPath = module.installPath(targetPath);
+ if (optVerboseLevel > 1)
+ std::wcout << "Installing: '" << module.name
+ << "' from " << module.sourcePath << " to "
+ << QDir::toNativeSeparators(installPath) << '\n';
+ if (installPath != targetPath && !createDirectory(installPath, errorMessage, options.dryRun))
+ return result;
+ unsigned updateFileFlags = options.updateFileFlags
+ | SkipQmlDesignerSpecificsDirectories;
+ unsigned qmlDirectoryFileFlags = 0;
+ if (options.deployPdb)
+ qmlDirectoryFileFlags |= QmlDirectoryFileEntryFunction::DeployPdb;
+ if (!updateFile(module.sourcePath, QmlDirectoryFileEntryFunction(module.sourcePath,
+ options.platform,
+ debugMatchMode,
+ qmlDirectoryFileFlags),
+ installPath, updateFileFlags, options.json, errorMessage)) {
+ return result;
}
- } // Quick 1
+ }
} // optQuickImports
if (options.translations) {
- if (!options.dryRun && !createDirectory(options.translationsDirectory, errorMessage))
+ if (!createDirectory(options.translationsDirectory, errorMessage, options.dryRun))
return result;
if (!deployTranslations(qtpathsVariables.value(QStringLiteral("QT_INSTALL_TRANSLATIONS")),
result.deployedQtLibraries, options.translationsDirectory, options,
@@ -1542,7 +1816,8 @@ static DeployResult deploy(const Options &options, const QMap<QString, QString>
}
static bool deployWebProcess(const QMap<QString, QString> &qtpathsVariables, const char *binaryName,
- const Options &sourceOptions, QString *errorMessage)
+ const PluginInformation &pluginInfo, const Options &sourceOptions,
+ QString *errorMessage)
{
// Copy the web process and its dependencies
const QString webProcess = webProcessBinary(binaryName, sourceOptions.platform);
@@ -1554,29 +1829,32 @@ static bool deployWebProcess(const QMap<QString, QString> &qtpathsVariables, con
options.binaries.append(options.directory + u'/' + webProcess);
options.quickImports = false;
options.translations = false;
- return deploy(options, qtpathsVariables, errorMessage);
+ return deploy(options, qtpathsVariables, pluginInfo, errorMessage);
}
static bool deployWebEngineCore(const QMap<QString, QString> &qtpathsVariables,
- const Options &options, bool isDebug, QString *errorMessage)
+ const PluginInformation &pluginInfo, const Options &options,
+ bool isDebug, QString *errorMessage)
{
- static const char *installDataFiles[] = {"icudtl.dat",
- "qtwebengine_devtools_resources.pak",
- "qtwebengine_resources.pak",
- "qtwebengine_resources_100p.pak",
- "qtwebengine_resources_200p.pak"};
+ static const char *installDataFiles[] = { "icudtl.dat",
+ "qtwebengine_devtools_resources.pak",
+ "qtwebengine_resources.pak",
+ "qtwebengine_resources_100p.pak",
+ "qtwebengine_resources_200p.pak",
+ isDebug ? "v8_context_snapshot.debug.bin"
+ : "v8_context_snapshot.bin" };
QByteArray webEngineProcessName(webEngineProcessC);
if (isDebug && platformHasDebugSuffix(options.platform))
webEngineProcessName.append('d');
if (optVerboseLevel)
std::wcout << "Deploying: " << webEngineProcessName.constData() << "...\n";
- if (!deployWebProcess(qtpathsVariables, webEngineProcessName, options, errorMessage))
+ if (!deployWebProcess(qtpathsVariables, webEngineProcessName, pluginInfo, options, errorMessage))
return false;
const QString resourcesSubDir = QStringLiteral("/resources");
const QString resourcesSourceDir = qtpathsVariables.value(QStringLiteral("QT_INSTALL_DATA"))
+ resourcesSubDir + u'/';
const QString resourcesTargetDir(options.directory + resourcesSubDir);
- if (!createDirectory(resourcesTargetDir, errorMessage))
+ if (!createDirectory(resourcesTargetDir, errorMessage, options.dryRun))
return false;
for (auto installDataFile : installDataFiles) {
if (!updateFile(resourcesSourceDir + QLatin1StringView(installDataFile),
@@ -1593,7 +1871,7 @@ static bool deployWebEngineCore(const QMap<QString, QString> &qtpathsVariables,
}
if (options.translations) {
// Copy the whole translations directory.
- return createDirectory(options.translationsDirectory, errorMessage)
+ return createDirectory(options.translationsDirectory, errorMessage, options.dryRun)
&& updateFile(translations.absoluteFilePath(), options.translationsDirectory,
options.updateFileFlags, options.json, errorMessage);
}
@@ -1606,7 +1884,7 @@ static bool deployWebEngineCore(const QMap<QString, QString> &qtpathsVariables,
}
const QString webEngineTranslationsDir = options.translationsDirectory + u'/'
+ translations.fileName();
- if (!createDirectory(webEngineTranslationsDir, errorMessage))
+ if (!createDirectory(webEngineTranslationsDir, errorMessage, options.dryRun))
return false;
return updateFile(enUSpak.absoluteFilePath(), webEngineTranslationsDir,
options.updateFileFlags, options.json, errorMessage);
@@ -1624,63 +1902,113 @@ int main(int argc, char **argv)
const QByteArray qtBinPath = QFile::encodeName(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()));
QByteArray path = qgetenv("PATH");
if (!path.contains(qtBinPath)) { // QTBUG-39177, ensure Qt is in the path so that qt.conf is taken into account.
- path += ';';
- path += qtBinPath;
+ path.prepend(QDir::listSeparator().toLatin1());
+ path.prepend(qtBinPath);
qputenv("PATH", path);
}
Options options;
QString errorMessage;
- { // Command line
- QCommandLineParser parser;
- QString errorMessage;
- const int result = parseArguments(QCoreApplication::arguments(), &parser, &options, &errorMessage);
- if (result & CommandLineParseError)
- std::wcerr << errorMessage << "\n\n";
- if (result & CommandLineParseHelpRequested)
- std::fputs(qPrintable(helpText(parser)), stdout);
- if (result & CommandLineParseError)
+ // Early parse the --qmake and --qtpaths options, because they are needed to determine the
+ // options that select/deselect Qt modules.
+ {
+ int result = parseEarlyArguments(QCoreApplication::arguments(), &options, &errorMessage);
+ if (result & CommandLineParseError) {
+ std::wcerr << "Error: " << errorMessage << "\n";
return 1;
- if (result & CommandLineParseHelpRequested)
- return 0;
+ }
}
const QMap<QString, QString> qtpathsVariables =
queryQtPaths(options.qtpathsBinary, &errorMessage);
const QString xSpec = qtpathsVariables.value(QStringLiteral("QMAKE_XSPEC"));
- options.platform = platformFromMkSpec(xSpec);
-
if (qtpathsVariables.isEmpty() || xSpec.isEmpty()
|| !qtpathsVariables.contains(QStringLiteral("QT_INSTALL_BINS"))) {
std::wcerr << "Unable to query qtpaths: " << errorMessage << '\n';
return 1;
}
+ options.platform = platformFromMkSpec(xSpec);
+ // We are on MSVC and not crosscompiling. We need the host arch
+ if (options.platform == WindowsDesktopMsvc) {
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ switch (si.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ case PROCESSOR_ARCHITECTURE_IA64:
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ options.platform |= IntelBased;
+ break;
+ case PROCESSOR_ARCHITECTURE_ARM:
+ case PROCESSOR_ARCHITECTURE_ARM64:
+ options.platform |= ArmBased;
+ break;
+ default:
+ options.platform = UnknownPlatform;
+ }
+ }
if (options.platform == UnknownPlatform) {
std::wcerr << "Unsupported platform " << xSpec << '\n';
return 1;
}
+ // Read the Qt module information from the Qt installation directory.
+ const QString modulesDir
+ = qtpathsVariables.value(QLatin1String("QT_INSTALL_ARCHDATA"))
+ + QLatin1String("/modules");
+ const QString translationsDir
+ = qtpathsVariables.value(QLatin1String("QT_INSTALL_TRANSLATIONS"));
+ if (!qtModuleEntries.populate(modulesDir, translationsDir, optVerboseLevel > 1,
+ &errorMessage)) {
+ std::wcerr << "Error: " << errorMessage << "\n";
+ return 1;
+ }
+ assignKnownModuleIds();
+
+ // Read the Qt plugin types information from the Qt installation directory.
+ PluginInformation pluginInfo{};
+ pluginInfo.generateAvailablePlugins(qtpathsVariables, options.platform);
+
+ // Parse the full command line.
+ {
+ QCommandLineParser parser;
+ QString errorMessage;
+ const int result = parseArguments(QCoreApplication::arguments(), &parser, &options, &errorMessage);
+ if (result & CommandLineParseError)
+ std::wcerr << errorMessage << "\n\n";
+ if (result & CommandLineVersionRequested) {
+ std::fputs(QT_VERSION_STR "\n", stdout);
+ return 0;
+ }
+ if (result & CommandLineParseHelpRequested)
+ std::fputs(qPrintable(helpText(parser, pluginInfo)), stdout);
+ if (result & CommandLineParseError)
+ return 1;
+ if (result & CommandLineParseHelpRequested)
+ return 0;
+ }
+
// Create directories
- if (!createDirectory(options.directory, &errorMessage)) {
+ if (!createDirectory(options.directory, &errorMessage, options.dryRun)) {
std::wcerr << errorMessage << '\n';
return 1;
}
if (!options.libraryDirectory.isEmpty() && options.libraryDirectory != options.directory
- && !createDirectory(options.libraryDirectory, &errorMessage)) {
+ && !createDirectory(options.libraryDirectory, &errorMessage, options.dryRun)) {
std::wcerr << errorMessage << '\n';
return 1;
}
- const DeployResult result = deploy(options, qtpathsVariables, &errorMessage);
+ const DeployResult result = deploy(options, qtpathsVariables, pluginInfo, &errorMessage);
if (!result) {
std::wcerr << errorMessage << '\n';
return 1;
}
- if (result.deployedQtLibraries & QtWebEngineCoreModule) {
- if (!deployWebEngineCore(qtpathsVariables, options, result.isDebug, &errorMessage)) {
+ if (result.deployedQtLibraries.test(QtWebEngineCoreModuleId)) {
+ if (!deployWebEngineCore(qtpathsVariables, pluginInfo, options, result.isDebug,
+ &errorMessage)) {
std::wcerr << errorMessage << '\n';
return 1;
}
diff --git a/src/tools/windeployqt/qmlutils.cpp b/src/tools/windeployqt/qmlutils.cpp
index 5104af0194..a7e63e7470 100644
--- a/src/tools/windeployqt/qmlutils.cpp
+++ b/src/tools/windeployqt/qmlutils.cpp
@@ -67,7 +67,8 @@ static void findFileRecursion(const QDir &directory, Platform platform,
const QFileInfoList &subDirs = directory.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
for (const QFileInfo &subDirFi : subDirs) {
QDir subDirectory(subDirFi.absoluteFilePath());
- if (subDirectory.isReadable())
+ // Don't enter other QML modules when recursing!
+ if (subDirectory.isReadable() && !subDirectory.exists(QStringLiteral("qmldir")))
findFileRecursion(subDirectory, platform, debugMatchMode, matches);
}
}
diff --git a/src/tools/windeployqt/qtmoduleinfo.cpp b/src/tools/windeployqt/qtmoduleinfo.cpp
new file mode 100644
index 0000000000..b928a64478
--- /dev/null
+++ b/src/tools/windeployqt/qtmoduleinfo.cpp
@@ -0,0 +1,183 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qtmoduleinfo.h"
+#include "utils.h"
+
+#include <QDirListing>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QDebug>
+
+#include <iostream>
+#include <algorithm>
+#include <unordered_map>
+
+using namespace Qt::StringLiterals;
+
+static QStringList toStringList(const QJsonArray &jsonArray)
+{
+ QStringList result;
+ for (const auto &item : jsonArray) {
+ if (item.isString())
+ result.append(item.toString());
+ }
+ return result;
+}
+
+struct TranslationCatalog
+{
+ QString name;
+ QStringList repositories;
+ QStringList modules;
+};
+
+using TranslationCatalogs = std::vector<TranslationCatalog>;
+
+static TranslationCatalogs readTranslationsCatalogs(const QString &translationsDir,
+ bool verbose,
+ QString *errorString)
+{
+ QFile file(translationsDir + QLatin1String("/catalogs.json"));
+ if (verbose) {
+ std::wcerr << "Trying to read translation catalogs from \""
+ << qUtf8Printable(file.fileName()) << "\".\n";
+ }
+ if (!file.open(QIODevice::ReadOnly)) {
+ *errorString = QLatin1String("Cannot open ") + file.fileName();
+ return {};
+ }
+
+ QJsonParseError jsonParseError;
+ QJsonDocument document = QJsonDocument::fromJson(file.readAll(), &jsonParseError);
+ if (jsonParseError.error != QJsonParseError::NoError) {
+ *errorString = jsonParseError.errorString();
+ return {};
+ }
+
+ if (!document.isArray()) {
+ *errorString = QLatin1String("Expected an array as root element of ") + file.fileName();
+ return {};
+ }
+
+ TranslationCatalogs catalogs;
+ for (const QJsonValueRef &item : document.array()) {
+ TranslationCatalog catalog;
+ catalog.name = item[QLatin1String("name")].toString();
+ catalog.repositories = toStringList(item[QLatin1String("repositories")].toArray());
+ catalog.modules = toStringList(item[QLatin1String("modules")].toArray());
+ if (verbose)
+ std::wcerr << "Found catalog \"" << qUtf8Printable(catalog.name) << "\".\n";
+ catalogs.emplace_back(std::move(catalog));
+ }
+
+ return catalogs;
+}
+
+static QtModule moduleFromJsonFile(const QString &filePath, QString *errorString)
+{
+ QFile file(filePath);
+ if (!file.open(QIODevice::ReadOnly)) {
+ *errorString = QLatin1String("Cannot open ") + file.fileName();
+ return {};
+ }
+
+ QJsonParseError jsonParseError;
+ QJsonDocument document = QJsonDocument::fromJson(file.readAll(), &jsonParseError);
+ if (jsonParseError.error != QJsonParseError::NoError) {
+ *errorString = jsonParseError.errorString();
+ return {};
+ }
+
+ if (!document.isObject()) {
+ *errorString = QLatin1String("Expected an object as root element of ") + file.fileName();
+ return {};
+ }
+
+ const QJsonObject obj = document.object();
+ QtModule module;
+ module.name = "Qt6"_L1 + obj[QLatin1String("name")].toString();
+ module.repository = obj[QLatin1String("repository")].toString();
+ module.internal = obj[QLatin1String("internal")].toBool();
+ module.pluginTypes = toStringList(obj[QLatin1String("plugin_types")].toArray());
+ return module;
+}
+
+static void dump(const QtModule &module)
+{
+ std::wcerr << "Found module \"" << qUtf8Printable(module.name) << "\".\n";
+ if (!module.pluginTypes.isEmpty())
+ qDebug().nospace() << " plugin types: " << module.pluginTypes;
+ if (!module.translationCatalog.isEmpty())
+ qDebug().nospace() << " translation catalog: "<< module.translationCatalog;
+}
+
+bool QtModuleInfoStore::populate(const QString &modulesDir, const QString &translationsDir,
+ bool verbose, QString *errorString)
+{
+ const TranslationCatalogs catalogs = readTranslationsCatalogs(translationsDir, verbose,
+ errorString);
+ if (!errorString->isEmpty()) {
+ std::wcerr << "Warning: Translations will not be available due to the following error."
+ << std::endl << *errorString << std::endl;
+ errorString->clear();
+ }
+ std::unordered_map<QString, QString> moduleToCatalogMap;
+ std::unordered_map<QString, QString> repositoryToCatalogMap;
+ for (const TranslationCatalog &catalog : catalogs) {
+ for (const QString &module : catalog.modules) {
+ moduleToCatalogMap.insert(std::make_pair(module, catalog.name));
+ }
+ for (const QString &repository : catalog.repositories) {
+ repositoryToCatalogMap.insert(std::make_pair(repository, catalog.name));
+ }
+ }
+
+ // Read modules, and assign a bit as ID.
+ for (const auto &dirEntry : QDirListing(modulesDir, {u"*.json"_s}, QDir::Files)) {
+ QtModule module = moduleFromJsonFile(dirEntry.filePath(), errorString);
+ if (!errorString->isEmpty())
+ return false;
+ if (module.internal && module.name.endsWith(QStringLiteral("Private")))
+ module.name.chop(7);
+ module.id = modules.size();
+ if (module.id == QtModule::InvalidId) {
+ *errorString = "Internal Error: too many modules for ModuleBitset to hold."_L1;
+ return false;
+ }
+
+ {
+ auto it = moduleToCatalogMap.find(module.name);
+ if (it != moduleToCatalogMap.end())
+ module.translationCatalog = it->second;
+ }
+ if (module.translationCatalog.isEmpty()) {
+ auto it = repositoryToCatalogMap.find(module.repository);
+ if (it != repositoryToCatalogMap.end())
+ module.translationCatalog = it->second;
+ }
+ if (verbose)
+ dump(module);
+ modules.emplace_back(std::move(module));
+ }
+
+ return true;
+}
+
+const QtModule &QtModuleInfoStore::moduleById(size_t id) const
+{
+ return modules.at(id);
+}
+
+size_t QtModuleInfoStore::moduleIdForPluginType(const QString &pluginType) const
+{
+ auto moduleHasPluginType = [&pluginType] (const QtModule &module) {
+ return module.pluginTypes.contains(pluginType);
+ };
+
+ auto it = std::find_if(modules.begin(), modules.end(), moduleHasPluginType);
+ if (it != modules.end())
+ return it->id ;
+
+ return QtModule::InvalidId;
+}
diff --git a/src/tools/windeployqt/qtmoduleinfo.h b/src/tools/windeployqt/qtmoduleinfo.h
new file mode 100644
index 0000000000..b35403a090
--- /dev/null
+++ b/src/tools/windeployqt/qtmoduleinfo.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QTMODULEINFO_H
+#define QTMODULEINFO_H
+
+#include <QString>
+#include <QStringList>
+
+#include <bitset>
+#include <vector>
+
+constexpr size_t ModuleBitsetSize = 1024;
+using ModuleBitset = std::bitset<ModuleBitsetSize>;
+
+struct QtModule
+{
+ static constexpr size_t InvalidId = ModuleBitsetSize - 1;
+ size_t id = InvalidId;
+ bool internal = false;
+ QString name;
+ QString repository;
+ QStringList pluginTypes;
+ QString translationCatalog;
+};
+
+inline bool contains(const ModuleBitset &modules, const QtModule &module)
+{
+ return modules.test(module.id);
+}
+
+class QtModuleInfoStore
+{
+public:
+ QtModuleInfoStore() = default;
+
+ bool populate(const QString &modulesDir, const QString &translationsDir, bool verbose,
+ QString *errorString);
+
+ size_t size() const { return modules.size(); }
+ std::vector<QtModule>::const_iterator begin() const { return modules.begin(); }
+ std::vector<QtModule>::const_iterator end() const { return modules.end(); }
+
+ const QtModule &moduleById(size_t id) const;
+ size_t moduleIdForPluginType(const QString &pluginType) const;
+
+private:
+ std::vector<QtModule> modules;
+};
+
+#endif
diff --git a/src/tools/windeployqt/qtplugininfo.cpp b/src/tools/windeployqt/qtplugininfo.cpp
new file mode 100644
index 0000000000..1deaa35f35
--- /dev/null
+++ b/src/tools/windeployqt/qtplugininfo.cpp
@@ -0,0 +1,100 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qtplugininfo.h"
+
+#include <QDir>
+
+static PluginDetection determinePluginLibrary(const QDir &platformPluginDir, const QString &infix)
+{
+ // Use the platform plugin to determine which dlls are there (release/debug/both)
+ QString platformReleaseFilter(QStringLiteral("qwindows"));
+ if (!infix.isEmpty())
+ platformReleaseFilter += infix;
+ QString platformFilter = platformReleaseFilter + u'*';
+ platformFilter += sharedLibrarySuffix();
+
+ const QFileInfoList &dlls =
+ platformPluginDir.entryInfoList(QStringList(platformFilter), QDir::Files);
+ if (dlls.size() == 1) {
+ const QFileInfo dllFi = dlls.first();
+ const bool hasDebugDlls =
+ dllFi.fileName() == QString(platformReleaseFilter + sharedLibrarySuffix()) ? false
+ : true;
+ return (hasDebugDlls ? PluginDetection::DebugOnly : PluginDetection::ReleaseOnly);
+ } else {
+ return PluginDetection::DebugAndRelease;
+ }
+}
+
+static QStringList findPluginNames(const QDir &pluginDir, const PluginDetection libraryType,
+ const Platform &platform)
+{
+ QString errorMessage{};
+ QStringList result{};
+ QString filter{};
+ filter += u"*";
+ filter += sharedLibrarySuffix();
+
+ const QFileInfoList &dlls =
+ pluginDir.entryInfoList(QStringList(filter), QDir::Files, QDir::Name);
+
+ for (const QFileInfo &dllFi : dlls) {
+ QString plugin = dllFi.fileName();
+ const int dotIndex = plugin.lastIndexOf(u'.');
+ // We don't need the .dll for the name
+ plugin = plugin.first(dotIndex);
+
+ if (libraryType == PluginDetection::DebugAndRelease) {
+ bool isDebugDll{};
+ if (!readPeExecutable(dllFi.absoluteFilePath(), &errorMessage, 0, 0, &isDebugDll,
+ (platform == WindowsDesktopMinGW))) {
+ std::wcerr << "Warning: Unable to read "
+ << QDir::toNativeSeparators(dllFi.absoluteFilePath()) << ": "
+ << errorMessage;
+ }
+ if (isDebugDll && platformHasDebugSuffix(platform))
+ plugin.removeLast();
+ }
+ else if (libraryType == PluginDetection::DebugOnly)
+ plugin.removeLast();
+
+ if (!result.contains(plugin))
+ result.append(plugin);
+ }
+ return result;
+}
+
+bool PluginInformation::isTypeForPlugin(const QString &type, const QString &plugin) const
+{
+ return m_pluginMap.at(plugin) == type;
+}
+
+void PluginInformation::populatePluginToType(const QDir &pluginDir, const QStringList &plugins)
+{
+ for (const QString &plugin : plugins)
+ m_pluginMap.insert({ plugin, pluginDir.dirName() });
+}
+
+void PluginInformation::generateAvailablePlugins(const QMap<QString, QString> &qtPathsVariables,
+ const Platform &platform)
+{
+ const QDir pluginTypesDir(qtPathsVariables.value(QLatin1String("QT_INSTALL_PLUGINS")));
+ const QDir platformPluginDir(pluginTypesDir.absolutePath() + QStringLiteral("/platforms"));
+ const QString infix(qtPathsVariables.value(QLatin1String(qmakeInfixKey)));
+ const PluginDetection debugDetection = determinePluginLibrary(platformPluginDir, infix);
+
+ const QFileInfoList &pluginTypes =
+ pluginTypesDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+ for (const QFileInfo &pluginType : pluginTypes) {
+ const QString pluginTypeName = pluginType.baseName();
+ m_typeMap.insert({ pluginTypeName, QStringList{} });
+ const QStringList plugins =
+ findPluginNames(pluginType.absoluteFilePath(), debugDetection, platform);
+ m_typeMap.at(pluginTypeName) = plugins;
+ populatePluginToType(pluginTypeName, plugins);
+ }
+ if (!m_typeMap.size() || !m_pluginMap.size())
+ std::wcerr << "Warning: could not parse available plugins properly, plugin "
+ "inclusion/exclusion options will not work\n";
+}
diff --git a/src/tools/windeployqt/qtplugininfo.h b/src/tools/windeployqt/qtplugininfo.h
new file mode 100644
index 0000000000..420b2b5e1a
--- /dev/null
+++ b/src/tools/windeployqt/qtplugininfo.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QTPLUGININFO_H
+#define QTPLUGININFO_H
+
+#include "utils.h"
+
+#include <QString>
+#include <QStringList>
+
+#include <unordered_map>
+
+enum class PluginDetection
+{
+ DebugOnly,
+ ReleaseOnly,
+ DebugAndRelease
+};
+
+struct PluginSelections
+{
+ QStringList disabledPluginTypes;
+ QStringList enabledPluginTypes;
+ QStringList excludedPlugins;
+ QStringList includedPlugins;
+ bool includeSoftPlugins = false;
+};
+
+class PluginInformation
+{
+public:
+ PluginInformation() = default;
+
+ bool isTypeForPlugin(const QString &type, const QString &plugin) const;
+
+ void generateAvailablePlugins(const QMap<QString, QString> &qtPathsVariables,
+ const Platform &platform);
+ void populatePluginToType(const QDir &pluginDir, const QStringList &plugins);
+
+ const std::unordered_map<QString, QStringList> &typeMap() const { return m_typeMap; }
+
+private:
+ std::unordered_map<QString, QStringList> m_typeMap;
+ std::unordered_map<QString, QString> m_pluginMap;
+};
+
+#endif
diff --git a/src/tools/windeployqt/utils.cpp b/src/tools/windeployqt/utils.cpp
index 41b2d2f063..5141119254 100644
--- a/src/tools/windeployqt/utils.cpp
+++ b/src/tools/windeployqt/utils.cpp
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "utils.h"
-#include "elfreader.h"
#include <QtCore/QString>
#include <QtCore/QDebug>
@@ -15,6 +14,7 @@
#include <QtCore/QStandardPaths>
#if defined(Q_OS_WIN)
# include <QtCore/qt_windows.h>
+# include <QtCore/private/qsystemerror_p.h>
# include <shlwapi.h>
# include <delayimp.h>
#else // Q_OS_WIN
@@ -61,7 +61,7 @@ bool createSymbolicLink(const QFileInfo &source, const QString &target, QString
return true;
}
-bool createDirectory(const QString &directory, QString *errorMessage)
+bool createDirectory(const QString &directory, QString *errorMessage, bool dryRun)
{
const QFileInfo fi(directory);
if (fi.isDir())
@@ -73,11 +73,13 @@ bool createDirectory(const QString &directory, QString *errorMessage)
}
if (optVerboseLevel)
std::wcout << "Creating " << QDir::toNativeSeparators(directory) << "...\n";
- QDir dir;
- if (!dir.mkpath(directory)) {
- *errorMessage = QString::fromLatin1("Could not create directory %1.").
- arg(QDir::toNativeSeparators(directory));
- return false;
+ if (!dryRun) {
+ QDir dir;
+ if (!dir.mkpath(directory)) {
+ *errorMessage = QString::fromLatin1("Could not create directory %1.")
+ .arg(QDir::toNativeSeparators(directory));
+ return false;
+ }
}
return true;
}
@@ -92,7 +94,7 @@ QStringList findSharedLibraries(const QDir &directory, Platform platform,
nameFilter += u'*';
if (debugMatchMode == MatchDebug && platformHasDebugSuffix(platform))
nameFilter += u'd';
- nameFilter += sharedLibrarySuffix(platform);
+ nameFilter += sharedLibrarySuffix();
QStringList result;
QString errorMessage;
const QFileInfoList &dlls = directory.entryInfoList(QStringList(nameFilter), QDir::Files);
@@ -116,22 +118,6 @@ QStringList findSharedLibraries(const QDir &directory, Platform platform,
}
#ifdef Q_OS_WIN
-QString winErrorMessage(unsigned long error)
-{
- QString rc = QString::fromLatin1("#%1: ").arg(error);
- char16_t *lpMsgBuf;
-
- const DWORD len = FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, error, 0, reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, NULL);
- if (len) {
- rc = QString::fromUtf16(lpMsgBuf, int(len));
- LocalFree(lpMsgBuf);
- } else {
- rc += QString::fromLatin1("<unknown error>");
- }
- return rc;
-}
// Case-Normalize file name via GetShortPathNameW()/GetLongPathNameW()
QString normalizeFileName(const QString &name)
@@ -263,8 +249,10 @@ bool runProcess(const QString &binary, const QStringList &args,
CloseHandle(si.hStdOutput);
if (stdErr)
CloseHandle(si.hStdError);
- if (errorMessage)
- *errorMessage = QStringLiteral("CreateProcessW failed: ") + winErrorMessage(GetLastError());
+ if (errorMessage) {
+ *errorMessage = QStringLiteral("CreateProcessW failed: ")
+ + QSystemError::windowsString();
+ }
return false;
}
@@ -373,7 +361,7 @@ bool runProcess(const QString &binary, const QStringList &args,
char **argv = new char *[args.size() + 2]; // Create argv.
char **ap = argv;
*ap++ = encodeFileName(binary);
- for (const QString &a : qAsConst(args))
+ for (const QString &a : std::as_const(args))
*ap++ = encodeFileName(a);
*ap = 0;
@@ -435,14 +423,18 @@ const char *qmakeInfixKey = "QT_INFIX";
QMap<QString, QString> queryQtPaths(const QString &qtpathsBinary, QString *errorMessage)
{
const QString binary = !qtpathsBinary.isEmpty() ? qtpathsBinary : QStringLiteral("qtpaths");
+ const QString colonSpace = QStringLiteral(": ");
QByteArray stdOut;
QByteArray stdErr;
unsigned long exitCode = 0;
- if (!runProcess(binary, QStringList(QStringLiteral("-query")), QString(), &exitCode, &stdOut, &stdErr, errorMessage))
+ if (!runProcess(binary, QStringList(QStringLiteral("-query")), QString(), &exitCode, &stdOut,
+ &stdErr, errorMessage)) {
+ *errorMessage = QStringLiteral("Error running binary ") + binary + colonSpace + *errorMessage;
return QMap<QString, QString>();
+ }
if (exitCode) {
*errorMessage = binary + QStringLiteral(" returns ") + QString::number(exitCode)
- + QStringLiteral(": ") + QString::fromLocal8Bit(stdErr);
+ + colonSpace + QString::fromLocal8Bit(stdErr);
return QMap<QString, QString>();
}
const QString output = QString::fromLocal8Bit(stdOut).trimmed().remove(u'\r');
@@ -478,7 +470,7 @@ QMap<QString, QString> queryQtPaths(const QString &qtpathsBinary, QString *error
}
} else {
std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(qconfigPriFile.fileName())
- << ": " << qconfigPriFile.errorString()<< '\n';
+ << colonSpace << qconfigPriFile.errorString()<< '\n';
}
return result;
}
@@ -564,37 +556,6 @@ bool updateFile(const QString &sourceFileName, const QStringList &nameFilters,
return true;
}
-bool readElfExecutable(const QString &elfExecutableFileName, QString *errorMessage,
- QStringList *dependentLibraries, unsigned *wordSize,
- bool *isDebug)
-{
- ElfReader elfReader(elfExecutableFileName);
- const ElfData data = elfReader.readHeaders();
- if (data.sectionHeaders.isEmpty()) {
- *errorMessage = QStringLiteral("Unable to read ELF binary \"")
- + QDir::toNativeSeparators(elfExecutableFileName) + QStringLiteral("\": ")
- + elfReader.errorString();
- return false;
- }
- if (wordSize)
- *wordSize = data.elfclass == Elf_ELFCLASS64 ? 64 : 32;
- if (dependentLibraries) {
- dependentLibraries->clear();
- const QList<QByteArray> libs = elfReader.dependencies();
- if (libs.isEmpty()) {
- *errorMessage = QStringLiteral("Unable to read dependenices of ELF binary \"")
- + QDir::toNativeSeparators(elfExecutableFileName) + QStringLiteral("\": ")
- + elfReader.errorString();
- return false;
- }
- for (const QByteArray &l : libs)
- dependentLibraries->push_back(QString::fromLocal8Bit(l));
- }
- if (isDebug)
- *isDebug = data.symbolsType != UnknownSymbols && data.symbolsType != NoSymbols;
- return true;
-}
-
#ifdef Q_OS_WIN
static inline QString stringFromRvaPtr(const void *rvaPtr)
@@ -716,13 +677,23 @@ static inline MsvcDebugRuntimeResult checkMsvcDebugRuntime(const QStringList &de
qsizetype pos = 0;
if (lib.startsWith("MSVCR"_L1, Qt::CaseInsensitive)
|| lib.startsWith("MSVCP"_L1, Qt::CaseInsensitive)
- || lib.startsWith("VCRUNTIME"_L1, Qt::CaseInsensitive)) {
+ || lib.startsWith("VCRUNTIME"_L1, Qt::CaseInsensitive)
+ || lib.startsWith("VCCORLIB"_L1, Qt::CaseInsensitive)
+ || lib.startsWith("CONCRT"_L1, Qt::CaseInsensitive)
+ || lib.startsWith("UCRTBASE"_L1, Qt::CaseInsensitive)) {
qsizetype lastDotPos = lib.lastIndexOf(u'.');
pos = -1 == lastDotPos ? 0 : lastDotPos - 1;
}
- if (pos > 0 && lib.contains("_app"_L1, Qt::CaseInsensitive))
- pos -= 4;
+ if (pos > 0) {
+ const auto removeExtraSuffix = [&lib, &pos](const QString &suffix) -> void {
+ if (lib.contains(suffix, Qt::CaseInsensitive))
+ pos -= suffix.size();
+ };
+ removeExtraSuffix("_app"_L1);
+ removeExtraSuffix("_atomic_wait"_L1);
+ removeExtraSuffix("_codecvt_ids"_L1);
+ }
if (pos)
return lib.at(pos).toLower() == u'd' ? MsvcDebugRuntime : MsvcReleaseRuntime;
@@ -731,28 +702,43 @@ static inline MsvcDebugRuntimeResult checkMsvcDebugRuntime(const QStringList &de
}
template <class ImageNtHeader>
+inline QStringList determineDependentLibs(const ImageNtHeader *nth, const void *fileMemory,
+ QString *errorMessage)
+{
+ return readImportSections(nth, fileMemory, errorMessage);
+}
+
+template <class ImageNtHeader>
+inline bool determineDebug(const ImageNtHeader *nth, const void *fileMemory,
+ QStringList *dependentLibrariesIn, QString *errorMessage)
+{
+ if (nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED)
+ return false;
+
+ const QStringList dependentLibraries = dependentLibrariesIn != nullptr ?
+ *dependentLibrariesIn :
+ determineDependentLibs(nth, fileMemory, errorMessage);
+
+ const bool hasDebugEntry = nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
+ // When an MSVC debug entry is present, check whether the debug runtime
+ // is actually used to detect -release / -force-debug-info builds.
+ const MsvcDebugRuntimeResult msvcrt = checkMsvcDebugRuntime(dependentLibraries);
+ if (msvcrt == NoMsvcRuntime)
+ return hasDebugEntry;
+ else
+ return hasDebugEntry && msvcrt == MsvcDebugRuntime;
+}
+
+template <class ImageNtHeader>
inline void determineDebugAndDependentLibs(const ImageNtHeader *nth, const void *fileMemory,
- bool isMinGW,
QStringList *dependentLibrariesIn,
bool *isDebugIn, QString *errorMessage)
{
- const bool hasDebugEntry = nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
- QStringList dependentLibraries;
- if (dependentLibrariesIn || (isDebugIn != nullptr && hasDebugEntry && !isMinGW))
- dependentLibraries = readImportSections(nth, fileMemory, errorMessage);
-
if (dependentLibrariesIn)
- *dependentLibrariesIn = dependentLibraries;
- if (isDebugIn != nullptr) {
- if (isMinGW) {
- // Use logic that's used e.g. in objdump / pfd library
- *isDebugIn = !(nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED);
- } else {
- // When an MSVC debug entry is present, check whether the debug runtime
- // is actually used to detect -release / -force-debug-info builds.
- *isDebugIn = hasDebugEntry && checkMsvcDebugRuntime(dependentLibraries) != MsvcReleaseRuntime;
- }
- }
+ *dependentLibrariesIn = determineDependentLibs(nth, fileMemory, errorMessage);
+
+ if (isDebugIn)
+ *isDebugIn = determineDebug(nth, fileMemory, dependentLibrariesIn, errorMessage);
}
// Read a PE executable and determine dependent libraries, word size
@@ -778,19 +764,22 @@ bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage
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()));
+ *errorMessage = QString::fromLatin1("Cannot open '%1': %2")
+ .arg(peExecutableFileName, QSystemError::windowsString());
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()));
+ *errorMessage = QString::fromLatin1("Cannot create file mapping of '%1': %2")
+ .arg(peExecutableFileName, QSystemError::windowsString());
break;
}
fileMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
if (!fileMemory) {
- *errorMessage = QString::fromLatin1("Cannot map '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
+ *errorMessage = QString::fromLatin1("Cannot map '%1': %2")
+ .arg(peExecutableFileName, QSystemError::windowsString());
break;
}
@@ -803,10 +792,10 @@ bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage
*wordSizeIn = wordSize;
if (wordSize == 32) {
determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS32 *>(ntHeaders),
- fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage);
+ fileMemory, dependentLibrariesIn, isDebugIn, errorMessage);
} else {
determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS64 *>(ntHeaders),
- fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage);
+ fileMemory, dependentLibrariesIn, isDebugIn, errorMessage);
}
if (machineArchIn)
@@ -868,7 +857,7 @@ QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wor
candidateVersions.append(prefix + QString::number(i) + suffix);
// Check the bin directory of the Qt SDK (in case it is shadowed by the
// Windows system directory in PATH).
- for (const QString &candidate : qAsConst(candidateVersions)) {
+ for (const QString &candidate : std::as_const(candidateVersions)) {
const QFileInfo fi(qtBinDir + u'/' + candidate);
if (fi.isFile())
return fi.absoluteFilePath();
@@ -877,7 +866,7 @@ QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wor
if (platform.testFlag(IntelBased)) {
QString errorMessage;
unsigned detectedWordSize;
- for (const QString &candidate : qAsConst(candidateVersions)) {
+ for (const QString &candidate : std::as_const(candidateVersions)) {
const QString dll = findInPath(candidate);
if (!dll.isEmpty()
&& readPeExecutable(dll, &errorMessage, 0, &detectedWordSize, 0)
@@ -889,6 +878,53 @@ QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wor
return QString();
}
+QStringList findDxc(Platform platform, const QString &qtBinDir, unsigned wordSize)
+{
+ QStringList results;
+ const QString kitDir = QString::fromLocal8Bit(qgetenv("WindowsSdkDir"));
+ const QString suffix = QLatin1StringView(windowsSharedLibrarySuffix);
+ for (QString prefix : { QStringLiteral("dxcompiler"), QStringLiteral("dxil") }) {
+ QString name = prefix + suffix;
+ if (!kitDir.isEmpty()) {
+ QString redistDirPath = QDir::cleanPath(kitDir) + QStringLiteral("/Redist/D3D/");
+ if (platform.testFlag(ArmBased)) {
+ redistDirPath += wordSize == 32 ? QStringLiteral("arm") : QStringLiteral("arm64");
+ } else {
+ redistDirPath += wordSize == 32 ? QStringLiteral("x86") : QStringLiteral("x64");
+ }
+ QDir redistDir(redistDirPath);
+ if (redistDir.exists()) {
+ const QFileInfoList files = redistDir.entryInfoList(QStringList(prefix + u'*' + suffix), QDir::Files);
+ if (!files.isEmpty()) {
+ results.append(files.front().absoluteFilePath());
+ continue;
+ }
+ }
+ }
+ // Check the bin directory of the Qt SDK (in case it is shadowed by the
+ // Windows system directory in PATH).
+ const QFileInfo fi(qtBinDir + u'/' + name);
+ if (fi.isFile()) {
+ results.append(fi.absoluteFilePath());
+ continue;
+ }
+ // Try to find it in the PATH (e.g. the Vulkan SDK ships these, even if Windows itself doesn't).
+ if (platform.testFlag(IntelBased)) {
+ QString errorMessage;
+ unsigned detectedWordSize;
+ const QString dll = findInPath(name);
+ if (!dll.isEmpty()
+ && readPeExecutable(dll, &errorMessage, 0, &detectedWordSize, 0)
+ && detectedWordSize == wordSize)
+ {
+ results.append(dll);
+ continue;
+ }
+ }
+ }
+ return results;
+}
+
#else // Q_OS_WIN
bool readPeExecutable(const QString &, QString *errorMessage,
@@ -903,6 +939,11 @@ QString findD3dCompiler(Platform, const QString &, unsigned)
return QString();
}
+QStringList findDxc(Platform, const QString &, unsigned)
+{
+ return QStringList();
+}
+
#endif // !Q_OS_WIN
// Search for "qt_prfxpath=xxxx" in \a path, and replace it with "qt_prfxpath=."
diff --git a/src/tools/windeployqt/utils.h b/src/tools/windeployqt/utils.h
index a9ee509d7a..fb3ba0b40b 100644
--- a/src/tools/windeployqt/utils.h
+++ b/src/tools/windeployqt/utils.h
@@ -20,7 +20,6 @@ QT_BEGIN_NAMESPACE
enum PlatformFlag {
// OS
WindowsBased = 0x00001,
- UnixBased = 0x00002,
// CPU
IntelBased = 0x00010,
ArmBased = 0x00020,
@@ -30,11 +29,12 @@ enum PlatformFlag {
ClangMsvc = 0x00400,
ClangMinGW = 0x00800,
// Platforms
- WindowsDesktopMsvc = WindowsBased + IntelBased + Msvc,
+ WindowsDesktopMsvc = WindowsBased + Msvc,
+ WindowsDesktopMsvcIntel = WindowsDesktopMsvc + IntelBased,
+ WindowsDesktopMsvcArm = WindowsDesktopMsvc + ArmBased,
WindowsDesktopMinGW = WindowsBased + IntelBased + MinGW,
WindowsDesktopClangMsvc = WindowsBased + IntelBased + ClangMsvc,
WindowsDesktopClangMinGW = WindowsBased + IntelBased + ClangMinGW,
- Unix = UnixBased,
UnknownPlatform
};
@@ -68,7 +68,7 @@ inline std::wostream &operator<<(std::wostream &str, const QString &s)
// Container class for JSON output
class JsonOutput
{
- using SourceTargetMapping = QPair<QString, QString>;
+ using SourceTargetMapping = std::pair<QString, QString>;
using SourceTargetMappings = QList<SourceTargetMapping>;
public:
@@ -137,13 +137,12 @@ inline QString normalizeFileName(const QString &name) { return name; }
#endif // !Q_OS_WIN
static const char windowsSharedLibrarySuffix[] = ".dll";
-static const char unixSharedLibrarySuffix[] = ".so";
-inline QString sharedLibrarySuffix(Platform platform) { return QLatin1StringView((platform & WindowsBased) ? windowsSharedLibrarySuffix : unixSharedLibrarySuffix); }
+inline QString sharedLibrarySuffix() { return QLatin1StringView(windowsSharedLibrarySuffix); }
bool isBuildDirectory(Platform platform, const QString &dirName);
bool createSymbolicLink(const QFileInfo &source, const QString &target, QString *errorMessage);
-bool createDirectory(const QString &directory, QString *errorMessage);
+bool createDirectory(const QString &directory, QString *errorMessage, bool dryRun);
QString findInPath(const QString &file);
extern const char *qmakeInfixKey; // Fake key containing the libinfix
@@ -170,19 +169,6 @@ bool runProcess(const QString &binary, const QStringList &args,
bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage,
QStringList *dependentLibraries = 0, unsigned *wordSize = 0,
bool *isDebug = 0, bool isMinGW = false, unsigned short *machineArch = nullptr);
-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, unsigned short *machineArch = nullptr)
-{
- return platform == Unix ?
- readElfExecutable(executableFileName, errorMessage, dependentLibraries, wordSize, isDebug) :
- readPeExecutable(executableFileName, errorMessage, dependentLibraries, wordSize, isDebug,
- (platform == WindowsDesktopMinGW), machineArch);
-}
#ifdef Q_OS_WIN
# if !defined(IMAGE_FILE_MACHINE_ARM64)
@@ -193,14 +179,15 @@ QString getArchString (unsigned short machineArch);
// Return dependent modules of executable files.
-inline QStringList findDependentLibraries(const QString &executableFileName, Platform platform, QString *errorMessage)
+inline QStringList findDependentLibraries(const QString &executableFileName, QString *errorMessage)
{
QStringList result;
- readExecutable(executableFileName, platform, errorMessage, &result);
+ readPeExecutable(executableFileName, errorMessage, &result);
return result;
}
QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wordSize);
+QStringList findDxc(Platform platform, const QString &qtBinDir, unsigned wordSize);
bool patchQtCore(const QString &path, QString *errorMessage);